feat:新增微信支付demo
This commit is contained in:
parent
f3adc25ddb
commit
a1d07aae6a
33
weChatPay/.gitignore
vendored
Normal file
33
weChatPay/.gitignore
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
72
weChatPay/pom.xml
Normal file
72
weChatPay/pom.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.2</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>weChatPay</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>weChatPay</name>
|
||||
<description>weChatPay</description>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.wechatpay-apiv3</groupId>
|
||||
<artifactId>wechatpay-apache-httpclient</artifactId>
|
||||
<version>0.4.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>2.0.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,13 @@
|
||||
package com.example.wechatpay;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class WeChatPayApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WeChatPayApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.example.wechatpay.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* t_pay_history
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class PayHistoryPo {
|
||||
private Integer id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Long createTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private Integer createUser;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Long updateTime;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private Integer updateUser;
|
||||
|
||||
/**
|
||||
* 预支付id
|
||||
*/
|
||||
private String prePayId;
|
||||
|
||||
/**
|
||||
* 应用流水号
|
||||
*/
|
||||
private String sn;
|
||||
|
||||
/**
|
||||
* 支付金额
|
||||
*/
|
||||
private BigDecimal price;
|
||||
|
||||
/**
|
||||
* 描述信息
|
||||
*/
|
||||
private String description;
|
||||
|
||||
|
||||
/**
|
||||
* 0:未支付,1:已支付
|
||||
*/
|
||||
private String status;
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.example.wechatpay.service;
|
||||
|
||||
|
||||
import com.example.wechatpay.entity.PayHistoryPo;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Map;
|
||||
|
||||
public interface WeChatPayService {
|
||||
|
||||
/**
|
||||
* 微信支付预定单生成
|
||||
*
|
||||
* @param body body
|
||||
* @return java.util.Map<java.lang.String, java.lang.String>
|
||||
* @author fanxb
|
||||
* date 2022/7/25 21:50
|
||||
*/
|
||||
Map<String, String> pay(PayHistoryPo body);
|
||||
|
||||
/**
|
||||
* 微信支付回调处理
|
||||
*
|
||||
* @author fanxb
|
||||
* date 2022/7/25 22:27
|
||||
*/
|
||||
void notify(HttpServletRequest servletRequest);
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
package com.example.wechatpay.service.impl;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.example.wechatpay.entity.PayHistoryPo;
|
||||
import com.example.wechatpay.service.WeChatPayService;
|
||||
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.auth.WechatPay2Validator;
|
||||
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
|
||||
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
|
||||
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
|
||||
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
|
||||
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.util.EntityUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class WeChatPayServiceImpl implements WeChatPayService {
|
||||
|
||||
/**
|
||||
* 小程序appId
|
||||
*/
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 商户id
|
||||
*/
|
||||
private String merchantId;
|
||||
|
||||
/**
|
||||
* 证书序列号
|
||||
*/
|
||||
private String merchantSerialNumber;
|
||||
|
||||
/**
|
||||
* 私钥文件路径
|
||||
*/
|
||||
private String certKeyPath;
|
||||
|
||||
/**
|
||||
* 商户密钥
|
||||
*/
|
||||
private String apiV3Key;
|
||||
/**
|
||||
* 微信专业httpClient
|
||||
*/
|
||||
private static CloseableHttpClient httpClient;
|
||||
/**
|
||||
* 生成签名用
|
||||
*/
|
||||
private static Sign sign;
|
||||
|
||||
/**
|
||||
* 证书管理器
|
||||
*/
|
||||
private static CertificatesManager certificatesManager;
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws Exception {
|
||||
log.info("私钥路径:{}", certKeyPath);
|
||||
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(certKeyPath));
|
||||
// 获取证书管理器实例
|
||||
certificatesManager = CertificatesManager.getInstance();
|
||||
sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, merchantPrivateKey.getEncoded(), null);
|
||||
|
||||
// 向证书管理器增加需要自动更新平台证书的商户信息
|
||||
certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
|
||||
new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
|
||||
// 从证书管理器中获取verifier
|
||||
Verifier verifier = certificatesManager.getVerifier(merchantId);
|
||||
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
|
||||
.withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
|
||||
.withValidator(new WechatPay2Validator(verifier));
|
||||
httpClient = builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> pay(PayHistoryPo body) {
|
||||
long now = System.currentTimeMillis();
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("mchid", merchantId);
|
||||
obj.put("appid", appId);
|
||||
obj.put("description", body.getDescription());
|
||||
obj.put("out_trade_no", body.getSn());
|
||||
obj.put("notify_url", "https://backend/asdfasdf/notify");
|
||||
Map<String, String> attach = new HashMap<>();
|
||||
attach.put("sn", body.getSn());
|
||||
obj.put("attach", JSON.toJSONString(attach));
|
||||
JSONObject amount = new JSONObject();
|
||||
amount.put("total", body.getPrice().multiply(BigDecimal.valueOf(100)).intValue());
|
||||
obj.put("amount", amount);
|
||||
JSONObject payer = new JSONObject();
|
||||
//放入用户的openId
|
||||
payer.put("openid", "");
|
||||
obj.put("payer", payer);
|
||||
log.info("请求参数为:" + JSON.toJSONString(obj));
|
||||
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
|
||||
httpPost.addHeader("Accept", "application/json");
|
||||
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
|
||||
httpPost.setEntity(new StringEntity(obj.toJSONString(), "UTF-8"));
|
||||
try {
|
||||
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||
String bodyAsString = EntityUtils.toString(response.getEntity());
|
||||
String prePayId = JSONObject.parseObject(bodyAsString).getString("prepay_id");
|
||||
|
||||
//准备小程序端的请求参数
|
||||
Map<String, String> map = new HashMap<>(6);
|
||||
map.put("appId", appId);
|
||||
String timeStamp = String.valueOf(now / 1000);
|
||||
map.put("timeStamp", timeStamp);
|
||||
String nonceStr = IdUtil.fastSimpleUUID();
|
||||
map.put("nonceStr", nonceStr);
|
||||
String packageStr = "prepay_id=" + prePayId;
|
||||
map.put("package", packageStr);
|
||||
map.put("signType", "RSA");
|
||||
map.put("paySign", Base64.encode(sign.sign(appId + "\n" + timeStamp + "\n" + nonceStr + "\n" + packageStr + "\n")));
|
||||
return map;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(HttpServletRequest servletRequest) {
|
||||
|
||||
String timeStamp = servletRequest.getHeader("Wechatpay-Timestamp");
|
||||
String nonce = servletRequest.getHeader("Wechatpay-Nonce");
|
||||
String signature = servletRequest.getHeader("Wechatpay-Signature");
|
||||
String certSn = servletRequest.getHeader("Wechatpay-Serial");
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(servletRequest.getInputStream()))) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
stringBuilder.append(line);
|
||||
}
|
||||
String obj = stringBuilder.toString();
|
||||
log.info("回调数据:{},{},{},{},{}", obj, timeStamp, nonce, signature, certSn);
|
||||
Verifier verifier = certificatesManager.getVerifier(merchantId);
|
||||
String sn = verifier.getValidCertificate().getSerialNumber().toString(16).toUpperCase(Locale.ROOT);
|
||||
NotificationRequest request = new NotificationRequest.Builder().withSerialNumber(sn)
|
||||
.withNonce(nonce)
|
||||
.withTimestamp(timeStamp)
|
||||
.withSignature(signature)
|
||||
.withBody(obj)
|
||||
.build();
|
||||
NotificationHandler handler = new NotificationHandler(verifier, apiV3Key.getBytes(StandardCharsets.UTF_8));
|
||||
// 验签和解析请求体
|
||||
Notification notification = handler.parse(request);
|
||||
JSONObject res = JSON.parseObject(notification.getDecryptData());
|
||||
//做一些操作
|
||||
JSONObject attach = res.getJSONObject("attach");
|
||||
} catch (Exception e) {
|
||||
log.error("微信支付回调失败", e);
|
||||
}
|
||||
}
|
||||
}
|
1
weChatPay/src/main/resources/application.properties
Normal file
1
weChatPay/src/main/resources/application.properties
Normal file
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,13 @@
|
||||
package com.example.wechatpay;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class WeChatPayApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user