diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsRequest.java index dc4ebfe4a..5fbc02a77 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsRequest.java @@ -1,6 +1,8 @@ package com.github.binarywang.wxpay.bean.ecommerce; import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @@ -17,7 +19,9 @@ import java.util.List; * @author cloudX */ @Data +@Builder @NoArgsConstructor +@AllArgsConstructor public class PartnerTransactionsRequest implements Serializable { private static final long serialVersionUID = -1550405819444680465L; @@ -277,6 +281,7 @@ public class PartnerTransactionsRequest implements Serializable { @Data @NoArgsConstructor + @AllArgsConstructor public static class Amount implements Serializable { private static final long serialVersionUID = -4967636398225864273L; @@ -312,6 +317,7 @@ public class PartnerTransactionsRequest implements Serializable { @Data @NoArgsConstructor + @AllArgsConstructor public static class Payer implements Serializable { private static final long serialVersionUID = -3946401119476159971L; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java index bd50ac89d..7d894fcd8 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java @@ -1,5 +1,7 @@ package com.github.binarywang.wxpay.bean.ecommerce; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,7 +12,9 @@ import java.io.Serializable; * 文档地址: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-yan-zheng */ @Data +@Builder @NoArgsConstructor +@AllArgsConstructor public class SignatureHeader implements Serializable { private static final long serialVersionUID = -6958015499416059949L; /** diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java index d010637a8..daa6d6e72 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java @@ -1,5 +1,7 @@ package com.github.binarywang.wxpay.bean.notify; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,7 +12,9 @@ import java.io.Serializable; * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_1.shtml */ @Data +@Builder @NoArgsConstructor +@AllArgsConstructor public class SignatureHeader implements Serializable { private static final long serialVersionUID = -1L; /** diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPartnerPayScoreRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPartnerPayScoreRequest.java new file mode 100644 index 000000000..8948d3d4c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPartnerPayScoreRequest.java @@ -0,0 +1,39 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +/** + * @author hallkk + * @date 2022/05/18 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class WxPartnerPayScoreRequest extends WxPayScoreRequest { + private static final long serialVersionUID = 6269843192878112955L; + + public String toJson() { + return WxGsonBuilder.create().toJson(this); + } + + @SerializedName("sub_appid") + private String subAppid; + + @SerializedName("sub_mchid") + private String subMchid; + + @SerializedName("out_apply_no") + private String outApplyNo; + + @SerializedName("result_notify_url") + private String resultNotifyUrl; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPartnerPayScoreResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPartnerPayScoreResult.java new file mode 100644 index 000000000..d8a350efe --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPartnerPayScoreResult.java @@ -0,0 +1,42 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +/** + * @author hallkk + * @date 2022/05/18 + */ +@Data +@NoArgsConstructor +public class WxPartnerPayScoreResult extends WxPayScoreResult { + private static final long serialVersionUID = 718267574622164410L; + + public static WxPartnerPayScoreResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxPartnerPayScoreResult.class); + } + + @SerializedName("sub_appid") + private String subAppid; + + @SerializedName("sub_mchid") + private String subMchid; + + @SerializedName("sub_openid") + private String subOpenId; + + @SerializedName("out_apply_no") + private String outApplyNo; + + @SerializedName("result_notify_url") + private String resultNotifyUrl; + + @SerializedName("apply_state") + private String applyState; + + @SerializedName("reject_reason") + private String rejectReason; + +} 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 0f4b92a7b..295b7000e 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 @@ -10,6 +10,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; import me.chanjar.weixin.common.util.json.WxGsonBuilder; /** @@ -17,7 +18,7 @@ import me.chanjar.weixin.common.util.json.WxGsonBuilder; * @date 2020/5/12 16:36 */ @Data -@Builder +@SuperBuilder @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @@ -82,6 +83,6 @@ public class WxPayScoreRequest implements Serializable { @SerializedName("detail") private Detail detail; @SerializedName("authorization_code") - private String authorizationCode; + private String authorizationCode; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreResult.java index 8e78b1b98..1cd4eef53 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreResult.java @@ -85,20 +85,23 @@ public class WxPayScoreResult implements Serializable { @SerializedName("payScoreSignInfo") private Map payScoreSignInfo; + @SerializedName("openid") + private String openId; + @SerializedName("apply_permissions_token") - private String applyPermissionsToken; + private String applyPermissionsToken; @SerializedName("authorization_code") - private String authorizationCode; + private String authorizationCode; @SerializedName("authorization_state") - private String authorizationState; + private String authorizationState; @SerializedName("cancel_authorization_time") - private String cancelAuthorizationTime; + private String cancelAuthorizationTime; @SerializedName("authorization_success_time") - private String authorizationSuccessTime; + private String authorizationSuccessTime; @SerializedName("openid") private String openid; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PartnerPayScoreService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PartnerPayScoreService.java new file mode 100644 index 000000000..e55b83785 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PartnerPayScoreService.java @@ -0,0 +1,277 @@ +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.WxPartnerPayScoreRequest; +import com.github.binarywang.wxpay.bean.payscore.WxPartnerPayScoreResult; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + *
+ *  服务商支付分相关服务类.
+ *   微信支付分是对个人的身份特质、支付行为、使用历史等情况的综合计算分值,旨在为用户提供更简单便捷的生活方式。
+ *   微信用户可以在具体应用场景中,开通微信支付分。开通后,用户可以在【微信—>钱包—>支付分】中查看分数和使用记录。
+ *   (即需在应用场景中使用过一次,钱包才会出现支付分入口)
+ *
+ * @author hallkk
+ * @date 2022/05/18
+ */
+public interface PartnerPayScoreService {
+
+
+  /**
+   * 
+   * 支付分商户预授权API
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter5_1.shtml
+   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/partner/permissions
+   * 
+ * + * @param request 请求对象 + * @return WxPartnerPayScoreResult wx partner payscore result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult permissions(WxPartnerPayScoreRequest request) throws WxPayException; + + + /** + *
+   * 支付分查询与用户授权记录(授权协议号)API
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter5_2.shtml
+   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/partner/permissions/authorization-code/{authorization_code}
+   * 
+ * + * @param serviceId + * @param subMchid + * @param authorizationCode + * @return WxPayScoreResult wx partner payscore result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult permissionsQueryByAuthorizationCode(String serviceId, String subMchid, + String authorizationCode) throws WxPayException; + + + /** + *
+   * 解除用户授权关系(授权协议号)API
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter5_4.shtml
+   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/partner/permissions/authorization-code/{authorization_code}/terminate
+   * 
+ * + * @param serviceId + * @param subMchid + * @param authorizationCode + * @param reason + * @return WxPartnerPayScoreResult wx partner payscore result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult permissionsTerminateByAuthorizationCode(String serviceId, String subMchid, + String authorizationCode, String reason) throws WxPayException; + + + /** + *
+   * 支付分查询与用户授权记录(openid)API
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter5_3.shtml
+   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/partner/permissions/search
+   * 
+ * + * @param serviceId + * @param subMchid + * @param subAppid + * @param openId + * @param subOpenid + * @return WxPayScoreResult wx partner payscore result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult permissionsQueryByOpenId(String serviceId, String appId, String subMchid, String subAppid, + String openId, String subOpenid) throws WxPayException; + + + /** + *
+   * 解除用户授权关系(openid)API
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter5_5.shtml
+   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/partner/permissions/openid/{openid}/terminate
+   * 
+ * + * @param serviceId + * @param subMchid + * @param subAppid + * @param openId + * @param subOpenid + * @param reason + * @return WxPayScoreResult wx partner payscore result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult permissionsTerminateByOpenId(String serviceId, String appId, String subMchid, String subAppid, + String openId, String subOpenid, String reason) throws WxPayException; + + + /** + *
+   * 支付分创建订单API.
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter3_1.shtml
+   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/partner/serviceorder
+   * 
+ * + * @param request 请求对象 + * @return WxPayScoreResult wx partner payscore result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult createServiceOrder(WxPartnerPayScoreRequest request) throws WxPayException; + + /** + *
+   * 支付分查询订单API.
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter3_2.shtml
+   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/partner/serviceorder
+   * 
+ * + * @param serviceId + * @param subMchid + * @param outOrderNo the out order no + * @param queryId the query id + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult queryServiceOrder(String serviceId, String subMchid, + String outOrderNo, String queryId) throws WxPayException; + + /** + *
+   * 支付分取消订单API.
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter3_3.shtml
+   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/partner/serviceorder/{out_order_no}/cancel
+   * 
+ *

+ * + * @param serviceId + * @param subMchid + * @param outOrderNo the out order no + * @param reason the reason + * @return com.github.binarywang.wxpay.bean.payscore.WxPayScoreResult wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult cancelServiceOrder(String serviceId, String appId, String subMchid, + String outOrderNo, String reason) throws WxPayException; + + /** + *

+   * 支付分修改订单金额API.
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter3_4.shtml
+   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/partner/serviceorder/{out_order_no}/modify
+   * 
+ *

+ * + * @param request the request + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult modifyServiceOrder(WxPartnerPayScoreRequest request) throws WxPayException; + + /** + *

+   * 支付分完结订单API.
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter3_5.shtml
+   * 请求URL:https://api.mch.weixin.qq.com/v3/payscore/partner/serviceorder/{out_order_no}/complete
+   * 
+ * + * @param request the request + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + void completeServiceOrder(WxPartnerPayScoreRequest request) throws WxPayException; + + /** + *
+   * 商户发起催收扣款API.
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter3_6.shtml
+   * 请求URL:https://api.mch.weixin.qq.com/v3/payscore/partner/serviceorder/{out_order_no}/pay
+   *
+   * 
+ * + * @param serviceId + * @param subMchid + * @param outOrderNo the out order no + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult payServiceOrder(String serviceId, String appId, String subMchid, String outOrderNo) throws WxPayException; + + /** + *
+   * 支付分订单收款API.
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter3_7.shtml
+   * 请求URL: https://api.mch.weixin.qq.com/v3/payscore/partner/serviceorder/{out_order_no}/sync
+   * 
+ * + * @param request the request + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult syncServiceOrder(WxPartnerPayScoreRequest request) throws WxPayException; + + /** + *
+   * 收付通子商户申请绑定支付分服务API.
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter9_1.shtml
+   * 请求URL: https://api.mch.weixin.qq.com/v3/payscore/partner/service-account-applications
+   * 
+ * + * @param request the request + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult applyServiceAccount(WxPartnerPayScoreRequest request) throws WxPayException; + + /** + *
+   * 查询收付通子商户服务绑定结果API.
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore_partner/chapter9_2.shtml
+   * 请求URL: https://api.mch.weixin.qq.com/v3/payscore/partner/service-account-applications/{out_apply_no}
+   * 
+ * + * @param outApplyNo 商户申请绑定单号 + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult queryServiceAccountState(String outApplyNo) 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; + + /** + *
+   * 支付分回调内容解析方法
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter5_2.shtml
+   * 
+ * + * @param data the data + * @return the wx pay score result + */ + PayScoreNotifyData parseNotifyData(String data, SignatureHeader header) throws WxPayException; + + /** + *
+   * 支付分回调NotifyData解密resource
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter5_2.shtml
+   * 
+ * + * @param data the data + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPartnerPayScoreResult decryptNotifyDataResource(PayScoreNotifyData data) throws WxPayException; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PartnerPayScoreServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PartnerPayScoreServiceImpl.java new file mode 100644 index 000000000..48ea1b2f6 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PartnerPayScoreServiceImpl.java @@ -0,0 +1,341 @@ +package com.github.binarywang.wxpay.service.impl; + +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.WxPartnerPayScoreRequest; +import com.github.binarywang.wxpay.bean.payscore.WxPartnerPayScoreResult; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.PartnerPayScoreService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.util.AesUtils; +import com.google.common.collect.Maps; +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.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author hallkk + * @date 2022/05/18 + */ +@RequiredArgsConstructor +public class PartnerPayScoreServiceImpl implements PartnerPayScoreService { + private static final Gson GSON = new GsonBuilder().create(); + private final WxPayService payService; + + @Override + public WxPartnerPayScoreResult permissions(WxPartnerPayScoreRequest request) throws WxPayException { + String url = this.payService.getPayBaseUrl() + "/v3/payscore/partner/permissions"; + request.setAppid(request.getAppid()); + request.setServiceId(request.getServiceId()); + WxPayConfig config = this.payService.getConfig(); + String permissionNotifyUrl = config.getPayScorePermissionNotifyUrl(); + if (StringUtils.isBlank(permissionNotifyUrl)) { + throw new WxPayException("授权回调地址未配置"); + } + String authorizationCode = request.getAuthorizationCode(); + if (StringUtils.isBlank(authorizationCode)) { + throw new WxPayException("authorizationCode不允许为空"); + } + request.setNotifyUrl(permissionNotifyUrl); + String result = this.payService.postV3(url, request.toJson()); + return WxPartnerPayScoreResult.fromJson(result); + } + + @Override + public WxPartnerPayScoreResult permissionsQueryByAuthorizationCode(String serviceId, String subMchid, String authorizationCode) + throws WxPayException { + if (StringUtils.isBlank(authorizationCode)) { + throw new WxPayException("authorizationCode不允许为空"); + } + String url = String.format("%s/v3/payscore/partner/permissions/authorization-code/%s", this.payService.getPayBaseUrl(), authorizationCode); + URIBuilder uriBuilder; + try { + uriBuilder = new URIBuilder(url); + } catch (URISyntaxException e) { + throw new WxPayException("未知异常!", e); + } + + uriBuilder.setParameter("service_id", serviceId); + uriBuilder.setParameter("sub_mchid", subMchid); + try { + String result = payService.getV3(uriBuilder.build().toString()); + return WxPartnerPayScoreResult.fromJson(result); + } catch (URISyntaxException e) { + throw new WxPayException("未知异常!", e); + } + } + + @Override + public WxPartnerPayScoreResult permissionsTerminateByAuthorizationCode(String serviceId, String subMchid, + String authorizationCode, String reason) throws WxPayException { + if (StringUtils.isBlank(authorizationCode)) { + throw new WxPayException("authorizationCode不允许为空"); + } + String url = String.format( + "%s/v3/payscore/partner/permissions/authorization-code/%s/terminate", + this.payService.getPayBaseUrl(), + authorizationCode + ); + Map map = new HashMap<>(4); + map.put("service_id", serviceId); + map.put("sub_mchid", subMchid); + map.put("reason", reason); + String result = payService.postV3(url, WxGsonBuilder.create().toJson(map)); + return WxPartnerPayScoreResult.fromJson(result); + } + + @Override + public WxPartnerPayScoreResult permissionsQueryByOpenId(String serviceId, String appId, String subMchid, + String subAppid, String openId, String subOpenid) throws WxPayException { + if (StringUtils.isAllEmpty(openId, subOpenid) || !StringUtils.isAnyEmpty(openId, subOpenid)) { + throw new WxPayException("open_id,sub_openid不允许都填写或都不填写"); + } + if (StringUtils.isBlank(subMchid)) { + throw new WxPayException("sub_mchid不允许都为空"); + } + String url = String.format("%s/v3/payscore/partner/permissions/openid/%s", this.payService.getPayBaseUrl(), openId); + URIBuilder uriBuilder; + try { + uriBuilder = new URIBuilder(url); + } catch (URISyntaxException e) { + throw new WxPayException("未知异常!", e); + } + + uriBuilder.setParameter("appid", appId); + uriBuilder.setParameter("service_id", serviceId); + uriBuilder.setParameter("sub_mchid", subMchid); + uriBuilder.setParameter("sub_appid", subAppid); + uriBuilder.setParameter("openid", openId); + uriBuilder.setParameter("sub_openid", subOpenid); + + if (StringUtils.isNotEmpty(openId)) { + uriBuilder.setParameter("openid", openId); + } + if (StringUtils.isNotEmpty(subOpenid)) { + uriBuilder.setParameter("sub_openid", subOpenid); + } + + try { + String result = payService.getV3(uriBuilder.build().toString()); + return WxPartnerPayScoreResult.fromJson(result); + } catch (URISyntaxException e) { + throw new WxPayException("未知异常!", e); + } + } + + @Override + public WxPartnerPayScoreResult permissionsTerminateByOpenId(String serviceId, String appId, String subMchid, String subAppid, String openId, String subOpenid, String reason) throws WxPayException { + if (StringUtils.isAllEmpty(openId, subOpenid) || !StringUtils.isAnyEmpty(openId, subOpenid)) { + throw new WxPayException("open_id,sub_openid不允许都填写或都不填写"); + } + String url = String.format("%s/v3/payscore/partner/permissions/openid/%s/terminate", this.payService.getPayBaseUrl(), openId); + Map map = new HashMap<>(4); + map.put("appid", appId); + map.put("sub_appid", subAppid); + map.put("service_id", serviceId); + if (StringUtils.isNotEmpty(openId)) { + map.put("openid", openId); + } + if (StringUtils.isNotEmpty(subOpenid)) { + map.put("sub_openid", subOpenid); + } + map.put("sub_mchid", subMchid); + map.put("reason", reason); + String result = payService.postV3(url, WxGsonBuilder.create().toJson(map)); + return WxPartnerPayScoreResult.fromJson(result); + } + + @Override + public WxPartnerPayScoreResult createServiceOrder(WxPartnerPayScoreRequest request) throws WxPayException { + String url = this.payService.getPayBaseUrl() + "/v3/payscore/partner/serviceorder"; + + WxPayConfig config = this.payService.getConfig(); + request.setNotifyUrl(config.getPayScoreNotifyUrl()); + String result = this.payService.postV3(url, request.toJson()); + + return WxPartnerPayScoreResult.fromJson(result); + } + + @Override + public WxPartnerPayScoreResult queryServiceOrder(String serviceId, String subMchid, String outOrderNo, + String queryId) throws WxPayException { + String url = this.payService.getPayBaseUrl() + "/v3/payscore/partner/serviceorder"; + + URIBuilder uriBuilder; + try { + uriBuilder = new URIBuilder(url); + } catch (URISyntaxException e) { + throw new WxPayException("未知异常!", e); + } + uriBuilder.setParameter("service_id", serviceId); + uriBuilder.setParameter("sub_mchid", subMchid); + if (StringUtils.isAllEmpty(outOrderNo, queryId) || !StringUtils.isAnyEmpty(outOrderNo, queryId)) { + throw new WxPayException("out_order_no,query_id不允许都填写或都不填写"); + } + if (StringUtils.isNotEmpty(outOrderNo)) { + uriBuilder.setParameter("out_order_no", outOrderNo); + } + if (StringUtils.isNotEmpty(queryId)) { + uriBuilder.setParameter("query_id", queryId); + } + + try { + String result = payService.getV3(uriBuilder.build().toString()); + return WxPartnerPayScoreResult.fromJson(result); + } catch (URISyntaxException e) { + throw new WxPayException("未知异常!", e); + } + } + + @Override + public WxPartnerPayScoreResult cancelServiceOrder(String serviceId, String appId, String subMchid, String outOrderNo, String reason) throws WxPayException { + String url = String.format("%s/v3/payscore/partner/serviceorder/%s/cancel", this.payService.getPayBaseUrl(), outOrderNo); + Map map = new HashMap<>(4); + map.put("appid", appId); + map.put("service_id", serviceId); + map.put("sub_mchid", subMchid); + map.put("reason", reason); + if (StringUtils.isAnyEmpty(appId, serviceId, subMchid, reason)) { + throw new WxPayException("appid, service_id, sub_mchid, reason都不能为空"); + } + String result = payService.postV3(url, WxGsonBuilder.create().toJson(map)); + return WxPartnerPayScoreResult.fromJson(result); + } + + @Override + public WxPartnerPayScoreResult modifyServiceOrder(WxPartnerPayScoreRequest request) throws WxPayException { + String outOrderNo = request.getOutOrderNo(); + String url = String.format("%s/v3/payscore/partner/serviceorder/%s/modify", this.payService.getPayBaseUrl(), outOrderNo); + request.setAppid(this.payService.getConfig().getAppId()); + request.setOutOrderNo(null); + String result = payService.postV3(url, request.toJson()); + return WxPartnerPayScoreResult.fromJson(result); + } + + @Override + public void completeServiceOrder(WxPartnerPayScoreRequest request) throws WxPayException { + String outOrderNo = request.getOutOrderNo(); + String url = String.format("%s/v3/payscore/partner/serviceorder/%s/complete", this.payService.getPayBaseUrl(), outOrderNo); + request.setAppid(request.getAppid()); + request.setServiceId(request.getServiceId()); + request.setOutOrderNo(null); + request.setSubMchid(request.getSubMchid()); + this.payService.postV3(url, request.toJson()); + } + + @Override + public WxPartnerPayScoreResult payServiceOrder(String serviceId, String appId, String subMchid, String outOrderNo) throws WxPayException { + String url = String.format("%s/v3/payscore/partner/serviceorder/%s/pay", this.payService.getPayBaseUrl(), outOrderNo); + Map map = new HashMap<>(3); + map.put("appid", appId); + map.put("service_id", serviceId); + map.put("sub_mchid", subMchid); + if (StringUtils.isAnyEmpty(appId, serviceId, subMchid)) { + throw new WxPayException("appid, service_id, sub_mchid都不能为空"); + } + String result = payService.postV3(url, WxGsonBuilder.create().toJson(map)); + return WxPartnerPayScoreResult.fromJson(result); + } + + @Override + public WxPartnerPayScoreResult syncServiceOrder(WxPartnerPayScoreRequest request) throws WxPayException { + String outOrderNo = request.getOutOrderNo(); + String url = String.format("%s/v3/payscore/partner/serviceorder/%s/sync", this.payService.getPayBaseUrl(), outOrderNo); + request.setAppid(this.payService.getConfig().getAppId()); + request.setOutOrderNo(null); + String result = payService.postV3(url, request.toJson()); + return WxPartnerPayScoreResult.fromJson(result); + } + + @Override + public WxPartnerPayScoreResult applyServiceAccount(WxPartnerPayScoreRequest request) throws WxPayException { + String url = String.format("%s/v3/payscore/partner/service-account-applications", this.payService.getPayBaseUrl()); + Map params = Maps.newHashMap(); + params.put("service_id", request.getServiceId()); + params.put("appid", request.getAppid()); + params.put("sub_mchid", request.getSubMchid()); + params.put("sub_appid", request.getSubAppid()); + params.put("out_apply_no", request.getOutApplyNo()); + params.put("result_notify_url", request.getResultNotifyUrl()); + + String result = payService.postV3(url, WxGsonBuilder.create().toJson(params)); + return WxPartnerPayScoreResult.fromJson(result); + + } + + @Override + public WxPartnerPayScoreResult queryServiceAccountState(String outApplyNo) throws WxPayException { + String url = String.format("%s/v3/payscore/partner/service-account-applications/%s", this.payService.getPayBaseUrl(), outApplyNo); + String result = payService.getV3(url); + return WxPartnerPayScoreResult.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, SignatureHeader header) throws WxPayException { + if (Objects.nonNull(header) && !this.verifyNotifySign(header, data)) { + throw new WxPayException("非法请求,头部信息验证失败"); + } + return GSON.fromJson(data, PayScoreNotifyData.class); + } + + @Override + public WxPartnerPayScoreResult decryptNotifyDataResource(PayScoreNotifyData data) throws WxPayException { + PayScoreNotifyData.Resource resource = data.getResource(); + String cipherText = resource.getCipherText(); + String associatedData = resource.getAssociatedData(); + String nonce = resource.getNonce(); + String apiV3Key = this.payService.getConfig().getApiV3Key(); + try { + return WxPartnerPayScoreResult.fromJson(AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key)); + } catch (GeneralSecurityException | IOException e) { + 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 this.payService.getConfig().getVerifier().verify( + header.getSerialNo(), + beforeSign.getBytes(StandardCharsets.UTF_8), + header.getSigned() + ); + } +}