SpringBoot 集成 wxJava 微信公众号:订单支付
从上一篇文章的集成【SpringBoot 学习】49、SpringBoot 集成 wxJava 微信公众号:授权登录
, 已经整合好了基本配置,这里直接增加支付相关配置即可
1、SpringBoot 集成 wxjava 公众号支付
<!-- 微信支付 -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.4.0</version>
</dependency>
配置文件
# 微信配置
wx:
# 公众号配置
mp:
configs:
- appid:
secret:
token: #微信小程序消息服务器配置的token
aesKey: #微信小程序消息服务器配置的EncodingAESKey
msgDataFormat: JSON
# 支付配置
pay:
appId:
mchId:
mchKey:
keyPath: cert/apiclient_cert.p12
notifyUrl: ${business.serviceUrl}/au/orderInfo/notifyUrl
wxjava 属性配置
package com.ruoyi.business.appuser.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* wxpay pay properties.
*
* @author Binary Wang
*/
@Data
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayProperties {
/**
* 设置微信公众号或者小程序等的appid
*/
private String appId;
/**
* 微信支付商户号
*/
private String mchId;
/**
* 微信支付商户密钥
*/
private String mchKey;
/**
* 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除
*/
private String subAppId;
/**
* 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除
*/
private String subMchId;
/**
* apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定
*/
private String keyPath;
/**
* 支付回调
*/
private String notifyUrl;
}
wxjava 支付配置
package com.ruoyi.business.appuser.config;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Binary Wang
*/
@Configuration
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WxPayProperties.class)
@AllArgsConstructor
public class WxPayConfiguration {
private WxPayProperties properties;
@Bean
@ConditionalOnMissingBean
public WxPayService wxService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
下单接口
@ApiOperation("创建订单")
@RepeatSubmit
@PostMapping("createOrder")
public AjaxResult createOrder(@Validated @RequestBody TOrderInfoVo entity) {
return weiXinPayService.createOrder(entity);
}
@ApiOperation("支付回调")
@RequestMapping("notifyUrl")
public String notifyUrl() {
return weiXinPayService.notifyUrl();
}
service 接口
package com.ruoyi.business.appuser.service;
import com.ruoyi.business.vo.TOrderInfoVo;
import com.ruoyi.common.core.domain.AjaxResult;
/**
* @author Tellsea
* @date 2022/8/17
*/
public interface WeiXinPayService {
/**
* 统一下单
*
* @param entity
* @return
*/
AjaxResult createOrder(TOrderInfoVo entity);
/**
* 支付回调
*
* @return
*/
String notifyUrl();
}
实现类
/**
* @author Tellsea
* @date 2022/8/17
*/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class WeiXinPayServiceImpl implements WeiXinPayService {
private final WxMpService wxMpService;
private final WxPayService wxPayService;
private final WxPayProperties wxPayProperties;
private final BusinessProperties businessProperties;
@Override
@Transactional
public AjaxResult createOrder(TOrderInfoVo entity) {
try {
String openId = SecurityUtils.getLoginUser().getUser().getUserName();
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
orderRequest.setSignType(WxPayConstants.SignType.MD5);
orderRequest.setBody("请求支付订单");
orderRequest.setOutTradeNo(entity.getOrderNumber());
orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
orderRequest.setTotalFee(BaseWxPayRequest.yuanToFen(String.valueOf(entity.getPayMoney())));
orderRequest.setOpenid(openId);
orderRequest.setSpbillCreateIp(ServletUtil.getClientIP(ServletUtils.getRequest()));
orderRequest.setNotifyUrl(wxPayProperties.getNotifyUrl());
Object order = wxPayService.createOrder(orderRequest);
WxJsapiSignature jsapiSignature = wxMpService.createJsapiSignature(entity.getUrl());
JSONObject jsonObject = new JSONObject();
jsonObject.put("order", order);
jsonObject.put("jsapiSignature", jsapiSignature);
jsonObject.put("orderId", entity.getId());
log.info("下单成功:{}", JSONObject.toJSONString(jsonObject));
return AjaxResult.success("下单成功", jsonObject);
} catch (WxPayException e) {
e.printStackTrace();
log.error("下单失败:{}", e.toString());
return AjaxResult.error(e.getMessage());
} catch (WxErrorException e) {
e.printStackTrace();
log.error("下单失败:{}", e.toString());
return AjaxResult.error(e.getMessage());
}
}
@Override
public String notifyUrl() {
try {
HttpServletRequest request = ServletUtils.getRequest();
HttpServletResponse response = ServletUtils.getResponse();
String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
WxPayOrderNotifyResult result = wxPayService.parseOrderNotifyResult(xmlResult);
log.info("订单支付回调:{}", result);
// 加入自己处理订单的业务逻辑,需要判断订单是否已经支付过,否则可能会重复调用
String outTradeNo = result.getOutTradeNo();
String transactionId = result.getTransactionId();
String totalFee = BaseWxPayResult.fenToYuan(result.getTotalFee());
return WxPayNotifyResponse.success("支付成功");
} catch (Exception e) {
log.error("微信回调结果异常,异常原因{}", e.getMessage());
return WxPayNotifyResponse.fail(e.getMessage());
}
}
}
2、Uniapp 用户端
安装 jweixin,因为 wx.config 已经被 uniapp 底层占用了,所以这里需要自己引入一个其他名称
npm install jweixin-module --save
完整流程
<script>
const jweixin = require('jweixin-module');
let that;
export default {
data() {
return {
baseUrl: this.$config.baseUrl,
// 下单参数
orderInfo: {
payMoney: 0,
storeId: 0,
userAddressId: 0,
orderDetailJson: '',
orderRemark: '',
url: window.location.href.split('#')[0],
}
}
},
methods: {
// 创建订单
submit() {
uni.showLoading({
title: '下单中...'
});
uni.$u.http.post('/au/orderInfo/createOrder', that.orderInfo).then(res => {
let config = res.data.jsapiSignature;
let data = res.data.order;
let orderId = res.data.orderId;
jweixin.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: config.appId, // 必填,公众号的唯一标识
nonceStr: config.nonceStr, // 必填,生成签名的随机串
signature: config.signature, // 必填,签名
timestamp: config.timestamp, // 必填,生成签名的时间戳
jsApiList: ['checkJsApi', 'chooseWXPay'] // 必填,需要使用的JS接口列表
});
jweixin.ready(() => {
jweixin.checkJsApi({
jsApiList: ['chooseWXPay'],
success(res) {
uni.hideLoading();
jweixin.chooseWXPay({
appId: data.appId,
nonceStr: data.nonceStr, // 必填,生成签名的随机串
package: data.packageValue,
paySign: data.paySign, // 必填,签名
signType: data.signType,
timestamp: data.timeStamp, // 必填,生成签名的时间戳
success(res) {
that.$msg('支付成功');
console.log('支付成功: ' + JSON.stringify(res));
uni.removeStorageSync(that.$config.cachePrefix + 'cart-' + uni.getStorageSync(that.$config.cachePrefix + 'storeId'));
uni.$u.route({url: '/pages/order/detail', params: {id: orderId}});
},
cancel(res) {
that.$msg('取消支付');
console.log('取消支付' + JSON.stringify(res));
uni.$u.route({url: '/pages/order/detail', params: {id: orderId}});
},
fail(res) {
that.$msg('支付失败');
console.log('支付失败: ' + JSON.stringify(res));
},
// success:接口调用成功时执行的回调函数。
// fail:接口调用失败时执行的回调函数。
// complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。
// cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。
// trigger: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。
})
},
fail(res) {
that.$msg('检查API失败');
console.log('检查API失败: ' + JSON.stringify(res));
}
});
});
jweixin.error(err => {
// that.$msg('支付错误:' + JSON.stringify(err));
});
});
},
toCategory() {
uni.$u.route({type: 'tab', url: '/pages/category/category'});
},
toAddress() {
uni.$u.route({url: '/pages/my/address/list'});
},
}
}
</script>