Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

微信支付分相关改动 #1827

Merged
merged 2 commits into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;

/**
* 后付费项目.
*
Expand All @@ -29,5 +30,5 @@ public class PostPayment implements Serializable {
@SerializedName("description")
private String description;
@SerializedName("count")
private int count;
private Integer count;
}
Original file line number Diff line number Diff line change
@@ -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;

}
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
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;
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
Expand Down Expand Up @@ -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<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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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>
Expand All @@ -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>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
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;
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
* @date 2020/5/14 9:43
*/
@RequiredArgsConstructor
public class PayScoreServiceImpl implements PayScoreService {

private static final Gson GSON = new GsonBuilder().create();
private final WxPayService payService;

@Override
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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
Expand All @@ -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());
}
}