调用统一下单api之前,需要先获取openid,请先查看https://blog.csdn.net/hjfcgt123/article/details/104172909这篇博文。
一、配置JSAPI支付授权目录
登录企业公众号关联的商户平台https://pay.weixin.qq.com/index.php/core/info,产品中心--开发配置--支付配置--JSAPI支付--支付授权目录,配置如下
JSAPI支付在请求支付的时候会校验请求来源是否有在商户平台做了配置,所以必须确保支付目录已经正确的被配置,否则将验证失败,请求支付不成功。
二、引入依赖
这里使用一个新的sdk包
github地址:https://github.com/Pay-Group/best-pay-sdk
码云地址:https://gitee.com/liaoshixiong/best-pay-sdk
maven依赖
<dependency>
<groupId>cn.springboot</groupId>
<artifactId>best-pay-sdk</artifactId>
<version>1.1.0</version>
</dependency>
三、主要调用api
创建预支付订单:com.lly835.bestpay.service.impl.BestPayServiceImpl.pay(PayRequest request)
四、代码实现
1、添加配置文件
在application.yml文件中添加配置
wechat:
#公众号appId
mpAppId: xxxxxx
#公众号appSecret
mpAppSecret: xxxxxx
#商户号
mchId: xxxxxx
#商户秘钥
mchKey: xxxxxx
#商户API证书下载之后放置在服务器上的地址(退款的时候需要验证证书)
keyPath: D:\\h5.p12
#异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
notifyUrl: http://hungteshun.viphk.ngrok.org/sell/pay/notify
2、将配置文件写入对象
@Data
@Component
@ConfigurationProperties(prefix = "wechat")
public class WechatAccountConfig {
private String mpAppId;
private String mpAppSecret;
/**
* 商户id
*/
private String mchId;
/**
* 商户秘钥
*/
private String mchKey;
/**
* 商户证书路径
*/
private String keyPath;
/**
* 微信支付异步通知地址
*/
private String notifyUrl;
}
3、构建com.lly835.bestpay.service.impl.BestPayServiceImpl对象,并交由spring管理
@Component
public class WechatPayConfig {
@Autowired
private WechatAccountConfig wechatAccountConfig;
@Bean
public BestPayServiceImpl bestPayService () {
BestPayServiceImpl bestPayService = new BestPayServiceImpl();
bestPayService.setWxPayH5Config(wxPayH5Config());
return bestPayService;
}
@Bean
public WxPayH5Config wxPayH5Config() {
WxPayH5Config wxPayH5Config = new WxPayH5Config();
wxPayH5Config.setAppId(wechatAccountConfig.getMpAppId());
wxPayH5Config.setAppSecret(wechatAccountConfig.getMpAppSecret());
wxPayH5Config.setMchId(wechatAccountConfig.getMchId());
wxPayH5Config.setMchKey(wechatAccountConfig.getMchKey());
wxPayH5Config.setKeyPath(wechatAccountConfig.getKeyPath());
wxPayH5Config.setNotifyUrl(wechatAccountConfig.getNotifyUrl());
return wxPayH5Config;
}
}
4、controller
@Controller
@RequestMapping("/pay")
public class PayController {
@Autowired
private OrderService orderService;
@Autowired
private PayService payService;
@GetMapping("/create")
public ModelAndView create(@RequestParam("orderId") String orderId,
@RequestParam("returnUrl") String returnUrl,
Map<String, Object> map) {
//1、查询订单
OrderDTO orderDTO = orderService.findOne(orderId);
if (ObjectUtils.isEmpty(orderDTO)) {
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}
//2、发起微信支付
PayResponse payResponse = payService.create(orderDTO);
map.put("payResponse", payResponse);
map.put("returnUrl", returnUrl);
return new ModelAndView("pay/create", map);
}
}
5、controller返回的视图
使用的是freemarker模板,文件路径:src\main\resources\templates\pay\create.ftl
<script>
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"${payResponse.appId}", //公众号名称,由商户传入
"timeStamp":"${payResponse.timeStamp}", //时间戳,自1970年以来的秒数
"nonceStr":"${payResponse.nonceStr}", //随机串
"package":"${payResponse.packAge}",
"signType":"${payResponse.signType}", //微信签名方式:
"paySign":"${payResponse.paySign}" //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
location.href = "${returnUrl}";
} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
</script>
6、service实现类
@Service
@Slf4j
public class PayServiceImpl implements PayService {
private static final String orderName = "微信点餐订单";
@Autowired
private BestPayServiceImpl bestPayService;
@Override
public PayResponse create(OrderDTO orderDTO) {
PayRequest payRequest = new PayRequest();
payRequest.setOpenid(orderDTO.getBuyerOpenid());
payRequest.setOrderId(orderDTO.getOrderId());
payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
payRequest.setOrderName(orderName);
payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
return bestPayService.pay(payRequest);
}
}
四、前端调用
官网链接:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
前端调用方法
this.$http.post("/sell/buyer/order/create", {
'openid': getCookie('openid'),
'phone': this.phone,
'name': this.name,
'address': this.address,
'items': JSON.stringify(goods)}
).then((respones) => {
respones = respones.body;
if (respones.code == ERR_OK) {
location.href = config.wechatPayUrl +
'?openid=' + getCookie('openid') +
'&orderId=' + respones.data.orderId +
'&returnUrl=' + encodeURIComponent(config.sellUrl + '/#/order/' + respones.data.orderId);
}else {
alert(respones.msg);
}
});
注意:WeixinJSBridge内置对象在其他浏览器中无效,所以需要在微信浏览器里面打开H5网页中执行JS调起支付。接口输入输出数据格式为JSON。
其中config.wechatPayUrl 和 config.sellUrl地址为
sellUrl: 'http://sell.com',
wechatPayUrl: 'http://hungteshun.viphk.ngrok.org/sell/pay/create'
思路是:请求生成预支付订单之后跳转到订单详情页面。