mirror of
https://github.com/dromara/payment-spring-boot.git
synced 2026-03-14 05:43:46 +08:00
356 lines
14 KiB
Markdown
356 lines
14 KiB
Markdown
<div align="center" style="margin-bottom: 10px"><h1>最全最好用的微信支付V3 Spring Boot 组件</h1></div>
|
||
|
||
<p align="center">
|
||
<a target="_blank" href="https://github.com/dromara/payment-spring-boot/blob/release/LICENSE">
|
||
<img alt="" src="https://img.shields.io/github/license/dromara/payment-spring-boot"/>
|
||
</a>
|
||
<a target="_blank" href="https://felord.cn">
|
||
<img alt="" src="https://img.shields.io/badge/java-8-red"/>
|
||
</a>
|
||
<a target="_blank" href="https://spring.io">
|
||
<img alt="" src="https://img.shields.io/badge/spring%20boot-2.4%2B-brightgreen"/>
|
||
</a>
|
||
<a target="_blank" href="https://mvnrepository.com/artifact/cn.felord/payment-spring-boot">
|
||
<img alt="" src="https://img.shields.io/maven-central/v/cn.felord/payment-spring-boot.svg?style=flat-square"/>
|
||
</a>
|
||
<a target="_blank" href="https://github.com/dromara/payment-spring-boot">
|
||
<img alt="" src="https://img.shields.io/github/stars/dromara/payment-spring-boot?style=social"/>
|
||
</a>
|
||
<a target="_blank" href="https://gitee.com/dromara/payment-spring-boot/stargazers">
|
||
<img alt="" src="https://gitee.com/felord/payment-spring-boot/badge/star.svg?theme=white"/>
|
||
</a>
|
||
<a target="_blank" href="https://work.weixin.qq.com/kfid/kfc9d9d759f27f087e1">
|
||
<img alt="点击立即微信咨询" src="https://img.shields.io/badge/%E7%82%B9%E5%87%BB-%E5%BE%AE%E4%BF%A1%E5%92%A8%E8%AF%A2-brightgreen"/>
|
||
</a>
|
||
<a target="_blank" href="#">
|
||
<img alt="点击加入QQ交流①群(满)" src="https://img.shields.io/badge/QQ%E4%BA%A4%E6%B5%81%E7%BE%A4-945342113(满)-ff69b4"/>
|
||
</a>
|
||
<a target="_blank" href="https://jq.qq.com/?_wv=1027&k=cCiv8Vlv">
|
||
<img alt="点击加入QQ交流②群" src="https://img.shields.io/badge/QQ%E4%BA%A4%E6%B5%81%E7%BE%A4-549174561-ff69b4"/>
|
||
</a>
|
||
</p>
|
||
|
||
|
||
<p align="center">如果你感觉这个项目不错,请点击右上角的Star以鼓励作者,谢谢。</p>
|
||
|
||
## 简介
|
||
|
||
Java微信支付V3支付Spring Boot
|
||
Starter,支持微信优惠券,代金券、商家券、智慧商圈、商家转账到零钱、公众号支付、微信小程序支付、分账、支付分、商家券、合单支付、先享卡、电商收付通等全部微信支付功能API,同时满足多个服务商、多个商户开发需求。一键集成,屏蔽了复杂度,API友好,上手快,欢迎star。
|
||
|
||
## Maven 最新中央仓库坐标
|
||
|
||
```xml
|
||
|
||
<dependency>
|
||
<groupId>cn.felord</groupId>
|
||
<artifactId>payment-spring-boot-starter</artifactId>
|
||
<version>1.0.19.RELEASE</version>
|
||
</dependency>
|
||
```
|
||
|
||
## JDK问题
|
||
|
||
**推荐使用Open JDK**,原因参见[FBI Warning](https://github.com/dromara/payment-spring-boot/issues/5)
|
||
|
||
## 文档地址
|
||
|
||
- [payment-spring-boot GitHub文档](https://dromara.github.io/payment-spring-boot)
|
||
|
||
## API清单
|
||
|
||
目前已经实现绝大部分微信支付直连商户和服务商的接口,具体的API明细可查看[API清单](https://dromara.github.io/payment-spring-boot/#/wechat_v3_api)
|
||
> 随着版本迭代功能会增加,也可通过API注册表类`WechatPayV3Type`进行API接口检索。
|
||
|
||
## CHANGELOG
|
||
|
||
更新日志[CHANGELOG](https://dromara.github.io/payment-spring-boot/#/changelog)
|
||
|
||
## 使用入门
|
||
|
||
### 集成配置
|
||
|
||
关于集成配置请详细阅读[payment-spring-boot GitHub文档](https://dromara.github.io/payment-spring-boot)
|
||
中[快速接入](https://dromara.github.io/payment-spring-boot/#/quick_start)章节
|
||
|
||
### 调用示例
|
||
|
||
#### 开启支付
|
||
|
||
需要手动通过`@EnableMobilePay`注解开启支付
|
||
|
||
```java
|
||
import cn.felord.payment.autoconfigure.EnableMobilePay;
|
||
import org.springframework.context.annotation.Configuration;
|
||
|
||
@EnableMobilePay
|
||
@Configuration
|
||
public class PayConfig {
|
||
}
|
||
```
|
||
|
||
#### 支付接口调用
|
||
|
||
这里简单以小程序支付为例,写了一个Spring MVC 控制器,在实践中建议对`WechatApiProvider`进行二次封装作服务层调用
|
||
|
||
```java
|
||
import cn.felord.payment.wechat.enumeration.TradeBillType;
|
||
import cn.felord.payment.wechat.v3.WechatApiProvider;
|
||
import cn.felord.payment.wechat.v3.WechatDirectPayApi;
|
||
import cn.felord.payment.wechat.v3.model.*;
|
||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.context.annotation.Profile;
|
||
import org.springframework.core.io.Resource;
|
||
import org.springframework.http.ResponseEntity;
|
||
import org.springframework.web.bind.annotation.*;
|
||
|
||
import java.time.LocalDate;
|
||
import java.time.Month;
|
||
|
||
/**
|
||
* 支付接口开发样例,以小程序支付为例.
|
||
*/
|
||
@Profile({"wechat", "dev"})
|
||
@RestController
|
||
@RequestMapping("/marketing")
|
||
public class PayController {
|
||
@Autowired
|
||
private WechatApiProvider wechatApiProvider;
|
||
String TENANT_ID = "mobile";
|
||
|
||
/**
|
||
* 总流程建议为 生成商品订单 -> 生成对应的支付订单 -> 支付操作 -> 支付结果回调更新 -> 结束
|
||
* <p>
|
||
* 此处建议在商品订单生成之后调用
|
||
*
|
||
* @param orderId 商品订单id
|
||
* @return the object node
|
||
*/
|
||
@PostMapping("/js")
|
||
public ObjectNode js(@RequestParam String orderId) {
|
||
|
||
//TODO
|
||
// 查询该orderId下是否生成了支付订单
|
||
// 如果没有
|
||
// 新增支付订单存入数据库 并标明支付状态为【待支付】
|
||
// 根据新生成的支付订单信息向微信支付发起支付 并根据返回结果进行处理
|
||
// 如果有状态为待支付
|
||
// 根据待支付订单信息向微信支付发起支付 并根据返回结果进行处理
|
||
// 如果有状态为待支付之外的状态
|
||
// 根据产品的业务设计自行实现
|
||
// 支付状态更新逻辑在【回调接口 /wxpay/callbacks/transaction】中处理 需要幂等处理
|
||
|
||
// 开发时需要指定使用的商户租户配置 这里为 mobile 请参考 application-wechat.yml
|
||
|
||
|
||
PayParams payParams = new PayParams();
|
||
|
||
payParams.setDescription("felord.cn");
|
||
//
|
||
// 商户侧唯一订单号 建议为商户侧支付订单号 订单表主键 或者唯一标识字段
|
||
payParams.setOutTradeNo("X135423420201521613448");
|
||
// 需要定义回调通知
|
||
payParams.setNotifyUrl("/wxpay/callbacks/transaction");
|
||
Amount amount = new Amount();
|
||
amount.setTotal(100);
|
||
payParams.setAmount(amount);
|
||
// 此类支付 Payer 必传 且openid需要同appid有绑定关系 具体去看文档
|
||
Payer payer = new Payer();
|
||
payer.setOpenid("ooadI5kQYrrCqpgbisvC8bEw_oUc");
|
||
payParams.setPayer(payer);
|
||
|
||
return wechatApiProvider.directPayApi(TENANT_ID)
|
||
.jsPay(payParams)
|
||
.getBody();
|
||
}
|
||
|
||
|
||
/**
|
||
* 下载对账单 如果要解析内容的话自行实现
|
||
*
|
||
* @return the response entity
|
||
*/
|
||
@GetMapping("/tradebill")
|
||
public ResponseEntity<Resource> download() {
|
||
WechatDirectPayApi wechatDirectPayApi = wechatApiProvider.directPayApi(TENANT_ID);
|
||
|
||
TradeBillParams tradeBillParams = new TradeBillParams();
|
||
tradeBillParams.setBillDate(LocalDate.of(2021, Month.MAY, 20));
|
||
tradeBillParams.setBillType(TradeBillType.ALL);
|
||
return wechatDirectPayApi.downloadTradeBill(tradeBillParams);
|
||
}
|
||
|
||
/**
|
||
* 下载申请资金账单 如果要解析内容的话自行实现
|
||
*
|
||
* @return the response entity
|
||
*/
|
||
@GetMapping("/fundflowbill")
|
||
public ResponseEntity<Resource> fundFlowBill() {
|
||
WechatDirectPayApi wechatDirectPayApi = wechatApiProvider.directPayApi(TENANT_ID);
|
||
|
||
FundFlowBillParams fundFlowBillParams = new FundFlowBillParams();
|
||
fundFlowBillParams.setBillDate(LocalDate.of(2021, Month.MAY, 20));
|
||
|
||
return wechatDirectPayApi.downloadFundFlowBill(fundFlowBillParams);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 回调示例
|
||
|
||
回调可通过以下示例实现,多租户的回调可将租户ID`tenantId`作为路径参数来实现
|
||
|
||
```java
|
||
import cn.felord.payment.wechat.v3.WechatApiProvider;
|
||
import cn.felord.payment.wechat.v3.WechatMarketingFavorApi;
|
||
import cn.felord.payment.wechat.v3.WechatPayCallback;
|
||
import cn.felord.payment.wechat.v3.model.ResponseSignVerifyParams;
|
||
import lombok.SneakyThrows;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.context.annotation.Profile;
|
||
import org.springframework.web.bind.annotation.PostMapping;
|
||
import org.springframework.web.bind.annotation.RequestHeader;
|
||
import org.springframework.web.bind.annotation.RequestMapping;
|
||
import org.springframework.web.bind.annotation.RestController;
|
||
|
||
import javax.servlet.http.HttpServletRequest;
|
||
import java.util.Map;
|
||
import java.util.stream.Collectors;
|
||
|
||
/**
|
||
* 注意为了演示该配置在使用微信配置application-wechat.yaml才生效
|
||
* <p>
|
||
* 务必保证回调接口的幂等性
|
||
* <p>
|
||
* 微信回调控制器,当支付成功、代金券核销成功后,微信支付服务器会通过回调进行通知商户侧。
|
||
* 商户侧可以根据微信的回调通知进行支付的后续处理,例如支付状态的变更等等。
|
||
* 需要注意的是回调接口需要白名单放行。
|
||
* <p>
|
||
* 开发者只需要编写对结果的{@link java.util.function.Consumer}即可。
|
||
* <p>
|
||
* 请注意:返回的格格式必须是{@link WechatPayCallback} 给出的格式,不能被包装和更改,切记!
|
||
* @author felord.cn
|
||
* @since 1.0.0.RELEASE
|
||
*/
|
||
@Profile({"wechat", "dev"})
|
||
@RestController
|
||
@RequestMapping("/wxpay/callbacks")
|
||
public class CallbackController {
|
||
private static final String TENANT_ID = "mobile";
|
||
@Autowired
|
||
private WechatApiProvider wechatApiProvider;
|
||
|
||
|
||
/**
|
||
* 代金券核销通知.
|
||
* <p>
|
||
* 需要手动调用{@link WechatMarketingFavorApi#setMarketingFavorCallback(String)} 设置,一次性操作!
|
||
*
|
||
* @param wechatpaySerial the wechatpay serial
|
||
* @param wechatpaySignature the wechatpay signature
|
||
* @param wechatpayTimestamp the wechatpay timestamp
|
||
* @param wechatpayNonce the wechatpay nonce
|
||
* @param request the request
|
||
* @return the map
|
||
*/
|
||
@SneakyThrows
|
||
@PostMapping("/coupon")
|
||
public Map<String, ?> couponCallback(
|
||
@RequestHeader("Wechatpay-Serial") String wechatpaySerial,
|
||
@RequestHeader("Wechatpay-Signature") String wechatpaySignature,
|
||
@RequestHeader("Wechatpay-Timestamp") String wechatpayTimestamp,
|
||
@RequestHeader("Wechatpay-Nonce") String wechatpayNonce,
|
||
HttpServletRequest request) {
|
||
String body = request.getReader().lines().collect(Collectors.joining());
|
||
// 对请求头进行验签 以确保是微信服务器的调用
|
||
ResponseSignVerifyParams params = new ResponseSignVerifyParams();
|
||
params.setWechatpaySerial(wechatpaySerial);
|
||
params.setWechatpaySignature(wechatpaySignature);
|
||
params.setWechatpayTimestamp(wechatpayTimestamp);
|
||
params.setWechatpayNonce(wechatpayNonce);
|
||
params.setBody(body);
|
||
return wechatApiProvider.callback(TENANT_ID).couponCallback(params, data -> {
|
||
//TODO 对回调解析的结果进行消费 需要保证消费的幂等性 微信有可能多次调用此接口
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 微信支付成功回调.
|
||
* <p>
|
||
* 无需开发者判断,只有扣款成功微信才会回调此接口
|
||
*
|
||
* @param wechatpaySerial the wechatpay serial
|
||
* @param wechatpaySignature the wechatpay signature
|
||
* @param wechatpayTimestamp the wechatpay timestamp
|
||
* @param wechatpayNonce the wechatpay nonce
|
||
* @param request the request
|
||
* @return the map
|
||
*/
|
||
@SneakyThrows
|
||
@PostMapping("/transaction")
|
||
public Map<String, ?> transactionCallback(
|
||
@RequestHeader("Wechatpay-Serial") String wechatpaySerial,
|
||
@RequestHeader("Wechatpay-Signature") String wechatpaySignature,
|
||
@RequestHeader("Wechatpay-Timestamp") String wechatpayTimestamp,
|
||
@RequestHeader("Wechatpay-Nonce") String wechatpayNonce,
|
||
HttpServletRequest request) {
|
||
String body = request.getReader().lines().collect(Collectors.joining());
|
||
// 对请求头进行验签 以确保是微信服务器的调用
|
||
ResponseSignVerifyParams params = new ResponseSignVerifyParams();
|
||
params.setWechatpaySerial(wechatpaySerial);
|
||
params.setWechatpaySignature(wechatpaySignature);
|
||
params.setWechatpayTimestamp(wechatpayTimestamp);
|
||
params.setWechatpayNonce(wechatpayNonce);
|
||
params.setBody(body);
|
||
return wechatApiProvider.callback(TENANT_ID).transactionCallback(params, data -> {
|
||
//TODO 对回调解析的结果进行消费 需要保证消费的幂等性 微信有可能多次调用此接口
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 微信合单支付成功回调.
|
||
* <p>
|
||
* 无需开发者判断,只有扣款成功微信才会回调此接口
|
||
*
|
||
* @param wechatpaySerial the wechatpay serial
|
||
* @param wechatpaySignature the wechatpay signature
|
||
* @param wechatpayTimestamp the wechatpay timestamp
|
||
* @param wechatpayNonce the wechatpay nonce
|
||
* @param request the request
|
||
* @return the map
|
||
*/
|
||
@SneakyThrows
|
||
@PostMapping("/combine_transaction")
|
||
public Map<String, ?> combineTransactionCallback(
|
||
@RequestHeader("Wechatpay-Serial") String wechatpaySerial,
|
||
@RequestHeader("Wechatpay-Signature") String wechatpaySignature,
|
||
@RequestHeader("Wechatpay-Timestamp") String wechatpayTimestamp,
|
||
@RequestHeader("Wechatpay-Nonce") String wechatpayNonce,
|
||
HttpServletRequest request) {
|
||
String body = request.getReader().lines().collect(Collectors.joining());
|
||
// 对请求头进行验签 以确保是微信服务器的调用
|
||
ResponseSignVerifyParams params = new ResponseSignVerifyParams();
|
||
params.setWechatpaySerial(wechatpaySerial);
|
||
params.setWechatpaySignature(wechatpaySignature);
|
||
params.setWechatpayTimestamp(wechatpayTimestamp);
|
||
params.setWechatpayNonce(wechatpayNonce);
|
||
params.setBody(body);
|
||
return wechatApiProvider.callback(TENANT_ID).combineTransactionCallback(params, data -> {
|
||
//TODO 对回调解析的结果进行消费 需要保证消费的幂等性 微信有可能多次调用此接口
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
## 开源协议
|
||
|
||
**Apache 2.0**
|
||
|
||
## 仓库地址
|
||
|
||
- [GitHub](https://github.com/dromara/payment-spring-boot)
|
||
- [Gitee](https://gitee.com/dromara/payment-spring-boot)
|
||
|