网站照片如何处理,商务网站开发开题报告,做网站费用需要分摊吗,网站淘客怎么做本文是【浅析微信支付】系列文章的第八篇#xff0c;主要讲解商户如何处理微信申请退款、退款回调、查询退款接口#xff0c;其中有一些坑的地方#xff0c;会着重强调。 浅析微信支付系列已经更新七篇了哟#xff5e;#xff0c;没有看过的朋友们可以看一下哦。 浅析微信… 本文是【浅析微信支付】系列文章的第八篇主要讲解商户如何处理微信申请退款、退款回调、查询退款接口其中有一些坑的地方会着重强调。 浅析微信支付系列已经更新七篇了哟没有看过的朋友们可以看一下哦。 浅析微信支付查询订单和关闭订单 浅析微信支付支付结果通知 浅析微信支付统一下单接口 在实际场景中申请退款和退款回调接口是比较常用到的微信支付接口这里我们会讲原路返回方式的退款还有的是使用直接为用户付款到零钱、现金红包等方式来退款此种情况主要会出现在客服退款时不是全部退款的情况也有的会出现在使用了微信代金券-单品券的时候因为单品券不能部分退款所以只能走企业付款用户的方式以下我们主要讲原路返回退款。 PS原路返回的意思就是从你支付时的关联支付单中扣款微信会记录相关数据可以在客户端通知中展示。 1、申请退款接口 以下为微信官方的申请退款文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter9_4 1.1. 应用场景 当交易发生之后一段时间内由于买家或者卖家的原因需要退款时卖家可以通过退款接口将支付款退还给买家微信支付将在收到退款请求并且验证成功之后按照退款规则将支付款按原路退到买家帐号上。 注意
1、交易时间超过一年的订单无法提交退款
2、微信支付退款支持单笔交易分多次退款多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交请不要更换退款单号请使用原商户退款单号
3、请求频率限制150qps即每秒钟正常的申请退款请求次数不超过150次
错误或无效请求频率限制6qps即每秒钟异常或错误的退款申请请求不超过6次
4、每个支付订单的部分退款次数不能超过50次 PS以上限制一般情况下不会出现但我们也必须写入系统异常场景处理中请求频率可以使用队列或增加延迟等方式来处理部分退款此时不要超过微信的限制。 1.2. 接口链接 https://api.mch.weixin.qq.com/secapi/pay/refund 1.3. 是否需要证书 请求需要双向证书。 PS关于微信证书可以在 [商户平台-账户中心-API安全] 去下载此证书很多支付接口均需要使用请将证书地址配置为常量具体实现可以参考作者github源码。 1.4. 调用接口 先看源码如下 /*** [微信退款接口] - 保存调用的相关记录* param refundPayment 退款订单的支付记录* param tradePayment 历史付款单* return map* throws Exception e** author yclimb* date 2018/6/21*/
public MapString,String saveWxPayRefund(Payment refundPayment, Payment tradePayment) throws Exception {if (refundPayment null || tradePayment null) {return null;}// 微信订单号/商户订单号必须传入其中一个此处默认传入商户订单号// 微信订单号微信生成的订单号在支付通知中有返回// String transaction_id null;// 商户订单号商户系统内部订单号要求32个字符内只能是数字、大小写字母_-|* 且在同一个商户号下唯一。String out_trade_no tradePayment.getFlowNumer();// 商户退款单号商户系统内部的退款单号商户系统内部唯一只能是数字、大小写字母_-|* 同一退款单号多次请求只退一笔。String out_refund_no refundPayment.getFlowNumer();// 订单总金额传入参数单位为元String total_fee String.valueOf(tradePayment.getAmount());// 退款总金额订单总金额传入参数单位为元String refund_fee String.valueOf(refundPayment.getAmount());// 退款原因若商户传入会在下发给用户的退款消息中体现退款原因String refund_desc refundPayment.getBody();// 微信支付对象WXPay wxPay new WXPay(WXPayConfigImpl.getInstance());// 微信退款接口MapString, String resultMap wxPay.refund(refundUrl, null, out_trade_no, out_refund_no, total_fee, refund_fee, refund_desc);logger.info(saveWxPayRefund:resultMap: resultMap.toString());// 记录付款流水// 下单失败进行处理if (WXPayConstants.FAIL.equals(resultMap.get(WXPayConstants.RETURN_CODE)) ||WXPayConstants.FAIL.equals(resultMap.get(WXPayConstants.RESULT_CODE))) {// 处理结果返回无需继续执行resultMap.put(WXPayConstants.RESULT_CODE, WXPayConstants.FAIL);resultMap.put(WXPayConstants.ERR_CODE_DES, resultMap.get(WXPayConstants.RETURN_MSG));return resultMap;}return resultMap;
} 以上为sdk退款调用示例代码有几个参数需要我们注意 字段名变量名必填类型描述微信订单号transaction_id是String(32)微信生成的订单号在支付通知中有返回商户订单号out_trade_no是String(32)商户系统内部订单号要求32个字符内只能是数字、大小写字母_-商户退款单号out_refund_no是String(64)商户系统内部的退款单号商户系统内部唯一只能是数字、大小写字母_-退款金额refund_fee是Int退款总金额订单总金额单位为分只能为整数退款结果通知urlnotify_url否String(256)异步接收微信支付退款结果通知的回调地址通知URL必须为外网可访问的url不允许带参数如果参数中传了notify_url则商户平台上配置的回调地址将不会生效。PS推荐以上的参数都必填notify_url参数可配置为环境常量根据环境的不同配置调用不会的回调地址。 下面为具体的实际sdkwxPay.refund调用代码 /*** 作用申请退款br* 场景当交易发生之后一段时间内由于买家或者卖家的原因需要退款时卖家可以通过退款接口将支付款退还给买家* 微信支付将在收到退款请求并且验证成功之后按照退款规则将支付款按原路退到买家帐号上。* 接口文档地址https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter9_4** param notify_url 回调地址* param transaction_id 微信生成的订单号在支付通知中有返回* param out_trade_no 商户系统内部订单号要求32个字符内只能是数字、大小写字母_-|* 且在同一个商户号下唯一。* param out_refund_no 商户系统内部的退款单号商户系统内部唯一只能是数字、大小写字母_-|* 同一退款单号多次请求只退一笔。* param total_fee 订单总金额传入参数单位为元* param refund_fee 退款总金额订单总金额传入参数单位为元* param refund_desc 退款原因若商户传入会在下发给用户的退款消息中体现退款原因* return API返回数据* throws Exception e*/
public MapString, String refund(String notify_url, String transaction_id, String out_trade_no, String out_refund_no,String total_fee, String refund_fee, String refund_desc) throws Exception {/** 构造请求参数数据 **/MapString, String data new HashMap();// 变量名 字段名 必填 类型 示例值 描述// 微信订单号 二选一 String(32) 1.21775E27 微信生成的订单号在支付通知中有返回if (transaction_id ! null) {data.put(transaction_id, transaction_id);}// 商户订单号 String(32) 1.21775E27 商户系统内部订单号要求32个字符内只能是数字、大小写字母_-|* 且在同一个商户号下唯一。data.put(out_trade_no, out_trade_no);// 商户退款单号 是 String(64) 1.21775E27 商户系统内部的退款单号商户系统内部唯一只能是数字、大小写字母_-|* 同一退款单号多次请求只退一笔。data.put(out_refund_no, out_refund_no);// 订单金额 是 Int 100 订单总金额单位为分只能为整数详见支付金额data.put(total_fee, String.valueOf(new BigDecimal(total_fee).multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue()));// 退款金额 是 Int 100 退款总金额订单总金额单位为分只能为整数详见支付金额// 默认单位为分系统是元所以需要*100data.put(refund_fee, String.valueOf(new BigDecimal(refund_fee).multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue()));// 退款原因 否 String(80) 商品已售完 若商户传入会在下发给用户的退款消息中体现退款原因data.put(refund_desc, refund_desc);// 货币种类 否 String(8) CNY 货币类型符合ISO 4217标准的三位字母代码默认人民币CNY其他值列表详见货币类型data.put(refund_fee_type, WXPayConstants.FEE_TYPE_CNY);// 退款结果通知url 否 String(256) https://weixin.qq.com/notify/ 异步接收微信支付退款结果通知的回调地址通知URL必须为外网可访问的url不允许带参数,如果参数中传了notify_url则商户平台上配置的回调地址将不会生效。data.put(notify_url, notify_url);/** 以下参数为非必填参数 **/// 退款资金来源 否 String(30) REFUND_SOURCE_RECHARGE_FUNDS 仅针对老资金流商户使用;REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款默认使用未结算资金退款;REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款// data.put(refund_account, null);/** 以下五个参数在 this.fillRequestData 方法中会自动赋值 **//*// 小程序ID appid 是 String(32) wxd678efh567hg6787 微信分配的小程序IDdata.put(appid, WXPayConstants.APP_ID);// 商户号 mch_id 是 String(32) 1230000109 微信支付分配的商户号data.put(mch_id, WXPayConstants.MCH_ID);// 随机字符串 nonce_str 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串长度要求在32位以内。推荐随机数生成算法data.put(nonce_str, nonce_str);// 签名类型 sign_type 否 String(32) MD5 签名类型默认为MD5支持HMAC-SHA256和MD5。data.put(sign_type, WXPayConstants.MD5);// 签名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 通过签名算法计算得出的签名值详见签名生成算法data.put(sign, sign);*/// 微信退款接口MapString, String resultMap this.refund(data);WXPayUtil.getLogger().info(wxPay.refund: resultMap);return resultMap;
} 以上已经详细说明的具体的字段含义有不明白的同学可以查看微信的官方文档具体的源码可以查看作者的github。 这里有一个比较需要注意的点在我们调用退款之后会返回一些异常处理情况官方文档中收录了一系列错误码code我们可以在系统中对其进行处理这里就不细说了。 2、退款回调接口 以下为微信官方的退款结果通知文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter9_16index10 2.1. 应用场景 当商户申请的退款有结果后微信会把相关结果发送给商户商户需要接收处理并返回应答。 对后台通知交互时如果微信收到商户的应答不是成功或超时微信认为通知失败微信会通过一定的策略定期重新发起通知尽可能提高通知的成功率但微信不保证通知最终能成功。 通知频率为15/15/30/180/1800/1800/1800/1800/3600单位秒 注意同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是当收到通知进行处理时首先检查对应业务数据的状态判断该通知是否已经处理过如果没有处理过再进行处理如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前要采用数据锁进行并发控制以避免函数重入造成的数据混乱。 特别说明退款结果对重要的数据进行了加密商户需要用商户秘钥进行解密后才能获得结果通知的内容 2.2. 接口链接 在申请退款接口中上传参数“notify_url”以开通该功能 如果链接无法访问商户将无法接收到微信通知。 通知url必须为直接可访问的url不能携带参数。 示例notify_url“https://pay.weixin.qq.com/wxpay/pay.action” 2.3. 解密方式 解密步骤如下
1对加密串A做base64解码得到加密串B
2对商户key做md5得到32位小写key* ( key设置路径微信商户平台--账户设置--API安全--密钥设置 )
3用key*对加密串B做AES-256-ECB解密PKCS7Padding PS特别注意如果要进行微信AES解密因为GJ的进口管制限制Java发布的运行环境包中的加解密有一定的限制。默认不允许256位密钥的AES加解密解决方法就是修改策略文件我们需要从官方网站下载无限制权限策略文件注意自己JDK的版本别下错了。 jdk8的jce下载地址https://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html 将local_policy.jar和US_export_policy.jar这两个文件替换%JRE_HOME%\lib\security和%JDK_HOME%\jre\lib\security下原来的文件注意先备份原文件。 如果是jdk8可能会遇到安全目录下有policy文件夹的情况拿作者的电脑举例jdk路径为/opt/jdk1.8.0_152/jre/lib/security/policy此目录下有两个子文件夹limited、limited需要替换limited文件夹下的文件 local_policy.jar、US_export_policy.jar这两个最好先备份哦替换后重启项目即可。 2.4. 调用接口 因为退款回调接口是咋们系统被动接收微信的消息所以此处和支付回调接口一致也是使用了流的方式格式为xml下面我们来看代码
/*** 退款结果通知* p* 在申请退款接口中上传参数“notify_url”以开通该功能* 如果链接无法访问商户将无法接收到微信通知。* 通知url必须为直接可访问的url不能携带参数。示例notify_url“https://pay.weixin.qq.com/wxpay/pay.action”* p* 当商户申请的退款有结果后微信会把相关结果发送给商户商户需要接收处理并返回应答。* 对后台通知交互时如果微信收到商户的应答不是成功或超时微信认为通知失败微信会通过一定的策略定期重新发起通知尽可能提高通知的成功率但微信不保证通知最终能成功。* 通知频率为15/15/30/180/1800/1800/1800/1800/3600单位秒* 注意同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。* 推荐的做法是当收到通知进行处理时首先检查对应业务数据的状态判断该通知是否已经处理过如果没有处理过再进行处理如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前要采用数据锁进行并发控制以避免函数重入造成的数据混乱。* 特别说明退款结果对重要的数据进行了加密商户需要用商户秘钥进行解密后才能获得结果通知的内容* param request req* param response resp* return res xml** author yclimb* date 2018/6/21*/
ApiOperation(value 微信支付|微信退款回调接口, httpMethod POST, notes 该链接是通过【微信退款API】中提交的参数notify_url设置如果参数中传了notify_url则商户平台上配置的回调地址将不会生效。)
RequestMapping(/refund)
public void refund(HttpServletRequest request, HttpServletResponse response) {String resXml ;InputStream inStream;try {inStream request.getInputStream();ByteArrayOutputStream outSteam new ByteArrayOutputStream();byte[] buffer new byte[1024];int len 0;while ((len inStream.read(buffer)) ! -1) {outSteam.write(buffer, 0, len);}WXPayUtil.getLogger().info(refund:微信退款----start----);// 获取微信调用我们notify_url的返回信息String result new String(outSteam.toByteArray(), utf-8);WXPayUtil.getLogger().info(refund:微信退款----result---- result);// 关闭流outSteam.close();inStream.close();// xml转换为mapMapString, String map WXPayUtil.xmlToMap(result);if (WXPayConstants.SUCCESS.equalsIgnoreCase(map.get(WXPayConstants.RETURN_CODE))) {WXPayUtil.getLogger().info(refund:微信退款----返回成功);/** 以下字段在return_code为SUCCESS的时候有返回 **/// 加密信息加密信息请用商户秘钥进行解密详见解密方式String req_info map.get(req_info);/*** 解密方式* 解密步骤如下* 1对加密串A做base64解码得到加密串B* 2对商户key做md5得到32位小写key* ( key设置路径微信商户平台(pay.weixin.qq.com)--账户设置--API安全--密钥设置 )* 3用key*对加密串B做AES-256-ECB解密PKCS7Padding*/String resultStr AESUtil.decryptData(req_info);// WXPayUtil.getLogger().info(refund:解密后的字符串: resultStr);MapString, String aesMap WXPayUtil.xmlToMap(resultStr);/** 以下为返回的加密字段 **/// 商户退款单号 是 String(64) 1.21775E27 商户退款单号String out_refund_no aesMap.get(out_refund_no);// 退款状态 是 String(16) SUCCESS SUCCESS-退款成功、CHANGE-退款异常、REFUNDCLOSE—退款关闭String refund_status aesMap.get(refund_status);// 商户订单号 是 String(32) 1.21775E27 商户系统内部的订单号String out_trade_no aesMap.get(out_trade_no);/*// 微信订单号 是 String(32) 1.21775E27 微信订单号String transaction_id null;// 微信退款单号 是 String(32) 1.21775E27 微信退款单号String refund_id null;// 订单金额 是 Int 100 订单总金额单位为分只能为整数详见支付金额String total_fee null;// 应结订单金额 否 Int 100 当该订单有使用非充值券时返回此字段。应结订单金额订单金额-非充值代金券金额应结订单金额订单金额。String settlement_total_fee null;// 申请退款金额 是 Int 100 退款总金额,单位为分String refund_fee null;// 退款金额 是 Int 100 退款金额申请退款金额-非充值代金券退款金额退款金额申请退款金额String settlement_refund_fee null;*/// 退款是否成功if (!WXPayConstants.SUCCESS.equals(refund_status)) {resXml resFailXml;} else {// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.resXml resSuccessXml;isSuccess true;}// 根据付款单号查询付款记录 out_refund_no// 付款记录修改 记录付款日志if (payment ! null) {WXPayUtil.getLogger().error(refund:微信支付回调修改支付单);} else {WXPayUtil.getLogger().error(refund:微信支付回调找不到对应的支付单);}} else {WXPayUtil.getLogger().error(refund:支付失败,错误信息 map.get(WXPayConstants.RETURN_MSG));resXml resFailXml;}} catch (Exception e) {WXPayUtil.getLogger().error(refund:微信退款回调发布异常, e);} finally {try {// 处理业务完毕BufferedOutputStream out new BufferedOutputStream(response.getOutputStream());out.write(resXml.getBytes());out.flush();out.close();} catch (IOException e) {WXPayUtil.getLogger().error(refund:微信退款回调发布异常:out, e);}}
}以上代码详细解释了如何接收微信回调数据和解码数据具体的AESUtil.decryptData(req_info)请参考作者源码文末有地址这里就不细讲了。 具体的退款接收参数请参考微信官方文档需要注意的是商户退款单号和微信退款单号此两个参数是修改和记录退款的必要凭证。 3、查询退款 以下为微信官方的查询退款文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter9_5 3.1. 应用场景 提交退款申请后通过调用该接口查询退款状态。退款有一定延时用零钱支付的退款20分钟内到账银行卡支付的退款3个工作日后重新查询退款状态。 注意如果单个支付订单部分退款次数超过20次请使用退款单号查询 3.2. 接口链接 https://api.mch.weixin.qq.com/pay/refundquery 3.3. 是否需要证书 不需要 3.4. 调用接口 注意当一个订单部分退款超过10笔后商户用微信订单号或商户订单号调退款查询API查询退款时默认返回前10笔和total_refund_count订单总退款次数。商户需要查询同一订单下超过10笔的退款单时可传入订单号及offset来查询微信支付会返回offset及后面的10笔以此类推。当商户传入的offset超过total_refund_count则系统会返回报错PARAM_ERROR。 举例 一笔订单下的退款单有36笔当商户想查询第25笔时可传入订单号及offset24微信支付平台会返回第25笔到第35笔的退款单信息或商户可直接传入退款单号查询退款 以下为调用方式 private void doRefundQuery() {// 四选一微信订单号查询的优先级是 refund_id out_refund_no transaction_id out_trade_noHashMapString, String data new HashMapString, String();// 商户订单号data.put(out_trade_no, out_trade_no);// 微信订单号data.put(transaction_id, out_trade_no);// 商户退款单号 data.put(out_refund_no, out_trade_no);// 微信退款单号 data.put(refund_id, out_trade_no);try {MapString, String r wxpay.refundQuery(data);System.out.println(r);} catch (Exception e) {e.printStackTrace();}
} PS微信订单号查询的优先级是 refund_id out_refund_no transaction_id out_trade_no 需要注意的是查询退款时需要注意退款返回的错误码如果出现错误需要及时同步商户系统中的退款数据。 结语 以上为申请退款、退款回调接口、查询退款相关的解释和源码特别需要注意的是接收退款时的解密方式和替换安全文件小伙伴们一定要注意哦具体的源码可以看作者的github里面对每个方法有详细的注释。 预告下一篇文章 下载对账单和资金账单敬请期待 如果想要提前一览源码的小伙伴可以先看看我的 github地址如下 https://github.com/YClimb/wxpay-sdk/blob/master/README.md 加作者私人微信作者微信号如下 yclimb标明 微信支付 可拉入微信支付讨论群与小伙伴一起探讨哦一定要标明 微信支付 哦 到此本文就结束了关注公众号查看更多推送 转载于:https://www.cnblogs.com/yclimb/p/9934046.html