文章目录

  • 合单支付
    • 预支付
    • 调起支付
  • 支付回调通知
  • 查询合单订单
  • 参考链接

合单支付

合单支付是指可以在一个订单中包含多个商家的多个商品,一次性支付。

  • 关于JSAPI、APP、小程序等支付类型的区别:https://pay.weixin.qq.com/wiki/doc/api/sl.html
    我们是在小程序中嵌入支付功能的,最开始用的是APP支付一直提示“支付场景非法”,换成JSAPI支付后才成功调出支付确认框并成功支付。

  • 如果是公众号支付、扫码支付和H5支付还需要配置支付域名回调链接,请参考:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3

  • 在申请开通电商收付通时如果选择的是平台代扣手续费,那么建议在正式支付分账前向手续费账户充值至少100块钱,否则可能会导致分账失败;

  • 回调通知地址必须为域名并且为https协议,微信会以POST方式访问URL,以json格式通过body携带数据;

预支付

此处的测试方法只设置了一个子单,即只在一家店铺下了一个商品的订单;

    /*** 预支付*/@Testpublic void prepay() {JSONObject reqJsonObject = new JSONObject();//合单商户appidreqJsonObject.put("combine_appid", WxPayConfig.merchantAppId);//合单商户reqJsonObject.put("combine_mchid", WxPayConfig.merchantId);//合单商户订单号reqJsonObject.put("combine_out_trade_no", "202005260952_test001");//回调地址reqJsonObject.put("notify_url", WxPayConfig.callback_notify_pay_address);//支付者JSONObject combine_payer_info = new JSONObject();combine_payer_info.put("openid", "oaKTs4vq93MDDmCFKQO123456789");reqJsonObject.put("combine_payer_info", combine_payer_info);//子单信息数组JSONArray sub_orders = new JSONArray();//子单信息JSONObject sub_order = new JSONObject();//子单商户号,必须与发起方appid有绑定关系sub_order.put("mchid", WxPayConfig.merchantId);//附加数据,在查询API和支付通知中原样返回sub_order.put("attach", "订单号为202005260952_test001");//子单商户订单号sub_order.put("out_trade_no", "202005260952_test001");//二级商户号sub_order.put("sub_mchid", WxPayConfig.testMerchantId);//商品描述sub_order.put("description", "高端茶叶500g装");//子单信息-订单金额JSONObject amount = new JSONObject();//标价金额,子单金额,单位为分amount.put("total_amount", 100);//标价币种amount.put("currency", WxPayConfig.currency_cny);sub_order.put("amount", amount);//子单信息-结算信息JSONObject settle_info = new JSONObject();//是否指定分账,true:是,false:否settle_info.put("profit_sharing", true);sub_order.put("settle_info", settle_info);sub_orders.set(0, sub_order);reqJsonObject.put("sub_orders", sub_orders);System.out.println("请求的body:" + reqJsonObject + "\n\n");String headerToken = WxPayUtils.getHeaderAuthorization("POST",HttpUrl.parse(WxPayConfig.transactions_prepay_app_url), reqJsonObject.toJSONString());System.out.println("请求接口携带的 header:  \n" + headerToken + "\n\n");Map<String, String> headersMap = new HashMap<>();headersMap.put("User-Agent", WxPayConfig.userAgent);headersMap.put("Accept", "application/json");headersMap.put("Authorization", headerToken);headersMap.put("Wechatpay-Serial", WxPayConfig.api_v3_cert_serial_no);RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqJsonObject.toJSONString());ResponseAndStatusAndHeaders response = ClientUtil.getAndPostJson("POST", WxPayConfig.transactions_prepay_app_url, requestBody, headersMap);if (response.getStatus() != Status.SUCCESS || response.getResponseData() == null) {LogUtil.printErrorLog(LogUtil.log_front_wxpay + "合单下单接口请求错误,返回信息为:\n" + response.toString() + "\n请求的参数为:\n" + reqJsonObject + "\n\n");return;}System.out.println(response);JSONObject jsonObject = JSON.parseObject(response.getResponseData().toString());String prepay_id = jsonObject.get("prepay_id").toString();System.out.println("合单下单成功,预支付id为:" + prepay_id);//{"prepay_id":"up_wx26112728792815875842ded51824410800"}}

调起支付

后端处理好数据获得到预支付id后,需要将各种支付参数进行签名计算后的值返回给前端,由小程序方调起支付框(也就是输入支付密码的界面)。

    /*** 继续支付* @param requestJsonStr 接受的参数* @return 操作结果*/@PostMapping("/continuePay")public ResultBO continuePay(@RequestBody String requestJsonStr) {JSONObject requestJson = JSONObject.parseObject(requestJsonStr);String orderNo = requestJson.getString("orderNo");//是否为有效订单MallOrderDO mallOrder = mallOrderService.getByOrderNo(orderNo);if (mallOrder == null) {return Results.result(HttpStatusEnum.PARAMTER_ERROE, "订单不存在");}if (mallOrder.getOrderStatus() != 1 || StrUtil.isBlank(mallOrder.getWxPrepayId())) {return Results.result(HttpStatusEnum.PARAMTER_ERROE, "该订单已经支付过了,请勿重复支付");}JSONObject payObject = WxPayUtils.getPayObject(mallOrder.getWxPrepayId());JSONObject data = new JSONObject();data.put("payObject", payObject);return Results.success(HttpStatusEnum.SUCCESS, data);}

支付回调通知

  1. 微信回调通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m
  2. 回调处理必须校验签名,避免出现恶意调用攻击。
  3. 使用APIv3密钥对回调数据中的ciphertext进行AEAD_AES_256_GCM算法解密,得到原文。
  4. 回调处理必须检查订单状态,避免重复处理。
  5. 处理回调通知后做好日志记录,当回调正常接受返回的HTTP状态码为200或204,当回调处理失败返回的HTTP状态码应为500
    /*** 微信支付回调通知*/@PostMapping("payCallBack")public JSONObject callBackNotify(@RequestBody String requestJsonStr, @RequestHeader HttpHeaders headers, HttpServletResponse response) {LogUtil.printErrorLog(LogUtil.log_front_wxpay + "微信支付结果通知接受成功,header为:" + headers + "\n body为:" + requestJsonStr);//返回通知的应答报文,code(32):SUCCESS为清算机构接收成功;message(64):错误原因JSONObject responseJson = new JSONObject();responseJson.put("code", "FAIL");//支付通知http应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx。response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);if (!headers.containsKey("Wechatpay-Serial") || !headers.containsKey("Wechatpay-Timestamp")|| !headers.containsKey("Wechatpay-Nonce") || !headers.containsKey("Wechatpay-Signature")) {LogUtil.printErrorLog(LogUtil.log_front_wxpay + "回调请求header缺失");responseJson.put("message", "回调请求header缺失");return responseJson;}String WechatpaySerial = headers.getFirst("Wechatpay-Serial");//平台证书序列号String WechatpayTimestamp = headers.getFirst("Wechatpay-Timestamp");//应答时间戳String WechatpayNonce = headers.getFirst("Wechatpay-Nonce");//应答随机串String WechatpaySignature = headers.getFirst("Wechatpay-Signature"); //应答签名if (!WechatpaySerial.equals(WxPayConfig.api_v3_cert_serial_no)) {LogUtil.printErrorLog(LogUtil.log_front_wxpay + "回调请求证书序列化不一致");responseJson.put("message", "回调请求证书序列化不一致");return responseJson;}//获取签名串,验签String srcData = WechatpayTimestamp + "\n" + WechatpayNonce + "\n" + requestJsonStr + "\n";if (!WxPayUtils.verify(srcData, WechatpaySignature)) {LogUtil.printErrorLog(LogUtil.log_front_wxpay + "验签失败");responseJson.put("message", "验签失败");return responseJson;}JSONObject requestJson = JSONObject.parseObject(requestJsonStr);//通知的类型,支付成功通知的类型为 TRANSACTION.SUCCESSString event_type = requestJson.get("event_type").toString();//通知数据JSONObject resource = JSONObject.parseObject(requestJson.get("resource").toString());//数据密文,Base64编码后的开启/停用结果数据密文String ciphertext = resource.get("ciphertext").toString();String associated_data = resource.get("associated_data").toString();String nonce = resource.get("nonce").toString();//对密文串进行解密String verify = WxPayUtils.getNotifyData(associated_data, nonce, ciphertext);JSONObject paySuccess = JSONObject.parseObject(verify);JSONArray sub_orders = JSON.parseArray(paySuccess.get("sub_orders").toString());JSONObject sub_order = JSONObject.parseObject(sub_orders.get(0).toString());//微信子单订单号String wxOrderNo = sub_order.get("transaction_id").toString();if (StrUtil.isBlank(wxOrderNo)) {responseJson.put("message", "微信支付成功,未接受到微信订单号");return responseJson;}//合单商户订单号String orderNo = paySuccess.get("combine_out_trade_no").toString();if (StrUtil.isBlank(orderNo)) {responseJson.put("message", "微信支付成功,未接受到合单商户订单号");return responseJson;}MallOrderDO mallOrder = mallOrderService.getByOrderNo(orderNo);if (mallOrder == null) {responseJson.put("message", "操作失败,未查询到合单商户订单号");return responseJson;}//支付失败if (!event_type.equals("TRANSACTION.SUCCESS")) {boolean result = mallOrderService.payFail(orderNo, wxOrderNo);if (result) {LogUtil.printErrorLog(LogUtil.log_front_wxpay + "支付结果通知:支付失败,处理成功。订单号为:" + orderNo);response.setStatus(HttpServletResponse.SC_OK);responseJson.put("code", "SUCCESS");responseJson.put("message", "微信支付失败,商户处理成功");} else {LogUtil.printErrorLog(LogUtil.log_front_wxpay + "支付结果通知:支付失败,处理失败。订单号为:" + orderNo);responseJson.put("message", "微信支付失败,商户处理失败");}return responseJson;}//支付成功boolean result = mallOrderService.paySuccess(orderNo, wxOrderNo);if (result) {LogUtil.printErrorLog(LogUtil.log_front_wxpay + "支付结果通知:支付成功,处理成功。订单号为:" + orderNo);response.setStatus(HttpServletResponse.SC_OK);responseJson.put("code", "SUCCESS");responseJson.put("message", "微信支付成功,商户处理成功");} else {LogUtil.printErrorLog(LogUtil.log_front_wxpay + "支付结果通知:支付成功,处理失败。订单号为:" + orderNo);responseJson.put("message", "微信支付成功,商户处理失败");}return responseJson;}

查询合单订单

  • 我们系统设定用户下订单后一定时间内(24h)未付款会自动取消订单,为防止状态不同步,在正式取消订单前调用合单查询接口,确认最终支付状态。
  • 当超时(4h)未接受到支付结果通知时,手动调用查询接口确认最终支付状态。
/*** 合单查询订单* @param orderNo 订单号* @return 支付是否成功*/@Overridepublic Map<String, String> queryOrder(String orderNo) {Map<String, String> resultMap = new HashMap<>();Map<String, String> headersMap = WxPayUtils.getHeaderMap("GET", WxPayConfig.transactions_order_query_url + orderNo, "");ResponseAndStatusAndHeaders response = ClientUtil.getAndPostJson("GET", WxPayConfig.transactions_order_query_url + orderNo, null, headersMap);if (response.getStatus() != Status.SUCCESS || response.getResponseData() == null) {LogUtil.printErrorLog(LogUtil.log_front_wxpay + "合单查询订单接口请求错误,返回信息为:\n" + response.toString() + ",请求的参数:\n" + orderNo);return null;}JSONObject jsonObject = JSON.parseObject(response.getResponseData().toString());/*{"combine_appid": "wx4fcb17899329a2a1","combine_mchid": "1586786671","combine_out_trade_no": "20200528807139","combine_payer_info": {"openid": "oaKTs4vq93MDDmCFKQO123456789"},"scene_info": {"device_id": ""},"sub_orders": [{"amount": {"payer_amount": 100,"payer_currency": "CNY","total_amount": 100},"attach": "商户平台订单号为20200528807139","bank_type": "CFT","mchid": "1586786671","out_trade_no": "20200528807139","sub_mchid": "1596741381","success_time": "2020-05-28T22:11:13+08:00","trade_state": "SUCCESS","trade_type": "JSAPI","transaction_id": "4325300104202005287616306099"}]}*/JSONArray subOrders = JSON.parseArray(jsonObject.get("sub_orders").toString());JSONObject subOrder = JSON.parseObject(subOrders.get(0).toString());//交易状态,SUCCESS:支付成功//REFUND:转入退款//NOTPAY:未支付//CLOSED:已关闭//USERPAYING:用户支付中//PAYERROR:支付失败(其他原因,如银行返回失败)String trade_state = subOrder.getString("trade_state");String transaction_id = subOrder.getString("transaction_id");resultMap.put("trade_state", trade_state);resultMap.put("transaction_id", transaction_id);return resultMap;}

参考链接

微信电商收付通支付接口文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/combine/chapter3_1.shtml
微信API-v3回调报文解密文档:https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi

微信支付-电商收付通开发-04.支付相关推荐

  1. 微信支付-电商收付通开发-01.材料准备

    目录 功能介绍 需要准备的材料 1. 服务号/小程序 2. 商户号 3. 开通电商收付通 4 准备好你的证书 4.1. 商户API证书: 4.2. 商户API v3秘钥 4.3. 微信支付平台证书 参 ...

  2. 微信支付-电商收付通开发-06.商户提现

    文章目录 查询二级商户余额 二级商户余额提现 查询二级商户提现单 参考链接 查询二级商户余额 这里的特殊情况下商家可以在微信支付平台直接申请提现,我自己测试的小微商户并没有发现可以提现的地方,不过可以 ...

  3. .Net Core 对接微信支付电商收付通

    微信支付官方文档:电商收付通(二级商户进件)开发指引-文档中心-微信支付服务商平台 1.安装SDK(SKIT.FlurlHttpClient.Wechat.TenpayV3) 2.前期准备 1)APP ...

  4. 微信支付-电商收付通业务概述

    引言:电商收付通是微信支付推出的电商行业解决方案,主要是应对上文支付二清与第三方支付提到的电商行业面临的二清问题,其核心竞争点在于对资金灵活的把控能力. 电商行业关键能力 订单生成:电商服务商可以帮助 ...

  5. 微信服务商电商收付通V3 支付唤起微信签名错误

    最近最接微信服务商的电商收付通功能,之前对接了微信常规的支付及收款功能等等,按道理做这个收付通功能问题不会很大,但因为自己的疏略,导致浪费了自己的大量精力,是被自己蠢哭的那种节奏.在说解决问题前,先吐 ...

  6. 微信服务商模式(电商收付通)合单支付APIV3完整Demo,可直接使用,适用于(H5、JSAPI、H5、App、小程序)

  7. 微信电商收付通所遇到的坑 基于TP5+wechatpay-guzzle-middleware开发

    微信电商收付通开发完成已经2个月了,一直没有时间整理,现在抽个时间整理出来,我这边用的是TP5框架+官方的wechatpay-guzzle-middleware开发的电商通,现在把我遇到的坑给大家介绍 ...

  8. 电商收付通,公众号H5合单支付同步存储openId

    微信支付api有多种形式,电商收付通同属微信生态,当然也不例外.有合单APP.合单JSAPI(公众号H5支付).合单小程序.合单H5.合单Native.其中合单APP.合单H5.合单Native支付不 ...

  9. 电商收付通整合小程序文档整理

    一.开发环境 开发前准备材料 服务商平台中开通[收付通]工具箱. 商户私钥:商户申请商户API证书时,会生成商户私钥,并保存在本地证书文件夹的文件apiclient_key.pem中. 商户API证书 ...

最新文章

  1. web开发中常见的安全漏洞及避免方法
  2. EditText 被遮挡和显示不全问题
  3. 为女儿示范的两张石膏像素描
  4. python鼠标事件 详解_Python selenium键盘鼠标事件实现过程详解
  5. SpringBoot 整合 Redis 哨兵机制_02
  6. BugkuCTF-WEB题矛盾
  7. 微软投资混合连接,发布本地数据网关
  8. disable jboss JMXInvokerServlet .
  9. java 导出word 表格里写多行内容_论文Tips || 宅家写论文干货来了!是时候重启真正的论文了!...
  10. 干货:完全基于情感词典的文本情感分析
  11. cocos creator 微信小游戏 世界排行榜制作教程(云数据库)
  12. linux命令行取出网卡ip地址
  13. Kali渗透测试:散列密码破解
  14. 在list中筛选出符合条件的数据(返回list)
  15. 局部敏感哈希(Locality-Sensitive Hashing, LSH)
  16. MySQL8的URL和Driver的写法
  17. 手机计算机藏应用,把隐私藏进计算器!这款功能强大的隐私保护软件,层层防护,怒赞...
  18. [Android1.5]标签TabHost图片文字覆盖的问题
  19. IDEA 生成代码神器
  20. python中列表中增加逗号,Python 实现在文件中的每一行添加一个逗号

热门文章

  1. 中文可以做计算机语言吗,为什么不能用中文来做编程呢?
  2. js中文串转成首字母(html,小程序wxml)
  3. Thymeleaf th:insert、th:replace、th:include的使用
  4. 思维模型——贝索斯的遗憾最小化模型
  5. 云计算行业的前景如何?
  6. K8S 1.13.4安装部署
  7. 【vue】用WOW.js+animate.css实现页面滚动加载元素动画
  8. plugins报红的解决办法
  9. CAD中PDF怎么转DWG
  10. 求读取CATIA标注的方法或者样例代码 Annotion