新增支付管理器,完成支付通知签名验证,添加默认通知实现类
This commit is contained in:
parent
193a8e2a65
commit
59d22699d5
@ -44,7 +44,7 @@ public class MiniappPayAppController {
|
||||
payPlaceOrder.setDescription("测试商品");
|
||||
payPlaceOrder.setOutTradeNo(atomicOrder);
|
||||
payPlaceOrder.setTimeExpire(DateTime.now().plusMinutes(1).toString(DateTimeFormat.forPattern(ISystemConstant.DATE_YYYY_MM_DD_T_HH_MM_SS_TIMEZONE)));
|
||||
payPlaceOrder.setNotifyUrl("https://www.wgink.ink/study/wechat/pay/notice");
|
||||
payPlaceOrder.setNotifyUrl("https://wg.wgink.ink/study/wechat/pay/notice");
|
||||
|
||||
PayPlaceOrder.Amount amount = new PayPlaceOrder.Amount();
|
||||
amount.setTotal(1);
|
||||
|
@ -4,14 +4,17 @@ import ink.wgink.annotation.CheckRequestBodyAnnotation;
|
||||
import ink.wgink.common.base.DefaultBaseController;
|
||||
import ink.wgink.interfaces.consts.ISystemConstant;
|
||||
import ink.wgink.module.wechat.pojo.pay.v3.PayErrorResponse;
|
||||
import ink.wgink.module.wechat.pojo.pay.v3.PayNotice;
|
||||
import ink.wgink.module.wechat.service.pay.v3.IPayService;
|
||||
import io.swagger.annotations.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName: PayController
|
||||
@ -36,8 +39,10 @@ public class PayController extends DefaultBaseController {
|
||||
@PostMapping("notice")
|
||||
@CheckRequestBodyAnnotation
|
||||
public void notice(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestHeader("Wechatpay-Signature") String wechatpaySignature, @RequestBody PayNotice payNotice) {
|
||||
payService.notice(request, response, wechatpaySignature, payNotice);
|
||||
@RequestHeader("Wechatpay-Nonce") String nonce,
|
||||
@RequestHeader("Wechatpay-Timestamp") String timestamp,
|
||||
@RequestHeader("Wechatpay-Signature") String wechatpaySignature) throws IOException {
|
||||
payService.notice(request, response, nonce, timestamp, wechatpaySignature);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,26 @@
|
||||
package ink.wgink.module.wechat.manager.pay.v3;
|
||||
|
||||
import ink.wgink.exceptions.base.SystemException;
|
||||
import ink.wgink.module.wechat.enums.PayAuthorizationTypeEnum;
|
||||
import ink.wgink.module.wechat.service.pay.v3.certificates.ICertificateService;
|
||||
import ink.wgink.module.wechat.utils.pay.PayCertificateUtil;
|
||||
import ink.wgink.module.wechat.utils.pay.PaySignAuthorizationUtil;
|
||||
import ink.wgink.properties.wechat.pay.v3.PayProperties;
|
||||
import ink.wgink.util.UUIDUtil;
|
||||
import ink.wgink.util.string.WStringUtil;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* 1.系统调用微信接口需要用商家证书认证
|
||||
* 2.系统接收微信回调参数时,需要平台证书,平台证书定期更新(每6小时),需要通过接口下载保存
|
||||
* 3.商家证书每次读取文件
|
||||
* 4.平台证书读取缓存
|
||||
*
|
||||
* @ClassName: PayMiniappManager
|
||||
* @Description: 支付管理
|
||||
* @Author: wanggeng
|
||||
@ -16,7 +30,9 @@ import java.security.cert.X509Certificate;
|
||||
public class PayManager {
|
||||
|
||||
private static final PayManager PAY_MANAGER = PayMiniappManagerBuilder.payManager;
|
||||
private X509Certificate x509Certificate;
|
||||
private ICertificateService certificateService;
|
||||
private PayProperties payProperties;
|
||||
private X509Certificate platformCertificate;
|
||||
|
||||
private PayManager() {
|
||||
}
|
||||
@ -25,12 +41,63 @@ public class PayManager {
|
||||
return PAY_MANAGER;
|
||||
}
|
||||
|
||||
public X509Certificate getX509Certificate() {
|
||||
return x509Certificate;
|
||||
/**
|
||||
* 获取商户证书
|
||||
*
|
||||
* @return
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public X509Certificate getBusinessCertificate() throws FileNotFoundException {
|
||||
return PayCertificateUtil.getCertificate(new FileInputStream(payProperties.getCertificatePath() + File.separator + payProperties.getCertificateName()));
|
||||
}
|
||||
|
||||
public void setX509Certificate(String certificatePath) throws FileNotFoundException {
|
||||
this.x509Certificate = PayCertificateUtil.getCertificate(new FileInputStream(certificatePath));
|
||||
/**
|
||||
* 获取平台证书
|
||||
*
|
||||
* @return
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public X509Certificate getPlatformCertificate() {
|
||||
// 不存在读取默认证书
|
||||
if (platformCertificate == null) {
|
||||
throw new SystemException("平台证书为不存在");
|
||||
}
|
||||
return platformCertificate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证头
|
||||
*
|
||||
* @param requestMethod
|
||||
* @param urlSuffix
|
||||
* @param payProperties
|
||||
* @param jsonBody
|
||||
* @param payAuthorizationTypeEnum
|
||||
* @return
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public String getAuthorization(RequestMethod requestMethod, String urlSuffix, String serialNumber, PayProperties payProperties, String jsonBody, PayAuthorizationTypeEnum payAuthorizationTypeEnum) throws Exception {
|
||||
String randomSubStr = WStringUtil.randomSubStr(UUIDUtil.get32UUID(), 10).toUpperCase();
|
||||
return PaySignAuthorizationUtil.buildAuthorization(requestMethod, urlSuffix,
|
||||
payProperties.getMchid(), serialNumber,
|
||||
payProperties.getKeyFilePath(), jsonBody, randomSubStr, System.currentTimeMillis() / 1000,
|
||||
payAuthorizationTypeEnum.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新证书
|
||||
*/
|
||||
public void refreshPlatformCertificate() throws Exception {
|
||||
String newestCertificate = certificateService.getNewestPlatformCertificate();
|
||||
this.platformCertificate = PayCertificateUtil.getCertificate(new FileInputStream(newestCertificate));
|
||||
}
|
||||
|
||||
public void setPayProperties(PayProperties payProperties) {
|
||||
this.payProperties = payProperties;
|
||||
}
|
||||
|
||||
public void setCertificateService(ICertificateService certificateService) {
|
||||
this.certificateService = certificateService;
|
||||
}
|
||||
|
||||
private static class PayMiniappManagerBuilder {
|
||||
|
@ -2,7 +2,6 @@ package ink.wgink.module.wechat.pojo.pay.v3;
|
||||
|
||||
import ink.wgink.annotation.CheckBeanAnnotation;
|
||||
import ink.wgink.annotation.CheckEmptyAnnotation;
|
||||
import ink.wgink.annotation.CheckNumberAnnotation;
|
||||
|
||||
/**
|
||||
* @ClassName: PayNotice
|
||||
@ -21,7 +20,6 @@ public class PayNotice {
|
||||
private String event_type;
|
||||
@CheckEmptyAnnotation(name = "通知数据类型")
|
||||
private String resource_type;
|
||||
@CheckNumberAnnotation(name = "通知数据")
|
||||
@CheckBeanAnnotation
|
||||
private Resource resource;
|
||||
@CheckEmptyAnnotation(name = "回调摘要")
|
||||
|
@ -0,0 +1,118 @@
|
||||
package ink.wgink.module.wechat.pojo.pay.v3.certificates;
|
||||
|
||||
/**
|
||||
* @ClassName: Certificates
|
||||
* @Description: 平台证书
|
||||
* @Author: wanggeng
|
||||
* @Date: 2021/8/26 2:19 下午
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class Certificate {
|
||||
|
||||
private String serialNo;
|
||||
private String effectiveTime;
|
||||
private String expireTime;
|
||||
private EncryptCertificate encryptCertificate;
|
||||
|
||||
public String getSerialNo() {
|
||||
return serialNo == null ? "" : serialNo.trim();
|
||||
}
|
||||
|
||||
public void setSerialNo(String serialNo) {
|
||||
this.serialNo = serialNo;
|
||||
}
|
||||
|
||||
public String getEffectiveTime() {
|
||||
return effectiveTime == null ? "" : effectiveTime.trim();
|
||||
}
|
||||
|
||||
public void setEffectiveTime(String effectiveTime) {
|
||||
this.effectiveTime = effectiveTime;
|
||||
}
|
||||
|
||||
public String getExpireTime() {
|
||||
return expireTime == null ? "" : expireTime.trim();
|
||||
}
|
||||
|
||||
public void setExpireTime(String expireTime) {
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
public EncryptCertificate getEncryptCertificate() {
|
||||
return encryptCertificate;
|
||||
}
|
||||
|
||||
public void setEncryptCertificate(EncryptCertificate encryptCertificate) {
|
||||
this.encryptCertificate = encryptCertificate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("{");
|
||||
sb.append("\"serialNo\":\"")
|
||||
.append(serialNo).append('\"');
|
||||
sb.append(",\"effectiveTime\":\"")
|
||||
.append(effectiveTime).append('\"');
|
||||
sb.append(",\"expireTime\":\"")
|
||||
.append(expireTime).append('\"');
|
||||
sb.append(",\"encryptCertificate\":")
|
||||
.append(encryptCertificate);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static class EncryptCertificate {
|
||||
private String algorithm;
|
||||
private String nonce;
|
||||
private String associatedData;
|
||||
private String ciphertext;
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm == null ? "" : algorithm.trim();
|
||||
}
|
||||
|
||||
public void setAlgorithm(String algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
public String getNonce() {
|
||||
return nonce == null ? "" : nonce.trim();
|
||||
}
|
||||
|
||||
public void setNonce(String nonce) {
|
||||
this.nonce = nonce;
|
||||
}
|
||||
|
||||
public String getAssociatedData() {
|
||||
return associatedData == null ? "" : associatedData.trim();
|
||||
}
|
||||
|
||||
public void setAssociatedData(String associatedData) {
|
||||
this.associatedData = associatedData;
|
||||
}
|
||||
|
||||
public String getCiphertext() {
|
||||
return ciphertext == null ? "" : ciphertext.trim();
|
||||
}
|
||||
|
||||
public void setCiphertext(String ciphertext) {
|
||||
this.ciphertext = ciphertext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("{");
|
||||
sb.append("\"algorithm\":\"")
|
||||
.append(algorithm).append('\"');
|
||||
sb.append(",\"nonce\":\"")
|
||||
.append(nonce).append('\"');
|
||||
sb.append(",\"associatedData\":\"")
|
||||
.append(associatedData).append('\"');
|
||||
sb.append(",\"ciphertext\":\"")
|
||||
.append(ciphertext).append('\"');
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -4,11 +4,6 @@ import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
||||
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
||||
import ink.wgink.exceptions.base.SystemException;
|
||||
import ink.wgink.module.wechat.enums.PayAuthorizationTypeEnum;
|
||||
import ink.wgink.module.wechat.utils.pay.PaySignAuthorizationUtil;
|
||||
import ink.wgink.properties.wechat.pay.v3.PayProperties;
|
||||
import ink.wgink.util.UUIDUtil;
|
||||
import ink.wgink.util.string.WStringUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -19,7 +14,6 @@ import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@ -28,7 +22,6 @@ import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
@ -190,25 +183,6 @@ public abstract class AbstractPayRequest<RequestBean, BodyType, ResponseType> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证头
|
||||
*
|
||||
* @param requestMethod
|
||||
* @param urlSuffix
|
||||
* @param payProperties
|
||||
* @param jsonBody
|
||||
* @param payAuthorizationTypeEnum
|
||||
* @return
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public String getAuthorization(RequestMethod requestMethod, String urlSuffix, String serialNumber, PayProperties payProperties, String jsonBody, PayAuthorizationTypeEnum payAuthorizationTypeEnum) throws Exception {
|
||||
String randomSubStr = WStringUtil.randomSubStr(UUIDUtil.get32UUID(), 10).toUpperCase();
|
||||
return PaySignAuthorizationUtil.buildAuthorization(requestMethod, urlSuffix,
|
||||
payProperties.getMchid(), serialNumber,
|
||||
payProperties.getKeyPath(), jsonBody, randomSubStr, System.currentTimeMillis() / 1000,
|
||||
payAuthorizationTypeEnum.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求头
|
||||
*
|
||||
|
@ -0,0 +1,61 @@
|
||||
package ink.wgink.module.wechat.request.pay.v3.certificates;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import ink.wgink.exceptions.base.SystemException;
|
||||
import ink.wgink.module.wechat.pojo.pay.v3.PayErrorResponse;
|
||||
import ink.wgink.module.wechat.pojo.pay.v3.certificates.Certificate;
|
||||
import ink.wgink.module.wechat.request.pay.v3.AbstractPayRequest;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName: CertificatesPayRequest
|
||||
* @Description: 获取平台证书
|
||||
* @Author: wanggeng
|
||||
* @Date: 2021/8/26 2:13 下午
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class CertificatePayRequestImpl extends AbstractPayRequest<JSONObject, JSONObject, List<Certificate>> {
|
||||
|
||||
@Override
|
||||
public JSONObject bodyJson(JSONObject jsonObject) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Certificate> response(ResponseEntity<String> responseEntity) {
|
||||
JSONObject resultObject = JSONObject.parseObject(responseEntity.getBody());
|
||||
List<Certificate> certificateList = new ArrayList<>();
|
||||
JSONArray certificatesData = resultObject.getJSONArray("data");
|
||||
for (int i = 0; i < certificatesData.size(); i++) {
|
||||
JSONObject certificateJsonObject = certificatesData.getJSONObject(i);
|
||||
Certificate certificate = new Certificate();
|
||||
certificate.setSerialNo(certificateJsonObject.getString("serial_no"));
|
||||
certificate.setEffectiveTime(certificateJsonObject.getString("effective_time"));
|
||||
certificate.setExpireTime(certificateJsonObject.getString("expire_time"));
|
||||
|
||||
JSONObject encryptCertificateJsonObject = certificateJsonObject.getJSONObject("encrypt_certificate");
|
||||
Certificate.EncryptCertificate encryptCertificate = new Certificate.EncryptCertificate();
|
||||
encryptCertificate.setAlgorithm(encryptCertificateJsonObject.getString("algorithm"));
|
||||
encryptCertificate.setNonce(encryptCertificateJsonObject.getString("nonce"));
|
||||
encryptCertificate.setAssociatedData(encryptCertificateJsonObject.getString("associated_data"));
|
||||
encryptCertificate.setCiphertext(encryptCertificateJsonObject.getString("ciphertext"));
|
||||
|
||||
certificate.setEncryptCertificate(encryptCertificate);
|
||||
certificateList.add(certificate);
|
||||
}
|
||||
return certificateList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(HttpClientErrorException e) {
|
||||
LOG.debug("Response error status code:{}", e.getStatusCode());
|
||||
PayErrorResponse payErrorResponse = JSONObject.parseObject(e.getResponseBodyAsString(), PayErrorResponse.class);
|
||||
LOG.debug("Response error detail: {}", payErrorResponse.getDetail());
|
||||
throw new SystemException(payErrorResponse.getMessage());
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import ink.wgink.module.wechat.pojo.pay.v3.PayErrorResponse;
|
||||
import ink.wgink.module.wechat.pojo.pay.v3.jsapi.pojo.PayPlaceOrder;
|
||||
import ink.wgink.module.wechat.pojo.pay.v3.jsapi.pojo.PayPrepay;
|
||||
import ink.wgink.module.wechat.request.pay.v3.AbstractPayRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
|
||||
@ -48,7 +49,10 @@ public class PlaceOrderPayRequestImpl extends AbstractPayRequest<PayPlaceOrder,
|
||||
|
||||
@Override
|
||||
public PayPrepay response(ResponseEntity<String> responseEntity) {
|
||||
|
||||
LOG.debug("Response success status code: {}", responseEntity.getStatusCode());
|
||||
if (responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||
return JSONObject.parseObject(responseEntity.getBody(), PayPrepay.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,14 @@ package ink.wgink.module.wechat.service.pay;
|
||||
|
||||
import ink.wgink.common.base.DefaultBaseService;
|
||||
import ink.wgink.exceptions.PropertiesException;
|
||||
import ink.wgink.module.wechat.enums.PayAuthorizationTypeEnum;
|
||||
import ink.wgink.module.wechat.manager.pay.v3.PayManager;
|
||||
import ink.wgink.properties.wechat.pay.v3.PayProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* @ClassName: BasePayService
|
||||
@ -20,6 +26,32 @@ public class BasePayService extends DefaultBaseService {
|
||||
@Autowired
|
||||
protected PayProperties payProperties;
|
||||
|
||||
/**
|
||||
* 获得证书序列号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String getSerialNumber() throws FileNotFoundException {
|
||||
X509Certificate x509Certificate = PayManager.getInstance().getBusinessCertificate();
|
||||
String serialNumber = x509Certificate.getSerialNumber().toString(16);
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证头
|
||||
*
|
||||
* @param requestMethod
|
||||
* @param urlSuffix
|
||||
* @param payProperties
|
||||
* @param jsonBody
|
||||
* @param payAuthorizationTypeEnum
|
||||
* @return
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public String getAuthorization(RequestMethod requestMethod, String urlSuffix, String serialNumber, PayProperties payProperties, String jsonBody, PayAuthorizationTypeEnum payAuthorizationTypeEnum) throws Exception {
|
||||
return PayManager.getInstance().getAuthorization(requestMethod, urlSuffix, serialNumber, payProperties, jsonBody, payAuthorizationTypeEnum);
|
||||
}
|
||||
|
||||
protected String getPayBaseUrl() {
|
||||
if (!payProperties.getActive()) {
|
||||
throw new PropertiesException("微信支付未激活");
|
||||
|
@ -1,9 +1,8 @@
|
||||
package ink.wgink.module.wechat.service.pay.v3;
|
||||
|
||||
import ink.wgink.module.wechat.pojo.pay.v3.PayNotice;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName: IPayService
|
||||
@ -19,8 +18,9 @@ public interface IPayService {
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param nonce
|
||||
* @param timestamp
|
||||
* @param wechatpaySignature
|
||||
* @param payNotice
|
||||
*/
|
||||
void notice(HttpServletRequest request, HttpServletResponse response, String wechatpaySignature, PayNotice payNotice);
|
||||
void notice(HttpServletRequest request, HttpServletResponse response, String nonce, String timestamp, String wechatpaySignature) throws IOException;
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package ink.wgink.module.wechat.service.pay.v3.certificates;
|
||||
|
||||
/**
|
||||
* @ClassName: ICertificatesService
|
||||
* @Description: 平台证书业务
|
||||
* @Author: wanggeng
|
||||
* @Date: 2021/8/26 2:40 下午
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public interface ICertificateService {
|
||||
|
||||
/**
|
||||
* 最新的平台证书
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getNewestPlatformCertificate() throws Exception;
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package ink.wgink.module.wechat.service.pay.v3.certificates.impl;
|
||||
|
||||
import ink.wgink.exceptions.base.SystemException;
|
||||
import ink.wgink.module.wechat.enums.PayAuthorizationTypeEnum;
|
||||
import ink.wgink.module.wechat.pojo.pay.v3.certificates.Certificate;
|
||||
import ink.wgink.module.wechat.request.pay.v3.AbstractPayRequest;
|
||||
import ink.wgink.module.wechat.request.pay.v3.certificates.CertificatePayRequestImpl;
|
||||
import ink.wgink.module.wechat.service.pay.BasePayService;
|
||||
import ink.wgink.module.wechat.service.pay.v3.certificates.ICertificateService;
|
||||
import ink.wgink.util.date.DateUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName: ICertificatesService
|
||||
* @Description: 平台证书业务
|
||||
* @Author: wanggeng
|
||||
* @Date: 2021/8/26 2:40 下午
|
||||
* @Version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class CertificateServiceImpl extends BasePayService implements ICertificateService {
|
||||
|
||||
@Override
|
||||
public String getNewestPlatformCertificate() throws Exception {
|
||||
String urlSuffix = "/v3/certificates";
|
||||
String url = getPayBaseUrl() + urlSuffix;
|
||||
|
||||
LOG.debug("获得证书序列号");
|
||||
String serialNumber = getSerialNumber();
|
||||
CertificatePayRequestImpl certificatesPayRequest = new CertificatePayRequestImpl();
|
||||
String authorization = getAuthorization(RequestMethod.GET, urlSuffix, serialNumber, payProperties, "", PayAuthorizationTypeEnum.WECHATPAY2_SHA256_RSA2048);
|
||||
List<Certificate> certificates = certificatesPayRequest.get(url, authorization, serialNumber);
|
||||
LOG.debug("获取最新的平台证书");
|
||||
Certificate newestCertificate = certificates.get(certificates.size() - 1);
|
||||
LOG.debug("解析密文");
|
||||
AbstractPayRequest.AesUtil aesUtil = new AbstractPayRequest.AesUtil(payProperties.getApiV3Secretkey().getBytes(StandardCharsets.UTF_8));
|
||||
Certificate.EncryptCertificate encryptCertificate = newestCertificate.getEncryptCertificate();
|
||||
String ciphertext = aesUtil.decryptToString(encryptCertificate.getAssociatedData().getBytes(StandardCharsets.UTF_8), encryptCertificate.getNonce().getBytes(StandardCharsets.UTF_8), encryptCertificate.getCiphertext());
|
||||
String certificateFilePath = payProperties.getCertificatePath() + File.separator + DateUtil.getDays()+ "_wxp_pub.pem";
|
||||
LOG.debug("新证书文件路径:{}", certificateFilePath);
|
||||
try (FileWriter fileWriter = new FileWriter(certificateFilePath)) {
|
||||
fileWriter.write(ciphertext);
|
||||
fileWriter.flush();
|
||||
} catch (IOException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw new SystemException("证书生成异常");
|
||||
}
|
||||
LOG.debug("保存新平台证书成功");
|
||||
return certificateFilePath;
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ public class DefaultPayNoticeService extends DefaultBaseService implements IPayN
|
||||
|
||||
@Override
|
||||
public void handle(PayNoticeCiphertext payNoticeCiphertext) throws Exception {
|
||||
LOG.debug(payNoticeCiphertext.toString());
|
||||
LOG.debug("Pay notice: {}", payNoticeCiphertext.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ package ink.wgink.module.wechat.service.pay.v3.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import ink.wgink.common.base.DefaultBaseService;
|
||||
import ink.wgink.exceptions.ParamsException;
|
||||
import ink.wgink.module.wechat.enums.PayErrorMsgEnum;
|
||||
import ink.wgink.module.wechat.manager.pay.v3.PayManager;
|
||||
import ink.wgink.module.wechat.pojo.pay.v3.PayErrorResponse;
|
||||
import ink.wgink.module.wechat.pojo.pay.v3.PayNotice;
|
||||
import ink.wgink.module.wechat.pojo.pay.v3.PayNoticeCiphertext;
|
||||
@ -11,14 +13,14 @@ import ink.wgink.module.wechat.service.pay.v3.IPayNoticeService;
|
||||
import ink.wgink.module.wechat.service.pay.v3.IPayService;
|
||||
import ink.wgink.module.wechat.utils.pay.PayVerifyUtil;
|
||||
import ink.wgink.properties.wechat.pay.v3.PayProperties;
|
||||
import org.joda.time.DateTime;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@ -39,15 +41,17 @@ public class IPayServiceImpl extends DefaultBaseService implements IPayService {
|
||||
private IPayNoticeService payNoticeService;
|
||||
|
||||
@Override
|
||||
public void notice(HttpServletRequest request, HttpServletResponse response, String wechatpaySignature, PayNotice payNotice) {
|
||||
String nonce = payNotice.getResource().getNonce();
|
||||
DateTime createTimeDateTime = DateTime.parse(payNotice.getCreate_time());
|
||||
long millis = createTimeDateTime.getMillis();
|
||||
String payNoticeJsonString = JSONObject.toJSONString(payNotice);
|
||||
|
||||
public void notice(HttpServletRequest request, HttpServletResponse response, String nonce, String timestamp, String wechatpaySignature) throws IOException {
|
||||
String payNoticeJsonString = getBodyString(request);
|
||||
if (StringUtils.isBlank(payNoticeJsonString)) {
|
||||
throw new ParamsException("请求body为空");
|
||||
}
|
||||
LOG.debug("payNoticeJsonString: {}", payNoticeJsonString);
|
||||
PayNotice payNotice = JSONObject.parseObject(payNoticeJsonString, PayNotice.class);
|
||||
try {
|
||||
LOG.debug("wechatpaySignature: {}", wechatpaySignature);
|
||||
LOG.debug("验证签名");
|
||||
boolean checkVerify = PayVerifyUtil.verifySignature(wechatpaySignature, payNoticeJsonString, nonce, String.valueOf(millis), new FileInputStream(payProperties.getCertificatePath()));
|
||||
boolean checkVerify = PayVerifyUtil.verifySignature(wechatpaySignature, payNoticeJsonString, nonce, timestamp, PayManager.getInstance().getPlatformCertificate());
|
||||
if (!checkVerify) {
|
||||
errorResult(response, PayErrorMsgEnum.SIGN_ERROR_401);
|
||||
return;
|
||||
@ -104,4 +108,13 @@ public class IPayServiceImpl extends DefaultBaseService implements IPayService {
|
||||
}
|
||||
}
|
||||
|
||||
private String getBodyString(HttpServletRequest request) throws IOException {
|
||||
BufferedReader reader = request.getReader();
|
||||
StringBuilder bodyString = new StringBuilder();
|
||||
for (String line; (line = reader.readLine()) != null; ) {
|
||||
bodyString.append(line);
|
||||
}
|
||||
return bodyString.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import ink.wgink.module.wechat.pojo.pay.v3.jsapi.pojo.PayPrepay;
|
||||
import ink.wgink.module.wechat.request.pay.v3.jsapi.PlaceOrderPayRequestImpl;
|
||||
import ink.wgink.module.wechat.service.pay.BasePayService;
|
||||
import ink.wgink.module.wechat.service.pay.v3.jsapi.IJsapiService;
|
||||
import ink.wgink.module.wechat.utils.pay.PayCertificateUtil;
|
||||
import ink.wgink.module.wechat.utils.pay.PayPrivateKeyUtil;
|
||||
import ink.wgink.properties.wechat.miniapp.MiniappProperties;
|
||||
import ink.wgink.util.BeanPropertyCheckUtil;
|
||||
@ -18,9 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* @ClassName: JsapiServiceImpl
|
||||
* @Description: Jsapi业务
|
||||
@ -43,19 +39,19 @@ public class JsapiServiceImpl extends BasePayService implements IJsapiService {
|
||||
String urlSuffix = "/v3/pay/transactions/jsapi";
|
||||
String url = getPayBaseUrl() + urlSuffix;
|
||||
|
||||
LOG.debug("获得证书");
|
||||
X509Certificate certificate = PayCertificateUtil.getCertificate(new FileInputStream(payProperties.getCertificatePath()));
|
||||
String serialNumber = certificate.getSerialNumber().toString(16);
|
||||
LOG.debug("获得证书序列号");
|
||||
String serialNumber = getSerialNumber();
|
||||
|
||||
LOG.debug("调用微信支付,发起预支付");
|
||||
PlaceOrderPayRequestImpl placeOrderPayRequest = new PlaceOrderPayRequestImpl();
|
||||
JSONObject body = placeOrderPayRequest.bodyJson(payPlaceOrder);
|
||||
String authorization = placeOrderPayRequest.getAuthorization(RequestMethod.POST, urlSuffix, serialNumber, payProperties, body.toString(), PayAuthorizationTypeEnum.WECHATPAY2_SHA256_RSA2048);
|
||||
String authorization = getAuthorization(RequestMethod.POST, urlSuffix, serialNumber, payProperties, body.toString(), PayAuthorizationTypeEnum.WECHATPAY2_SHA256_RSA2048);
|
||||
|
||||
LOG.debug("请求获得结果");
|
||||
PayPrepay payPrepay = placeOrderPayRequest.post(url, authorization, serialNumber, body);
|
||||
|
||||
LOG.debug("生成返回结果");
|
||||
long timestamp = System.currentTimeMillis();
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String nonceStr = WStringUtil.randomSubStr(UUIDUtil.get32UUID(), 10).toUpperCase();
|
||||
|
||||
PaySign paySign = new PaySign();
|
||||
@ -82,7 +78,7 @@ public class JsapiServiceImpl extends BasePayService implements IJsapiService {
|
||||
.append(timestamp).append("\n")
|
||||
.append(nonceStr).append("\n")
|
||||
.append("prepay_id=").append(prepayId).append("\n");
|
||||
String key = PayPrivateKeyUtil.getPrivateKey(payProperties.getKeyPath());
|
||||
String key = PayPrivateKeyUtil.getPrivateKey(payProperties.getKeyFilePath());
|
||||
// 生成签名
|
||||
return PayPrivateKeyUtil.encryptByPrivateKey(signBS.toString(), key);
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package ink.wgink.module.wechat.startup;
|
||||
|
||||
import ink.wgink.module.wechat.manager.MiniappManager;
|
||||
import ink.wgink.module.wechat.manager.OfficialAccountAccessTokenManager;
|
||||
import ink.wgink.module.wechat.manager.pay.v3.PayManager;
|
||||
import ink.wgink.module.wechat.service.pay.v3.certificates.ICertificateService;
|
||||
import ink.wgink.properties.wechat.miniapp.MiniappProperties;
|
||||
import ink.wgink.properties.wechat.official.account.OfficialAccountProperties;
|
||||
import ink.wgink.properties.wechat.pay.v3.PayProperties;
|
||||
@ -32,12 +34,18 @@ public class WechatStartUp implements ApplicationRunner {
|
||||
private OfficialAccountProperties officialAccountProperties;
|
||||
@Autowired
|
||||
private PayProperties payProperties;
|
||||
@Autowired
|
||||
private ICertificateService certificateService;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
new Thread(() -> {
|
||||
refreshOfficialAccountAccessToken();
|
||||
}).start();
|
||||
refreshOfficialAccountAccessToken();
|
||||
removeTimeoutMiniAppUser();
|
||||
// 支付
|
||||
PayManager payManager = PayManager.getInstance();
|
||||
payManager.setCertificateService(certificateService);
|
||||
payManager.setPayProperties(payProperties);
|
||||
refreshCertificate();
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0/5 * * * ?")
|
||||
@ -59,4 +67,15 @@ public class WechatStartUp implements ApplicationRunner {
|
||||
MiniappManager.getInstance().removeTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新证书,0小时开始,每6小时执行一次
|
||||
*/
|
||||
@Scheduled(cron = "0 0 0/6 * * ?")
|
||||
public void refreshCertificate() throws Exception {
|
||||
if (!payProperties.getActive()) {
|
||||
return;
|
||||
}
|
||||
PayManager.getInstance().refreshPlatformCertificate();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package ink.wgink.module.wechat.utils.pay;
|
||||
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
@ -15,7 +14,6 @@ import java.security.cert.X509Certificate;
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class PayVerifyUtil {
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*
|
||||
@ -27,10 +25,10 @@ public class PayVerifyUtil {
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static boolean verifySignature(String signature, String body, String nonce, String timestamp, InputStream certInputStream) throws Exception {
|
||||
public static boolean verifySignature(String signature, String body, String nonce, String timestamp, X509Certificate certificate) throws Exception {
|
||||
String buildSignMessage = buildSignMessage(timestamp, nonce, body);
|
||||
System.out.println("buildSignMessage: " + buildSignMessage);
|
||||
// 获取证书
|
||||
X509Certificate certificate = PayCertificateUtil.getCertificate(certInputStream);
|
||||
PublicKey publicKey = certificate.getPublicKey();
|
||||
return checkByPublicKey(buildSignMessage, signature, publicKey);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user