先看支付核心代码
package com.ruoyi.thirdpart.service;
import com.github.binarywang.wxpay.service.WxPayService;
/**
* 店员 业务层
*
* @author ruoyi
*/
public interface IBizWechatPayService
{
Object createOrder(String productTitle, String outTradeNo, Integer totalFee, String openId, String payType, String shopId);
WxPayService wxPayService(String payType, String shopId);
}
package com.ruoyi.thirdpart.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.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.market.domain.ConfigPay;
import com.ruoyi.market.domain.ConfigProgram;
import com.ruoyi.market.mapper.ConfigPayMapper;
import com.ruoyi.market.mapper.ConfigProgramMapper;
import com.ruoyi.thirdpart.service.IBizWechatPayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.stereotype.Service;
import java.net.InetAddress;
@ConditionalOnClass(WxPayService.class)
/**
* 微信支付 业务层处理
*
* @author ruoyi
*/
@Service
public class BizWechatPayServiceImpl implements IBizWechatPayService
{
@Autowired
private ConfigPayMapper configPayMapper;
@Autowired
private ConfigProgramMapper configProgramMapper;
public WxPayService wxPayService(String payType, String shopId) {
WxPayConfig payConfig = new WxPayConfig();
ConfigPay configPay = configPayMapper.selectConfigPayByShopId(shopId);
ConfigProgram configProgram = configProgramMapper.selectConfigProgramByShopId(shopId);
if (payType.equals(UserConstants.PAY_TYPE_WX_OFFICIAL_ACCOUNT)) {
payConfig.setAppId(configProgram.getOfficialAppId());
}
if (payType.equals(UserConstants.PAY_TYPE_WX_MINI_PROGRAM)) {
payConfig.setAppId(configProgram.getWxMpAppId());
}
payConfig.setMchId(configPay.getWxMchId());
payConfig.setMchKey(configPay.getWxMchSecret());
payConfig.setKeyPath(configPay.getWxBackUrl() + configPay.getWxTwelveCertBook());
payConfig.setPrivateKeyPath(configPay.getWxBackUrl() + configPay.getWxKeyBook());
payConfig.setPrivateCertPath(configPay.getWxBackUrl() + configPay.getWxPemCertBook());
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
payConfig.setSignType("MD5");
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
/**
* 创建微信支付订单
*
* @param productTitle 商品标题
* @param outTradeNo 订单号
* @param totalFee 总价
* @param openId openId
* @param payType 支付方式
* @param financeType
* @param shopId
* @return
*/
@Override
public Object createOrder(String productTitle, String outTradeNo, Integer totalFee, String openId,
String payType, String financeType, String shopId) {
try {
ConfigPay configPay = configPayMapper.selectConfigPayByShopId(shopId);
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
// 支付描述
request.setBody(productTitle);
// 订单号
request.setOutTradeNo(outTradeNo);
// 请按照分填写
request.setTotalFee(totalFee);
// 小程序需要传入 openid
request.setOpenid(openId);
// 回调链接 --- 实际就是微信要调用你的接口,不是前端页面地址
request.setNotifyUrl(configPay.getWxBackUrl() + "/thirdpart/pay/payCallback/" + payType + "/" + financeType + "/" + shopId);
// 终端IP.
request.setSpbillCreateIp(InetAddress.getLocalHost().getHostAddress());
// 设置类型为JSAPI
request.setTradeType(WxPayConstants.TradeType.JSAPI);
// 一定要用 createOrder 不然得自己做二次校验
Object order = wxPayService(payType, shopId).createOrder(request);
return order;
} catch (Exception e) {
System.out.println(" ======== 微信createOrder 方法异常 ======== ");
System.out.println(e);
return null;
}
}
/**
* 微信退款
*
* @param tradeNo 订单号
* @param totalFee 总价
* @param payType 支付方式
* @param financeType
* @param shopId 店铺Id
* @return
*/
@Override
public WxPayRefundResult wxRefund(String tradeNo, Integer totalFee, String payType, String financeType, String shopId) {
ConfigPay configPay = configPayMapper.selectConfigPayByShopId(shopId);
WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
// 商户订单号-out_trade_no 微信支付订单号-transaction_id transaction_id和out_trade_no必须二选一进行传参。
wxPayRefundRequest.setOutTradeNo(tradeNo);
wxPayRefundRequest.setOutRefundNo(String.valueOf(System.currentTimeMillis()));
wxPayRefundRequest.setTotalFee(totalFee);
wxPayRefundRequest.setRefundFee(totalFee);
wxPayRefundRequest.setNotifyUrl(configPay.getWxBackUrl() + "/thirdpart/pay/wxRefundCallback/" + payType + "/" + financeType + "/" + shopId);
try {
WxPayRefundResult refund = wxPayService(payType, shopId).refundV2(wxPayRefundRequest);
if (refund.getReturnCode().equals("SUCCESS") && refund.getResultCode().equals("SUCCESS")) {
return refund;
}
} catch (WxPayException e) {
System.out.println("========= 微信退款异常 =========");
System.out.println(e);
e.printStackTrace();
}
return null;
}
}
微信回调通知
package com.ruoyi.web.controller.module.thirdpard;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.order.domain.OrderGift;
import com.ruoyi.order.domain.OrderGoods;
import com.ruoyi.order.domain.OrderWallet;
import com.ruoyi.order.service.IOrderGiftService;
import com.ruoyi.order.service.IOrderGoodsService;
import com.ruoyi.order.service.IOrderWalletService;
import com.ruoyi.thirdpart.service.IBizWechatPayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
/**
* 支付
*
* @author ganluhua
*/
@RestController
@RequestMapping("/thirdpart/pay")
public class PayController extends BaseController
{
@Autowired
private IBizWechatPayService wechatPayService;
/**
* 微信公众号支付回调
*/
@RequestMapping("/wxPayCallback/{payType}/{financeType}/{shopId}")
@Log(title = "微信公众号支付回调", businessType = BusinessType.INSERT)
public void wxPayNotify(@PathVariable("payType") String payType,
@PathVariable("financeType") String financeType,
@PathVariable("shopId") String shopId,
@RequestBody(required = false) String data)
{
try {
WxPayOrderNotifyResult result = wechatPayService.wxPayService(payType, shopId).parseOrderNotifyResult(data);
// 支付返回信息
if ("SUCCESS".equals(result.getReturnCode())) {
// 可以实现自己的业务逻辑
// logger.info("来自微信支付的回调:{}", result);
}
} catch (WxPayException e) {
logger.error(e.getMessage());
}
}
/**
* 微信退款回调
*/
@RequestMapping("/wxRefundCallback/{payType}/{financeType}/{shopId}")
@Log(title = "微信退款回调", businessType = BusinessType.INSERT)
public void wxRefundNotify(@PathVariable("payType") String payType,
@PathVariable("financeType") String financeType,
@PathVariable("shopId") String shopId,
@RequestBody(required = false) String data){
System.out.println(" ============ 微信退款回调 ============
try {
WxPayRefundNotifyResult result = wechatPayService.wxPayService(payType, shopId).parseRefundNotifyResult(data);
WxPayRefundNotifyResult.ReqInfo reqInfo = result.getReqInfo();
if (reqInfo.getRefundStatus().equals(UserConstants.STATUS_SUCCESS)) {
// 退款成功 可以实现自己的业务逻辑
}else{
// 退款失败 可以实现自己的业务逻辑
}
}catch (WxPayException e) {
logger.error(e.getMessage());
}
}
}
这里有个细节要注意,要在SecurityConfig中开启接口白名单,否则微信回调会失败
.antMatchers("/你的回调接口/**").permitAll()
客户端调用vue
// #ifdef H5
var jweixin = require('jweixin-module');
// #endif
jweixin.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: res.data.data.payParams.appId, // 必填,公众号的唯一标识
timestamp: res.data.data.payParams.timeStamp, // 必填,生成签名的时间戳
nonceStr: res.data.data.payParams.nonceStr, // 必填,生成签名的随机串
signature: res.data.data.payParams.paySign, // 必填,签名
jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表
})
jweixin.ready(function() {
jweixin.chooseWXPay({
appId: res.data.data.payParams.appId,
timestamp: res.data.data.payParams.timeStamp, // 时间戳
nonceStr: res.data.data.payParams.nonceStr, // 随机数
package: res.data.data.payParams.packageValue, // prepay_id=xxx
signType: res.data.data.payParams.signType,
paySign: res.data.data.payParams.paySign, // 签名
success: function() {
setTimeout(() => {
that.navTo(
'/pages/pay/orderDetail?orderNo=' +
res.data.data.orderNo)
}, 300)
},
cancel: function() {
uni.showToast({
title: '取消支付',
icon: 'none'
})
that.disabled = false
},
fail: function(e) {
console.log("====支付失败 原因===")
console.log(e)
uni.showToast({
title: '支付失败:' + e.errMsg,
icon: 'none'
})
that.disabled = false
}
})
})