From 139abee8659e9dcc4045c08a0a9fd546e2b5db17 Mon Sep 17 00:00:00 2001 From: xhxu Date: Wed, 11 Oct 2023 15:36:36 +0800 Subject: [PATCH] //native --- .../enums/EnumProductOrderPayVendor.java | 2 +- .../main/java/com/iformall/pay/WxPayV3.java | 14 +- .../service/pay/PayServiceFactory.java | 11 +- .../WxNativePayV3AdapterService.java | 279 ++++++++++++++++++ 4 files changed, 300 insertions(+), 6 deletions(-) create mode 100644 suimangService/src/main/java/com/iformall/service/pay/service/pay/wx/v3/nativePay/WxNativePayV3AdapterService.java diff --git a/suimangService/src/main/java/com/iformall/enums/EnumProductOrderPayVendor.java b/suimangService/src/main/java/com/iformall/enums/EnumProductOrderPayVendor.java index 488be99..222afc0 100644 --- a/suimangService/src/main/java/com/iformall/enums/EnumProductOrderPayVendor.java +++ b/suimangService/src/main/java/com/iformall/enums/EnumProductOrderPayVendor.java @@ -6,7 +6,7 @@ package com.iformall.enums; public enum EnumProductOrderPayVendor { PAY_WAY_WECHAT(1, "微信小程序",EnumAppPlat.WX.getCode(),EnumProfitSharing.PROFIT_SHARING_NO.getCode()), - PAY_WAY_WECHAT_WAP(2, "微信H5",null,null), + PAY_WAY_WECHAT_NATIVE(2, "微信Native",null,null), PAY_WAY_ALIPAY(3, "支付宝小程序",null,null), PAY_WAY_ALIPAY_WAP(4, "支付宝H5",null,null), PAY_WAY_TT(5, "抖音小程序",EnumAppPlat.TOUTIAO.getCode(),EnumProfitSharing.PROFIT_SHARING_NO.getCode()), diff --git a/suimangService/src/main/java/com/iformall/pay/WxPayV3.java b/suimangService/src/main/java/com/iformall/pay/WxPayV3.java index e14b286..38b0a7c 100644 --- a/suimangService/src/main/java/com/iformall/pay/WxPayV3.java +++ b/suimangService/src/main/java/com/iformall/pay/WxPayV3.java @@ -45,10 +45,12 @@ public class WxPayV3 { private static final String PAY_MINIAPP_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; //小程序下单-普通支付 private static final String PAY_COMMON_MINIAPP_URL = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi"; + //native下单-(单商户模式) + private static final String PAY_NATIVE_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/native"; - //小程序下单-普通支付-查询(单商户模式) + //普通支付-查询(单商户模式) private static final String PAY_QUERY_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s"; - //小程序下单-普通支付-查询 + //普通支付-查询 private static final String PAY_COMMON_QUERY_URL = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/%s"; //小程序下单-合单支付 @@ -153,6 +155,14 @@ public class WxPayV3 { return payService.postV3WithWechatpaySerial(PAY_COMMON_MINIAPP_URL, JSON.toJSONString(payReq)); } + /** + * 普通支付-native下单 https://pay.weixin.qq.com/docs/merchant/apis/native-payment/direct-jsons/native-prepay.html + * @throws WxPayException + */ + public static String payCommonNative(WxPayService payService, V3CreatePayReq payReq) throws WxPayException { + return payService.postV3WithWechatpaySerial(PAY_NATIVE_URL, JSON.toJSONString(payReq)); + } + /** * 普通支付-查询https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_2.shtml * @throws WxPayException diff --git a/suimangService/src/main/java/com/iformall/service/pay/PayServiceFactory.java b/suimangService/src/main/java/com/iformall/service/pay/PayServiceFactory.java index 0a6ae57..01ec892 100644 --- a/suimangService/src/main/java/com/iformall/service/pay/PayServiceFactory.java +++ b/suimangService/src/main/java/com/iformall/service/pay/PayServiceFactory.java @@ -5,6 +5,7 @@ import java.util.concurrent.ConcurrentHashMap; import com.iformall.enums.EnumAppPlat; import com.iformall.enums.EnumPayVersion; import com.iformall.enums.EnumProductOrderPayVendor; +import com.iformall.service.pay.service.pay.wx.v3.nativePay.WxNativePayV3AdapterService; import com.iformall.service.pay.service.refund.douyin.TtRefundAdapterService; import com.iformall.service.pay.service.refund.wx.v2.WxRefundAdapterService; import com.iformall.service.pay.service.refund.wx.v3.WxRefundV3AdapterService; @@ -58,7 +59,11 @@ public class PayServiceFactory { WxMiniAppPaySFTAdapterService wxMiniAppPaySFTAdapterService; @Autowired WxMiniAppPayV3AdapterService wxMiniAppPayV3AdapterService; - + + @Autowired + WxNativePayV3AdapterService wxNativePayV3AdapterService; + + @Autowired WxMiniMaPayAdapterService wxMiniAppMaPayService; @@ -147,7 +152,7 @@ public class PayServiceFactory { if (null == payVendorServiceMap) { payVendorServiceMap = new ConcurrentHashMap(); payVendorServiceMap.put(EnumProductOrderPayVendor.PAY_WAY_WECHAT.getCode()+"_", wxMiniAppPayV3AdapterService); - payVendorServiceMap.put(EnumProductOrderPayVendor.PAY_WAY_WECHAT_WAP.getCode()+"_", wxH5PayService); + payVendorServiceMap.put(EnumProductOrderPayVendor.PAY_WAY_WECHAT_NATIVE.getCode()+"_", wxNativePayV3AdapterService); // payVendorServiceMap.put(EnumProductOrderPayVendor.PAY_WAY_ALIPAY.getCode()+"_", ); // payVendorServiceMap.put(EnumProductOrderPayVendor.PAY_WAY_ALIPAY_WAP.getCode()+"_",); payVendorServiceMap.put(EnumProductOrderPayVendor.PAY_WAY_TT.getCode()+"_",ttMiniAppPayService); @@ -211,7 +216,7 @@ public class PayServiceFactory { if (null == payVendorShareMap ) { payVendorShareMap = new ConcurrentHashMap(); payVendorShareMap.put(EnumProductOrderPayVendor.PAY_WAY_WECHAT.getCode()+"_", wxPayShareV3Service); - payVendorShareMap.put(EnumProductOrderPayVendor.PAY_WAY_WECHAT_WAP.getCode()+"_", null); + payVendorShareMap.put(EnumProductOrderPayVendor.PAY_WAY_WECHAT_NATIVE.getCode()+"_", null); // payVendorShareMap.put(EnumProductOrderPayVendor.PAY_WAY_ALIPAY.getCode()+"_", ); // payVendorShareMap.put(EnumProductOrderPayVendor.PAY_WAY_ALIPAY_WAP.getCode()+"_",); payVendorShareMap.put(EnumProductOrderPayVendor.PAY_WAY_TT.getCode()+"_",ttShareService); diff --git a/suimangService/src/main/java/com/iformall/service/pay/service/pay/wx/v3/nativePay/WxNativePayV3AdapterService.java b/suimangService/src/main/java/com/iformall/service/pay/service/pay/wx/v3/nativePay/WxNativePayV3AdapterService.java new file mode 100644 index 0000000..799d2df --- /dev/null +++ b/suimangService/src/main/java/com/iformall/service/pay/service/pay/wx/v3/nativePay/WxNativePayV3AdapterService.java @@ -0,0 +1,279 @@ +package com.iformall.service.pay.service.pay.wx.v3.nativePay; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.iformall.common.ErrorCode; +import com.iformall.domain.po.*; +import com.iformall.enums.*; +import com.iformall.exception.MallinkException; +import com.iformall.pay.WxPayV3; +import com.iformall.service.helper.WxPayOrderServiceHelper; +import com.iformall.service.order.entity.WxComposeOrder; +import com.iformall.service.pay.entity.PayExtraParam; +import com.iformall.service.pay.service.pay.CDrivingPayService; +import com.iformall.service.pay.service.pay.entity.PayAdapterResult; +import com.iformall.service.pay.service.pay.entity.PayQueryAdapterResult; +import com.iformall.service.pay.service.pay.wx.sft.entity.SFTPayQueryReq; +import com.iformall.service.pay.service.pay.wx.v3.BaseWxPayV3AdapterService; +import com.iformall.service.pay.service.pay.wx.v3.entity.*; +import com.iformall.utils.DateUtils; +import com.iformall.utils.MaUtil; +import com.iformall.utils.Utility; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.util.*; + +@Slf4j +@Service +public class WxNativePayV3AdapterService extends BaseWxPayV3AdapterService implements CDrivingPayService{ + + @Autowired + MaUtil maUtil; + + private V3CreatePayReq generateCreatePayRequest(ProductOrder productOrder,WxAppinfo appInfo,WxPayAccount payAccount) throws Exception { + V3CreatePayReq req = new V3CreatePayReq(); + req.setAppid(appInfo.getAppId()); + req.setMchid(payAccount.getSubMchId()); + try { + //中文必须要这样,否则会双方签名失败 + req.setDescription(WxPayV3.handleChinese(appInfo.getName()+"-"+productOrder.getProductTitle())); + } catch (UnsupportedEncodingException e) { + req.setDescription("weixin miniApp product"); + } + req.setOut_trade_no(productOrder.getOrderNumber()); + req.setNotify_url(payAccount.getPayNotifyV3Url(appInfo.getProjectType())); + + V3PayAmountReq amout = new V3PayAmountReq(); + amout.setTotal(productOrder.getOrderPrice()); + req.setAmount(amout); + + V3Payer payer = new V3Payer(); + payer.setOpenid(productOrder.getOpenId()); + req.setPayer(payer); + + return req; + } + + private PayAdapterResult getOrderPResult(String response,WxPayService payService,WxAppinfo appInfo,WxPayAccount payAccount,String order_no) { + PayAdapterResult par = new PayAdapterResult(); + if (StringUtils.isBlank(response)) { + par.setSuccess(false); + par.setMsg("pay v3 has no response."); + }else { + JSONObject result = JSON.parseObject(response); + String codeUrl = result.getString("code_url"); + if (StringUtils.isBlank(codeUrl)) { + par.setSuccess(false); + par.setMsg("pay v3 has no response."); + }else { + par.setSuccess(true); + par.setMsg("success."); + par.setData(getPayRetMap(codeUrl, order_no)); + } + } + return par; + } + + private Map getPayRetMap(String codeUrl,String order_no) { + Map returnMap = new HashMap(); + returnMap.put("codeUrl", codeUrl); + returnMap.put("payOrderId", order_no); + return returnMap; + } + + + @Override + public PayAdapterResult pay(WxPayAccount payAccount,WxPayOrder record,WxComposeOrder composeOrder,List childOrders, + String productName,EnumPayShare isShare,WxAppinfo appInfo,Date currentDate, PayExtraParam params) throws Exception { + return null; + } + + @Override + public PayAdapterResult createPay(ProductOrder order, WxAppinfo appInfo, WxPayAccount payAccount) throws Exception { + + WxPayService payService = maUtil.getWxPayServiceBySelfModel(appInfo, payAccount); + + V3CreatePayReq payReq = generateCreatePayRequest(order,appInfo,payAccount); + try { + String response = WxPayV3.payCommonNative(payService, payReq); + return getOrderPResult(response,payService,appInfo,payAccount,order.getOrderNumber()); + }catch(WxPayException e) { + log.error("wexin pay v3 error",e); + throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),e.getCustomErrorMsg()); + } + } + + private PayQueryAdapterResult getPayCommonQueryResult(String response) { + JSONObject result = JSON.parseObject(response); + String transtionId = result.getString("transaction_id"); + String tradeState = result.getString("trade_state"); + String tradeStateDesc = result.getString("trade_state_desc"); + String successTime = result.getString("success_time"); + EnumPayStatus payStatus = WxPayOrderServiceHelper.getPayStatus(tradeState); + if (null == payStatus) { + throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), "订单查询微信支付状态非法["+tradeState+"]"); + } + if (StringUtils.isBlank(tradeStateDesc)) { + tradeStateDesc = payStatus.getMessage(); + } + PayQueryAdapterResult qresult = new PayQueryAdapterResult(payStatus.getCode(), tradeStateDesc,null, response,transtionId,successTime); + return qresult; + } + + private PayQueryAdapterResult getPayQueryResult(String response) { + JSONObject result = JSON.parseObject(response); + String transtionId = result.getString("transaction_id"); + String tradeState = result.getString("trade_state"); + String tradeStateDesc = result.getString("trade_state_desc"); + String successTime = result.getString("success_time"); + JSONObject payer = result.getJSONObject("payer"); + String openid = null; + if(payer != null){ + openid = payer.getString("openid"); + } + + EnumProductOrderStatus payStatus = WxPayOrderServiceHelper.getProductOrderStatus(tradeState); + if (null == payStatus) { + throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), "订单查询微信支付状态非法["+tradeState+"]"); + } + if (StringUtils.isBlank(tradeStateDesc)) { + tradeStateDesc = payStatus.getMessage(); + } + PayQueryAdapterResult qresult = new PayQueryAdapterResult(); + qresult.setCode(payStatus.getCode()); + qresult.setMsg(tradeStateDesc); + qresult.setTransactionId(transtionId); + qresult.setOpenId(openid); + if(StringUtils.isNotBlank(successTime)){ + qresult.setPayTime(DateUtils.rfc3339Formatter(successTime)); + } + + return qresult; + } + + + @Override + public PayQueryAdapterResult queryPayStatus(WxPayOrder oldRecord, WxAppinfo appInfo, + WxPayAccount payAccount) throws Exception { + WxPayService payService = maUtil.getWxPayService(appInfo, payAccount); + V3PayQueryReq req = new V3PayQueryReq(); + if (!oldRecord.isMulity()) { + req.setSp_mchid(payAccount.getMchId()); + //总分 + if (payAccount.getMchType() == EnumPayMchType.TOTAL.getCode()) { + req.setSub_mchid(payAccount.getSubMchId()); + }else if (payAccount.getMchType() == EnumPayMchType.DIRECT.getCode()) { + req.setSub_mchid(oldRecord.getSingleChildOrderShare().getMerchantUid()); + } + req.setOut_trade_no(oldRecord.getPayOrderNo()); + try { + String response = WxPayV3.payCommonQuery(payService, req); + if (StringUtils.isBlank(response)){ + log.error("pay common v3 query response is null."+JSON.toJSONString(req)); + throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"pay common v3 query response is null."+JSON.toJSONString(req)); + } + return getPayCommonQueryResult(response); + }catch(WxPayException e) { + log.error("pay common v3 query response error",e); + throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),e.getCustomErrorMsg()); + } + + }else { + throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"合单查询无法处理,暂时不支持合单支付查询"); + } + } + + /** + * 单商户模式 + * @param order + * @param appInfo + * @param payAccount + * @return + * @throws Exception + */ + @Override + public PayQueryAdapterResult queryPayStatus(ProductOrder order, WxAppinfo appInfo, WxPayAccount payAccount) throws Exception { + WxPayService payService = maUtil.getWxPayServiceBySelfModel(appInfo, payAccount); + V3PayQuery req = new V3PayQuery(); + req.setMchid(payAccount.getSubMchId()); + req.setOut_trade_no(order.getOrderNumber()); + try { + String response = WxPayV3.payQuery(payService, req); + if (StringUtils.isBlank(response)){ + log.error("pay common v3 query response is null."+JSON.toJSONString(req)); + throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"pay common v3 query response is null."+JSON.toJSONString(req)); + } + return getPayQueryResult(response); + }catch(WxPayException e) { + log.error("pay common v3 query response error",e); + if("ORDER_CLOSED".equals(e.getErrCode())){ + PayQueryAdapterResult result = new PayQueryAdapterResult(); + result.setCode(EnumProductOrderStatus.ORDER_STATUS_OVERTIME_CANCEL.getCode()); + result.setMsg(EnumProductOrderStatus.ORDER_STATUS_OVERTIME_CANCEL.getMessage()); + return result; + }else if("ORDER_NOT_EXIST".equals(e.getErrCode()) || "ORDERNOTEXIST".equals(e.getErrCode())){ + PayQueryAdapterResult result = new PayQueryAdapterResult(); + result.setCode(EnumProductOrderStatus.ORDER_STATUS_PENDING_PAYMENT.getCode()); + result.setMsg(EnumProductOrderStatus.ORDER_STATUS_PENDING_PAYMENT.getMessage()); + return result; + } + throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),e.getCustomErrorMsg()); + } + } + + @Override + public int queryPayStatus(PayQueryAdapterResult statusObject, String orderOutNo) throws Exception { + return getPayCommonQueryResult((String)statusObject.getData()).getCode(); + } + + @Override + public int queryPayStatusCode(WxPayOrder oldRecord, WxAppinfo appInfo, WxPayAccount payAccount) + throws Exception { + return queryPayStatus(oldRecord, appInfo, payAccount).getCode(); + } + + @Override + public PayAdapterResult payOrderClose(WxAppinfo appInfo, WxPayOrder record,WxPayAccount payAccount) throws Exception { + return null; + } + + @Override + public PayAdapterResult payOrderReverse(WxAppinfo appInfo, WxPayOrder record, WxPayAccount payAccount) + throws Exception { + return super.payOrderReverse(appInfo, record, payAccount,payAccount.getSubMchId()); + } + + @Override + public PayAdapterResult payOrderPush(String openId,WxAppinfo appInfo, WxBatchOrder batchOrder,WxPayOrder payOrder) throws Exception { + return null; + } + + @Override + public File getQrcode(WxAppinfo appinfo,String pageUrl,int type,String sceneParam) throws Exception{ + return super.getQrCode(appinfo, pageUrl, type, sceneParam); + } + + @Override + public String getScheme(WxAppinfo appinfo, String pageUrl, String sceneParam,Long expireTime) throws Exception { + return super.generateScheme(appinfo, pageUrl, sceneParam,expireTime); + } + + @Override + public PayAdapterResult noCreatePay(WxPayOrder record, WxComposeOrder composeOrder, List childOrders) { + throw new MallinkException(ErrorCode.SYS_PARAMETER_ERROR); + } + + @Override + public void sendSubscribeMsg(WxAppinfo appinfo, WxTemplateMsg wxTemplateMsg, String openId, String toPage, Map param) throws Exception { + super.subscribeMsg(appinfo,wxTemplateMsg,openId,toPage,param); + } + +}