# 微信支付
# 扫码支付
# 微信扫码支付介绍
具体参考官方文档
# 微信扫码支付流程
总结我们接下来要做的事情
- 调用下单接口,获取 code_url
- 根据 code_url 生成二维码
- 查询订单的支付状态
# 统一下单
# 核心对象创建
1. 添加依赖 (wechatpay-apache-httpclient)
2. 初始化 httpclient 对象、加载商户私钥、加载平台证书
3. 定义商户私钥等参数
1. 添加依赖
<dependency> | |
<groupId>com.github.wechatpay-apiv3</groupId> | |
<artifactId>wechatpay-apache-httpclient</artifactId> | |
<version>0.4.9</version> | |
</dependency> |
2. 初始化 httpclient 对象、加载商户私钥、加载平台证书
@Before | |
public void setup() throws IOException { | |
// 加载商户私钥(privateKey:私钥字符串) | |
PrivateKey merchantPrivateKey = PemUtil | |
.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8"))); | |
// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3 密钥) | |
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier( | |
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),apiV3Key.getBytes("utf-8")); | |
// 初始化 httpClient | |
httpClient = WechatPayHttpClientBuilder.create() | |
.withMerchant(mchId, mchSerialNo, merchantPrivateKey) | |
.withValidator(new WechatPay2Validator(verifier)).build(); | |
} | |
@After | |
public void after() throws IOException { | |
httpClient.close(); | |
} |
3. 定义商户私钥等参数
private String mchId = "xxx"; // 商户号 | |
//private String appId = "wx6592a2db3f85ed25"; // 应用号 | |
private String appId = "xxx"; // 应用号 | |
private String privateKey = "xx" + | |
"-----END PRIVATE KEY-----\n"; // 私钥字符串 | |
private String mchSerialNo = "xxx"; // 商户证书序列号 | |
private String apiV3Key = "xxx"; //V3 密钥 |
# native 下单接口代码
1. 定义参数实体
金额实体类:
package com.itheima.pojo; | |
import lombok.Builder; | |
import lombok.Data; | |
@Builder | |
@Data | |
public class Amount { | |
private Integer total; | |
private String currency; | |
} |
请求参数实体类:
package com.itheima.pojo; | |
import lombok.Builder; | |
import lombok.Data; | |
@Builder | |
@Data | |
public class NativePayParams { | |
private String appid; // 应用 id | |
private String mchid; // 商户 id | |
private String description; // 商品描述 | |
private String out_trade_no; // 订单号 | |
private String notify_url; // 支付成功回调通知地址 | |
private Amount amount; // 订单金额信息 | |
} |
2. 修改官方提供的代码如下:
@Test | |
public void testNativePay() throws Exception{ | |
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native"); | |
// 请求 body 参数 | |
Amount amount = Amount.builder().currency("CNY").total(1).build(); | |
NativePayParams nativePayParams = NativePayParams.builder().appid(appId) | |
.description("百世可乐") | |
.mchid(mchId) | |
.notify_url("http://localhost:8080") | |
.out_trade_no("AEFA234DAF342342FRADFAS1") | |
.amount(amount) | |
.build(); | |
String reqdata = JSON.toJSONString(nativePayParams); | |
StringEntity entity = new StringEntity(reqdata,"utf-8"); | |
entity.setContentType("application/json"); | |
httpPost.setEntity(entity); | |
httpPost.setHeader("Accept", "application/json"); | |
// 完成签名并执行请求 | |
CloseableHttpResponse response = httpClient.execute(httpPost); | |
try { | |
int statusCode = response.getStatusLine().getStatusCode(); | |
if (statusCode == 200) { // 处理成功 | |
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity())); | |
} else if (statusCode == 204) { // 处理成功,无返回 Body | |
System.out.println("success"); | |
} else { | |
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity())); | |
throw new IOException("request failed"); | |
} | |
} finally { | |
response.close(); | |
} | |
} |
# 测试
# 二维码
# 二维码生成
直接拷贝资料 /qrcodejs 中的 index.html 和 jquery.min.js 和 qrcode.js 文件到工程的 resource/static 目录下,如下图所示:
# 二维码扫码测试
直接打开 index.html 文件,然后将微信支付的地址粘贴到数据框中,按下 enter 键,生成新的二维码图片,如下图所示:
打开手机微信进行扫码测试
# 支付成功通知
# 微信主动通知
详细查阅微信官方文档
核心思路分析
- 编写接口,接受微信的支付成功通知
- 支付通知的内容需要解密
- 本地测试的 ip 不是公网 ip,外部无法访问,需要域名穿透
- 微信主动通知的地址是通过下单接口中的请求参数 “notify_url” 来设置的 ,要求必须为 https 地址 。
# 代码实现
根据微信官方的接口文档定义实体,接收参数
package com.itheima.pojo;
import lombok.Data;
@Data
public class ResourceDto {
private String algorithm;
private String ciphertext;
private String associated_data;
private String original_type;
private String nonce;
}
package com.itheima.pojo;
import lombok.Data;
@Data
public class NotifyDto {
private String id;
private String create_time;
private String event_type;
private String resource_type;
private ResourceDto resource;
private String summary;
}
定义接口:
package com.itheima.controller; | |
import com.itheima.pojo.NotifyDto; | |
import com.itheima.service.NativePayService; | |
import lombok.extern.log4j.Log4j2; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.web.bind.annotation.PostMapping; | |
import org.springframework.web.bind.annotation.RequestBody; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.RestController; | |
import javax.servlet.ServletInputStream; | |
import javax.servlet.http.HttpServletRequest; | |
import java.util.HashMap; | |
import java.util.Map; | |
@RestController | |
@RequestMapping("/native") | |
@Log4j2 | |
public class NativePayController { | |
@Autowired | |
private NativePayService nativePayService; | |
@PostMapping("/notify") | |
public Map<String,String> nativeNotify(@RequestBody NotifyDto dto){ | |
System.out.println("--微信回调--"); | |
Map<String ,String> map = null; | |
try { | |
nativePayService.notify(dto); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
map = new HashMap<>(); | |
map.put("code","FAIL"); | |
map.put("message","失败"); | |
} | |
return map; | |
} | |
} |
service
此处的解密参考官方文档
package com.itheima.service.impl; | |
import com.alibaba.fastjson2.JSON; | |
import com.itheima.pojo.NotifyDto; | |
import com.itheima.service.NativePayService; | |
import com.itheima.utils.AesUtil; | |
import org.springframework.stereotype.Service; | |
import java.io.IOException; | |
import java.security.GeneralSecurityException; | |
import java.util.Map; | |
@Service | |
public class NativePayServiceImpl implements NativePayService { | |
private String apiV3Key ="CZBK51236435wxpay435434323FFDuv3"; | |
@Override | |
public void notify(NotifyDto dto) { | |
String jsonStr = null; | |
try { | |
jsonStr = new AesUtil(apiV3Key.getBytes()) | |
.decryptToString(dto.getResource().getAssociated_data().getBytes(), | |
dto.getResource().getNonce().getBytes(), | |
dto.getResource().getCiphertext()); | |
} catch (GeneralSecurityException e) { | |
e.printStackTrace(); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
Map map = JSON.parseObject(jsonStr, Map.class); | |
String outTradeNo = map.get("out_trade_no").toString(); | |
System.out.println("订单"+outTradeNo+"支付成功"); | |
} | |
} |
# 域名穿透
本地电脑的 ip 是局域网 ip,外界无法访问。此处我们只能使用域名穿透的一些软件。此处个人使用的是花生壳
然后再下单的代码中修改 notify_url 的地址为域名穿透地址 / 资源路径,代码如下:
# 测试
此时只需要再次支付即可,对比解析后的订单号和支付的订单号:
# 主动查询支付结果
微信官方不保证支付通知的调用是正确的,所以我们必须加上双保险,可以主动去查询支付的结果,参考官方文档
参考下单的 api 和接口文档,编写测试代码如下:
@Test | |
public void checkPayStatus() throws Exception{ | |
String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/1AEFA234DAF442342FRADFAS12?mchid=1561414331"; | |
HttpGet httpGet = new HttpGet(url); | |
httpGet.setHeader("Accept", "application/json"); | |
// 完成签名并执行请求 | |
CloseableHttpResponse response = httpClient.execute(httpGet); | |
try { | |
int statusCode = response.getStatusLine().getStatusCode(); | |
if (statusCode == 200) { // 处理成功 | |
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity())); | |
} else if (statusCode == 204) { // 处理成功,无返回 Body | |
System.out.println("success"); | |
} else { | |
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity())); | |
throw new IOException("request failed"); | |
} | |
} finally { | |
response.close(); | |
} | |
} |
将请求的结果通过三方 json 解析工具解析:得到如下结果:
提问:什么时候去查询订单的支付状态呢?
# 退款
# api 介绍
详细参考官方文档
# 代码实现
实体对象
package com.itheima.pojo; | |
import lombok.Builder; | |
import lombok.Data; | |
@Data | |
@Builder | |
public class RefundAmount { | |
private int refund;// 退款金额 | |
private int total; // 原支付交易的订单总金额 | |
private String currency;// | |
} |
package com.itheima.pojo; | |
import lombok.Builder; | |
import lombok.Data; | |
@Data | |
@Builder | |
public class RefundParams { | |
private String out_trade_no; // 支付交易对应的商户订单号 | |
private String out_refund_no; // 商户系统内部的退款单号 | |
private RefundAmount amount; | |
} |
接口代码:
@Test | |
public void refund() throws Exception{ | |
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"); | |
// 请求 body 参数 | |
RefundAmount refundAmount = RefundAmount.builder() | |
.currency("CNY") | |
.refund(1) | |
.total(1) | |
.build(); | |
RefundParams refundParams = RefundParams.builder() | |
.out_refund_no("AAAAAEWRTWERWE23432423") | |
.amount(refundAmount) | |
.out_trade_no("1AEFA234DAF442342FRADFAS12") | |
.build(); | |
String reqdata = JSON.toJSONString(refundParams); | |
StringEntity entity = new StringEntity(reqdata,"utf-8"); | |
entity.setContentType("application/json"); | |
httpPost.setEntity(entity); | |
httpPost.setHeader("Accept", "application/json"); | |
// 完成签名并执行请求 | |
CloseableHttpResponse response = httpClient.execute(httpPost); | |
try { | |
int statusCode = response.getStatusLine().getStatusCode(); | |
if (statusCode == 200) { // 处理成功 | |
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity())); | |
} else if (statusCode == 204) { // 处理成功,无返回 Body | |
System.out.println("success"); | |
} else { | |
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity())); | |
throw new IOException("request failed"); | |
} | |
} finally { | |
response.close(); | |
} | |
} |
# 测试
将结果使用三方 json 工具解析:如下图所示
然后再调用上面的查单,查询结果
# springBoot 的 starter 封装
# springBoot 自动化配置原理
基于启动类上的注解 @SpringBootApplication 中的 @EnableAutoConfiguration 来开启自动化配置,这个注解通过 @Import 来导入了配置类,如下图所示:
上述配置类会加载所有 jar 包以及源码下的 META-INF 下得 spring.factories 文件,如下图所示:
然后会加载配置中指定的自动化配置 key 所对应的所有自动化配置类,示例如下图所示:
但是自动化配置类并不是都需要加载的,springBoot 通过自动化配置类上添加的各种条件注解 @Conditional 来判断这个自动化配置类是否满足条件,如果满足条件,即加载配置类,如下图 webmvc 自动化配置类示例:
# 微信扫码支付 starter 封装
流程分析 -- 逆向思维
我们最终需要能够实现的目的 --> 引入依赖,yaml 中做好配置,直接依赖注入 xxxTemplate 能够直接使用
---> 需要创建一个 xxxTemplate 对象 ---> 提供一个 xxxTemplate 类,封装扫码支付的 api,一些环境级别的参数做成配置
--> 提供 xxxProperties 配置类,然后加载 yaml 中的配置
最终实现步骤整理
0. 创建工程,引入相关依赖
- 提供 WxPayProperties 配置类,用于加载 application.yaml 中的环境配置
- 提供 WxPayTemplate 类,封装扫码支付的 api,环境配置依赖注入得到
- 提供配置类,启动 WxPayProperties 类对象,创建 WxPayTemplate 对象
- 提供 META-INF/spring.factory 配置类,配置自动化配置
- 创建新测试工程,测试 starter 使用
代码实现
0. 创建工程,引入相关依赖
<dependencies> | |
<dependency> | |
<groupId>com.github.wechatpay-apiv3</groupId> | |
<artifactId>wechatpay-apache-httpclient</artifactId> | |
<version>0.4.9</version> | |
</dependency> | |
<dependency> | |
<groupId>org.projectlombok</groupId> | |
<artifactId>lombok</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-configuration-processor</artifactId> | |
<optional>true</optional> | |
</dependency> | |
<dependency> | |
<groupId>com.alibaba</groupId> | |
<artifactId>fastjson</artifactId> | |
<version>1.2.83</version> | |
</dependency> | |
</dependencies> |
- 提供 WxPayProperties 配置类,用于加载 application.yaml 中的环境配置
package com.heima.wxpay.config; | |
import lombok.Data; | |
import org.springframework.boot.context.properties.ConfigurationProperties; | |
import org.springframework.stereotype.Component; | |
@Data | |
@ConfigurationProperties(prefix = "wxpay") | |
public class WxPayProperties { | |
private String mchId = "1561414331"; // 商户号 | |
//private String appId = "wx6592a2db3f85ed25"; // 应用号 | |
private String appId = "wxffb3637a228223b8"; // 应用号 | |
private String privateKey = "-----BEGIN PRIVATE KEY-----\n" + | |
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDBHGgIh80193Gh\n" + | |
"dpD1LtMZfTRpcWI0fImyuBCyrd3gYb3rrsARebGcHdJsQA3mVjVqVp5ybhEZDPa4\n" + | |
"ecoK4Ye1hTppNpI/lmLt4/uUV/zhF5ahli7hi+116Ty6svHSbuMQBuUZeTFOwGrx\n" + | |
"jvofU/4pGIwh8ZvkcSnyOp9uX2177UVxDBkhgbZbJp9XF2b83vUa5eHo93CziPzn\n" + | |
"3hFdAlBCdTXB7DH+m0nN3Jou0szGukvq7cIgGpHku4ycKSTkIhhl9WRhN6OoSEJx\n" + | |
"q88MXzjkzTruc85PHN52aUTUifwg3T8Y4XqFQ61dTnEmgxeD2O6/pLdB9gLsp6yC\n" + | |
"GqN5Lqk7AgMBAAECggEBAL4X+WzUSbSjFS9NKNrCMjm4H1zgqTxjj6TnPkC1mGEl\n" + | |
"tjAHwLgzJBw62wWGdGhWWpSIGccpBBm1wjTMZpAZfF66fEpP1t1Ta6UjtGZNyvfF\n" + | |
"IZmE3jdWZ/WXGBnsxtFQKKKBNwrBW0Fbdqq9BQjLxLitmlxbmwrgPttcy855j6vZ\n" + | |
"qq4MBT1v8CtUT/gz4UWW2xWovVnmWOrRSScv7Nh0pMbRpPLkNHXrBwSSNz/keORz\n" + | |
"XB9JSm85wlkafa7n5/IJbdTml3A/uAgW3q3JZZQotHxQsYvD4Zb5Cnc9CPAXE5L2\n" + | |
"Yk877kVXZMGt5QPIVcPMj/72AMtaJT67Y0fN0RYHEGkCgYEA38BIGDY6pePgPbxB\n" + | |
"7N/l6Df0/OKPP0u8mqR4Q0aQD3VxeGiZUN1uWXEFKsKwlOxLfIFIFk1/6zQeC0xe\n" + | |
"tNTKk0gTL8hpMUTNkE7vI9gFWws2LY6DE86Lm0bdFEIwh6d7Fr7zZtyQKPzMsesC\n" + | |
"3XV9sdSUExEi5o/VwAyf+xZlOXcCgYEA3PGZYlILjg3esPNkhDz2wxFw432i8l/B\n" + | |
"CPD8ZtqIV9eguu4fVtFYcUVfawBb0T11RamJkc4eiSOqayC+2ehgb+GyRLJNK4Fq\n" + | |
"bFcsIT+CK0HlscZw51jrMR0MxTc4RzuOIMoYDeZqeGB6/YnNyG4pw2sD8bIwHm84\n" + | |
"06gtJsX/v10CgYAo8g3/aEUZQHcztPS3fU2cTkkl0ev24Ew2XGypmwsX2R0XtMSB\n" + | |
"uNPNyFHyvkgEKK2zrhDcC/ihuRraZHJcUyhzBViFgP5HBtk7VEaM36YzP/z9Hzw7\n" + | |
"bqu7kZ85atdoq6xpwC3Yn/o9le17jY8rqamD1mv2hUdGvAGYsHbCQxnpBwKBgHTk\n" + | |
"eaMUBzr7yZLS4p435tHje1dQVBJpaKaDYPZFrhbTZR0g+IGlNmaPLmFdCjbUjiPy\n" + | |
"A2+Znnwt227cHz0IfWUUAo3ny3419QkmwZlBkWuzbIO2mms7lwsf9G6uvV6qepKM\n" + | |
"eVd5TWEsokVbT/03k27pQmfwPxcK/wS0GFdIL/udAoGAOYdDqY5/aadWCyhzTGI6\n" + | |
"qXPLvC+fsJBPhK2RXyc+jYV0KmrEv4ewxlK5NksuFsNkyB7wlI1oMCa/xB3T/2vT\n" + | |
"BALgGFPi8BJqceUjtnTYtI4R2JIVEl08RtEJwyU5JZ2rvWcilsotVZYwfuLZ9Kfd\n" + | |
"hkTrgNxlp/KKkr+UuKce4Vs=\n" + | |
"-----END PRIVATE KEY-----\n"; // 私钥字符串 | |
private String mchSerialNo = "25FBDE3EFD31B03A4377EB9A4A47C517969E6620"; // 商户证书序列号 | |
private String apiV3Key = "CZBK51236435wxpay435434323FFDuv3"; //V3 密钥 | |
} |
- 提供 WxPayTemplate 类,封装扫码支付的 api,环境配置依赖注入得到
2 个参数实体类:
package com.heima.wxpay.dto; | |
import lombok.Builder; | |
import lombok.Data; | |
@Builder | |
@Data | |
public class NativePayParams { | |
private String appid; // 应用 id | |
private String mchid; // 商户 id | |
private String description; // 商品描述 | |
private String out_trade_no; // 订单号 | |
private String notify_url; // 支付成功回调通知地址 | |
private Amount amount; // 订单金额信息 | |
} |
package com.heima.wxpay.dto; | |
import lombok.Builder; | |
import lombok.Data; | |
@Builder | |
@Data | |
public class Amount { | |
private Integer total; | |
private String currency; | |
} |
工具类:
package com.heima.wxpay.config; | |
import com.alibaba.fastjson.JSON; | |
import com.heima.wxpay.dto.Amount; | |
import com.heima.wxpay.dto.NativePayParams; | |
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 java.io.IOException; | |
import java.util.Map; | |
public class WxPayTemplate { | |
private WxPayProperties wxPayProperties; | |
private CloseableHttpClient httpClient; | |
public WxPayTemplate(WxPayProperties wxPayProperties,CloseableHttpClient httpClient) { | |
this.wxPayProperties = wxPayProperties; | |
this.httpClient = httpClient; | |
} | |
public String nativePay(Integer total, String description, String outTradeNo) throws Exception{ | |
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native"); | |
// 请求 body 参数 | |
Amount amount = Amount.builder().currency("CNY").total(total).build(); | |
NativePayParams nativePayParams = NativePayParams.builder().appid(wxPayProperties.getAppId()) | |
.description(description) | |
.mchid(wxPayProperties.getMchId()) | |
.notify_url("https://36d5634033.vicp.fun/native/notify") | |
.out_trade_no(outTradeNo) | |
.amount(amount) | |
.build(); | |
String reqdata = JSON.toJSONString(nativePayParams); | |
StringEntity entity = new StringEntity(reqdata,"utf-8"); | |
entity.setContentType("application/json"); | |
httpPost.setEntity(entity); | |
httpPost.setHeader("Accept", "application/json"); | |
// 完成签名并执行请求 | |
CloseableHttpResponse response = httpClient.execute(httpPost); | |
String url = ""; | |
try { | |
int statusCode = response.getStatusLine().getStatusCode(); | |
if (statusCode == 200) { // 处理成功 | |
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity())); | |
url = JSON.parseObject( EntityUtils.toString(response.getEntity()), Map.class).get("code_url").toString(); | |
} else if (statusCode == 204) { // 处理成功,无返回 Body | |
System.out.println("success"); | |
} else { | |
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity())); | |
throw new IOException("request failed"); | |
} | |
} finally { | |
response.close(); | |
} | |
return url; | |
} | |
} |
- 提供配置类,启动 WxPayProperties 类对象,创建 WxPayTemplate 对象
package com.heima.wxpay.config; | |
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; | |
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier; | |
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner; | |
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.util.PemUtil; | |
import org.apache.http.impl.client.CloseableHttpClient; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
import java.io.ByteArrayInputStream; | |
import java.security.PrivateKey; | |
@Configuration | |
@EnableConfigurationProperties(WxPayProperties.class) | |
public class WxPayAutoConfig { | |
@Bean | |
public CloseableHttpClient httpClient(WxPayProperties wxPayProperties) throws Exception{ | |
// 加载商户私钥(privateKey:私钥字符串) | |
PrivateKey merchantPrivateKey = PemUtil | |
.loadPrivateKey(new ByteArrayInputStream(wxPayProperties.getPrivateKey().getBytes("utf-8"))); | |
// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3 密钥) | |
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier( | |
new WechatPay2Credentials(wxPayProperties.getMchId(), new PrivateKeySigner(wxPayProperties.getMchSerialNo(), merchantPrivateKey)),wxPayProperties.getApiV3Key().getBytes("utf-8")); | |
// 初始化 httpClient | |
return WechatPayHttpClientBuilder.create() | |
.withMerchant(wxPayProperties.getMchId(), wxPayProperties.getMchSerialNo(), merchantPrivateKey) | |
.withValidator(new WechatPay2Validator(verifier)).build(); | |
} | |
@Bean | |
public WxPayTemplate wxPayTemplate(WxPayProperties wxPayProperties,CloseableHttpClient httpClient){ | |
return new WxPayTemplate(wxPayProperties,httpClient); | |
} | |
} |
- 提供 META-INF/spring.factory 配置类,配置自动化配置
# Auto Configure | |
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ | |
com.heima.wxpay.config.WxPayAutoConfig |
- 创建新测试工程,测试 starter 使用
导入 starter 依赖:
<dependency> | |
<groupId>com.heima</groupId> | |
<artifactId>wx_pay_starter</artifactId> | |
<version>1.0-SNAPSHOT</version> | |
</dependency> |
直接 springBoot 测试
package com.itheima.wxpay; | |
import com.heima.wxpay.config.WxPayTemplate; | |
import org.junit.jupiter.api.Test; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.boot.test.context.SpringBootTest; | |
@SpringBootTest | |
public class StarterTest { | |
@Autowired | |
private WxPayTemplate wxPayTemplate; | |
@Test | |
public void testNativePay() throws Exception{ | |
String url = wxPayTemplate.nativePay(1, "javaEE企业级开发", "ADFADSFS4353534"); | |
System.out.println(url); | |
} | |
} |