微信分账相关内容,简化版:
分账相关内容:
1:确定对应微信支付的类型,需要区分是v2还是v3,发起分账前需要针对在支付的时候新增参数,确定订单属于分账订单类型:
如果是v2类型支付的话需要新增profit_sharing参数,字符串类型:
pay.weixin.qq.com/doc/v2/merc…
如果是v3类型的分账支付的话,字段名称一样,类型为布尔类型:
pay.weixin.qq.com/doc/v3/merc…
2:分账异常提示内容
1:订单刚发起,暂时不能分账
相关官方文档建议等待一分钟后发起:
原先处理是下单后调用,后面改为定时任务统一处理分账发起以及分账回退的内容
2:appId与openId不一致的问题
如果是个人分账的话小程序用户的openid和 对应app微信授权登录用户的openid不一样,需要区分好,可能混用了openid,比如说app支付了,分账的openid是对应小程序的,会出现异常。
相关文档汇总:
相关工具类内容:
原先项目支付接入的是v2的微信支付,找了相关v2的sdk接入:封装了一些实体类啥的,不用像微信小店api接入那样,另外再根据文档在多写一些内容:
binarywang里面的xstream的话版本低有风险,另外的引入了一个高版本的
<properties>
//...
<binarywang.version>4.1.0</binarywang.version>
</properties>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>${binarywang.version}</version>
<exclusions>
<exclusion>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.19</version>
</dependency>
package com.test.service
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingQueryRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingQueryResult;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingReceiverRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingReturnQueryRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingReturnRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingReturnResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.ProfitSharingService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
/**
* @Author vnjohn
* @since 2022/10/26
*/
@Slf4j
public abstract class AbstractWxPayService {
public static final String SUCCESS_STRING = "SUCCESS";
public static final String HMAC_SHA256 = "HMAC-SHA256";
public static final String MD5 = "MD5";
/**
* 证书内容缓存redis key前缀
*/
public final static String FILE_CERT_REDIS_PREFIX = "cert_data_";
/**
* 证书文件路径连接符
*/
public final static String FILE_CERT_URL_JOIN = "/";
private static volatile WxPayService wxPayService;
// /**
// * 获取支付配置服务实例
// *
// * @param payConfig
// * @param keyContext
// * @return
// */
// protected static WxPayService getWxPayServiceInstance(PayConfig payConfig, byte[] keyContext) {
// if (null == wxPayService) {
// synchronized (WxPayService.class) {
// if (null == wxPayService) {
// wxPayService = getWxPayService(payConfig, keyContext);
// }
// }
// }
// return wxPayService;
// }
/**
* 获取支付配置服务实例
*
* @param payConfig
* @param keyContext
* @return
*/
protected static WxPayService getWxPayServiceInstance(PayConfig payConfig, byte[] keyContext) {
if (null == wxPayService) {
synchronized (WxPayService.class) {
if (null == wxPayService) {
wxPayService = getWxPayService(payConfig,keyContext);
}
}
}
return wxPayService;
}
/**
* 添加配置信息
*
* @return
*/
public static WxPayService getWxPayService(PayConfig payConfig, byte[] keyContext) {
log.info("添加微信配置信息:{},keyContext:{}", JSONUtil.toJsonStr(payConfig), keyContext);
WxPayConfig wxPayConfig = new WxPayConfig();
wxPayConfig.setSignType(MD5);
// wxPayConfig.setKeyPath(payConfig.getCertPath());
wxPayConfig.setKeyContent(keyContext);
wxPayConfig.setAppId(StringUtils.trimToNull(payConfig.getWxAppId()));
wxPayConfig.setMchId(StringUtils.trimToNull(payConfig.getMchId()));
if (StrUtil.isNotBlank(payConfig.getSubMchId())) {
wxPayConfig.setSubAppId(payConfig.getSubMchId());
wxPayConfig.setSubMchId(payConfig.getSubMchId());
}
wxPayConfig.setMchKey(StringUtils.trimToNull(payConfig.getMchKey()));
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(wxPayConfig);
return wxPayService;
}
/**
* 微信分账付款入口,返回外部交易的订单号
*
* @param payConfig
* @return
*/
public String pay(PayConfig payConfig) {
if (payConfig == null) {
log.warn("支付配置为空,不发起转账行为");
return null;
}
if (StringUtils.isEmpty(payConfig.getOutTransactionId())) {
log.error("无法进行前置分账,微信订单号为空");
return null;
}
// boolean prePayResult;
// try {
// 前置操作分账账号在微信平台提前配置,无需每次都调用api
// prePayResult = preparePay(payConfig);
// } catch (Exception e) {
// log.error("微信付款前的准备发生异常:{}", e.getMessage(), e);
// // 暂时返回 null 预处理下一条
// return null;
// }
String resStr = null;
try {
resStr = beginPay(payConfig);
} catch (Exception e) {
log.error("微信分账发生异常:{}", e.getMessage(), e);
return null;
}
return resStr;
}
/**
* 微信分账退款入口,返回外部交易的订单号
*
* @param payConfig
* @return
*/
public ProfitSharingReturnResult refund(PayConfig payConfig,ProfitSharingReturnRequest profitSharingReturnRequest) {
if (payConfig == null) {
log.warn("支付配置为空,不发起转账行为");
return null;
}
if (StringUtils.isEmpty(payConfig.getOutTransactionId())) {
log.error("无法进行前置分账回退,微信订单号为空");
return null;
}
ProfitSharingReturnResult resStr = null;
try {
resStr = refundSharingOrder(payConfig, profitSharingReturnRequest);
} catch (Exception e) {
log.error("微信分账发生异常:{}", e.getMessage(), e);
return null;
}
return resStr;
}
/**
* 分账前置工作
*
* @param payConfig
* @return
* @throws Exception
*/
protected abstract boolean preparePay(PayConfig payConfig) throws Exception;
/**
* 请求分账
*
* @param payConfig
* @return
* @throws Exception
*/
protected abstract String beginPay(PayConfig payConfig) throws Exception;
/**
* 分账回退
*
* @param payConfig
* @return
* @throws Exception
*/
protected abstract Map<String, String> afterPay(PayConfig payConfig) throws Exception;
/**
* 创建支付随机字符串
*
* @return
*/
protected static String getNonceStr() {
return RandomStringUtils.randomAlphanumeric(32);
}
protected abstract byte[] getCertBytes(String mchId, String certFilePath);
/**
* 构建sha256参数的签名值
*
* @param params
* @param paternerKey
* @return
* @throws UnsupportedEncodingException
*/
public static String getSha256Sign(Map<String, String> params, String paternerKey) throws UnsupportedEncodingException {
String stringSignTemp = createSign(params, false) + "&key=" + paternerKey;
return HMACUtil.sha256_HMAC(stringSignTemp, paternerKey).toUpperCase();
}
/**
* 构造签名
*
* @param params
* @param encode
* @return
* @throws UnsupportedEncodingException
*/
protected static String createSign(Map<String, String> params, boolean encode) throws UnsupportedEncodingException {
Set<String> keysSet = params.keySet();
Object[] keys = keysSet.toArray();
Arrays.sort(keys);
StringBuffer temp = new StringBuffer();
boolean first = true;
for (Object key : keys) {
// 参数为空不参与签名
if (key == null || StringUtils.isEmpty(params.get(key))) {
continue;
}
if (first) {
first = false;
} else {
temp.append("&");
}
temp.append(key).append("=");
Object value = params.get(key);
String valueStr = "";
if (null != value) {
valueStr = value.toString();
}
if (encode) {
temp.append(URLEncoder.encode(valueStr, "UTF-8"));
} else {
temp.append(valueStr);
}
}
return temp.toString();
}
/**
* 分账信息查询
* @param profitSharingQueryRequest
* @return
*/
public ProfitSharingQueryResult profitSharingQuery(ProfitSharingQueryRequest profitSharingQueryRequest){
ProfitSharingQueryResult profitSharingQueryResult = null;
ProfitSharingService profitSharingService = wxPayService.getProfitSharingService();
try{
profitSharingQueryResult = profitSharingService.profitSharingQuery(profitSharingQueryRequest);
}catch (Exception e) {
log.error("微信分账信息查询异常:{}", e.getMessage(), e);
return null;
}
return profitSharingQueryResult;
}
/**
* 分账回退
* @param profitSharingQueryRequest
* @return
*/
public ProfitSharingReturnResult refundSharingOrder(PayConfig payConfig,ProfitSharingReturnRequest profitSharingReturnRequest){
ProfitSharingReturnResult profitSharingReturnResult = null;
ProfitSharingService profitSharingService = AbstractWxPayService.getWxPayServiceInstance(payConfig,getCertBytes(payConfig.getMchId(), payConfig.getCertPath())).getProfitSharingService();
try{
profitSharingReturnResult = profitSharingService.profitSharingReturn(profitSharingReturnRequest);
}catch (Exception e) {
log.error("微信分账回退发生异常:{}", e.getMessage(), e);
return null;
}
return profitSharingReturnResult;
}
/**
* 分账回退查询
* @param profitSharingQueryRequest
* @return
*/
public ProfitSharingReturnResult refundQuery(ProfitSharingReturnQueryRequest returnQueryRequest){
ProfitSharingReturnResult profitSharingReturnResult = null;
ProfitSharingService profitSharingService = wxPayService.getProfitSharingService();
try{
profitSharingReturnResult = profitSharingService.profitSharingReturnQuery(returnQueryRequest);
}catch (Exception e) {
log.error("微信分账回退发生异常:{}", e.getMessage(), e);
return null;
}
return profitSharingReturnResult;
}
}
//payConfig 需要保持和对应支付时的一些参数一致 v2的话sdk CertPath需要传入对应的证书地址
//payConfig.setWxAppId(accountSharingMerchant.getWxAppId());
//payConfig.setMchKey(wxDevelopProperties.getMchSecret());
//payConfig.setMchId(wxDevelopProperties.getMchId());
//payConfig.setCertPath(cert_path);
其他内容:
自动续费的相关流程以及文档:
第三方交互,都是调用,以及回调处理,要么是多组回调事件区分,要么就是单个事件多个状态位的区分
针对首次自动续费的话,多一次签约api的发起:
相关配置内容:
1:分账商户后台 添加对应的分账方,也可以用通过相关api调用添加对应的分账方:
2:设置相关分账的最大比例: