mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2025-08-23 22:11:40 +08:00
🎨 #1827 微信支付分相关接口优化
1. 将原有请求模型类中一些基础数据类型改为对应的包装类,因为在用户没有显式set的情况下,这些基础数据类型序列化为json时也会以默认值的形式作为参数传到微信端,造成微信端返回错误。 2. 微信支付分相关的回调数据处理方法加上签名验证。 3. 增加方法授权/解除授权服务回调数据处理
This commit is contained in:
parent
8bd95bcc22
commit
7eb11c8799
@ -1,11 +1,12 @@
|
||||
package com.github.binarywang.wxpay.bean.payscore;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 后付费项目.
|
||||
*
|
||||
@ -29,5 +30,5 @@ public class PostPayment implements Serializable {
|
||||
@SerializedName("description")
|
||||
private String description;
|
||||
@SerializedName("count")
|
||||
private int count;
|
||||
private Integer count;
|
||||
}
|
||||
|
@ -0,0 +1,138 @@
|
||||
package com.github.binarywang.wxpay.bean.payscore;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 授权/解除授权服务回调通知结果
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter4_4.shtml
|
||||
* </pre>
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class UserAuthorizationStatusNotifyResult implements Serializable {
|
||||
|
||||
/**
|
||||
* 源数据
|
||||
*/
|
||||
private PayScoreNotifyData rawData;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:公众账号ID
|
||||
* 变量名:appid
|
||||
* 是否必填:是
|
||||
* 类型:string[1,32]
|
||||
* 描述:
|
||||
* 调用授权服务接口提交的公众账号ID。
|
||||
* 示例值:wxd678efh567hg6787
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "appid")
|
||||
private String appid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商户号
|
||||
* 变量名:mchid
|
||||
* 是否必填:是
|
||||
* 类型:string[1,32]
|
||||
* 描述:
|
||||
* 调用授权服务接口提交的商户号。
|
||||
* 示例值:1230000109
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "mchid")
|
||||
private String mchid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商户签约单号
|
||||
* 变量名:out_request_no
|
||||
* 是否必填:否
|
||||
* 类型: string[1,64]
|
||||
* 描述:
|
||||
* 调用授权服务接口提交的商户请求唯一标识(新签约的用户,且在授权签约中上传了该字段,则在解约授权回调通知中有返回)。
|
||||
* 示例值:1234323JKHDFE1243252
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "out_request_no")
|
||||
private String outRequestNo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:服务ID
|
||||
* 变量名:service_id
|
||||
* 是否必填:是
|
||||
* 类型: string[1,32]
|
||||
* 描述:
|
||||
* 调用授权服务接口提交的服务ID。
|
||||
* 示例值:1234323JKHDFE1243252
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "service_id")
|
||||
private String serviceId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:用户标识
|
||||
* 变量名:openid
|
||||
* 是否必填:是
|
||||
* 类型: string[1,128]
|
||||
* 描述:
|
||||
* 微信用户在商户对应appid下的唯一标识。
|
||||
* 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "openid")
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:回调状态
|
||||
* 变量名:user_service_status
|
||||
* 是否必填:否
|
||||
* 类型: string[1,32]
|
||||
* 描述:
|
||||
* 1、USER_OPEN_SERVICE:授权成功
|
||||
* 2、USER_CLOSE_SERVICE:解除授权成功
|
||||
* 示例值:USER_OPEN_SERVICE
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "user_service_status")
|
||||
private String userServiceStatus;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:服务授权/解除授权时间
|
||||
* 变量名:openorclose_time
|
||||
* 是否必填:否
|
||||
* 类型: string[1,32]
|
||||
* 描述:
|
||||
* 服务授权/解除授权成功时间。
|
||||
* 示例值:20180225112233
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "openorclose_time")
|
||||
private String openOrCloseTime;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:授权协议号
|
||||
* 变量名:authorization_code
|
||||
* 是否必填:否
|
||||
* 类型: string[1,32]
|
||||
* 描述:
|
||||
* 授权协议号,预授权时返回,非预授权不返回
|
||||
* 示例值:1275342195190894594
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "authorization_code")
|
||||
private String authorizationCode;
|
||||
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
package com.github.binarywang.wxpay.bean.payscore;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@ -8,9 +12,6 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author doger.wang
|
||||
* @date 2020/5/12 16:36
|
||||
@ -63,15 +64,15 @@ public class WxPayScoreRequest implements Serializable {
|
||||
@SerializedName("openid")
|
||||
private String openid;
|
||||
@SerializedName("need_user_confirm")
|
||||
private boolean needUserConfirm;
|
||||
private Boolean needUserConfirm;
|
||||
@SerializedName("profit_sharing")
|
||||
private boolean profitSharing;
|
||||
private Boolean profitSharing;
|
||||
@SerializedName("post_payments")
|
||||
private List<PostPayment> postPayments;
|
||||
@SerializedName("post_discounts")
|
||||
private List<PostDiscount> postDiscounts;
|
||||
@SerializedName("total_amount")
|
||||
private int totalAmount;
|
||||
private Integer totalAmount;
|
||||
@SerializedName("reason")
|
||||
private String reason;
|
||||
@SerializedName("goods_tag")
|
||||
|
@ -112,7 +112,8 @@ public class WxPayOrderNotifyResultConverter extends AbstractReflectionConverter
|
||||
Object val = context.convertAnother(obj, field.getType());
|
||||
try {
|
||||
if (val != null) {
|
||||
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), obj.getClass());
|
||||
//这里加一个看似多余的(String)强转可解决高jdk版本下的编译报错问题,详情见讨论https://github.com/vaadin/framework/issues/10737
|
||||
PropertyDescriptor pd = new PropertyDescriptor((String)field.getName(), obj.getClass());
|
||||
pd.getWriteMethod().invoke(obj, val);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.github.binarywang.wxpay.service;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
|
||||
import com.github.binarywang.wxpay.bean.payscore.PayScoreNotifyData;
|
||||
import com.github.binarywang.wxpay.bean.payscore.UserAuthorizationStatusNotifyResult;
|
||||
import com.github.binarywang.wxpay.bean.payscore.WxPayScoreRequest;
|
||||
import com.github.binarywang.wxpay.bean.payscore.WxPayScoreResult;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
@ -196,6 +198,19 @@ public interface PayScoreService {
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
WxPayScoreResult syncServiceOrder(WxPayScoreRequest request) throws WxPayException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 授权/解除授权服务回调数据处理
|
||||
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter4_4.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @param notifyData 通知数据
|
||||
* @param header 通知头部数据,不传则表示不校验头
|
||||
* @return 解密后通知数据 return user authorization status notify result
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
UserAuthorizationStatusNotifyResult parseUserAuthorizationStatusNotifyResult(String notifyData, SignatureHeader header) throws WxPayException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
@ -206,7 +221,7 @@ public interface PayScoreService {
|
||||
* @param data the data
|
||||
* @return the wx pay score result
|
||||
*/
|
||||
PayScoreNotifyData parseNotifyData(String data);
|
||||
PayScoreNotifyData parseNotifyData(String data,SignatureHeader header) throws WxPayException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
|
@ -1,6 +1,19 @@
|
||||
package com.github.binarywang.wxpay.service.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
|
||||
import com.github.binarywang.wxpay.bean.payscore.PayScoreNotifyData;
|
||||
import com.github.binarywang.wxpay.bean.payscore.UserAuthorizationStatusNotifyResult;
|
||||
import com.github.binarywang.wxpay.bean.payscore.WxPayScoreRequest;
|
||||
import com.github.binarywang.wxpay.bean.payscore.WxPayScoreResult;
|
||||
import com.github.binarywang.wxpay.config.WxPayConfig;
|
||||
@ -8,16 +21,11 @@ import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
import com.github.binarywang.wxpay.service.PayScoreService;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import com.github.binarywang.wxpay.v3.util.AesUtils;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author doger.wang
|
||||
@ -25,6 +33,8 @@ import java.util.Map;
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class PayScoreServiceImpl implements PayScoreService {
|
||||
|
||||
private static final Gson GSON = new GsonBuilder().create();
|
||||
private final WxPayService payService;
|
||||
|
||||
@Override
|
||||
@ -129,7 +139,7 @@ public class PayScoreServiceImpl implements PayScoreService {
|
||||
|
||||
@Override
|
||||
public WxPayScoreResult createServiceOrder(WxPayScoreRequest request) throws WxPayException {
|
||||
boolean needUserConfirm = request.isNeedUserConfirm();
|
||||
boolean needUserConfirm = request.getNeedUserConfirm();
|
||||
WxPayConfig config = this.payService.getConfig();
|
||||
String url = this.payService.getPayBaseUrl() + "/v3/payscore/serviceorder";
|
||||
request.setAppid(config.getAppId());
|
||||
@ -247,10 +257,31 @@ public class PayScoreServiceImpl implements PayScoreService {
|
||||
String result = payService.postV3(url, request.toJson());
|
||||
return WxPayScoreResult.fromJson(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAuthorizationStatusNotifyResult parseUserAuthorizationStatusNotifyResult(String notifyData, SignatureHeader header) throws WxPayException {
|
||||
PayScoreNotifyData response = parseNotifyData(notifyData,header);
|
||||
PayScoreNotifyData.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);
|
||||
UserAuthorizationStatusNotifyResult notifyResult = GSON.fromJson(result, UserAuthorizationStatusNotifyResult.class);
|
||||
notifyResult.setRawData(response);
|
||||
return notifyResult;
|
||||
} catch (GeneralSecurityException | IOException e) {
|
||||
throw new WxPayException("解析报文异常!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayScoreNotifyData parseNotifyData(String data) {
|
||||
return WxGsonBuilder.create().fromJson(data, PayScoreNotifyData.class);
|
||||
public PayScoreNotifyData parseNotifyData(String data,SignatureHeader header) throws WxPayException {
|
||||
if(Objects.nonNull(header) && !this.verifyNotifySign(header, data)){
|
||||
throw new WxPayException("非法请求,头部信息验证失败");
|
||||
}
|
||||
return GSON.fromJson(data, PayScoreNotifyData.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -266,4 +297,19 @@ public class PayScoreServiceImpl implements PayScoreService {
|
||||
throw new WxPayException("解析报文异常!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验通知签名
|
||||
* @param header 通知头信息
|
||||
* @param data 通知数据
|
||||
* @return true:校验通过 false:校验不通过
|
||||
*/
|
||||
private boolean verifyNotifySign(SignatureHeader header, String data) {
|
||||
String beforeSign = String.format("%s\n%s\n%s\n",
|
||||
header.getTimeStamp(),
|
||||
header.getNonce(),
|
||||
data);
|
||||
return payService.getConfig().getVerifier().verify(header.getSerialNo(),
|
||||
beforeSign.getBytes(StandardCharsets.UTF_8), header.getSigned());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user