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 fef0b5ab8b..3486c14012 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 0000000000..2a97d29738 --- /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 6091df26f9..0f4b92a7b7 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.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 String toJson() { @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 postPayments; @SerializedName("post_discounts") private List postDiscounts; @SerializedName("total_amount") - private int totalAmount; + private Integer totalAmount; @SerializedName("reason") private String reason; @SerializedName("goods_tag") diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java index cc8a80dad1..3fa2e5bf9c 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java @@ -112,7 +112,8 @@ private void setFieldValue(UnmarshallingContext context, WxPayOrderNotifyResult 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) { diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PayScoreService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PayScoreService.java index d92a2b20eb..5b4f692033 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PayScoreService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PayScoreService.java @@ -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; + + /** + *
+   * 授权/解除授权服务回调数据处理
+   * 文档地址: 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 221128c297..2f4dd76964 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.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 @@
  */
 @RequiredArgsConstructor
 public class PayScoreServiceImpl implements PayScoreService {
+	
+  private static final Gson GSON = new GsonBuilder().create();
   private final WxPayService payService;
 
   @Override
@@ -129,7 +139,7 @@ public WxPayScoreResult permissionsTerminateByOpenId(String openId, String reaso
 
   @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 WxPayScoreResult syncServiceOrder(WxPayScoreRequest request) throws WxPay
     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 WxPayScoreResult decryptNotifyDataResource(PayScoreNotifyData data) throw
       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());
+  }
 }