支付功能(未实现)
This commit is contained in:
parent
3be9a64ae5
commit
43fa1e70c1
18
pom.xml
18
pom.xml
|
@ -68,6 +68,24 @@
|
|||
<artifactId>wechatpay-apache-httpclient</artifactId>
|
||||
<version>0.4.8</version>
|
||||
</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>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -9,9 +9,9 @@ import org.springframework.stereotype.Component;
|
|||
@Data
|
||||
public class WeChatProperties {
|
||||
|
||||
private String appid; //小程序的appid
|
||||
private String appId; //小程序的appid
|
||||
private String secret; //小程序的秘钥
|
||||
private String mchid; //商户号
|
||||
private String mchId; //商户号
|
||||
private String mchSerialNo; //商户API证书的证书序列号
|
||||
private String privateKeyFilePath; //商户私钥文件
|
||||
private String apiV3Key; //证书解密的密钥
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,10 +24,7 @@ import java.math.BigDecimal;
|
|||
import java.security.PrivateKey;
|
||||
import java.security.Signature;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 微信支付工具类
|
||||
|
@ -36,7 +33,7 @@ import java.util.List;
|
|||
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";
|
||||
|
@ -60,7 +57,7 @@ public class WeChatPayUtil {
|
|||
List<X509Certificate> wechatPayCertificates = Arrays.asList(x509Certificate);
|
||||
|
||||
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
|
||||
.withMerchant(weChatProperties.getMchid(), weChatProperties.getMchSerialNo(), merchantPrivateKey)
|
||||
.withMerchant(weChatProperties.getMchId(), weChatProperties.getMchSerialNo(), merchantPrivateKey)
|
||||
.withWechatPay(wechatPayCertificates);
|
||||
|
||||
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
|
||||
|
@ -133,10 +130,11 @@ public class WeChatPayUtil {
|
|||
*/
|
||||
private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("appid", weChatProperties.getAppid());
|
||||
jsonObject.put("mchid", weChatProperties.getMchid());
|
||||
jsonObject.put("appid", weChatProperties.getAppId());
|
||||
jsonObject.put("mchid", weChatProperties.getMchId());
|
||||
jsonObject.put("description", description);
|
||||
jsonObject.put("out_trade_no", orderNum);
|
||||
jsonObject.put("nonce_str", UUID.randomUUID().toString());
|
||||
jsonObject.put("notify_url", weChatProperties.getNotifyUrl());
|
||||
|
||||
JSONObject amount = new JSONObject();
|
||||
|
@ -151,7 +149,7 @@ public class WeChatPayUtil {
|
|||
jsonObject.put("payer", payer);
|
||||
|
||||
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 nonceStr = RandomStringUtils.randomNumeric(32);
|
||||
ArrayList<Object> list = new ArrayList<>();
|
||||
list.add(weChatProperties.getAppid());
|
||||
list.add(weChatProperties.getAppId());
|
||||
list.add(timeStamp);
|
||||
list.add(nonceStr);
|
||||
list.add("prepay_id=" + prepayId);
|
||||
|
|
|
@ -16,13 +16,13 @@ mybatis-plus:
|
|||
map-underscore-to-camel-case: true
|
||||
dx:
|
||||
wechat:
|
||||
appid: wx865aefa5a7115ae0
|
||||
secret: 3f9849429894435abc935eea88178dfd
|
||||
mchid: 1684540409
|
||||
appId: wx865aefa5a7115ae0
|
||||
secret: df0817d59696a6160de2770222d8ec53
|
||||
mchId: 1684540409
|
||||
mchSerialNo: 250414966CEADA52CF0A989445FB3190C5A82F40
|
||||
privateKeyFilePath: template/bddf2dc508484b6bb086fe748e813260.pem
|
||||
privateKeyFilePath: classpath:template/bddf2dc508484b6bb086fe748e813260.pem
|
||||
apiV3Key: d5a58d44588b42cbbe01daa5cfa4e792
|
||||
#未填写及以下
|
||||
weChatPayCertFilePath: template/wechatpay_429733475DFDCEBE2A6135485F984EE337297F4F.pem
|
||||
weChatPayCertFilePath: classpath:template/wechatpay_429733475DFDCEBE2A6135485F984EE337297F4F.pem
|
||||
notifyUrl: https://www.weixin.qq.com/wxpay/pay.php
|
||||
refundNotifyUrl: https://www.weixin.qq.com/wxpay/pay.php
|
||||
|
|
|
@ -1,7 +1,22 @@
|
|||
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.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
|
||||
class WxappServerApplicationTests {
|
||||
|
@ -10,4 +25,35 @@ class WxappServerApplicationTests {
|
|||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue