mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2025-04-05 17:38:05 +08:00
🆕 #2627【微信支付】增加V3分账动账通知解析方法
This commit is contained in:
parent
40f10de24b
commit
42c57b6de9
@ -0,0 +1,142 @@
|
||||
package com.github.binarywang.wxpay.bean.profitsharingV3;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
*
|
||||
* 微信V3接口
|
||||
* 通用通知实体
|
||||
*
|
||||
* @author yuanbo
|
||||
* @create 2022-04-26-21:33 PM
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class ProfitSharingNotifyData implements Serializable{
|
||||
|
||||
|
||||
private static final long serialVersionUID = 4242383310854692441L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:通知ID
|
||||
* 是否必填:是
|
||||
* 描述:通知的唯一ID
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("id")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:通知创建时间
|
||||
* 是否必填:是
|
||||
* 描述:通知创建的时间,Rfc3339标准
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("create_time")
|
||||
private String createTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:通知数据类型
|
||||
* 是否必填:是
|
||||
* 描述:通知的资源数据类型
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("resource_type")
|
||||
private String resourceType;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:通知类型
|
||||
* 是否必填:是
|
||||
* 描述:通知的类型
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("event_type")
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:通知数据
|
||||
* 是否必填:是
|
||||
* 描述:通知资源数据
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("resource")
|
||||
private Resource resource;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:通知简要说明
|
||||
* 是否必填:是
|
||||
* 描述:通知简要说明
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("summary")
|
||||
private String summary;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class Resource implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8530711804335261449L;
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:加密算法类型
|
||||
* 是否必填:是
|
||||
* 描述:对分账结果数据进行加密的加密算法,目前只支持AEAD_AES_256_GCM
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("algorithm")
|
||||
private String algorithm;
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:加密前的对象类型
|
||||
* 是否必填:是
|
||||
* 描述:加密前的对象类型,分账动账通知的类型为profitsharing
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("original_type")
|
||||
private String originalType;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:数据密文
|
||||
* 是否必填:是
|
||||
* 描述:Base64编码后的分账结果数据密文
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("ciphertext")
|
||||
private String cipherText;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:随机串
|
||||
* 是否必填:是
|
||||
* 描述:加密使用的随机串
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("nonce")
|
||||
private String nonce;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:附加数据
|
||||
* 是否必填:否
|
||||
* 描述:附加数据
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("associated_data")
|
||||
private String associatedData;
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
package com.github.binarywang.wxpay.bean.profitsharingV3;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
*
|
||||
* 微信V3接口
|
||||
* 分账动账通知解密后数据实体
|
||||
*
|
||||
* @author yuanbo
|
||||
* @create 2022-04-26-21:08 PM
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class ProfitSharingNotifyResult implements Serializable {
|
||||
|
||||
|
||||
private static final long serialVersionUID = -2875006651351414624L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:直连商户号
|
||||
* 是否必填:是
|
||||
* 描述:直连模式分账发起和出资商户
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("mchid")
|
||||
private String mchId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:微信订单号
|
||||
* 是否必填:是
|
||||
* 描述:微信支付订单号
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("transaction_id")
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:微信分账/回退单号
|
||||
* 是否必填:是
|
||||
* 描述:微信分账/回退单号
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("order_id")
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商户分账/回退单号
|
||||
* 是否必填:是
|
||||
* 描述:分账方系统内部的分账/回退单号
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("out_order_no")
|
||||
private String outOrderNo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:分账接收方
|
||||
* 是否必填:是
|
||||
* 描述:分账接收方对象
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("receiver")
|
||||
private Receiver receiver;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:成功时间
|
||||
* 是否必填:是
|
||||
* 描述:成功时间,Rfc3339标准
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("success_time")
|
||||
private String successTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class Receiver implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -931070141604645363L;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:分账接收方类型
|
||||
* 是否必填:是
|
||||
* 描述:MERCHANT_ID:商户号(mch_id或者sub_mch_id)
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("type")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:分账接收方账号
|
||||
* 是否必填:是
|
||||
* 描述:申请本功能商户号
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("account")
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:分账动账金额
|
||||
* 是否必填:是
|
||||
* 描述:分账动账金额,单位为分,只能为整数
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("amount")
|
||||
private Integer amount;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:分账/回退描述
|
||||
* 是否必填:是
|
||||
* 描述:分账/回退描述
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("description")
|
||||
private String description;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.github.binarywang.wxpay.service;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
|
||||
import com.github.binarywang.wxpay.bean.profitsharingV3.*;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
|
||||
@ -161,4 +162,22 @@ public interface ProfitSharingV3Service {
|
||||
*/
|
||||
ProfitSharingReceiver deleteProfitSharingReceiver(ProfitSharingReceiver receiver) throws WxPayException;
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 分账动账通知
|
||||
*
|
||||
* 分账或分账回退成功后,微信会把相关变动结果发送给分账接收方(只支持商户)。
|
||||
* 对后台通知交互时,如果微信收到应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_10.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @param notifyData 分账通知实体
|
||||
* @param header 分账通知头 {@link SignatureHeader}
|
||||
* @return {@link ProfitSharingNotifyData} 资源对象
|
||||
* @throws WxPayException the wx pay exception
|
||||
* @see <a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_10.shtml">微信文档</a>
|
||||
*/
|
||||
ProfitSharingNotifyData getProfitSharingNotifyData(String notifyData, SignatureHeader header) throws WxPayException;
|
||||
|
||||
}
|
||||
|
@ -1,15 +1,24 @@
|
||||
package com.github.binarywang.wxpay.service.impl;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
|
||||
import com.github.binarywang.wxpay.bean.profitsharingV3.*;
|
||||
import com.github.binarywang.wxpay.config.WxPayConfig;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
import com.github.binarywang.wxpay.service.ProfitSharingV3Service;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import com.github.binarywang.wxpay.v3.auth.Verifier;
|
||||
import com.github.binarywang.wxpay.v3.util.AesUtils;
|
||||
import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 微信支付V3-资金应用-分账Service
|
||||
*
|
||||
@ -82,4 +91,48 @@ public class ProfitSharingV3ServiceImpl implements ProfitSharingV3Service {
|
||||
String result = this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request));
|
||||
return GSON.fromJson(result, ProfitSharingReceiver.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfitSharingNotifyData getProfitSharingNotifyData(String notifyData, SignatureHeader header) throws WxPayException {
|
||||
ProfitSharingNotifyData response = parseNotifyData(notifyData, header);
|
||||
ProfitSharingNotifyData.Resource resource = response.getResource();
|
||||
String cipherText = resource.getCipherText();
|
||||
String associatedData = resource.getAssociatedData();
|
||||
String nonce = resource.getNonce();
|
||||
String apiV3Key = this.payService.getConfig().getApiV3Key();
|
||||
try {
|
||||
String result = AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key);
|
||||
ProfitSharingNotifyData notifyResult = GSON.fromJson(result, ProfitSharingNotifyData.class);
|
||||
return notifyResult;
|
||||
} catch (GeneralSecurityException | IOException e) {
|
||||
throw new WxPayException("解析报文异常!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ProfitSharingNotifyData parseNotifyData(String data, SignatureHeader header) throws WxPayException {
|
||||
if (Objects.nonNull(header) && !this.verifyNotifySign(header, data)) {
|
||||
throw new WxPayException("非法请求,头部信息验证失败");
|
||||
}
|
||||
return GSON.fromJson(data, ProfitSharingNotifyData.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验通知签名
|
||||
*
|
||||
* @param header 通知头信息
|
||||
* @param data 通知数据
|
||||
* @return true:校验通过 false:校验不通过
|
||||
*/
|
||||
private boolean verifyNotifySign(SignatureHeader header, String data) throws WxPayException {
|
||||
String beforeSign = String.format("%s\n%s\n%s\n",
|
||||
header.getTimeStamp(),
|
||||
header.getNonce(),
|
||||
data);
|
||||
Verifier verifier = this.payService.getConfig().getVerifier();
|
||||
if (verifier == null) {
|
||||
throw new WxPayException("证书检验对象为空");
|
||||
}
|
||||
return verifier.verify(header.getSerialNo(),
|
||||
beforeSign.getBytes(StandardCharsets.UTF_8), header.getSigned());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package com.github.binarywang.wxpay.service.impl;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import com.github.binarywang.wxpay.testbase.ApiTestModule;
|
||||
import com.google.inject.Inject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.annotations.Guice;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* 测试类
|
||||
*
|
||||
* @author yuanbo
|
||||
* @create 2022-04-26-22:33 PM
|
||||
*/
|
||||
@Test
|
||||
@Guice(modules = ApiTestModule.class)
|
||||
public class ProfitSharingV3ServiceImplTest {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@Inject
|
||||
private WxPayService payService;
|
||||
|
||||
@Test
|
||||
public void testProfitSharingNotifyData() throws WxPayException {
|
||||
SignatureHeader header = new SignatureHeader();
|
||||
header.setSerialNo("Wechatpay-Serial");
|
||||
header.setTimeStamp("Wechatpay-Timestamp");
|
||||
header.setNonce("Wechatpay-Nonce");
|
||||
header.setSigned("Wechatpay-Signature");
|
||||
String data = "body";
|
||||
this.logger.info(this.payService.getProfitSharingV3Service().getProfitSharingNotifyData(data,header).toString());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user