# 微信支付
**Repository Path**: Curtain-lichunhui/wechat-payment
## Basic Information
- **Project Name**: 微信支付
- **Description**: 基于微信wechatpay-java开发的微信支付功能
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-05-15
- **Last Updated**: 2023-05-16
## Categories & Tags
**Categories**: Uncategorized
**Tags**: wechatpay-java, 微信支付
## README
# 微信支付
#### 介绍
基于微信wechatpay-java开发的微信支付
#### 安装教程
1. 引入依赖
```xml
com.github.wechatpay-apiv3
wechatpay-java
0.2.7
```
2. 编写配置项
```yaml
wechatpay:
#appId
appId: xxxxx
#商户号
mchId: xxxxx
#apiv3秘钥
apiV3Key: xxxxxx
#证书序列号
merchantSerialNumber: xxxx
#支付回调地址
payNotifyUrl: https://xxxx/wxpay/payNotify
#退款回调地址
refundNotifyUrl: https://xxxxx/wxpay/refundNotify
#私钥
privateKey: xxxx
```
3. 新增配置类
```java
@Configuration
@ConfigurationProperties(prefix = "wechatpay")
@Data
public class WxPayConfig {
private String appId;
private String mchId;
private String apiV3Key;
private String privateKey;
private String merchantSerialNumber;
private String payNotifyUrl;
private String refundNotifyUrl;
private RSAAutoCertificateConfig config;
@PostConstruct
public void initConfig() {
if (config == null) {
config = new RSAAutoCertificateConfig.Builder()
.merchantId(mchId)
.privateKey(privateKey)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
}
}
@Bean
public RSAAutoCertificateConfig getConfig() {
return config;
}
}
```
4. 新增微信服务类
```java
@Service
@Slf4j
public class NativeService {
@Resource
private WxPayConfig wxPayConfig;
@Resource
private RSAAutoCertificateConfig config;
/**
* 下单
* @param orderNo 订单号
* @param money 支付金额(元)
* @param describe 订单描述
* @return 支付链接
*/
public PrepayResponse prepay(String orderNo, BigDecimal money, String describe){
NativePayService service = new NativePayService.Builder().config(config).build();
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(money.multiply(new BigDecimal("100")).intValue());
request.setAmount(amount);
request.setAppid(wxPayConfig.getAppId());
request.setMchid(wxPayConfig.getMchId());
request.setDescription(describe);
request.setOutTradeNo(orderNo);
request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
log.info("请求参数 ===> {}",request);
PrepayResponse prepay = null;
try{
prepay = service.prepay(request);
}catch (HttpException e){ // 发送HTTP请求失败
log.info(String.valueOf(e.getHttpRequest()));
}catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
log.info(String.valueOf(e.getResponseBody()));
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
// 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义
log.info(String.valueOf(e.getMessage()));
}
return prepay;
}
/**
* 根据商户订单号查询订单
* @param orderNo 订单号
* @return 订单信息
*/
public Transaction queryOrderByOrderNo(String orderNo){
NativePayService service = new NativePayService.Builder().config(config).build();
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setMchid(wxPayConfig.getMchId());
request.setOutTradeNo(orderNo);
Transaction transaction = null;
try{
transaction = service.queryOrderByOutTradeNo(request);
}catch (HttpException e){ // 发送HTTP请求失败
log.info(String.valueOf(e.getHttpRequest()));
}catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
log.info(String.valueOf(e.getResponseBody()));
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
// 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义
log.info(String.valueOf(e.getMessage()));
}
return transaction;
}
/**
* 根据退款单号查询退款信息
* @param refundNo 退款单号
* @return 退款信息
*/
public Refund queryRefundByRefundNo(String refundNo){
RefundService service = new RefundService.Builder().config(config).build();
QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
request.setOutRefundNo(refundNo);
Refund refund = null;
try{
refund = service.queryByOutRefundNo(request);
}catch (HttpException e){ // 发送HTTP请求失败
log.info(String.valueOf(e.getHttpRequest()));
}catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
log.info(String.valueOf(e.getResponseBody()));
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
log.info(String.valueOf(e.getMessage()));
}
return refund;
}
/**
* 根据订单号申请退款
* @param orderNo 订单号
* @param refundNo 退款单号
* @param money 退款金额(元)
* @param reason 退退款原因
* @return 退款信息
*/
public Refund refundByOrderNo(String orderNo, String refundNo, BigDecimal money, String reason){
RefundService service = new RefundService.Builder().config(config).build();
CreateRequest request = new CreateRequest();
AmountReq amount = new AmountReq();
long refundMoney = money.multiply(new BigDecimal("100")).longValue();
amount.setRefund(refundMoney);
amount.setTotal(refundMoney);
amount.setCurrency("CNY");
request.setAmount(amount);
request.setOutTradeNo(orderNo);
request.setOutRefundNo(refundNo);
request.setReason(reason);
request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());
Refund refund = null;
try{
refund = service.create(request);
}catch (HttpException e){ // 发送HTTP请求失败
log.info(String.valueOf(e.getHttpRequest()));
}catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
log.info(String.valueOf(e.getResponseBody()));
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
log.info(String.valueOf(e.getMessage()));
}
log.info("退款通知 ===> {}",refund);
return refund;
}
/**
* 回调解析
* @param request 请求
* @param returnType 转化对象:Transaction(支付回调对象)、RefundNotification(退款回调对象)
* @return payNotify
* @throws IOException 异常
*/
public T parseNotify(HttpServletRequest request, Class returnType) throws IOException {
String signature = request.getHeader("Wechatpay-Signature");
String serialNumber = request.getHeader("Wechatpay-Serial");
String nonce = request.getHeader("Wechatpay-Nonce");
String timestamp = request.getHeader("Wechatpay-Timestamp");
String requestBody = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
// 构造 RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serialNumber)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.body(requestBody)
.build();
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(config);
//验签、解密并转换成 Transaction(支付回调对象)、RefundNotification(退款回调对象)
return parser.parse(requestParam,returnType);
}
}
```
5. 回调接口
```java
//微信支付回调接口
@RestController
@Slf4j
@RequestMapping("/wxpay")
public class WxpayController {
@Resource
private NativeService nativeService;
//支付结果回调
@PostMapping("/payNotify")
public String payNotify(HttpServletRequest request, HttpServletResponse response){
Map map = new HashMap<>();
try {
//支付解析
Transaction payNotify = nativeService.parseNotify(request, Transaction.class);
log.info("====> 支付回调通知 ===> {} <====",payNotify);
//交易状态
Transaction.TradeStateEnum status = payNotify.getTradeState();
if (Transaction.TradeStateEnum.SUCCESS.equals(status)){
//处理订单支付状态
//TODO
//成功应答
response.setStatus(200);
map.put("code","SUCCESS");
map.put("message","成功");
}else {
//失败应答
log.info("====> 失败应答交易状态 ===> {} <====",status);
response.setStatus(500);
map.put("code","ERROR");
map.put("message","失败");
}
return JSONObject.toJSONString(map);
} catch (IOException e) {
e.printStackTrace();
//失败应答
response.setStatus(500);
map.put("code","ERROR");
map.put("message","失败");
return JSONObject.toJSONString(map);
}
}
//退款结果回调
@PostMapping("/refundNotify")
public String refundNotify(HttpServletRequest request, HttpServletResponse response){
Map map = new HashMap<>();
try {
//退款解析
RefundNotification refundNotify = nativeService.parseNotify(request, RefundNotification.class);
log.info("====> 退款回调通知 ===> {} <====",refundNotify);
Status status = refundNotify.getRefundStatus();
if (Status.SUCCESS.equals(status)){
//处理退款状态
//TODO
//成功应答
response.setStatus(200);
map.put("code","SUCCESS");
map.put("message","成功");
}else {
//失败应答
response.setStatus(500);
map.put("code","ERROR");
map.put("message","失败");
}
return JSONObject.toJSONString(map);
} catch (IOException e) {
e.printStackTrace();
//失败应答
response.setStatus(500);
map.put("code","ERROR");
map.put("message","失败");
return JSONObject.toJSONString(map);
}
}
}
```