看效果
参考文章:SpringBoot + 小程序 整合微信支付和退款 V2版 - 码农教程
本文仅作记录
参考文章: UniApp + SpringBoot 实现微信支付和退款 - 奔跑的砖头 - 博客园
Springboot整合支付宝支付参考: UniApp + SpringBoot 实现支付宝支付和退款 - 奔跑的砖头 - 博客园
官方网站: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
org.springframework.boot
spring-boot-starter-web
com.github.binarywang
weixin-java-pay
4.2.5.B
org.jodd
jodd-http
6.0.8
org.springframework.boot
spring-boot-configuration-processor
true
org.projectlombok
lombok
true
回调地址 我这里使用 ngrok内网穿透 官网: ngrok - Online in One Line
# 服务启动端口
server:
port: 8081
# 微信pay相关
wxpay:
# appId
appId: # 自己的appId
# 商户id
mchId: # 自己的商户id
# 商户秘钥
mchKey: # 商户秘钥
# p12证书文件的绝对路径或者以classpath:开头的类路径.
keyPath: classpath:/wxpay_cert/apiclient_cert.p12
privateKeyPath: classpath:/wxpay_cert/apiclient_key.pem
privateCertPath: classpath:/wxpay_cert/apiclient_cert.pem
# 微信支付的异步通知接口
# 我这里使用的ngrok内网穿透
notifyUrl: https://bfb3-221-237-148-6.ap.ngrok.io/wechat/pay/notify
# 退款回调地址
refundNotifyUrl: https://bfb3-221-237-148-6.ap.ngrok.io/wechat/pay/refund_notify
package com.system.system.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "wxpay")
public class WechatPayConfig {
private String appId;
private String mchId;
private String mchKey;
private String keyPath;
private String privateKeyPath;
private String privateCertPath;
private String notifyUrl;
}
package com.system.system.service.impl;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;
import java.net.InetAddress;
/**
* 微信支付
*/
@Service
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WechatPayConfig.class)
@AllArgsConstructor
public class BizWechatPayService {
private WechatPayConfig wechatPayConfig;
public WxPayService wxPayService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(wechatPayConfig.getAppId());
payConfig.setMchId(wechatPayConfig.getMchId());
payConfig.setMchKey(wechatPayConfig.getMchKey());
payConfig.setKeyPath(wechatPayConfig.getKeyPath());
payConfig.setPrivateKeyPath(wechatPayConfig.getPrivateKeyPath());
payConfig.setPrivateCertPath(wechatPayConfig.getPrivateCertPath());
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
payConfig.setSignType("MD5");
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
/**
* 创建微信支付订单
*
* @param productTitle 商品标题
* @param outTradeNo 订单号
* @param totalFee 总价
* @param openId openId
* @return
*/
public Object createOrder(String productTitle, String outTradeNo, Integer totalFee, String openId) {
try {
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
// 支付描述
request.setBody(productTitle);
// 订单号
request.setOutTradeNo(outTradeNo);
// 请按照分填写
request.setTotalFee(totalFee);
// 小程序需要传入 openid
request.setOpenid(openId);
// 回调链接
request.setNotifyUrl(wechatPayConfig.getNotifyUrl());
// 终端IP.
request.setSpbillCreateIp(InetAddress.getLocalHost().getHostAddress());
// 设置类型为JSAPI
request.setTradeType(WxPayConstants.TradeType.JSAPI);
// 一定要用 createOrder 不然得自己做二次校验
Object order = wxPayService().createOrder(request);
return order;
} catch (Exception e) {
return null;
}
}
/**
* 退款
*
* @param tradeNo 订单号
* @param totalFee 总价
* @return
*/
public WxPayRefundResult refund(String tradeNo, Integer totalFee) {
WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
wxPayRefundRequest.setTransactionId(tradeNo);
wxPayRefundRequest.setOutRefundNo(String.valueOf(System.currentTimeMillis()));
wxPayRefundRequest.setTotalFee(totalFee);
wxPayRefundRequest.setRefundFee(totalFee);
wxPayRefundRequest.setNotifyUrl(wechatPayConfig.getRefundNotifyUrl());
try {
WxPayRefundResult refund = wxPayService().refundV2(wxPayRefundRequest);
if (refund.getReturnCode().equals("SUCCESS") && refund.getResultCode().equals("SUCCESS")) {
return refund;
}
} catch (WxPayException e) {
e.printStackTrace();
}
return null;
}
}
package com.runbrick.paytest.controller;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.runbrick.paytest.util.wxpay.BizWechatPayService;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/wechat/pay")
@AllArgsConstructor
public class WechatController {
BizWechatPayService wechatPayService;
private static Logger logger = LoggerFactory.getLogger(WechatController.class);
/**
* 创建微信订单
*
* @return
*/
@RequestMapping(value = "/unified/request", method = RequestMethod.GET)
public Object appPayUnifiedRequest() {
// totalFee 必须要以分为单位
Object createOrderResult = wechatPayService.createOrder("测试支付", String.valueOf(System.currentTimeMillis()), 1, "从前端传入的openIdopendId");
logger.info("统一下单的生成的参数:{}", createOrderResult);
return createOrderResult;
}
@RequestMapping(method = RequestMethod.POST, value = "notify")
public String notify(@RequestBody String xmlData) {
try {
WxPayOrderNotifyResult result = wechatPayService.wxPayService().parseOrderNotifyResult(xmlData);
// 支付返回信息
if ("SUCCESS".equals(result.getReturnCode())) {
// 可以实现自己的业务逻辑
logger.info("来自微信支付的回调:{}", result);
}
return WxPayNotifyResponse.success("成功");
} catch (WxPayException e) {
logger.error(e.getMessage());
return WxPayNotifyResponse.fail("失败");
}
}
/**
* 退款
*
* @param transaction_id
*/
@RequestMapping(method = RequestMethod.POST, value = "refund")
public void refund(String transaction_id) {
// totalFee 必须要以分为单位,退款的价格可以这里只做的全部退款
WxPayRefundResult refund = wechatPayService.refund(transaction_id, 1);
// 实现自己的逻辑
logger.info("退款本地回调:{}", refund);
}
/**
* 退款回调
*
* @param xmlData
* @return
*/
@RequestMapping(method = RequestMethod.POST, value = "refund_notify")
public String refundNotify(@RequestBody String xmlData) {
// 实现自己的逻辑
logger.info("退款远程回调:{}", xmlData);
// 必须要返回 SUCCESS 不过有 WxPayNotifyResponse 给整合成了 xml了
return WxPayNotifyResponse.success("成功");
}
}
{{title}}
待解决的问题
这段前端是怎么传值的,一直没有解决
@RequestMapping(method = RequestMethod.POST, value = "notify")
public String notify(@RequestBody String xmlData) {
try {
WxPayOrderNotifyResult result = wechatPayService.wxPayService().parseOrderNotifyResult(xmlData);
// 支付返回信息
if ("SUCCESS".equals(result.getReturnCode())) {
// 可以实现自己的业务逻辑
logger.info("来自微信支付的回调:{}", result);
}
return WxPayNotifyResponse.success("成功");
} catch (WxPayException e) {
logger.error(e.getMessage());
return WxPayNotifyResponse.fail("失败");
}
}