支付功能(未实现)

This commit is contained in:
Cool 2024-09-03 23:13:29 +08:00
parent 3be9a64ae5
commit 43fa1e70c1
8 changed files with 314 additions and 17 deletions

18
pom.xml
View File

@ -68,6 +68,24 @@
<artifactId>wechatpay-apache-httpclient</artifactId> <artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.8</version> <version>0.4.8</version>
</dependency> </dependency>
<!-- 引入jar包-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.14</version>
</dependency>
<!-- 微信支付API -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.1.0</version>
</dependency>
<dependency> <dependency>
<groupId>commons-lang</groupId> <groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId> <artifactId>commons-lang</artifactId>

View File

@ -0,0 +1,45 @@
package com.bigdata.wxappserver.config;
import com.bigdata.wxappserver.properties.WeChatProperties;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Created with IntelliJ IDEA.
*
* @Author: Cool
* @Date: 2024/09/03/22:19
* @Description:
*/
@Configuration
@ConditionalOnClass(WxPayService.class)
public class MyWxPayConfig {
@Autowired
private WeChatProperties properties;
@Bean
@ConditionalOnMissingBean
public WxPayService wxService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
payConfig.setMchKey(StringUtils.trimToNull(this.properties.getSecret()));
payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getPrivateKeyFilePath()));
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}

View File

@ -0,0 +1,52 @@
package com.bigdata.wxappserver.controller;
import com.bigdata.wxappserver.result.Result;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.service.WxPayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created with IntelliJ IDEA.
*
* @Author: Cool
* @Date: 2024/09/03/22:25
* @Description:
*/
@RestController
@Slf4j
public class TestController {
@Autowired
private WxPayService wxService;
@PostMapping("/unifiedOrder")
public Result unifiedOrder(String outTradeNo) throws Exception
{
WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
wxPayUnifiedOrderRequest.setBody("测试");
wxPayUnifiedOrderRequest.setOutTradeNo(outTradeNo);
wxPayUnifiedOrderRequest.setTotalFee(50);
wxPayUnifiedOrderRequest.setSpbillCreateIp("127.0.0.1");
wxPayUnifiedOrderRequest.setNotifyUrl("payCallBack");
wxPayUnifiedOrderRequest.setTradeType("JSAPI");
wxPayUnifiedOrderRequest.setOpenid("oU5Ta5f9Vx6f-***********");
wxPayUnifiedOrderRequest.setSignType("MD5");
return Result.success(wxService.unifiedOrder(wxPayUnifiedOrderRequest));
}
@PostMapping("/queryOrder")
public Result queryOrder(String outTradeNo) throws Exception
{
WxPayOrderQueryResult wxPayOrderQueryResult = wxService.queryOrder(null, outTradeNo);
log.info(wxPayOrderQueryResult.toString());
return Result.success(wxPayOrderQueryResult);
}
}

View File

@ -9,9 +9,9 @@ import org.springframework.stereotype.Component;
@Data @Data
public class WeChatProperties { public class WeChatProperties {
private String appid; //小程序的appid private String appId; //小程序的appid
private String secret; //小程序的秘钥 private String secret; //小程序的秘钥
private String mchid; //商户号 private String mchId; //商户号
private String mchSerialNo; //商户API证书的证书序列号 private String mchSerialNo; //商户API证书的证书序列号
private String privateKeyFilePath; //商户私钥文件 private String privateKeyFilePath; //商户私钥文件
private String apiV3Key; //证书解密的密钥 private String apiV3Key; //证书解密的密钥

View File

@ -0,0 +1,138 @@
package com.bigdata.wxappserver.utils;
import okhttp3.HttpUrl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Random;
/**
* Created with IntelliJ IDEA.
*
* @Author: Cool
* @Date: 2024/09/03/20:34
* @Description:
*/
@Component
public class SignV3Util{
//V3主商户ID
private static String merchantId;
//微信商户平台APIv3证书序列号
private static String certificateSerialNo;
//私钥不要把私钥文件暴露在公共场合如上传到Github写在客户端代码等
private static String privateKey;
//配置文件配置好主商户号
@Value("${dx.wechat.mchid}")
public void setMerchantId(String merchantId) {
SignV3Util.merchantId = merchantId;
}
//配置文件配置好序列号
@Value("${dx.wechat.mchSerialNo}")
public void setCertificateSerialNo(String certificateSerialNo) {
SignV3Util.certificateSerialNo = certificateSerialNo;
}
//配置文件配置好私钥
@Value("${dx.wechat.apiV3Key}")
public void setPrivateKey(String privateKey) {
SignV3Util.privateKey = privateKey;
}
/**
* 使用方法
*
* @param method 请求方法
* @param url 请求url
* @param body 请求内容
* @return
*/
public static HashMap<String, String> getSignMap(String method, String url, String body) throws InvalidKeySpecException, NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException, SignatureException {
String authorization = getSign(method, url, body);
HashMap<String, String> headsMap = new HashMap<>();
headsMap.put("Authorization", authorization);
headsMap.put("Content-Type", "application/json");
headsMap.put("Accept", "application/json");
return headsMap;
}
public static String getSign(String method, String url, String body) throws NoSuchAlgorithmException, SignatureException, InvalidKeySpecException, InvalidKeyException, UnsupportedEncodingException {
return "WECHATPAY2-SHA256-RSA2048 " + getToken(method, HttpUrl.parse(url), body);
}
public static String getToken(String method, HttpUrl url, String body) throws UnsupportedEncodingException, SignatureException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
String nonceStr = nonceString();
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage(method, url, timestamp, nonceStr, body);
String signature = sign(message.getBytes("utf-8"));
return "mchid=\"" + merchantId + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"" + certificateSerialNo + "\","
+ "signature=\"" + signature + "\"";
}
public static String sign(byte[] message) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(getPKCS8PrivateKey(privateKey));
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
String canonicalUrl = url.encodedPath();
if (url.encodedQuery() != null) {
canonicalUrl += "?" + url.encodedQuery();
}
return method + "\n"
+ canonicalUrl + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ body + "\n";
}
private static PrivateKey getPKCS8PrivateKey(String strPk) throws NoSuchAlgorithmException, InvalidKeySpecException {
String realPK = strPk.replaceAll("-----END PRIVATE KEY-----", "")
.replaceAll("-----BEGIN PRIVATE KEY-----", "")
.replaceAll("\n", "");
byte[] b1 = Base64.getDecoder().decode(realPK);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(b1);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
public static String nonceString() {
String currTime = String.format("%d", (long) System.currentTimeMillis() / 1000);
String strTime = currTime.substring(8, currTime.length());
Random random = new Random();
int num = (int) (random.nextDouble() * (1000000 - 100000) + 100000);
String code = String.format("%06d", num);
String nonce_str = currTime.substring(2) + code;
return nonce_str;
}
}

View File

@ -24,10 +24,7 @@ import java.math.BigDecimal;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.Signature; import java.security.Signature;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
/** /**
* 微信支付工具类 * 微信支付工具类
@ -36,7 +33,7 @@ import java.util.List;
public class WeChatPayUtil { public class WeChatPayUtil {
//微信支付下单接口地址 //微信支付下单接口地址
public static final String JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; public static final String PAY_API = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//申请退款接口地址 //申请退款接口地址
public static final String REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; public static final String REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
@ -60,7 +57,7 @@ public class WeChatPayUtil {
List<X509Certificate> wechatPayCertificates = Arrays.asList(x509Certificate); List<X509Certificate> wechatPayCertificates = Arrays.asList(x509Certificate);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(weChatProperties.getMchid(), weChatProperties.getMchSerialNo(), merchantPrivateKey) .withMerchant(weChatProperties.getMchId(), weChatProperties.getMchSerialNo(), merchantPrivateKey)
.withWechatPay(wechatPayCertificates); .withWechatPay(wechatPayCertificates);
// 通过WechatPayHttpClientBuilder构造的HttpClient会自动的处理签名和验签 // 通过WechatPayHttpClientBuilder构造的HttpClient会自动的处理签名和验签
@ -133,10 +130,11 @@ public class WeChatPayUtil {
*/ */
private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception { private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception {
JSONObject jsonObject = new JSONObject(); JSONObject jsonObject = new JSONObject();
jsonObject.put("appid", weChatProperties.getAppid()); jsonObject.put("appid", weChatProperties.getAppId());
jsonObject.put("mchid", weChatProperties.getMchid()); jsonObject.put("mchid", weChatProperties.getMchId());
jsonObject.put("description", description); jsonObject.put("description", description);
jsonObject.put("out_trade_no", orderNum); jsonObject.put("out_trade_no", orderNum);
jsonObject.put("nonce_str", UUID.randomUUID().toString());
jsonObject.put("notify_url", weChatProperties.getNotifyUrl()); jsonObject.put("notify_url", weChatProperties.getNotifyUrl());
JSONObject amount = new JSONObject(); JSONObject amount = new JSONObject();
@ -151,7 +149,7 @@ public class WeChatPayUtil {
jsonObject.put("payer", payer); jsonObject.put("payer", payer);
String body = jsonObject.toJSONString(); String body = jsonObject.toJSONString();
return post(JSAPI, body); return post(PAY_API, body);
} }
/** /**
@ -175,7 +173,7 @@ public class WeChatPayUtil {
String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonceStr = RandomStringUtils.randomNumeric(32); String nonceStr = RandomStringUtils.randomNumeric(32);
ArrayList<Object> list = new ArrayList<>(); ArrayList<Object> list = new ArrayList<>();
list.add(weChatProperties.getAppid()); list.add(weChatProperties.getAppId());
list.add(timeStamp); list.add(timeStamp);
list.add(nonceStr); list.add(nonceStr);
list.add("prepay_id=" + prepayId); list.add("prepay_id=" + prepayId);

View File

@ -16,13 +16,13 @@ mybatis-plus:
map-underscore-to-camel-case: true map-underscore-to-camel-case: true
dx: dx:
wechat: wechat:
appid: wx865aefa5a7115ae0 appId: wx865aefa5a7115ae0
secret: 3f9849429894435abc935eea88178dfd secret: df0817d59696a6160de2770222d8ec53
mchid: 1684540409 mchId: 1684540409
mchSerialNo: 250414966CEADA52CF0A989445FB3190C5A82F40 mchSerialNo: 250414966CEADA52CF0A989445FB3190C5A82F40
privateKeyFilePath: template/bddf2dc508484b6bb086fe748e813260.pem privateKeyFilePath: classpath:template/bddf2dc508484b6bb086fe748e813260.pem
apiV3Key: d5a58d44588b42cbbe01daa5cfa4e792 apiV3Key: d5a58d44588b42cbbe01daa5cfa4e792
#未填写及以下 #未填写及以下
weChatPayCertFilePath: template/wechatpay_429733475DFDCEBE2A6135485F984EE337297F4F.pem weChatPayCertFilePath: classpath:template/wechatpay_429733475DFDCEBE2A6135485F984EE337297F4F.pem
notifyUrl: https://www.weixin.qq.com/wxpay/pay.php notifyUrl: https://www.weixin.qq.com/wxpay/pay.php
refundNotifyUrl: https://www.weixin.qq.com/wxpay/pay.php refundNotifyUrl: https://www.weixin.qq.com/wxpay/pay.php

View File

@ -1,7 +1,22 @@
package com.bigdata.wxappserver; package com.bigdata.wxappserver;
import com.alibaba.fastjson.JSON;
import com.bigdata.wxappserver.utils.SignV3Util;
import org.apache.http.HttpEntity;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest @SpringBootTest
class WxappServerApplicationTests { class WxappServerApplicationTests {
@ -10,4 +25,35 @@ class WxappServerApplicationTests {
void contextLoads() { void contextLoads() {
} }
@Test
public void test() throws Exception {
CloseableHttpClient httpClient = HttpClients.createDefault();
//处理请求参数
String param = JSON.toJSONString("请求参数");
//获取签名请求头
HashMap<String, String> heads = SignV3Util.getSignMap("POST", "https://api.mch.weixin.qq.com/pay/unifiedorder", param);
//请求微信接口
HttpPost post = new HttpPost("https://api.mch.weixin.qq.com/pay/unifiedorder");
for (Map.Entry<String, String> entry : heads.entrySet()) {
post.addHeader(entry.getKey(), entry.getValue());
}
// 设置请求参数
StringEntity entity = new StringEntity(JSON.toJSONString("请求参数"), "UTF-8");
post.setEntity(entity);
// 发送POST请求
CloseableHttpResponse response = httpClient.execute(post);
try {
// 获取响应实体
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null) {
// 打印响应内容
System.out.println("Response content: " + EntityUtils.toString(responseEntity, "UTF-8"));
}
} finally {
response.close();
}
}
} }