zoukankan      html  css  js  c++  java
  • 微信支付04

    还是接上文:订单查询 、我们获取到订单详情之后、接下来就要开始  退款接口了、在我们调运退款接口的时候、我们可以先调运订单查询接口、看订单状态是否正常或者订单是否存在,如果订单存在并且订单状态正常、那我们就可以调运退款借口了。

    如下
     
         退款API

    业务功能

    商户针对某一个已经成功支付的订单发起退款,操作结果在同一会话中同步返回。

    一、退款方式

    目前只支持原路返回退款

    说明:退到银行卡则是非实时的,每个银行的处理速度不同,一般发起退款后1-3个工作日内到账。

    同一笔单的部分退款需要设置相同的订单号和不同的 out_refund_no 。一笔退款失败后重新提交,要采用原来 的out_refund_no。总退款金额不能超过用户实际支付金额(现金券金额不能退款)

    二、退款限制

    商户在退款操作时应该注意退款限制,避免发起不会成功的退款请求,下面是主要的退款限制:

    1.在平台中,只要退款累计金额不超过交易单支付总额,一笔交易单可以多次退款,退款申请单号(退款接口中有此参数)唯一确定一次退       款,而不是交易单号确定一次退款。退款申请单号由商户生成,所以商户一定要保证退款申请单的唯一性。商家在退款过程中要特别注 意,只有在能确定退款失败的情况下,才能重新发起另一笔退款。一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号。

    2.请求频率限制:150qps,即每秒钟正常的申请退款请求次数不超过150次。

    3.错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次。

    4.每个支付订单的部分退款次数不能超过50次。

    交互模式

    后台系统调用交互模式

    请求参数列表

    请求url:https://pay.swiftpass.cn/pay/gateway

    POST XML 内容体进行请求

    字段名 变量名 必填 类型 说明
    接口类型 service String(32) 接口类型:unified.trade.refund
    版本号 version String(8) 版本号,version默认值是1.0。
    字符集 charset String(8) 可选值 UTF-8 ,默认为 UTF-8。
    签名方式 sign_type String(8) 签名类型,取值:MD5默认:MD5
    商户号 mch_id String(32) 商户号,由平台分配
    商户订单号 out_trade_no String(32) 商户系统内部的订单号, out_trade_no和transaction_id至少一个必填,同时存在时transaction_id优先
    平台订单号 transaction_id String(32) 平台单号, out_trade_no和transaction_id至少一个必填,同时存在时transaction_id优先
    商户退款单号 out_refund_no String(32) 商户退款单号,32个字符内、可包含字母,确保在商户系统唯一。同个退款单号多次请求,平台当一个单处理,只会退一次款。如果出现退款不成功,请采用原退款单号重新发起,避免出现重复退款。
    总金额 total_fee Int 订单总金额,单位为分
    退款金额 refund_fee Int 退款总金额,单位为分,可以做部分退款
    操作员 op_user_id String(32) 操作员帐号,默认为商户号
    退款渠道 refund_channel String(16) ORIGINAL-原路退款,默认
    随机字符串 nonce_str String(32) 随机字符串,不长于 32 位
    签名 sign String(32) MD5签名结果,详见“安全规范”

    返回结果

    数据按XML的格式实时返回

    字段名 变量名 必填 类型 说明
    版本号 version String(8) 版本号,version默认值是2.0。
    字符集 charset String(8) 可选值 UTF-8 ,默认为 UTF-8。
    签名方式 sign_type String(8) 签名类型,取值:MD5默认:MD5
    返回状态码 status String(16) 0表示成功,非0表示失败此字段是通信标识,非交易标识,交易是否成功需要查看 result_code 来判断
    返回信息 message String(128) 异常或错误时返回信息,具体描述请看文档最后返回信息列表
    以下字段在 status 为 0的时候有返回
    业务结果 result_code String(16) 0表示成功,非0表示失败
    注:此处返回0表示退款申请接收成功,实际的退款结果根据退款查询接口查询
    商户号 mch_id String(32) 商户号,由平台分配
    设备号 device_info String(32) 平台分配的终端设备号
    随机字符串 nonce_str String(32) 随机字符串,不长于 32 位
    错误代码 err_code String(32) 具体错误码请看文档最后错误码列表
    签名 sign String(32) MD5签名结果,详见“安全规范”
    以下字段在 status 和 result_code 都为 0的时候有返回
    平台订单号 transaction_id String(32) 平台订单号。
    商户订单号 out_trade_no String(32) 商户系统内部的订单号
    商户退款单号 out_refund_no String(32) 商户退款单号
    平台退款单号 refund_id String(32) 平台退款单号
    退款渠道 refund_channel String(16) ORIGINAL—原路退款,默认
    退款金额 refund_fee Int 退款总金额,单位为分,可以做部分退款
    现金券退款金额 coupon_refund_fee Int 现金券退款金额 <= 退款金额, 退款金额-现金券退款金额为现金

     
     
    由文档我们可以获取到我们需要传的参数和返回的参数、所以、最重要的、、突然想到、、要有一个自己的商户才好啊 ,,,,  不闲扯了
    注意:文档中的必传参数
    下面我们看demo
     1  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     2         req.setCharacterEncoding("utf-8");
     3         resp.setCharacterEncoding("utf-8");
     4         
     5         SortedMap<String,String> map = XmlUtils.getParameterMap(req);
     6         System.out.println(XmlUtils.toXml(map));
     7         String key = SwiftpassConfig.key;
     8         String res = null;
     9         String reqUrl = SwiftpassConfig.req_url;
    10         map.put("mch_id", SwiftpassConfig.mch_id);
    11         map.put("op_user_id", SwiftpassConfig.mch_id);
    12         map.put("nonce_str", String.valueOf(new Date().getTime()));
    13         
    14         Map<String,String> params = SignUtils.paraFilter(map);
    15         StringBuilder buf = new StringBuilder((params.size() +1) * 10);
    16         SignUtils.buildPayParams(buf,params,false);
    17         String preStr = buf.toString();
    18         String sign = MD5.sign(preStr, "&key=" + key, "utf-8");
    19         map.put("sign", sign);
    20         
    21         System.out.println("reqUrl:" + reqUrl);
    22         
    23         CloseableHttpResponse response = null;
    24         CloseableHttpClient client = null;
    25         try {
    26             HttpPost httpPost = new HttpPost(reqUrl);
    27             StringEntity entityParams = new StringEntity(XmlUtils.parseXML(map),"utf-8");
    28             httpPost.setEntity(entityParams);
    29             httpPost.setHeader("Content-Type", "text/xml;charset=ISO-8859-1");
    30             client = HttpClients.createDefault();
    31             response = client.execute(httpPost);
    32             if(response != null && response.getEntity() != null){
    33                 Map<String,String> resultMap = XmlUtils.toMap(EntityUtils.toByteArray(response.getEntity()), "utf-8");
    34                 res = XmlUtils.toXml(resultMap);
    35                 System.out.println("请求结果:" + res);
    36                 
    37                 if(resultMap.containsKey("sign") && !SignUtils.checkParam(resultMap, key)){
    38                     res = "验证签名不通过";
    39                 }
    40             }else{
    41                 res = "操作失败!";
    42             }
    43         } catch (Exception e) {
    44             e.printStackTrace();
    45             res = "操作失败";
    46         } finally {
    47             if(response != null){
    48                 response.close();
    49             }
    50             if(client != null){
    51                 client.close();
    52             }
    53         }
    54         if(res.startsWith("<")){
    55             resp.setHeader("Content-type", "text/xml;charset=UTF-8");
    56         }else{
    57             resp.setHeader("Content-type", "text/html;charset=UTF-8");
    58         }
    59         resp.getWriter().write(res);
    60     }

     demo中直接是把返回结果打印在了页面、response掉了、所以、我们需要拿到那个    resultMap  这里面就是返回的各种数据 --  上面文档中的返回数据

    注意:1. 第5行的那个map 、那里面已经从req里面获取到了很多参数、比如service之类的、如果我们要开发的话、我们就要把那儿改一下、下面会讲到:

    现在是我们本地的demo:

     1 public Pair<Boolean, Map<String,String>> doOrderRefund(Payparams paramss) throws Exception {
     2         boolean result = false;
     3         Map<String,String> resultMap = new HashMap<String, String>();
     4         HashMap<String, String> rspData = new HashMap<String,String>();
     5         logger.info("---退款开始---");
     6         String orderId = paramss.getOrderId();
     7         String merId = paramss.getMerId();
     8         String merOrderId = paramss.getMerOrderId();
     9         String txnAmt = paramss.getTxnAmt();
    10         String refundOrderId = "TK" + orderId;  //退款单号 = TK + 订单号
    11         
    12         //组织请求报文
    13         SortedMap<String,String> map = new TreeMap<String, String>();  //  标志02  下面解释
    14         System.out.println(XmlUtils.toXml(map));
    15         String key = SwiftpassConfig.key;
    16         String res = null;
    17         String reqUrl = SwiftpassConfig.req_url;
    18         map.put("out_trade_no",orderId); //订单号
    19         map.put("out_refund_no", refundOrderId); //商户退单号
    20         map.put("service", "unified.trade.refund"); //接口类型   
    21         map.put("mch_id", merId);
    22         map.put("op_user_id", merId);
    23         map.put("total_fee", txnAmt); //int 总金额  单位是分
    24         map.put("refund_fee", txnAmt);  //int  退款金额  单位是分
    25         
    26         map.put("nonce_str", String.valueOf(new Date().getTime())); //随机字符串
    27         
    28         Map<String,String> params = SignUtils.paraFilter(map);
    29         StringBuilder buf = new StringBuilder((params.size() +1) * 10);
    30         SignUtils.buildPayParams(buf,params,false);
    31         String preStr = buf.toString();
    32         String sign = MD5.sign(preStr, "&key=" + key, "utf-8");
    33         map.put("sign", sign);
    34         
    35         logger.info("reqUrl:" + reqUrl);
    36         
    37         CloseableHttpResponse response = null;
    38         CloseableHttpClient client = null;
    39         try {
    40             HttpPost httpPost = new HttpPost(reqUrl);
    41             StringEntity entityParams = new StringEntity(XmlUtils.parseXML(map),"utf-8");
    42             httpPost.setEntity(entityParams);
    43             httpPost.setHeader("Content-Type", "text/xml;charset=ISO-8859-1");
    44             client = HttpClients.createDefault();
    45             response = client.execute(httpPost);
    46             if(response != null && response.getEntity() != null){
    47                 resultMap = XmlUtils.toMap(EntityUtils.toByteArray(response.getEntity()), "utf-8");
    48                 res = XmlUtils.toXml(resultMap);
    49                 System.out.println("请求结果:" + res);
    50                 
    51                 if(resultMap.containsKey("sign") && !SignUtils.checkParam(resultMap, key)){
    52                     res = "验证签名不通过";
    53                 }
    54             }else{
    55                 res = "操作失败!";
    56             }
    57         } catch (Exception e) {
    58             e.printStackTrace();
    59             res = "操作失败";
    60         } finally {
    61             if(response != null){
    62                 response.close();
    63             }
    64             if(client != null){
    65                 client.close();
    66             }
    67         }
    68         
    69         //转换成json对象
    70         JSONObject respJson = JSONObject.fromObject(resultMap); //标志 03 下面解释
    71         Assert.notNull(respJson, "微信退款返回信息解析 json 字串为空");
    72         //解析json
    73         Assert.notNull(respJson.get("result_code"), "微信退款返回码异常");
    74         String respCode = respJson.getString("result_code");
    75         
    76         PayRes payRes = PayReturnCode.getPayRes(Contents.WX_CODE, respCode);
    77         logger.info("退款结果,返回码:" + respCode + ", 退款信息:" + payRes.getMsg());
    78         
    79         if(respCode.equals("SUCCESS")){
    80             result=true;
    81         }
    82         
    91         return new Pair<Boolean, Map<String,String>>(result, rspData);
    92     }
     
    注释:标识02 : 因为在demo示例中那个sortMap是将request的参数排序取出来了、所以在这里我们没有request的情况下、我们就new一个TreeMap(); 然后将下面红色的那部分参数传进来、放到map里面、请求微信的接口
                标识03 : 我们获取到的是一个map<String , String> 类型的map、而微信的整个参数都在这个map中、我们在这里把map转换为json之后、就方便了我们下面对数据的处理。
     
     
     
    今天在开发的时候、微信测试的商家都超过测试限额了、、、尴尬的啊、明天再说吧。
     
     
     
     
     
     
     
     
  • 相关阅读:
    ios 开发证书制作
    iOS UILable 高度自适
    asp 中创建日志打印文件夹
    ios iphone、ipad启动画面尺寸
    ios 更改UITableview中Section的字体颜色
    Azure Blob 存储简介
    java追加文件
    java读取文件
    DNS原理及其解析过程
    单点登录原理与简单实现
  • 原文地址:https://www.cnblogs.com/mzlb520/p/9549580.html
Copyright © 2011-2022 走看看