diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PostPayment.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PostPayment.java index fef0b5ab8..3486c1401 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PostPayment.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PostPayment.java @@ -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; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/UserAuthorizationStatusNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/UserAuthorizationStatusNotifyResult.java new file mode 100644 index 000000000..2a97d2973 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/UserAuthorizationStatusNotifyResult.java @@ -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; + +/** + * 授权/解除授权服务回调通知结果 + *
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter4_4.shtml + *+ */ +@Data +@NoArgsConstructor +public class UserAuthorizationStatusNotifyResult implements Serializable { + + /** + * 源数据 + */ + private PayScoreNotifyData rawData; + + /** + *
+ * 字段名:公众账号ID + * 变量名:appid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 调用授权服务接口提交的公众账号ID。 + * 示例值:wxd678efh567hg6787 + *+ */ + @SerializedName(value = "appid") + private String appid; + + /** + *
+ * 字段名:商户号 + * 变量名:mchid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 调用授权服务接口提交的商户号。 + * 示例值:1230000109 + *+ */ + @SerializedName(value = "mchid") + private String mchid; + + /** + *
+ * 字段名:商户签约单号 + * 变量名:out_request_no + * 是否必填:否 + * 类型: string[1,64] + * 描述: + * 调用授权服务接口提交的商户请求唯一标识(新签约的用户,且在授权签约中上传了该字段,则在解约授权回调通知中有返回)。 + * 示例值:1234323JKHDFE1243252 + *+ */ + @SerializedName(value = "out_request_no") + private String outRequestNo; + + /** + *
+ * 字段名:服务ID + * 变量名:service_id + * 是否必填:是 + * 类型: string[1,32] + * 描述: + * 调用授权服务接口提交的服务ID。 + * 示例值:1234323JKHDFE1243252 + *+ */ + @SerializedName(value = "service_id") + private String serviceId; + + /** + *
+ * 字段名:用户标识 + * 变量名:openid + * 是否必填:是 + * 类型: string[1,128] + * 描述: + * 微信用户在商户对应appid下的唯一标识。 + * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o + *+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+ * 字段名:回调状态 + * 变量名:user_service_status + * 是否必填:否 + * 类型: string[1,32] + * 描述: + * 1、USER_OPEN_SERVICE:授权成功 + * 2、USER_CLOSE_SERVICE:解除授权成功 + * 示例值:USER_OPEN_SERVICE + *+ */ + @SerializedName(value = "user_service_status") + private String userServiceStatus; + + /** + *
+ * 字段名:服务授权/解除授权时间 + * 变量名:openorclose_time + * 是否必填:否 + * 类型: string[1,32] + * 描述: + * 服务授权/解除授权成功时间。 + * 示例值:20180225112233 + *+ */ + @SerializedName(value = "openorclose_time") + private String openOrCloseTime; + + /** + *
+ * 字段名:授权协议号 + * 变量名:authorization_code + * 是否必填:否 + * 类型: string[1,32] + * 描述: + * 授权协议号,预授权时返回,非预授权不返回 + * 示例值:1275342195190894594 + *+ */ + @SerializedName(value = "authorization_code") + private String authorizationCode; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreRequest.java index 6091df26f..0f4b92a7b 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreRequest.java @@ -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
+ * 授权/解除授权服务回调数据处理 + * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter4_4.shtml + *+ * + * @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; /** *
@@ -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;
/**
*
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImpl.java
index 221128c29..2f4dd7696 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImpl.java
@@ -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());
+ }
}