| @@ -6,7 +6,7 @@ package com.iformall.enums; | |||
| public enum EnumPayVersion { | |||
| WX_PAY_V2(1, "微信支付2.0,老版本"), | |||
| WX_PAY_V3(2, "微信支付3.0,暂时还未接"), | |||
| WX_PAY_V3(2, "微信支付3.0"), | |||
| WX_PAY_V3_SFT(7,"对接电商收付通版本"), | |||
| DY_PAY_V2(3,"抖音支付2.0,对接商品库"), | |||
| NEU_POS_V1(4,"东软POS支付1.0版本"), | |||
| @@ -14,6 +14,14 @@ import com.github.binarywang.wxpay.exception.WxPayException; | |||
| import com.github.binarywang.wxpay.service.WxPayService; | |||
| import com.github.binarywang.wxpay.v3.util.AesUtils; | |||
| import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil; | |||
| import com.iformall.service.pay.service.pay.wx.sft.entity.SFTCombineCloseReq; | |||
| import com.iformall.service.pay.service.pay.wx.sft.entity.SFTCombinePayCommonMiniAppReq; | |||
| import com.iformall.service.pay.service.pay.wx.sft.entity.SFTPayCommonMiniAppReq; | |||
| import com.iformall.service.pay.service.pay.wx.sft.entity.SFTPayQueryReq; | |||
| import com.iformall.service.pay.service.pay.wx.v3.entity.V3CombinePayCommonMiniAppReq; | |||
| import com.iformall.service.pay.service.pay.wx.v3.entity.V3PayCommonMiniAppReq; | |||
| import com.iformall.service.pay.service.pay.wx.v3.entity.V3PayQueryReq; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| @@ -25,6 +33,22 @@ public class WxPayV3 { | |||
| //查询商家转账到零钱 | |||
| 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 PAY_COMMON_MINIAPP_URL = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi"; | |||
| //小程序下单-普通支付-查询 | |||
| private static final String PAY_COMMON_QUERY_URL = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/%s"; | |||
| //小程序下单-合单支付 | |||
| private static final String PAY_COMBINE_MINIAPP_URL = "https://api.mch.weixin.qq.com/v3/combine-transactions/jsapi"; | |||
| //小程序下单-合单支付-查询 | |||
| private static final String PAY_COMBINE_QUERY_URL = "https://api.mch.weixin.qq.com/v3/combine-transactions/out-trade-no/%s"; | |||
| //小程序下单-普通支付-关闭订单 | |||
| private static final String PAY_COMMON_CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/%s/close"; | |||
| //小程序下单-合单支付-关闭订单 | |||
| private static final String PAY_COMBINE_CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/v3/combine-transactions/out-trade-no/%s/close"; | |||
| //小程序下单-分账 | |||
| private static final String SHARE_URL = "https://api.mch.weixin.qq.com/v3/ecommerce/profitsharing/orders"; | |||
| private WxPayV3() { | |||
| } | |||
| @@ -84,4 +108,55 @@ public class WxPayV3 { | |||
| return payService.postV3WithWechatpaySerial(String.format(MERCHANT_TRANSFER_QUERY_URL, batchNo), JSON.toJSONString(param)); | |||
| } | |||
| /** | |||
| * 普通支付-小程序下单 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_5_1.shtml | |||
| * @throws WxPayException | |||
| */ | |||
| public static String payCommonWithMiniApp(WxPayService payService,V3PayCommonMiniAppReq payReq) throws WxPayException { | |||
| return payService.postV3WithWechatpaySerial(PAY_COMMON_MINIAPP_URL, JSON.toJSONString(payReq)); | |||
| } | |||
| /** | |||
| * 普通支付-查询https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_5_2.shtml | |||
| * @throws WxPayException | |||
| */ | |||
| public static String payCommonQuery(WxPayService payService,V3PayQueryReq payQuery) throws WxPayException { | |||
| return payService.postV3WithWechatpaySerial(String.format(PAY_COMMON_QUERY_URL, payQuery.getOut_trade_no()), JSON.toJSONString(payQuery)); | |||
| } | |||
| /** | |||
| * 合单支付-小程序下单https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter5_1_4.shtml | |||
| * @throws WxPayException | |||
| */ | |||
| public static String payCombineCommonWithMiniApp(WxPayService payService,V3CombinePayCommonMiniAppReq payReq) throws WxPayException { | |||
| return payService.postV3WithWechatpaySerial(PAY_COMBINE_MINIAPP_URL, JSON.toJSONString(payReq)); | |||
| } | |||
| /** | |||
| * 合单支付-查询 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter5_1_11.shtml | |||
| * @throws WxPayException | |||
| */ | |||
| public static String payCombineQuery(WxPayService payService,V3PayQueryReq payQuery) throws WxPayException { | |||
| return payService.postV3WithWechatpaySerial(String.format(PAY_COMBINE_QUERY_URL, payQuery.getOut_trade_no()), null); | |||
| } | |||
| /** | |||
| * 普通支付-关闭 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_5_3.shtml | |||
| * @throws WxPayException | |||
| */ | |||
| public static String payCommonClose(WxPayService payService,SFTPayQueryReq payQuery) throws WxPayException { | |||
| return payService.postV3WithWechatpaySerial(String.format(PAY_COMMON_CLOSE_ORDER_URL, payQuery.getOut_trade_no()), JSON.toJSONString(payQuery)); | |||
| } | |||
| /** | |||
| * 合单支付-关闭 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter5_1_12.shtml | |||
| * @throws WxPayException | |||
| */ | |||
| public static String payCombineClose(WxPayService payService,SFTCombineCloseReq closeOrder) throws WxPayException { | |||
| return payService.postV3WithWechatpaySerial(String.format(PAY_COMBINE_CLOSE_ORDER_URL, closeOrder.getCombine_out_trade_no()), JSON.toJSONString(closeOrder)); | |||
| } | |||
| } | |||
| @@ -10,15 +10,14 @@ import com.iformall.service.pay.service.refund.wx.v3.WxRefundV3AdapterService; | |||
| import com.iformall.service.pay.service.share.douyin.TtPayShareService; | |||
| import com.iformall.service.pay.service.share.wx.sft.WxPayShareSFTService; | |||
| import com.iformall.service.pay.service.share.wx.v2.WxPayShareService; | |||
| import com.iformall.service.pay.service.share.wx.v3.WxPayShareV3Service; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Service; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.enums.EnumPayWay; | |||
| import com.iformall.exception.MallinkException; | |||
| import com.iformall.service.pay.service.cashout.CashOutAdapterService; | |||
| import com.iformall.service.pay.service.cashout.wx.v2.WxCashOutAdapterService; | |||
| import com.iformall.service.pay.service.cashout.wx.v3.WxCashOutV3AdapterService; | |||
| import com.iformall.service.pay.service.cashout.wx.sft.WxCashOutSFTAdapterService; | |||
| import com.iformall.service.pay.service.cashout.wx.v2.WxCashOutQYFKAdapterService; | |||
| import com.iformall.service.pay.service.pay.CDrivingPayService; | |||
| import com.iformall.service.pay.service.pay.CPassivePayService; | |||
| import com.iformall.service.pay.service.pay.PayAdapterService; | |||
| @@ -28,6 +27,7 @@ import com.iformall.service.pay.service.pay.wx.sft.miniApp.appPay.WxMiniAppPaySF | |||
| import com.iformall.service.pay.service.pay.wx.v2.h5.WxH5PayService; | |||
| import com.iformall.service.pay.service.pay.wx.v2.miniApp.appPay.WxMiniAppPayAdapterService; | |||
| import com.iformall.service.pay.service.pay.wx.v2.miniApp.maPay.WxMiniMaPayAdapterService; | |||
| import com.iformall.service.pay.service.pay.wx.v3.miniApp.appPay.WxMiniAppPayV3AdapterService; | |||
| import com.iformall.service.pay.service.refund.RefundPayAdapterService; | |||
| import com.iformall.service.pay.service.share.PayShareAdapterService; | |||
| @@ -48,6 +48,8 @@ public class PayServiceFactory { | |||
| WxMiniAppPayAdapterService wxMiniAppPayService; | |||
| @Autowired | |||
| WxMiniAppPaySFTAdapterService wxMiniAppPaySFTAdapterService; | |||
| @Autowired | |||
| WxMiniAppPayV3AdapterService wxMiniAppPayV3AdapterService; | |||
| @Autowired | |||
| WxMiniMaPayAdapterService wxMiniAppMaPayService; | |||
| @@ -77,9 +79,9 @@ public class PayServiceFactory { | |||
| TtRefundAdapterService ttRefundService; | |||
| @Autowired | |||
| WxCashOutAdapterService wxCashOutService; | |||
| WxCashOutQYFKAdapterService wxCashOutService; | |||
| @Autowired | |||
| WxCashOutV3AdapterService wxCashOutV3AdapterService; | |||
| WxCashOutSFTAdapterService wxCashOutSFTAdapterService; | |||
| @@ -88,7 +90,11 @@ public class PayServiceFactory { | |||
| serviceMap = new ConcurrentHashMap<String,PayAdapterService>(); | |||
| serviceMap.put(EnumPayWay.PAY_WAY_WECHAT.getCode()+"_"+EnumPayVersion.WX_PAY_V2.getCode(), wxMiniAppPayService); | |||
| serviceMap.put(EnumPayWay.PAY_WAY_WECHAT.getCode()+"_"+EnumPayVersion.WX_PAY_V3_SFT.getCode(), wxMiniAppPaySFTAdapterService); | |||
| serviceMap.put(EnumPayWay.PAY_WAY_WECHAT.getCode()+"_"+EnumPayVersion.WX_PAY_V3.getCode(), wxMiniAppPayV3AdapterService); | |||
| serviceMap.put(EnumPayWay.PAY_WAY_WECHAT_MA.getCode()+"_"+EnumPayVersion.WX_PAY_V2.getCode(), wxMiniAppMaPayService); | |||
| serviceMap.put(EnumPayWay.PAY_WAY_WECHAT_MA.getCode()+"_"+EnumPayVersion.WX_PAY_V3.getCode(), wxMiniAppMaPayService); | |||
| //电商收付通没有收款码支付 | |||
| //serviceMap.put(EnumPayWay.PAY_WAY_WECHAT_MA.getCode()+"_"+EnumPayVersion.WX_PAY_V3_SFT.getCode(), wxMiniAppMaPayService); | |||
| serviceMap.put(EnumPayWay.PAY_WAY_WECHAT_WAP.getCode()+"_"+EnumPayVersion.WX_PAY_V2.getCode(), wxH5PayService); | |||
| serviceMap.put(EnumPayWay.PAY_WAY_WECHAT_WAP.getCode()+"_"+EnumPayVersion.WX_PAY_V3_SFT.getCode(), wxH5PaySFTService); | |||
| @@ -160,8 +166,8 @@ public class PayServiceFactory { | |||
| cashoutMap = new ConcurrentHashMap<String, CashOutAdapterService>(); | |||
| cashoutMap.put(EnumPayWay.PAY_WAY_WECHAT.getCode()+"_"+EnumPayVersion.WX_PAY_V2.getCode(), wxCashOutService); | |||
| cashoutMap.put(EnumPayWay.PAY_WAY_WECHAT_MA.getCode()+"_"+EnumPayVersion.WX_PAY_V2.getCode(), wxCashOutService); | |||
| cashoutMap.put(EnumPayWay.PAY_WAY_WECHAT.getCode()+"_"+EnumPayVersion.WX_PAY_V3_SFT.getCode(), wxCashOutV3AdapterService); | |||
| cashoutMap.put(EnumPayWay.PAY_WAY_WECHAT_MA.getCode()+"_"+EnumPayVersion.WX_PAY_V3_SFT.getCode(), wxCashOutV3AdapterService); | |||
| cashoutMap.put(EnumPayWay.PAY_WAY_WECHAT.getCode()+"_"+EnumPayVersion.WX_PAY_V3_SFT.getCode(), wxCashOutSFTAdapterService); | |||
| cashoutMap.put(EnumPayWay.PAY_WAY_WECHAT_MA.getCode()+"_"+EnumPayVersion.WX_PAY_V3_SFT.getCode(), wxCashOutSFTAdapterService); | |||
| } | |||
| return cashoutMap; | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| package com.iformall.service.pay.service.cashout.wx.v3; | |||
| package com.iformall.service.pay.service.cashout.wx.sft; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.util.ArrayList; | |||
| @@ -60,7 +60,7 @@ import lombok.extern.slf4j.Slf4j; | |||
| @Slf4j | |||
| @Service | |||
| public class WxCashOutV3AdapterService implements CashOutAdapterService{ | |||
| public class WxCashOutSFTAdapterService implements CashOutAdapterService{ | |||
| @Autowired | |||
| WxAppinfoService wxAppinfoService; | |||
| @@ -58,6 +58,12 @@ import com.iformall.utils.XmlUtil; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| /** | |||
| * 商家转账到零钱(因会自动开通运营账户,暂时无法使用) | |||
| * @author iformall | |||
| * | |||
| */ | |||
| @Slf4j | |||
| @Service | |||
| public class WxCashOutAdapterService implements CashOutAdapterService{ | |||
| @@ -0,0 +1,317 @@ | |||
| package com.iformall.service.pay.service.cashout.wx.v2; | |||
| import java.util.Map; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.springframework.stereotype.Service; | |||
| import com.alibaba.fastjson.JSON; | |||
| import com.alibaba.fastjson.JSONObject; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.domain.po.WxAppinfo; | |||
| import com.iformall.domain.po.WxCashOut; | |||
| import com.iformall.domain.po.WxPayAccount; | |||
| import com.iformall.enums.EnumCashOutStatus; | |||
| import com.iformall.exception.MallinkException; | |||
| import com.iformall.pay.WxCashOutP; | |||
| import com.iformall.pay.WxCashOutQueryP; | |||
| import com.iformall.pay.WxPay; | |||
| import com.iformall.pay.WxPayment; | |||
| import com.iformall.service.pay.service.cashout.CashOutAdapterService; | |||
| import com.iformall.service.pay.service.cashout.entity.CashOutAdapterResult; | |||
| import com.iformall.utils.BeanUtils; | |||
| import com.iformall.utils.Utility; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| /** | |||
| * 企业付款到零钱 | |||
| * @author iformall | |||
| * | |||
| */ | |||
| @Slf4j | |||
| @Service | |||
| public class WxCashOutQYFKAdapterService implements CashOutAdapterService{ | |||
| JSONObject errorRefundReqMap = JSON.parseObject("{\n" + | |||
| " \"NO_AUTH\": {\n" + | |||
| " \"detail\": \"没有该接口权限\",\n" + | |||
| " \"reason\": \"1. 用户账号被冻结,无法付款;2. 产品权限没有开通或者被风控冻结;3. 此IP地址不允许调用接口,如有需要请登录微信支付商户平台更改配置\",\n" + | |||
| " \"resolution\": \"请根据具体的错误返回描述做对应处理,如返回描述不够明确,请参考此处的错误原因做排查。\"\n" + | |||
| " },\n" + | |||
| " \"AMOUNT_LIMIT\": {\n" + | |||
| " \"detail\": \"金额超限\",\n" + | |||
| " \"reason\": \"1. 被微信风控拦截,最低单笔付款限额调整为5元。2. 低于最低单笔付款限额或者高于最高单笔付款限额\",\n" + | |||
| " \"resolution\": \"目前最低付款金额为1元,最高10万元,请确认是否付款金额超限。\"\n" + | |||
| " },\n" + | |||
| " \"PARAM_ERROR\": {\n" + | |||
| " \"detail\": \"参数错误\",\n" + | |||
| " \"reason\": \"1. 请求参数校验错误 2. 字符中包含非utf8字符 3. 商户号和appid没有绑定关系\\t\",\n" + | |||
| " \"resolution\": \"请参照原因检查您的请求参数是否正确\"\n" + | |||
| " },\n" + | |||
| " \"OPENID_ERROR\": {\n" + | |||
| " \"detail\": \"Openid错误\",\n" + | |||
| " \"reason\": \"Openid格式错误或者不属于商家公众账号\",\n" + | |||
| " \"resolution\": \"Openid与appid是有一一映射关系的,请确保正确使用。\"\n" + | |||
| " },\n" + | |||
| " \"SEND_FAILED\": {\n" + | |||
| " \"detail\": \"付款错误\",\n" + | |||
| " \"reason\": \"付款错误,请查单确认付款结果\",\n" + | |||
| " \"resolution\": \"请查单确认付款结果,以查单结果为准。\"\n" + | |||
| " },\n" + | |||
| " \"NOTENOUGH\": {\n" + | |||
| " \"detail\": \"余额不足\",\n" + | |||
| " \"reason\": \"您的付款帐号余额不足或资金未到账\\t\",\n" + | |||
| " \"resolution\": \"如果要继续付款必须使用原商户订单号重试\"\n" + | |||
| " },\n" + | |||
| " \"SYSTEMERROR\": {\n" + | |||
| " \"detail\": \"系统繁忙,请稍后再试\",\n" + | |||
| " \"reason\": \"微信内部接口调用发生错误\",\n" + | |||
| " \"resolution\": \"请先调用查询接口,查看此次付款结果,如结果为不明确状态(如订单号不存在),请务必使用原商户订单号进行重试。\"\n" + | |||
| " },\n" + | |||
| " \"NAME_MISMATCH\": {\n" + | |||
| " \"detail\": \"姓名校验出错\",\n" + | |||
| " \"reason\": \"收款人身份校验不通过\",\n" + | |||
| " \"resolution\": \"如果要继续付款必须使用原商户订单号重试\"\n" + | |||
| " },\n" + | |||
| " \"SIGN_ERROR\": {\n" + | |||
| " \"detail\": \"签名错误\",\n" + | |||
| " \"reason\": \"校验签名错误\",\n" + | |||
| " \"resolution\": \"请检查您的请求参数和签名密钥KEY是否正确,如果要继续付款必须使用原商户订单号重试\"\n" + | |||
| " },\n" + | |||
| " \"XML_ERROR\": {\n" + | |||
| " \"detail\": \"Post内容出错\",\n" + | |||
| " \"reason\": \"Post请求数据不是合法的xml格式内容\",\n" + | |||
| " \"resolution\": \"格式问题,请检查请求格式是否正确。\"\n" + | |||
| " },\n" + | |||
| " \"FATAL_ERROR\": {\n" + | |||
| " \"detail\": \"两次请求参数不一致\",\n" + | |||
| " \"reason\": \"两次请求商户单号一样,但是参数不一致\",\n" + | |||
| " \"resolution\": \"重入必须保证所有参数值都不变\"\n" + | |||
| " },\n" + | |||
| " \"FREQ_LIMIT\": {\n" + | |||
| " \"detail\": \"超过频率限制,请稍后再试\",\n" + | |||
| " \"reason\": \"接口请求频率超时接口限制\",\n" + | |||
| " \"resolution\": \"调用接口过于频繁,请稍后再试,如果要继续付款必须使用原商户订单号重试\"\n" + | |||
| " },\n" + | |||
| " \"MONEY_LIMIT\": {\n" + | |||
| " \"detail\": \"已经达到今日付款总额上限/已达到付款给此用户额度上限\",\n" + | |||
| " \"reason\": \"请关注接口的付款限额条件\",\n" + | |||
| " \"resolution\": \"付款额度已经超限,请参考接口使用条件,如果要继续付款必须使用原商户订单号重试\"\n" + | |||
| " },\n" + | |||
| " \"CA_ERROR\": {\n" + | |||
| " \"detail\": \"商户API证书校验出错\",\n" + | |||
| " \"reason\": \"请求没带商户API证书或者带上了错误的商户API证书\",\n" + | |||
| " \"resolution\": \"您使用的调用证书有误,请确认是否使用了正确的证书,可以前往商户平台重新下载,证书需与商户号对应,如果要继续付款必须使用原商户订单号重试\"\n" + | |||
| " },\n" + | |||
| " \"V2_ACCOUNT_SIMPLE_BAN\": {\n" + | |||
| " \"detail\": \"无法给未实名用户付款\",\n" + | |||
| " \"reason\": \"用户微信支付账户未实名,无法付款\",\n" + | |||
| " \"resolution\": \"不支持给未实名用户付款,如果要继续付款必须使用原商户订单号重试\"\n" + | |||
| " },\n" + | |||
| " \"PARAM_IS_NOT_UTF8\": {\n" + | |||
| " \"detail\": \"请求参数中包含非utf8编码字符\",\n" + | |||
| " \"reason\": \"接口规范要求所有请求参数都必须为utf8编码\",\n" + | |||
| " \"resolution\": \"微信接口使用编码是UTF-8,请确认,如果要继续付款必须使用原商户订单号重试\"\n" + | |||
| " },\n" + | |||
| " \"SENDNUM_LIMIT\": {\n" + | |||
| " \"detail\": \"该用户今日付款次数超过限制,如有需要请进入【微信支付商户平台-产品中心-企业付款到零钱-产品设置】进行修改\",\n" + | |||
| " \"reason\": \"该用户今日付款次数超过限制,如有需要请进入【微信支付商户平台-产品中心-企业付款到零钱-产品设置】进行修改\",\n" + | |||
| " \"resolution\": \"向用户付款的次数超限了,请参考接口使用条件,如果要继续付款必须使用原商户订单号重试\"\n" + | |||
| " },\n" + | |||
| " \"RECV_ACCOUNT_NOT_ALLOWED\": {\n" + | |||
| " \"detail\": \"收款账户不在收款账户列表\",\n" + | |||
| " \"reason\": \"收款账户不在收款账户列表\",\n" + | |||
| " \"resolution\": \"请登陆商户平台,查看产品中心企业付款到零钱的产品配置\"\n" + | |||
| " },\n" + | |||
| " \"PAY_CHANNEL_NOT_ALLOWED\": {\n" + | |||
| " \"detail\": \"本商户号未配置API发起能力\",\n" + | |||
| " \"reason\": \"本商户号未配置API发起能力\",\n" + | |||
| " \"resolution\": \"请登陆商户平台,查看产品中心企业付款到零钱的产品配置\"\n" + | |||
| " }\n" + | |||
| "}"); | |||
| JSONObject errorQueryReqMap = JSON.parseObject("{\n" + | |||
| " \"CA_ERROR\": {\n" + | |||
| " \"detail\": \"请求未携带证书,或请求携带的证书出错\",\n" + | |||
| " \"reason\": \"请求未携带证书,或请求携带的证书出错\",\n" + | |||
| " \"resolution\": \"到商户平台下载证书,请求带上证书后重试。\"\n" + | |||
| " },\n" + | |||
| " \"SIGN_ERROR\": {\n" + | |||
| " \"detail\": \"商户签名错误\",\n" + | |||
| " \"reason\": \"商户签名错误\",\n" + | |||
| " \"resolution\": \"按文档要求重新生成签名后再重试。\"\n" + | |||
| " },\n" + | |||
| " \"FREQ_LIMIT\": {\n" + | |||
| " \"detail\": \"受频率限制\",\n" + | |||
| " \"reason\": \"受频率限制\\t\",\n" + | |||
| " \"resolution\": \"请对请求做频率控制\"\n" + | |||
| " },\n" + | |||
| " \"XML_ERROR\": {\n" + | |||
| " \"detail\": \"请求的xml格式错误,或者post的数据为空\",\n" + | |||
| " \"reason\": \"请求的xml格式错误,或者post的数据为空\",\n" + | |||
| " \"resolution\": \"检查请求串,确认无误后重试。\"\n" + | |||
| " },\n" + | |||
| " \"PARAM_ERROR\": {\n" + | |||
| " \"detail\": \"参数错误\",\n" + | |||
| " \"reason\": \"参数错误\",\n" + | |||
| " \"resolution\": \"请查看err_code_des,修改设置错误的参数。\"\n" + | |||
| " },\n" + | |||
| " \"SYSTEMERROR\": {\n" + | |||
| " \"detail\": \"系统繁忙,请再试\",\n" + | |||
| " \"reason\": \"系统繁忙,请再试\\t\",\n" + | |||
| " \"resolution\": \"系统繁忙\"\n" + | |||
| " },\n" + | |||
| " \"NOT_FOUND\": {\n" + | |||
| " \"detail\": \"1、指定单号数据不存在 2、指定单号数据不存在,单据查询超过有效期\",\n" + | |||
| " \"reason\": \"1、指定单号数据不存在 2、指定单号数据不存在,单据查询超过有效期\",\n" + | |||
| " \"resolution\": \"1、查询单号对应的数据不存在,请使用正确的商户订单号查询2、如需查询超过有效期的单据,请登录到商户平台进行查询。\"\n" + | |||
| " }\n" + | |||
| "}"); | |||
| /** | |||
| * https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 | |||
| * | |||
| * 在wx_pay_account里面要设置merchant_cert_path,登陆特约商户的商户后台,账号中心-API安全。 | |||
| */ | |||
| @Override | |||
| public CashOutAdapterResult cashOut(WxAppinfo appInfo, WxPayAccount payAccount, WxCashOut cashOut) { | |||
| if (StringUtils.isBlank(payAccount.getMerchantCertPath())) { | |||
| return new CashOutAdapterResult(false, EnumCashOutStatus.FAIL.getCode(), "零钱支付失败。商户证书为空。请联系管理员配置。", null); | |||
| } | |||
| if (StringUtils.isBlank(payAccount.getMerchantApiKey())) { | |||
| return new CashOutAdapterResult(false, EnumCashOutStatus.FAIL.getCode(), "零钱支付失败。商户密钥为空。请联系管理员配置。", null); | |||
| } | |||
| WxCashOutP wxCashOutP = generateWxCashOutP(payAccount, appInfo, cashOut); | |||
| Map signMap = null; | |||
| try { | |||
| signMap = BeanUtils.toStringMap(wxCashOutP); | |||
| } catch (Exception e) { | |||
| log.error("零钱支付命令生辰: " + e.getMessage(),e); | |||
| return new CashOutAdapterResult(false, EnumCashOutStatus.FAIL.getCode(), "零钱支付签名异常", null); | |||
| } | |||
| String signAgent = WxPayment.createSign(signMap, payAccount.getMerchantApiKey()); | |||
| signMap.put("sign", signAgent); | |||
| String response = null; | |||
| try { | |||
| response = WxPay.transfers(signMap, payAccount.getMerchantCertPath(), payAccount.getSubMchId()); | |||
| } catch (Exception e) { | |||
| log.error("零钱支付异常: " + e.getMessage(),e); | |||
| return new CashOutAdapterResult(false, EnumCashOutStatus.FAIL.getCode(), "微信零钱支付接口调用异常", null); | |||
| } | |||
| log.info("微信零钱支付:" + wxCashOutP.toString() +", response: " + response.toString()); | |||
| return getReusltFromp(response,cashOut); | |||
| // if (payAccount.getType() == EnumPayMode.MCH.getCode()) { | |||
| // 普通商户模式 | |||
| // } | |||
| } | |||
| private WxCashOutP generateWxCashOutP(WxPayAccount payAccount,WxAppinfo appInfo,WxCashOut cashOut) { | |||
| String noncestr = Utility.generate32UUID(); | |||
| WxCashOutP wxCashOutP = new WxCashOutP(); | |||
| wxCashOutP.setMch_appid(appInfo.getAppId()); | |||
| wxCashOutP.setMchid(payAccount.getSubMchId()); | |||
| wxCashOutP.setNonce_str(noncestr); | |||
| wxCashOutP.setAmount(cashOut.getTotalFee()); | |||
| wxCashOutP.setCheck_name("NO_CHECK"); | |||
| wxCashOutP.setDesc("客户结算"); | |||
| wxCashOutP.setOpenid(cashOut.getReciveOpenId()); | |||
| wxCashOutP.setPartner_trade_no(String.valueOf(cashOut.getId())); | |||
| return wxCashOutP; | |||
| } | |||
| private CashOutAdapterResult getReusltFromp(String response,WxCashOut cashOut) throws MallinkException{ | |||
| Map<String, String> returnMap = WxPayment.xmlToMap(response); | |||
| String result_no = returnMap.get("result_code"); | |||
| if ("SUCCESS".equals(result_no)) { | |||
| log.info("微信零钱支付申请成功: " + returnMap.toString()); | |||
| String wxPayNo = returnMap.get("payment_no"); | |||
| String payTime = returnMap.get("payment_time"); | |||
| cashOut.setWxPayNo(wxPayNo); | |||
| cashOut.setWxPayTime(payTime); | |||
| return new CashOutAdapterResult(true,EnumCashOutStatus.SUCCESS.getCode(),"微信零钱支付申请成功",returnMap); | |||
| } else { | |||
| log.error("微信零钱支付申请失败: " + response); | |||
| String errMsg = ""; | |||
| JSONObject errObj = errorRefundReqMap.getJSONObject(result_no); | |||
| if (errObj != null) { | |||
| errMsg = errObj.toJSONString(); | |||
| } else { | |||
| errMsg = returnMap.get("err_code_des"); | |||
| } | |||
| return new CashOutAdapterResult(false, EnumCashOutStatus.FAIL.getCode(), errMsg, returnMap); | |||
| } | |||
| } | |||
| @Override | |||
| public CashOutAdapterResult queryCashOut(WxAppinfo appInfo, WxPayAccount payAccount, WxCashOut cashOut) { | |||
| if (StringUtils.isBlank(payAccount.getMerchantCertPath())) { | |||
| return new CashOutAdapterResult(false, EnumCashOutStatus.FAIL.getCode(), "零钱支付失败。商户证书为空。请联系管理员配置。", null); | |||
| } | |||
| WxCashOutQueryP wxCashOutP = generateQueryWxCashOutP(payAccount, appInfo, cashOut); | |||
| Map signMap = null; | |||
| try { | |||
| signMap = BeanUtils.toStringMap(wxCashOutP); | |||
| } catch (Exception e) { | |||
| log.error("零钱支付命令生辰: " + e.getMessage(),e); | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_CAST_ERROR.getCode(), "零钱支付签名异常"); | |||
| } | |||
| String signAgent = WxPayment.createSign(signMap, payAccount.getMerchantApiKey()); | |||
| signMap.put("sign", signAgent); | |||
| String response = null; | |||
| try { | |||
| response = WxPay.getTransferInfo(signMap, payAccount.getMerchantCertPath(), payAccount.getSubMchId()); | |||
| } catch (Exception e) { | |||
| log.error("零钱支付查询异常: " + e.getMessage(),e); | |||
| throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR.getCode(), "微信零钱支付查询接口异常"); | |||
| } | |||
| log.info("微信零钱支付查询:" + wxCashOutP.toString() + ", response: " + response.toString()); | |||
| return getReusltFromQueryp(response,cashOut); | |||
| } | |||
| private WxCashOutQueryP generateQueryWxCashOutP(WxPayAccount payAccount,WxAppinfo appInfo,WxCashOut cashOut) { | |||
| String noncestr = Utility.generate32UUID(); | |||
| WxCashOutQueryP wxCashOutQueryP = new WxCashOutQueryP(); | |||
| wxCashOutQueryP.setAppid(appInfo.getAppId()); | |||
| wxCashOutQueryP.setMch_id(payAccount.getSubMchId()); | |||
| wxCashOutQueryP.setNonce_str(noncestr); | |||
| wxCashOutQueryP.setPartner_trade_no(String.valueOf(cashOut.getId())); | |||
| return wxCashOutQueryP; | |||
| } | |||
| private CashOutAdapterResult getReusltFromQueryp(String response,WxCashOut cashOut) throws MallinkException{ | |||
| Map<String, String> returnMap = WxPayment.xmlToMap(response); | |||
| String result_no = returnMap.get("result_code"); | |||
| if ("SUCCESS".equals(result_no)) { | |||
| String realStatus = returnMap.get("status"); | |||
| if ("SUCCESS".equals(realStatus) || "PROCESSING".equals(realStatus)) { | |||
| String detailId = returnMap.get("detail_id"); | |||
| cashOut.setWxPayNo(detailId); | |||
| String wxPayTime = returnMap.get("transfer_time"); | |||
| cashOut.setWxPayTime(wxPayTime); | |||
| return new CashOutAdapterResult(true,EnumCashOutStatus.SUCCESS.getCode(),"微信零钱支付查询成功",returnMap); | |||
| }else if("FAILED".equals(realStatus)) { | |||
| String msg = returnMap.get("reason"); | |||
| return new CashOutAdapterResult(false,EnumCashOutStatus.FAIL.getCode(),msg,returnMap); | |||
| }else { | |||
| log.error("微信零钱支付查询失败,微信返回未知状态: " + response); | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_CAST_ERROR.getCode(), "微信零钱支付查询失败, 微信返回未知状态:"+realStatus); | |||
| } | |||
| } else { | |||
| log.error("微信零钱支付查询失败: " + response); | |||
| String errMsg = returnMap.get("err_code_des"); | |||
| String errCode = returnMap.get("err_code"); | |||
| if ("NOT_FOUND".equals(errCode)) { | |||
| return new CashOutAdapterResult(false,EnumCashOutStatus.FAIL.getCode(),errMsg,returnMap); | |||
| } | |||
| // JSONObject errObj = errorQueryReqMap.getJSONObject(result_no); | |||
| // if (errObj != null) { | |||
| // errMsg = errObj.toJSONString(); | |||
| // } else { | |||
| // errMsg = returnMap.get("err_code_des"); | |||
| // } | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_CAST_ERROR.getCode(), "微信零钱支付查询失败:"+errMsg); | |||
| } | |||
| } | |||
| } | |||
| @@ -34,6 +34,7 @@ import com.iformall.pay.WxPayOrderSQ; | |||
| import com.iformall.pay.WxPayment; | |||
| import com.iformall.service.QrCodeService; | |||
| import com.iformall.service.helper.WxPayOrderServiceHelper; | |||
| import com.iformall.service.order.entity.WxComposeChildOrderShare; | |||
| import com.iformall.service.pay.service.pay.PayAdapterService; | |||
| import com.iformall.service.pay.service.pay.entity.PayAdapterResult; | |||
| import com.iformall.service.pay.service.pay.entity.PayQueryAdapterResult; | |||
| @@ -56,6 +57,11 @@ public class BaseWxPayAdapterService { | |||
| @Autowired | |||
| MaUtil maUtil; | |||
| protected String getMerchantUid(WxPayOrder payOrder,WxOrder order) { | |||
| WxComposeChildOrderShare share = payOrder.getChildOrderShare(order.getId()); | |||
| return share.getMerchantUid(); | |||
| } | |||
| protected File getQrCode(WxAppinfo appinfo,String pageUrl,int type,String sceneParam) throws WxErrorException { | |||
| boolean isFmOpen = false; | |||
| @@ -88,11 +88,6 @@ public class WxMiniAppPaySFTAdapterService extends BaseWxPaySFTAdapterService i | |||
| return req; | |||
| } | |||
| private String getMerchantUid(WxPayOrder payOrder,WxOrder order) { | |||
| WxComposeChildOrderShare share = payOrder.getChildOrderShare(order.getId()); | |||
| return share.getMerchantUid(); | |||
| } | |||
| private SFTCombinePayCommonMiniAppReq generateCombinePayReqeust(WxPayAccount payAccount,WxComposeOrder composeOrder,List<WxOrder> childOrders,String productName, | |||
| WxPayOrder record,WxAppinfo appInfo,String openId,String appId,Date currentDate,EnumPayShare isShare) throws Exception { | |||
| @@ -122,13 +117,13 @@ public class WxMiniAppPaySFTAdapterService extends BaseWxPaySFTAdapterService i | |||
| PayAdapterResult par = new PayAdapterResult(); | |||
| if (StringUtils.isBlank(response)) { | |||
| par.setSuccess(false); | |||
| par.setMsg("pay v3 has no response."); | |||
| par.setMsg("pay SFT has no response."); | |||
| }else { | |||
| JSONObject result = JSON.parseObject(response); | |||
| String prepayId = result.getString("prepay_id"); | |||
| if (StringUtils.isBlank(prepayId)) { | |||
| par.setSuccess(false); | |||
| par.setMsg("pay v3 has no response."); | |||
| par.setMsg("pay SFT has no response."); | |||
| }else { | |||
| par.setSuccess(true); | |||
| par.setMsg("success."); | |||
| @@ -180,8 +175,8 @@ public class WxMiniAppPaySFTAdapterService extends BaseWxPaySFTAdapterService i | |||
| } else { | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"因电商收付通合单查询无法处理,暂时不支持合单支付"); | |||
| //合单支付/小程序支付 | |||
| // CombinePayCommonMiniAppReq payReq = generateCombinePayReqeust(payAccount,composeOrder,childOrders,productName,record,appInfo,openId,appInfo.getAppId(),currentDate,isShare); | |||
| // String response = WxPayV3.payCombineCommonWithMiniApp(payService, payReq); | |||
| // SFTCombinePayCommonMiniAppReq payReq = generateCombinePayReqeust(payAccount,composeOrder,childOrders,productName,record,appInfo,openId,appInfo.getAppId(),currentDate,isShare); | |||
| // String response = WxPayV3SFT.payCombineCommonWithMiniApp(payService, payReq); | |||
| // log.info("pay order v3," + payReq.toString() + ", response: " + response.toString()); | |||
| // return getOrderPResult(response); | |||
| } | |||
| @@ -233,8 +228,8 @@ public class WxMiniAppPaySFTAdapterService extends BaseWxPaySFTAdapterService i | |||
| req.setOut_trade_no(String.valueOf(oldRecord.getId())); | |||
| String response = WxPayV3SFT.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)); | |||
| log.error("pay common sft query response is null."+JSON.toJSONString(req)); | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"pay common sft query response is null."+JSON.toJSONString(req)); | |||
| } | |||
| return getPayCommonQueryResult(response); | |||
| }else { | |||
| @@ -0,0 +1,11 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.entity; | |||
| import lombok.Data; | |||
| @Data | |||
| public class V3CombineCloseItemOrder { | |||
| private String mchid;//子单发起方商户号,必须与发起方appid有绑定关系 | |||
| private String out_trade_no;//子单商户订单号 | |||
| private String sub_mchid;//二级商户号,服务商子商户的商户号,被合单方。直连商户不用传二级商户号 | |||
| } | |||
| @@ -0,0 +1,39 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.entity; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| import com.alibaba.fastjson.annotation.JSONField; | |||
| import com.iformall.domain.po.WxPayAccount; | |||
| import com.iformall.domain.po.WxPayOrder; | |||
| import com.iformall.service.order.entity.WxComposeChildOrderShare; | |||
| import lombok.Data; | |||
| @Data | |||
| public class V3CombineCloseReq { | |||
| private String combine_appid;//合单发起方的appid | |||
| @JSONField(serialize = false) | |||
| private String combine_out_trade_no;//不放到body | |||
| private List<V3CombineCloseItemOrder> sub_orders; | |||
| public void initSubOrders(WxPayAccount payAccount,WxPayOrder payOrder) { | |||
| List<WxComposeChildOrderShare> childs = payOrder.getAllChilds(); | |||
| if (null != childs) { | |||
| List<V3CombineCloseItemOrder> suborders = new ArrayList<V3CombineCloseItemOrder>(); | |||
| for (WxComposeChildOrderShare child : childs) { | |||
| V3CombineCloseItemOrder ccio = new V3CombineCloseItemOrder(); | |||
| ccio.setMchid(payAccount.getMchId()); | |||
| ccio.setSub_mchid(child.getMerchantUid()); | |||
| ccio.setOut_trade_no(String.valueOf(child.getOrderId())); | |||
| suborders.add(ccio); | |||
| } | |||
| if (suborders.size() > 0) { | |||
| this.setSub_orders(suborders); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.entity; | |||
| import com.iformall.domain.po.WxAppinfo; | |||
| import com.iformall.domain.po.WxOrder; | |||
| import com.iformall.domain.po.WxPayAccount; | |||
| import lombok.Data; | |||
| @Data | |||
| public class V3CombineItemOrder { | |||
| private String mchid;//子单发起方商户号,必须与发起方appid有绑定关系 | |||
| private String attach;//附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用 | |||
| private V3CombinePayAmountReq amount;//订单金额 | |||
| private String out_trade_no;//子单商户订单号 | |||
| private String sub_mchid;//二级商户号,服务商子商户的商户号,被合单方。直连商户不用传二级商户号 | |||
| private String description;//商品描述,需传入应用市场上的APP名字-实际商品名称 | |||
| private V3PaySettleReq settle_info;//结算信息 | |||
| public void initByWxOrder(WxPayAccount payAccount,WxOrder order,String subMchId,WxAppinfo appInfo,String productName,String extParam) { | |||
| this.mchid = payAccount.getMchId(); | |||
| this.attach = extParam; | |||
| V3CombinePayAmountReq amount = new V3CombinePayAmountReq(); | |||
| amount.setTotal_amount(order.getPayment()); | |||
| this.setAmount(amount); | |||
| this.out_trade_no = String.valueOf(order.getId()); | |||
| this.sub_mchid = subMchId; | |||
| this.description = appInfo.getName()+"-"+productName; | |||
| V3PaySettleReq settle = new V3PaySettleReq(); | |||
| settle.setProfit_sharing(true); | |||
| this.setSettle_info(settle); | |||
| } | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.entity; | |||
| import lombok.Data; | |||
| @Data | |||
| public class V3CombinePayAmountReq { | |||
| private int total_amount;//总金额,单位为分。 | |||
| private String currency="CNY";//货币类型 | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.entity; | |||
| import java.util.List; | |||
| import lombok.Data; | |||
| @Data | |||
| public class V3CombinePayCommonMiniAppReq { | |||
| private String combine_appid;//合单发起方的appid | |||
| private String combine_mchid;//合单发起方商户号,服务商和电商模式下,传服务商商户号 | |||
| private String combine_out_trade_no;//合单支付总订单号 | |||
| private List<V3CombineItemOrder> sub_orders;//商品描述 | |||
| private V3CombinePayerReq combine_payer_info;// 支付者信息 | |||
| private String sub_appid;//子商户申请的应用ID | |||
| private String notify_url;//接收微信支付异步通知回调地址 | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.entity; | |||
| import lombok.Data; | |||
| @Data | |||
| public class V3CombinePayerReq { | |||
| private String sub_openid;//使用合单appid获取的对应用户openid | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.entity; | |||
| import lombok.Data; | |||
| @Data | |||
| public class V3PayAmountReq { | |||
| private int total;//总金额,单位为分。 | |||
| private String currency="CNY";//货币类型 | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.entity; | |||
| import lombok.Data; | |||
| @Data | |||
| public class V3PayCommonMiniAppReq { | |||
| private String sp_appid;//服务商应用ID | |||
| private String sp_mchid;//服务商户号 | |||
| private String sub_appid;//子商户申请的应用ID | |||
| private String sub_mchid;//二级商户号 | |||
| private String description;//商品描述 | |||
| private String out_trade_no;//商户订单号 | |||
| private String notify_url;//通知地址,不允许携带查询串 | |||
| private V3PaySettleReq settle_info;//结算信息 | |||
| private V3PayAmountReq amount;//订单金额 | |||
| private V3PayerReq payer;//支付者 | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.entity; | |||
| import com.alibaba.fastjson.annotation.JSONField; | |||
| import lombok.Data; | |||
| @Data | |||
| public class V3PayQueryReq { | |||
| private String sp_mchid;//服务商户号。 | |||
| private String sub_mchid;//二级商户号 | |||
| @JSONField(serialize = false) | |||
| private String out_trade_no;//商户订单号 | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.entity; | |||
| import lombok.Data; | |||
| @Data | |||
| public class V3PaySettleReq { | |||
| private boolean profit_sharing;//是否指定分账 | |||
| private int subsidy_amount;//补差金额;profit_sharing为true时,该金额才生效 | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.entity; | |||
| import lombok.Data; | |||
| @Data | |||
| public class V3PayerReq { | |||
| private String sub_openid;//用户在子商户appid下的唯一标识 | |||
| } | |||
| @@ -0,0 +1,303 @@ | |||
| package com.iformall.service.pay.service.pay.wx.v3.miniApp.appPay; | |||
| import java.io.File; | |||
| import java.util.ArrayList; | |||
| import java.util.Date; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import com.iformall.domain.po.*; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Service; | |||
| import com.alibaba.fastjson.JSON; | |||
| import com.alibaba.fastjson.JSONArray; | |||
| import com.alibaba.fastjson.JSONObject; | |||
| import com.github.binarywang.wxpay.service.WxPayService; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.common.Result; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.enums.EnumOrderStatus; | |||
| import com.iformall.enums.EnumPayMchType; | |||
| import com.iformall.enums.EnumPayMode; | |||
| import com.iformall.enums.EnumPayShare; | |||
| import com.iformall.enums.EnumPayStatus; | |||
| import com.iformall.exception.MallinkException; | |||
| import com.iformall.pay.WxPay; | |||
| import com.iformall.pay.WxPayOrderP; | |||
| import com.iformall.pay.WxPayOrderSP; | |||
| import com.iformall.pay.WxPayV3; | |||
| import com.iformall.pay.WxPayV3SFT; | |||
| import com.iformall.pay.WxPayment; | |||
| 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.SFTCombineItemOrder; | |||
| import com.iformall.service.pay.service.pay.wx.sft.entity.SFTCombinePayCommonMiniAppReq; | |||
| import com.iformall.service.pay.service.pay.wx.sft.entity.SFTCombinePayerReq; | |||
| import com.iformall.service.pay.service.pay.wx.sft.entity.SFTPayAmountReq; | |||
| import com.iformall.service.pay.service.pay.wx.sft.entity.SFTPayCommonMiniAppReq; | |||
| import com.iformall.service.pay.service.pay.wx.sft.entity.SFTPayQueryReq; | |||
| import com.iformall.service.pay.service.pay.wx.sft.entity.SFTPaySettleReq; | |||
| import com.iformall.service.pay.service.pay.wx.sft.entity.SFTPayerReq; | |||
| import com.iformall.service.pay.service.pay.wx.v2.BaseWxPayV2AdapterService; | |||
| import com.iformall.service.pay.service.pay.wx.v3.BaseWxPayV3AdapterService; | |||
| import com.iformall.service.pay.service.pay.wx.v3.entity.V3CombineItemOrder; | |||
| import com.iformall.service.pay.service.pay.wx.v3.entity.V3CombinePayCommonMiniAppReq; | |||
| import com.iformall.service.pay.service.pay.wx.v3.entity.V3CombinePayerReq; | |||
| import com.iformall.service.pay.service.pay.wx.v3.entity.V3PayAmountReq; | |||
| import com.iformall.service.pay.service.pay.wx.v3.entity.V3PayCommonMiniAppReq; | |||
| import com.iformall.service.pay.service.pay.wx.v3.entity.V3PayQueryReq; | |||
| import com.iformall.service.pay.service.pay.wx.v3.entity.V3PaySettleReq; | |||
| import com.iformall.service.pay.service.pay.wx.v3.entity.V3PayerReq; | |||
| import com.iformall.utils.BeanUtils; | |||
| import com.iformall.utils.MaUtil; | |||
| import com.iformall.utils.MapUtil; | |||
| import com.iformall.utils.Utility; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| @Slf4j | |||
| @Service | |||
| public class WxMiniAppPayV3AdapterService extends BaseWxPayV3AdapterService implements CDrivingPayService{ | |||
| @Autowired | |||
| MaUtil maUtil; | |||
| private V3PayCommonMiniAppReq generatePayCommonRequest(WxPayAccount payAccount,WxComposeOrder composeOrder,String productName, WxPayOrder record,String openId,WxAppinfo appInfo,Date currentDate) throws Exception { | |||
| V3PayCommonMiniAppReq req = new V3PayCommonMiniAppReq(); | |||
| req.setSp_appid(String.valueOf(appInfo.getParentAppId())); | |||
| req.setSp_mchid(payAccount.getMchId()); | |||
| req.setSub_appid(appInfo.getAppId()); | |||
| req.setSub_mchid(getMerchantUid(record,composeOrder.getSingleOrder())); | |||
| req.setDescription(appInfo.getName()+"-"+productName); | |||
| req.setOut_trade_no(String.valueOf(composeOrder.getMainOrderId())); | |||
| req.setNotify_url(payAccount.getPayNotifyV3Url()); | |||
| V3PaySettleReq settle = new V3PaySettleReq(); | |||
| settle.setProfit_sharing(true); | |||
| req.setSettle_info(settle); | |||
| V3PayAmountReq amout = new V3PayAmountReq(); | |||
| amout.setTotal(record.getPayAmount()); | |||
| req.setAmount(amout); | |||
| V3PayerReq payer = new V3PayerReq(); | |||
| payer.setSub_openid(openId); | |||
| req.setPayer(payer); | |||
| return req; | |||
| } | |||
| private PayAdapterResult getOrderPResult(String response,WxPayOrder record) { | |||
| 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 prepayId = result.getString("prepay_id"); | |||
| if (StringUtils.isBlank(prepayId)) { | |||
| par.setSuccess(false); | |||
| par.setMsg("pay v3 has no response."); | |||
| }else { | |||
| par.setSuccess(true); | |||
| par.setMsg("success."); | |||
| record.setPrepayId(prepayId); | |||
| record.setUpdateTime(new Date()); | |||
| } | |||
| } | |||
| return par; | |||
| } | |||
| private V3CombinePayCommonMiniAppReq generateCombinePayReqeust(WxPayAccount payAccount,WxComposeOrder composeOrder,List<WxOrder> childOrders,String productName, | |||
| WxPayOrder record,WxAppinfo appInfo,String openId,String appId,Date currentDate,EnumPayShare isShare) throws Exception { | |||
| V3CombinePayCommonMiniAppReq req = new V3CombinePayCommonMiniAppReq(); | |||
| req.setCombine_appid(appInfo.getAppId()); | |||
| req.setCombine_mchid(payAccount.getMchId()); | |||
| req.setCombine_out_trade_no(String.valueOf(composeOrder.getMainOrderId())); | |||
| List<V3CombineItemOrder> suborders = new ArrayList<V3CombineItemOrder>(); | |||
| for (WxOrder order : childOrders) { | |||
| V3CombineItemOrder so = new V3CombineItemOrder(); | |||
| String subMchId = getMerchantUid(record,order); | |||
| so.initByWxOrder(payAccount, order, subMchId, appInfo, productName, String.valueOf(record.getId())); | |||
| suborders.add(so); | |||
| } | |||
| req.setSub_orders(suborders); | |||
| V3CombinePayerReq payer = new V3CombinePayerReq(); | |||
| payer.setSub_openid(openId); | |||
| req.setCombine_payer_info(payer); | |||
| req.setNotify_url(payAccount.getPayNotifyV3Url()); | |||
| return req; | |||
| } | |||
| @Override | |||
| public PayAdapterResult pay(WxPayAccount payAccount,WxPayOrder record,WxComposeOrder composeOrder,List<WxOrder> childOrders, | |||
| String productName,EnumPayShare isShare,WxAppinfo appInfo,Date currentDate, PayExtraParam params) throws Exception { | |||
| if (null == params ) { | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"openId为空"); | |||
| } | |||
| String openId = (String) params.getValue("openId"); | |||
| if (StringUtils.isBlank(openId) ) { | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"openId为空"); | |||
| } | |||
| if (payAccount.getType() == EnumPayMode.MCH.getCode()) { | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"当前支付配置非服务商模式,不能使用该产品."); | |||
| } | |||
| //只能直连模式, | |||
| if (payAccount.getMchType() != EnumPayMchType.DIRECT.getCode()) { | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"当前支付版本不支持非直连模式"); | |||
| } | |||
| WxPayService payService = maUtil.getWxPayService(appInfo, payAccount); | |||
| if (null == childOrders || childOrders.size() <= 0 ) { | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"子订单为空"); | |||
| } | |||
| /* | |||
| * 普通支付/小程序支付 | |||
| * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_2_3.shtml | |||
| */ | |||
| if (childOrders.size() == 1) { | |||
| V3PayCommonMiniAppReq payReq = generatePayCommonRequest(payAccount,composeOrder,productName,record,openId,appInfo,currentDate); | |||
| String response = WxPayV3.payCommonWithMiniApp(payService, payReq); | |||
| log.info("pay order v3, " + payReq.toString() + ", response: " + response.toString()); | |||
| return getOrderPResult(response,record); | |||
| } else { | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"合单查询无法处理,暂时不支持合单支付"); | |||
| //合单支付/小程序支付 | |||
| // V3CombinePayCommonMiniAppReq payReq = generateCombinePayReqeust(payAccount,composeOrder,childOrders,productName,record,appInfo,openId,appInfo.getAppId(),currentDate,isShare); | |||
| // String response = WxPayV3.payCombineCommonWithMiniApp(payService, payReq); | |||
| // log.info("pay order v3," + payReq.toString() + ", response: " + response.toString()); | |||
| // return getOrderPResult(response); | |||
| } | |||
| } | |||
| 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 getPayCombineQueryResult(String response) { | |||
| JSONObject result = JSON.parseObject(response); | |||
| //TODO 只有各子订单的状态,没有统一的状态,暂时无法处理 | |||
| JSONArray subOrders = result.getJSONArray("sub_orders"); | |||
| ///######待处理 | |||
| 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+"]"); | |||
| } | |||
| PayQueryAdapterResult qresult = new PayQueryAdapterResult(payStatus.getCode(), tradeStateDesc,null, response,transtionId,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()); | |||
| req.setSub_mchid(oldRecord.getSingleChildOrderShare().getMerchantUid()); | |||
| req.setOut_trade_no(String.valueOf(oldRecord.getId())); | |||
| 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); | |||
| }else { | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"合单查询无法处理,暂时不支持合单支付查询"); | |||
| // req.setOut_trade_no(String.valueOf(oldRecord.getId())); | |||
| // String response = WxPayV3.payCombineQuery(payService, req); | |||
| // if (StringUtils.isBlank(response)){ | |||
| // log.error("pay combine 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 getPayCombineQueryResult(response); | |||
| } | |||
| } | |||
| @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 { | |||
| WxPayService payService = maUtil.getWxPayService(appInfo, payAccount); | |||
| if (!record.isMulity()) { | |||
| SFTPayQueryReq req = new SFTPayQueryReq(); | |||
| req.setSp_mchid(payAccount.getMchId()); | |||
| req.setSub_mchid(record.getSingleChildOrderShare().getMerchantUid()); | |||
| req.setOut_trade_no(String.valueOf(record.getId())); | |||
| WxPayV3SFT.payCommonClose(payService, req); | |||
| return new PayAdapterResult(true, "close success", req, null); | |||
| }else { | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"因合单查询无法处理,暂时不支持合单支付关闭"); | |||
| // CombineCloseReq req = new CombineCloseReq(); | |||
| // req.setCombine_appid(appInfo.getAppId()); | |||
| // req.setCombine_out_trade_no(String.valueOf(record.getId())); | |||
| // req.initSubOrders(payAccount, record); | |||
| // | |||
| // String response = WxPayV3.payCombineClose(payService, req); | |||
| // return new PayAdapterResult(true, "close success", req, 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 PayAdapterResult noCreatePay(WxPayOrder record, WxComposeOrder composeOrder, List<WxOrder> childOrders) { | |||
| throw new MallinkException(ErrorCode.SYS_PARAMETER_ERROR); | |||
| } | |||
| } | |||