Browse Source

基础配置

master
张彭杰 1 year ago
parent
commit
05c2a5aa15
4 changed files with 298 additions and 18 deletions
  1. 4
      dating-agency-mall-server/src/main/java/com/qniao/dam/application/service/paymentchannelorder/PaymentChannelOrderApplicationService.java
  2. 3
      dating-agency-mall-server/src/main/java/com/qniao/dam/application/service/paymentchannelorder/processor/IChannelPayService.java
  3. 80
      dating-agency-mall-server/src/main/java/com/qniao/dam/application/service/paymentchannelorder/processor/wechat/WeChatPayProcessor.java
  4. 229
      dating-agency-mall-server/src/main/java/com/qniao/dam/application/service/paymentchannelorder/processor/wechat/utils/WXPayV3Util.java

4
dating-agency-mall-server/src/main/java/com/qniao/dam/application/service/paymentchannelorder/PaymentChannelOrderApplicationService.java

@ -7,9 +7,7 @@ import com.qniao.dam.domain.aggregate.paymentchannelorder.entity.PaymentChannelO
import com.qniao.dam.domain.aggregate.paymentorder.entity.PaymentOrder; import com.qniao.dam.domain.aggregate.paymentorder.entity.PaymentOrder;
import com.qniao.dam.domain.aggregate.paymentorder.repository.PaymentOrderRepository; import com.qniao.dam.domain.aggregate.paymentorder.repository.PaymentOrderRepository;
import com.qniao.dam.query.useropenid.UserOpenIdQueryService; import com.qniao.dam.query.useropenid.UserOpenIdQueryService;
import com.qniao.dau.domain.aggregate.useropenid.entity.UserOpenId;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
@ -34,6 +32,6 @@ public class PaymentChannelOrderApplicationService {
paymentChannelOrder.setComment("订单交易"); paymentChannelOrder.setComment("订单交易");
paymentChannelOrderAggregate.create(paymentChannelOrder); paymentChannelOrderAggregate.create(paymentChannelOrder);
IChannelPayService channelPayService = IChannelPayService.getService(paymentChannelOrder.getPaymentMethod()); IChannelPayService channelPayService = IChannelPayService.getService(paymentChannelOrder.getPaymentMethod());
return channelPayService.handle(paymentChannelOrder, fieldList, appFrom);
return channelPayService.prepay(paymentChannelOrder, fieldList, appFrom);
} }
} }

3
dating-agency-mall-server/src/main/java/com/qniao/dam/application/service/paymentchannelorder/processor/IChannelPayService.java

@ -4,7 +4,6 @@ import com.qniao.dam.api.command.paymentchannelorder.user.request.ThirdPartyFiel
import com.qniao.dam.application.service.paymentchannelorder.processor.wechat.WeChatPayProcessor; import com.qniao.dam.application.service.paymentchannelorder.processor.wechat.WeChatPayProcessor;
import com.qniao.dam.domain.aggregate.paymentchannelorder.entity.PaymentChannelOrder; import com.qniao.dam.domain.aggregate.paymentchannelorder.entity.PaymentChannelOrder;
import com.qniao.dam.domian.aggregate.paymentorder.constant.PaymentMethodEnum; import com.qniao.dam.domian.aggregate.paymentorder.constant.PaymentMethodEnum;
import com.qniao.dau.domain.aggregate.useropenid.entity.UserOpenId;
import com.qniao.framework.exception.BizException; import com.qniao.framework.exception.BizException;
import com.qniao.framework.utils.SpringContextUtil; import com.qniao.framework.utils.SpringContextUtil;
@ -23,5 +22,5 @@ public abstract class IChannelPayService {
} }
} }
public abstract Map<String, String> handle(PaymentChannelOrder paymentChannelOrder, List<ThirdPartyField> fieldList, Long appFrom);
public abstract Map<String, String> prepay(PaymentChannelOrder paymentChannelOrder, List<ThirdPartyField> fieldList, Long appFrom);
} }

80
dating-agency-mall-server/src/main/java/com/qniao/dam/application/service/paymentchannelorder/processor/wechat/WeChatPayProcessor.java

@ -1,5 +1,9 @@
package com.qniao.dam.application.service.paymentchannelorder.processor.wechat; package com.qniao.dam.application.service.paymentchannelorder.processor.wechat;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult; import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.config.WxPayConfig;
@ -10,17 +14,17 @@ import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.github.binarywang.wxpay.util.SignUtils; import com.github.binarywang.wxpay.util.SignUtils;
import com.qniao.dam.api.command.paymentchannelorder.user.request.ThirdPartyField; import com.qniao.dam.api.command.paymentchannelorder.user.request.ThirdPartyField;
import com.qniao.dam.application.service.paymentchannelorder.processor.IChannelPayService; import com.qniao.dam.application.service.paymentchannelorder.processor.IChannelPayService;
import com.qniao.dam.application.service.paymentchannelorder.processor.wechat.utils.WXPayV3Util;
import com.qniao.dam.application.service.paymentchannelorder.processor.wechat.vobj.ChannelPaymentField; import com.qniao.dam.application.service.paymentchannelorder.processor.wechat.vobj.ChannelPaymentField;
import com.qniao.dam.application.service.paymentchannelorder.processor.wechat.vobj.WeChatPaymentBasic; import com.qniao.dam.application.service.paymentchannelorder.processor.wechat.vobj.WeChatPaymentBasic;
import com.qniao.dam.domain.aggregate.paymentchannelorder.entity.PaymentChannelOrder; import com.qniao.dam.domain.aggregate.paymentchannelorder.entity.PaymentChannelOrder;
import com.qniao.dau.application.UecServerApplicationService; import com.qniao.dau.application.UecServerApplicationService;
import com.qniao.dau.application.request.GetWechatSessionDto; import com.qniao.dau.application.request.GetWechatSessionDto;
import com.qniao.dau.application.response.WeChatAppletUserSessionVo; import com.qniao.dau.application.response.WeChatAppletUserSessionVo;
import com.qniao.dau.domain.aggregate.useropenid.entity.UserOpenId;
import com.qniao.framework.exception.BizException; import com.qniao.framework.exception.BizException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -31,32 +35,79 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@Component
@Service
@Slf4j @Slf4j
public class WeChatPayProcessor extends IChannelPayService { public class WeChatPayProcessor extends IChannelPayService {
@Value("${weixin.mchid}")
private String mchId;
@Value("${weixin.mchkey}") @Value("${weixin.mchkey}")
private String mchKey; private String mchKey;
@Value("${weixin.mchid}")
private String mchId;
@Value("${weixin.notifyurl}") @Value("${weixin.notifyurl}")
private String notifyUrl; private String notifyUrl;
@Resource @Resource
private UecServerApplicationService uecServerApplicationService; private UecServerApplicationService uecServerApplicationService;
@Resource
private WXPayV3Util wxPayV3Util;
@Override @Override
public Map<String, String> handle(PaymentChannelOrder paymentChannelOrder,
public Map<String, String> prepay(PaymentChannelOrder paymentChannelOrder,
List<ThirdPartyField> fieldList, List<ThirdPartyField> fieldList,
Long appFrom) { Long appFrom) {
// 获取账号 // 获取账号
String jsCode = ThirdPartyField.findFiledValueByKey(ThirdPartyField.JS_CODE, fieldList); String jsCode = ThirdPartyField.findFiledValueByKey(ThirdPartyField.JS_CODE, fieldList);
GetWechatSessionDto sessionDto = new GetWechatSessionDto(jsCode, appFrom); GetWechatSessionDto sessionDto = new GetWechatSessionDto(jsCode, appFrom);
WeChatAppletUserSessionVo sessionVo = uecServerApplicationService.getWechatSession(sessionDto); WeChatAppletUserSessionVo sessionVo = uecServerApplicationService.getWechatSession(sessionDto);
WxPayConfig wxPayConfig = getWxPayConfig(sessionVo.getPlatformAppId());
WxPayUnifiedOrderRequest request = buildUnifiedOrderRequest(paymentChannelOrder, wxPayConfig, sessionVo.getOpenid());
WeChatPaymentBasic weChatPaymentBasic = getWeChatPaymentBasic(request, wxPayConfig);
return transformToMap(weChatPaymentBasic);
try {
Map<String, Object> reqParams = new HashMap<>();
reqParams.put("mchid", mchId);
reqParams.put("out_trade_no", paymentChannelOrder.getId().toString());
reqParams.put("appid", sessionVo.getPlatformAppId());
reqParams.put("description", paymentChannelOrder.getComment());
reqParams.put("notify_url", notifyUrl);
reqParams.put("amount", new HashMap<String, Object>() {
{
put("total", paymentChannelOrder.getAmount().multiply(BigDecimal.valueOf(100)).intValue());
put("currency", "CNY");
}
});
reqParams.put("payer", new HashMap<String, Object>() {
{
put("openid", sessionVo.getOpenid());
}
});
JSONObject result = wxPayV3Util.doPostWeiXinV3(WXPayV3Util.api_v3_placeAnOrder_url, JSONUtil.toJsonStr(reqParams));
if (ObjectUtil.isNull(result.get("prepay_id"))) {
throw new BizException("微信支付下单失败,请检查配置");
}
String prepayId = result.getString("prepay_id");
// 获取app拉起支付签名
String nonceStr = String.valueOf(System.currentTimeMillis() / 1000);
long timestamp = System.currentTimeMillis() / 1000;
String sign;
try {
sign = wxPayV3Util.sign(sessionVo.getPlatformAppId(), timestamp, nonceStr, prepayId);
} catch (Exception e) {
log.error(" sign fail AppV3PayStrategyImpl {}", e.getMessage(), e);
throw new BizException("生成签名错误:" + e.getMessage());
}
log.info("sign____________________" + sign);
Map<String, String> returnMap = new HashMap<>();
returnMap.put("appId", sessionVo.getPlatformAppId());
returnMap.put("nonceStr", nonceStr);
returnMap.put("timeStamp", String.valueOf(timestamp));
returnMap.put("packages", "prepay_id=" + prepayId);
returnMap.put("signType", "RSA");
returnMap.put("paySign", sign);
log.info("returnMap________" + returnMap);
return returnMap;
} catch (Exception e) {
log.error("pay fail AppV3PayStrategyImpl {}", e.getMessage(), e);
throw new BizException("发起微信预支付失败");
}
} }
public WxPayConfig getWxPayConfig(String appId) { public WxPayConfig getWxPayConfig(String appId) {
@ -77,7 +128,8 @@ public class WeChatPayProcessor extends IChannelPayService {
* @param wxPayConfig * @param wxPayConfig
* @return * @return
*/ */
public WxPayUnifiedOrderRequest buildUnifiedOrderRequest(PaymentChannelOrder paymentChannelOrder, WxPayConfig wxPayConfig, String openId) {
public WxPayUnifiedOrderRequest buildUnifiedOrderRequest(PaymentChannelOrder paymentChannelOrder, WxPayConfig
wxPayConfig, String openId) {
// 微信统一下单请求对象 // 微信统一下单请求对象
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest(); WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
request.setBody(paymentChannelOrder.getComment()); request.setBody(paymentChannelOrder.getComment());
@ -94,7 +146,8 @@ public class WeChatPayProcessor extends IChannelPayService {
return request; return request;
} }
private WeChatPaymentBasic getWeChatPaymentBasic(WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest, WxPayConfig wxPayConfig) {
private WeChatPaymentBasic getWeChatPaymentBasic(WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest, WxPayConfig
wxPayConfig) {
WxPayService wxPayService = new WxPayServiceImpl(); WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(wxPayConfig); wxPayService.setConfig(wxPayConfig);
WxPayUnifiedOrderResult wxPayUnifiedOrderResult = null; WxPayUnifiedOrderResult wxPayUnifiedOrderResult = null;
@ -149,7 +202,8 @@ public class WeChatPayProcessor extends IChannelPayService {
return SignUtils.createSign(payInfo, wxPayConfig.getSignType(), wxPayConfig.getMchKey(), null); return SignUtils.createSign(payInfo, wxPayConfig.getSignType(), wxPayConfig.getMchKey(), null);
} }
private String appSign(String prepayid, String partnerid, String timestamp, String noncestr, String appid, String packageValue, WxPayConfig wxPayConfig) {
private String appSign(String prepayid, String partnerid, String timestamp, String noncestr, String
appid, String packageValue, WxPayConfig wxPayConfig) {
Map<String, String> configMap = new HashMap<>(); Map<String, String> configMap = new HashMap<>();
configMap.put("prepayid", prepayid); configMap.put("prepayid", prepayid);
configMap.put("partnerid", partnerid); configMap.put("partnerid", partnerid);

229
dating-agency-mall-server/src/main/java/com/qniao/dam/application/service/paymentchannelorder/processor/wechat/utils/WXPayV3Util.java

@ -0,0 +1,229 @@
package com.qniao.dam.application.service.paymentchannelorder.processor.wechat.utils;
import com.alibaba.fastjson.JSONObject;
import com.qniao.framework.exception.BizException;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import java.io.*;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Base64;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Component
@Slf4j
public class WXPayV3Util {
public static final String api_v3_placeAnOrder_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
public static final String api_v3_refund_url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
private static CertificatesManager certificatesManager;
private String APPLICATION_JSON = "application/json";
// 你的商户私钥
private String privateKey;
// 你的微信支付平台证书
private String certificate;
private CloseableHttpClient httpClient;
private Verifier verifier;
@Value("${weixin.mchid}")
private String mchId;
@Value("${weixin.apiV3Key}")
private String v3Key;
@Value("${weixin.mchSerialNo}")
private String mchSerialNo;
@Value("${weixin.privateKeyPath}")
private String privateKeyPath;
@Value("${weixin.platformCertPath}")
private String platformCertPath;
public void setup() throws Exception {
// 获取商户
try {
InputStream apiClientKey = Files.newInputStream(new File(privateKeyPath).toPath());
File appKey = new File("/tmp/apiclient_key_temp.crt");
FileUtils.copyInputStreamToFile(apiClientKey, appKey);
InputStream wechatPayKey = Files.newInputStream(new File(platformCertPath).toPath());
File appCert = new File("/tmp/wechatpay_cert_temp.crt");
FileUtils.copyInputStreamToFile(wechatPayKey, appCert);
this.privateKey = WXPayV3Util.getPrivateKeyStr(appKey.getAbsolutePath());
this.certificate = WXPayV3Util.getPrivateKeyStr(appCert.getAbsolutePath());
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
v3Key.getBytes(StandardCharsets.UTF_8));
// 从证书管理器中获取verifier
verifier = certificatesManager.getVerifier(mchId);
X509Certificate wechatPayCertificate = PemUtil.loadCertificate(
new ByteArrayInputStream(certificate.getBytes(StandardCharsets.UTF_8)));
ArrayList<X509Certificate> listCertificates = new ArrayList<>();
listCertificates.add(wechatPayCertificate);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withWechatPay(listCertificates);
httpClient = builder.build();
} catch (IOException e) {
log.error("setup IOException " + e.getMessage(), e);
throw new BizException("IO异常:" + e.getMessage());
} catch (NoSuchAlgorithmException e) {
log.error("setup NoSuchAlgorithmException " + e.getMessage(), e);
throw new BizException("未找到配置文件:" + e.getMessage());
} catch (InvalidKeySpecException e) {
log.error("InvalidKeySpecException " + e.getMessage(), e);
throw new BizException("key生成异常:" + e.getMessage());
}
}
/**
* api_v3下单
*
* @param body 请求体JSON字符串
* @return
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
public JSONObject doPostWeiXinV3(String url, String body) throws Exception {
if (httpClient == null) {
setup();
}
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Content-Type", "application/json;chartset=utf-8");
httpPost.addHeader("Accept", "application/json");
try {
if (body == null) {
throw new IllegalArgumentException("data参数不能为空");
}
StringEntity stringEntity = new StringEntity(body, "utf-8");
httpPost.setEntity(stringEntity);
// 直接执行execute方法官方会自动处理签名和验签并进行证书自动更新
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
if (httpResponse.getStatusLine().getStatusCode() == 200) {
String jsonResult = EntityUtils.toString(httpEntity);
return JSONObject.parseObject(jsonResult);
} else {
log.error("doPostWeiXinV3 status != 200, " + EntityUtils.toString(httpEntity));
}
} catch (Exception e) {
log.error("doPostWeiXinV3 fail, " + e.getMessage(), e);
throw new BizException("微信支付异常:" + e.getMessage());
}
return null;
}
/**
* 获取私钥字符串
*
* @param filename 私钥文件路径 (required)
* @return 私钥对象
*/
public static String getPrivateKeyStr(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
return new String(Files.readAllBytes(Paths.get(filename)), "UTF-8");
}
/**
* V3 SHA256withRSA 签名.
*
* @param appId
* @param timestamp
* @param nonceStr
* @param prepayId
* @return
* @throws SignatureException
* @throws NoSuchAlgorithmException
* @throws IOException
* @throws InvalidKeySpecException
* @throws InvalidKeyException
* @throws URISyntaxException
*/
public String sign(String appId, long timestamp, String nonceStr, String prepayId) throws SignatureException, NoSuchAlgorithmException, IOException, InvalidKeySpecException, InvalidKeyException, URISyntaxException {
String signatureStr = Stream.of(appId, String.valueOf(timestamp), nonceStr, prepayId)
.collect(Collectors.joining("\n", "", "\n"));
Signature sign = Signature.getInstance("SHA256withRSA");
File file = copyTempFileByResourcePath(privateKeyPath);
sign.initSign(WXPayV3Util.getPrivateKey(file.getAbsolutePath()));
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
return Base64Utils.encodeToString(sign.sign());
}
/**
* 使用流获取resoucre下的文件解决线上无法获取的问题
*
* @param resourcePath 相对于resource目录的路径
* @return
* @throws IOException
*/
public File copyTempFileByResourcePath(String resourcePath) throws IOException {
InputStream inputStream = Files.newInputStream(new File(resourcePath).toPath());
File file = new File("/tmp/apiclient_key_temp.crt");
FileUtils.copyInputStreamToFile(inputStream, file);
return file;
}
/**
* 获取私钥
*
* @param filename 私钥文件路径 (required)
* @return 私钥对象
*/
public static PrivateKey getPrivateKey(String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(filename)), "UTF-8");
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
}
Loading…
Cancel
Save