From 9d4d2779b2775ed3186ba828ff7548f66617aa5a Mon Sep 17 00:00:00 2001 From: zhengfangyuan Date: Thu, 11 Aug 2022 15:32:30 +0800 Subject: [PATCH] fix pay v3 --- .../com/iformall/enums/EnumComposeOrder.java | 10 +++ .../src/main/java/com/iformall/pay/WxPay.java | 49 ------------- .../main/java/com/iformall/pay/WxPayV3.java | 70 +++++++++++++++++++ .../wx/v2/WxCashOutAdapterService.java | 5 +- .../wx/v3/WxCashOutV3AdapterService.java | 5 +- .../appPay/WxMiniAppPayV3AdapterService.java | 51 ++++---------- 6 files changed, 99 insertions(+), 91 deletions(-) create mode 100644 mallinkService/src/main/java/com/iformall/pay/WxPayV3.java diff --git a/mallinkService/src/main/java/com/iformall/enums/EnumComposeOrder.java b/mallinkService/src/main/java/com/iformall/enums/EnumComposeOrder.java index ce37651d5..7c1870f10 100644 --- a/mallinkService/src/main/java/com/iformall/enums/EnumComposeOrder.java +++ b/mallinkService/src/main/java/com/iformall/enums/EnumComposeOrder.java @@ -47,6 +47,16 @@ public enum EnumComposeOrder { } } + //支付时是否是多个商家的 + public static boolean isMulityMerchantPay(Integer type) { + if (type.intValue() == ONE_NUMBER_ORDER_BATCH.getCode().intValue() || + type.intValue() == MULITY_NUMBER_ORDER_BATCH.getCode().intValue()) { + return true; + }else { + return false; + } + } + //子订单是单个还是一起,购物车的子订单是单个,可以单个支付,券包的子订单不能单个支付 // public static boolean childOrderIsSingle(Integer type) { // if (type.intValue() == SINGLE.getCode().intValue() || type.intValue() == ONE_NUMBER_ORDER_BATCH.getCode().intValue() diff --git a/mallinkService/src/main/java/com/iformall/pay/WxPay.java b/mallinkService/src/main/java/com/iformall/pay/WxPay.java index 21790939d..d95755537 100644 --- a/mallinkService/src/main/java/com/iformall/pay/WxPay.java +++ b/mallinkService/src/main/java/com/iformall/pay/WxPay.java @@ -47,11 +47,6 @@ public class WxPay { private static final String TRANSFERS_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; // 查询企业付款 private static final String GETTRANSFERINFO_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo"; - //商家转账到零钱 - private static final String MERCHANT_TRANSFER_CHANGE_URL = "https://api.mch.weixin.qq.com/v3/transfer/batches"; - //查询商家转账到零钱 - private static final String MERCHANT_TRANSFER_QUERY_URL = "https://api.mch.weixin.qq.com/v3/transfer/batches/out-batch-no/%s"; - // 查询代金券批次 private static final String GETCOUPONSTOCK_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/query_coupon_stock"; // 查询代金券信息 @@ -255,50 +250,6 @@ public class WxPay { return WxPay.doPostSSL(GETTRANSFERINFO_URL, params, certPath, certPassword); } - /** - * 商家转账到零钱 https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter4_3_1.shtml - * 注意:1. 需要登录到特约商户号自己的商户后台,产品中心---APPID账号管理----我关联的appId账号,把C端小程序关联 - * 2. 需要设置白名单IP - * 3.微信不能开通运营账户,如果运营账户开通,则资金会从运营账户里面扣取,不会从基本账户扣取 - * @param params - * 请求参数 - * @param certPath - * 证书文件目录 - * @param certPassword - * 证书密码 - * @return {String} - * @throws WxPayException - * @throws IllegalBlockSizeException - */ - public static String merchantTranferChange(WxPayService payService,WxCashOutPV3 cashOutV3) throws WxPayException, IllegalBlockSizeException { - X509Certificate certificate = payService.getConfig().getVerifier().getValidCertificate(); - List items = cashOutV3.getTransfer_detail_list(); - for (WxCashOutPV3Item it : items) { - if (!StringUtils.isBlank(it.getUser_name())) { - it.setUser_name(RsaCryptoUtil.encryptOAEP(it.getUser_name(), certificate)); - } - } - return payService.postV3WithWechatpaySerial(MERCHANT_TRANSFER_CHANGE_URL, JSON.toJSONString(cashOutV3)); - } - - /** - * 查询商家转账到零钱 - * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_5.shtml - * @param params - * 请求参数 - * @param certPath - * 证书文件目录 - * @param certPassword - * 证书密码 - * @return {String} - * @throws WxPayException - */ - public static String merchantTransferInfo(String batchNo, WxPayService payService) throws WxPayException { - Map param = new HashMap(); - param.put("need_query_detail", false); - return payService.postV3WithWechatpaySerial(String.format(MERCHANT_TRANSFER_QUERY_URL, batchNo), JSON.toJSONString(param)); - } - /** * 商户模式下 扫码模式一之生成二维码 * diff --git a/mallinkService/src/main/java/com/iformall/pay/WxPayV3.java b/mallinkService/src/main/java/com/iformall/pay/WxPayV3.java new file mode 100644 index 000000000..587a00437 --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/pay/WxPayV3.java @@ -0,0 +1,70 @@ +package com.iformall.pay; + +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.crypto.IllegalBlockSizeException; +import com.alibaba.fastjson.JSON; +import com.aliyun.openservices.shade.org.apache.commons.lang3.StringUtils; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WxPayV3 { + protected static Logger log = LoggerFactory.getLogger(WxPayV3.class); + + //商家转账到零钱 + private static final String MERCHANT_TRANSFER_CHANGE_URL = "https://api.mch.weixin.qq.com/v3/transfer/batches"; + //查询商家转账到零钱 + private static final String MERCHANT_TRANSFER_QUERY_URL = "https://api.mch.weixin.qq.com/v3/transfer/batches/out-batch-no/%s"; + + private WxPayV3() { + } + + /** + * 商家转账到零钱 https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter4_3_1.shtml + * 注意:1. 需要登录到特约商户号自己的商户后台,产品中心---APPID账号管理----我关联的appId账号,把C端小程序关联 + * 2. 需要设置白名单IP + * 3.微信不能开通运营账户,如果运营账户开通,则资金会从运营账户里面扣取,不会从基本账户扣取 + * @param params + * 请求参数 + * @param certPath + * 证书文件目录 + * @param certPassword + * 证书密码 + * @return {String} + * @throws WxPayException + * @throws IllegalBlockSizeException + */ + public static String merchantTranferChange(WxPayService payService,WxCashOutPV3 cashOutV3) throws WxPayException, IllegalBlockSizeException { + X509Certificate certificate = payService.getConfig().getVerifier().getValidCertificate(); + List items = cashOutV3.getTransfer_detail_list(); + for (WxCashOutPV3Item it : items) { + if (!StringUtils.isBlank(it.getUser_name())) { + it.setUser_name(RsaCryptoUtil.encryptOAEP(it.getUser_name(), certificate)); + } + } + return payService.postV3WithWechatpaySerial(MERCHANT_TRANSFER_CHANGE_URL, JSON.toJSONString(cashOutV3)); + } + + /** + * 查询商家转账到零钱 + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_5.shtml + * @param params + * 请求参数 + * @param certPath + * 证书文件目录 + * @param certPassword + * 证书密码 + * @return {String} + * @throws WxPayException + */ + public static String merchantTransferInfo(String batchNo, WxPayService payService) throws WxPayException { + Map param = new HashMap(); + param.put("need_query_detail", false); + return payService.postV3WithWechatpaySerial(String.format(MERCHANT_TRANSFER_QUERY_URL, batchNo), JSON.toJSONString(param)); + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/v2/WxCashOutAdapterService.java b/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/v2/WxCashOutAdapterService.java index 8e475839a..95b3ea0df 100644 --- a/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/v2/WxCashOutAdapterService.java +++ b/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/v2/WxCashOutAdapterService.java @@ -39,6 +39,7 @@ import com.iformall.pay.WxCashOutPV3; import com.iformall.pay.WxCashOutPV3Item; import com.iformall.pay.WxCashOutQueryP; import com.iformall.pay.WxPay; +import com.iformall.pay.WxPayV3; import com.iformall.pay.WxPayment; import com.iformall.pay.WxRefundOrderP; import com.iformall.pay.WxRefundOrderSP; @@ -221,7 +222,7 @@ public class WxCashOutAdapterService implements CashOutAdapterService{ } String response = null; try { - response = WxPay.merchantTranferChange(payService,generateWxCashOutPV3(payAccount, appInfo, cashOut)); + response = WxPayV3.merchantTranferChange(payService,generateWxCashOutPV3(payAccount, appInfo, cashOut)); } catch (Exception e) { log.error("零钱支付异常: " + e.getMessage(),e); return new CashOutAdapterResult(false, EnumCashOutStatus.FAIL.getCode(), "微信零钱支付接口调用异常:"+e.getMessage(), null); @@ -292,7 +293,7 @@ public class WxCashOutAdapterService implements CashOutAdapterService{ } String response = null; try { - response = WxPay.merchantTransferInfo(String.valueOf(cashOut.getId()), payService); + response = WxPayV3.merchantTransferInfo(String.valueOf(cashOut.getId()), payService); } catch (Exception e) { log.error("零钱支付查询异常: " + e.getMessage(),e); throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR.getCode(), "微信零钱支付查询接口异常:"+e.getMessage()); diff --git a/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/v3/WxCashOutV3AdapterService.java b/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/v3/WxCashOutV3AdapterService.java index 87490788a..9e8c3ecc5 100644 --- a/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/v3/WxCashOutV3AdapterService.java +++ b/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/v3/WxCashOutV3AdapterService.java @@ -39,6 +39,7 @@ import com.iformall.pay.WxCashOutPV3; import com.iformall.pay.WxCashOutPV3Item; import com.iformall.pay.WxCashOutQueryP; import com.iformall.pay.WxPay; +import com.iformall.pay.WxPayV3; import com.iformall.pay.WxPayment; import com.iformall.pay.WxRefundOrderP; import com.iformall.pay.WxRefundOrderSP; @@ -221,7 +222,7 @@ public class WxCashOutV3AdapterService implements CashOutAdapterService{ } String response = null; try { - response = WxPay.merchantTranferChange(payService,generateWxCashOutPV3(payAccount, appInfo, cashOut)); + response = WxPayV3.merchantTranferChange(payService,generateWxCashOutPV3(payAccount, appInfo, cashOut)); } catch (Exception e) { log.error("零钱支付异常: " + e.getMessage(),e); return new CashOutAdapterResult(false, EnumCashOutStatus.FAIL.getCode(), "微信零钱支付接口调用异常:"+e.getMessage(), null); @@ -292,7 +293,7 @@ public class WxCashOutV3AdapterService implements CashOutAdapterService{ } String response = null; try { - response = WxPay.merchantTransferInfo(String.valueOf(cashOut.getId()), payService); + response = WxPayV3.merchantTransferInfo(String.valueOf(cashOut.getId()), payService); } catch (Exception e) { log.error("零钱支付查询异常: " + e.getMessage(),e); throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR.getCode(), "微信零钱支付查询接口异常:"+e.getMessage()); diff --git a/mallinkService/src/main/java/com/iformall/service/pay/service/pay/wx/v3/miniApp/appPay/WxMiniAppPayV3AdapterService.java b/mallinkService/src/main/java/com/iformall/service/pay/service/pay/wx/v3/miniApp/appPay/WxMiniAppPayV3AdapterService.java index b56b84dcf..6ea9a07bb 100644 --- a/mallinkService/src/main/java/com/iformall/service/pay/service/pay/wx/v3/miniApp/appPay/WxMiniAppPayV3AdapterService.java +++ b/mallinkService/src/main/java/com/iformall/service/pay/service/pay/wx/v3/miniApp/appPay/WxMiniAppPayV3AdapterService.java @@ -14,6 +14,7 @@ import com.alibaba.fastjson.JSONObject; import com.iformall.common.ErrorCode; import com.iformall.common.Result; import com.iformall.common.ResultData; +import com.iformall.enums.EnumComposeOrder; import com.iformall.enums.EnumOrderStatus; import com.iformall.enums.EnumPayMode; import com.iformall.enums.EnumPayShare; @@ -40,24 +41,6 @@ import lombok.extern.slf4j.Slf4j; public class WxMiniAppPayV3AdapterService implements CDrivingPayService{ - JSONObject errorMap = JSON.parseObject("{" + - "\"NOAUTH\":{\"detail\":\"商户无此接口权限\",\"reason\":\"商户未开通此接口权限\",\"resolution\":\"请商户前往申请此接口权限\"}," + - "\"NOTENOUGH\":{\"detail\":\"余额不足\",\"reason\":\"用户帐号余额不足\",\"resolution\":\"用户帐号余额不足,请用户充值或更换支付卡后再支付\"}," + - "\"ORDERPAID\":{\"detail\":\"商户订单已支付\",\"reason\":\"商户订单已支付,无需重复操作\",\"resolution\":\"商户订单已支付,无需更多操作\"}," + - "\"ORDERCLOSED\":{\"detail\":\"订单已关闭\",\"reason\":\"当前订单已关闭,无法支付\",\"resolution\":\"当前订单已关闭,请重新下单\"}," + - "\"SYSTEMERROR\":{\"detail\":\"系统错误\t\",\"reason\":\"系统超时\",\"resolution\":\"系统异常,请用相同参数重新调用\"}," + - "\"APPID_NOT_EXIST\":{\"detail\":\"APPID不存在\",\"reason\":\"参数中缺少APPID\",\"resolution\":\"请检查APPID是否正确\"}," + - "\"MCHID_NOT_EXIST\":{\"detail\":\"MCHID不存在\",\"reason\":\"参数中缺少MCHID\",\"resolution\":\"请检查MCHID是否正确\"}," + - "\"APPID_MCHID_NOT_MATCH\":{\"detail\":\"appid和mch_id不匹配\",\"reason\":\"appid和mch_id不匹配\",\"resolution\":\"请确认appid和mch_id是否匹配\"}," + - "\"LACK_PARAMS\":{\"detail\":\"缺少参数\t\",\"reason\":\"缺少必要的请求参数\",\"resolution\":\"请检查参数是否齐全\"}," + - "\"OUT_TRADE_NO_USED\":{\"detail\":\"商户订单号重复\",\"reason\":\"同一笔交易不能多次提交\",\"resolution\":\"请核实商户订单号是否重复提交\"}," + - "\"SIGNERROR\":{\"detail\":\"签名错误\",\"reason\":\"参数签名结果不正确\",\"resolution\":\"请检查签名参数和方法是否都符合签名算法要求\"}," + - "\"XML_FORMAT_ERROR\":{\"detail\":\"XML格式错误\t\",\"reason\":\"XML格式错误\",\"resolution\":\"请检查XML参数格式是否正确\"}," + - "\"REQUIRE_POST_METHOD\":{\"detail\":\"请使用post方法\",\"reason\":\"未使用post传递参数\",\"resolution\":\"请检查请求参数是否通过post方法提交\"}," + - "\"POST_DATA_EMPTY\":{\"detail\":\"post数据为空\",\"reason\":\"post数据不能为空\",\"resolution\":\"请检查post数据是否为空\"}," + - "\"NOT_UTF8\":{\"detail\":\"编码格式错误\",\"reason\":\"未使用指定编码格式\",\"resolution\":\"请使用UTF-8编码格式\"}}"); - - private WxPayOrderP generateWxPayOrderP(WxPayAccount payAccount,WxComposeOrder composeOrder,String productName, WxPayOrder record,String openId,String appId,Date currentDate) throws Exception { // 统一下单 普通商户模式 String noncestr = Utility.generate32UUID(); @@ -112,14 +95,6 @@ public class WxMiniAppPayV3AdapterService implements CDrivingPayService{ log.info("back to UI: " + returnMap.toString()); } else { String errMsg = ""; - JSONObject errObj = errorMap.getJSONObject(result_code); - if (errObj != null) { - errMsg = errObj.toJSONString(); - record.setFailReason(errMsg); - } else { - errMsg = returnMap.get("return_msg"); - record.setFailReason(errMsg); - } record.setUpdateTime(new Date()); par.setSuccess(false); @@ -193,14 +168,6 @@ public class WxMiniAppPayV3AdapterService implements CDrivingPayService{ log.info("back to UI: " + returnMap.toString()); } else { String errMsg = ""; - JSONObject errObj = errorMap.getJSONObject(result_code); - if (errObj != null) { - errMsg = errObj.toJSONString(); - record.setFailReason(errMsg); - } else { - errMsg = returnMap.get("return_msg"); - record.setFailReason(errMsg); - } record.setUpdateTime(new Date()); par.setSuccess(false); par.setMsg(errMsg); @@ -210,7 +177,8 @@ public class WxMiniAppPayV3AdapterService implements CDrivingPayService{ } /** - * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_8_2.shtml + * 电商收付通 + * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/open/pay/chapter3_3_0.shtml */ @Override public PayAdapterResult pay(WxPayAccount payAccount, WxPayOrder record,WxComposeOrder composeOrder,String productName,EnumPayShare isShare,WxAppinfo appInfo,Date currentDate, PayExtraParam params) throws Exception { @@ -221,8 +189,15 @@ public class WxMiniAppPayV3AdapterService implements CDrivingPayService{ if (StringUtils.isBlank(openId) ) { throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"openId为空"); } - //普通商户模式 - if (payAccount.getType() == EnumPayMode.MCH.getCode()) { + + if (payAccount.getType() == EnumPayMode.MCH.getCode()) { + throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"当前商户号非服务商模式,不能使用该产品."); + } + /* + * 普通支付/小程序支付 + * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_2_3.shtml + */ + if (!EnumComposeOrder.isMulityMerchantPay(composeOrder.getComposeOrderType())) { WxPayOrderP wxPayOrderP = generateWxPayOrderP(payAccount,composeOrder,productName,record,openId,appInfo.getAppId(),currentDate); String response = WxPay.pushOrder(BeanUtils.toStringMap(wxPayOrderP)); log.info("pay order, wechat pushOrder, " + wxPayOrderP.toString() + ", response: " + response.toString()); @@ -230,7 +205,7 @@ public class WxMiniAppPayV3AdapterService implements CDrivingPayService{ return getOrderPResult(returnMap,record,payAccount,wxPayOrderP.getNonce_str()); } else { - // 统一下单 // 服务商模式 + //合单支付/小程序支付 WxPayOrderSP wxPayOrderSP = generateWxPayOrderSP(payAccount,composeOrder,productName,record,appInfo,openId,appInfo.getAppId(),currentDate,isShare); String response = WxPay.pushOrder(BeanUtils.toStringMap(wxPayOrderSP)); log.info("pay order, wechat pushOrder, " + wxPayOrderSP.toString() + ", response: " + response.toString());