一、准備
1、添加微信支付SDK
方式一:
service_trade中添加依賴:
<dependencies>
<!--微信支付-->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
</dependencies>
方式二:
將下載的sdk源碼放入service_trade源碼目錄中
2、配置yml參數
支付賬戶相關參數:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=3_1
weixin:
pay:
#關聯的公眾號appid
appId: wxf913bfa3a2c7eeeb
#商戶號
partner: 1543338551
#商戶key
partnerKey: atguigu3b0kn9g5v426MKfHQH7X8rKwb
#回調地址
notifyUrl: http://imhelen.free.idcfengye.com/api/trade/weixin-pay/callback/notify
配置后啟動ngrok內網穿透工具
3、參數讀取工具類
package com.atguigu.guli.service.trade.util;
@Data
@Component
@ConfigurationProperties(prefix="weixin.pay")
public class WeixinPayProperties {
private String appId;
private String partner;
private String partnerKey;
private String notifyUrl;
}
4、輔助業務方法
OrderService:getOrderByOrderNo 根據訂單號查詢訂單
接口:
Order getOrderByOrderNo(String orderNo);
實現:
public Order getOrderByOrderNo(String orderNo) {
QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_no", orderNo);
return baseMapper.selectOne(queryWrapper);
}
二、生成支付二維碼
1、統一下單接口文檔
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
調用統一下單接口,根據返回值中的code_url在前端使用javascript工具生成支付二維碼
2、業務層
接口:創建 WeixinPayService:createNative
package com.atguigu.guli.service.order.service;
public interface WeixinPayService {
Map<String, Object> createNative(String orderNo, String remoteAddr);
}
實現:
package com.atguigu.guli.service.trade.service.impl;
@Service
@Slf4j
public class WeixinPayServiceImpl implements WeixinPayService {
@Autowired
private OrderService orderService;
@Autowired
private WeixinPayProperties weixinPayProperties;
@Override
public Map<String, Object> createNative(String orderNo, String remoteAddr) {
try{
//根據課程訂單號獲取訂單
Order order = orderService.getOrderByOrderNo(orderNo);
//調用微信api接口:統一下單(支付訂單)
HttpClientUtils client = new HttpClientUtils("https://api.mch.weixin.qq.com/pay/unifiedorder");
//組裝接口參數
Map<String, String> params = new HashMap<>();
params.put("appid", weixinPayProperties.getAppId());//關聯的公眾號的appid
params.put("mch_id", weixinPayProperties.getPartner());//商戶號
params.put("nonce_str", WXPayUtil.generateNonceStr());//生成隨機字符串
params.put("body", order.getCourseTitle());
params.put("out_trade_no", orderNo);
//注意,這里必須使用字符串類型的參數(總金額:分)
String totalFee = order.getTotalFee().intValue() + "";
params.put("total_fee", totalFee);
params.put("spbill_create_ip", remoteAddr);
params.put("notify_url", weixinPayProperties.getNotifyUrl());
params.put("trade_type", "NATIVE");
//將參數轉換成xml字符串格式:生成帶有簽名的xml格式字符串
String xmlParams = WXPayUtil.generateSignedXml(params, weixinPayProperties.getPartnerKey());
log.info("\n xmlParams:\n" + xmlParams);
client.setXmlParam(xmlParams);//將參數放入請求對象的方法體
client.setHttps(true);//使用https形式發送
client.post();//發送請求
String resultXml = client.getContent();//得到響應結果
log.info("\n resultXml:\n" + resultXml);
//將xml響應結果轉成map對象
Map<String, String> resultMap = WXPayUtil.xmlToMap(resultXml);
//錯誤處理
if("FAIL".equals(resultMap.get("return_code")) || "FAIL".equals(resultMap.get("result_code"))){
log.error("微信支付統一下單錯誤 - "
+ "return_code: " + resultMap.get("return_code")
+ "return_msg: " + resultMap.get("return_msg")
+ "result_code: " + resultMap.get("result_code")
+ "err_code: " + resultMap.get("err_code")
+ "err_code_des: " + resultMap.get("err_code_des"));
throw new GuliException(ResultCodeEnum.PAY_UNIFIEDORDER_ERROR);
}
//組裝需要的內容
Map<String, Object> map = new HashMap<>();
map.put("result_code", resultMap.get("result_code"));//響應碼
map.put("code_url", resultMap.get("code_url"));//生成二維碼的url
map.put("course_id", order.getCourseId());//課程id
map.put("total_fee", order.getTotalFee());//訂單總金額
map.put("out_trade_no", orderNo);//訂單號
return map;
} catch (Exception e) {
log.error(ExceptionUtils.getMessage(e));
throw new GuliException(ResultCodeEnum.PAY_UNIFIEDORDER_ERROR);
}
}
}
3、web層
創建 ApiWeixinPayController:createNative
package com.atguigu.guli.service.trade.controller.api;
@RestController
@RequestMapping("/api/trade/weixin-pay")
@Api(description = "網站微信支付")
@CrossOrigin //跨域
@Slf4j
public class ApiWeixinPayController {
@Autowired
private WeixinPayService weixinPayService;
@GetMapping("create-native/{orderNo}")
public R createNative(@PathVariable String orderNo, HttpServletRequest request) {
String remoteAddr = request.getRemoteAddr();
Map map = weixinPayService.createNative(orderNo, remoteAddr);
return R.ok().data(map);
}
}
三、支付前端
1、安裝二維碼生成器
npm install vue-qriously@1.1.1
2、配置插件
創建 plugins/vue-qriously-plugin.js
import Vue from 'vue'
import VueQriously from 'vue-qriously'
Vue.use(VueQriously)
nuxt.config.js中配置
plugins: [
{ src: '~/plugins/vue-qriously-plugin.js', ssr: true }
],
3、api
創建 api/pay.js
import request from '~/utils/request'
export default {
createNative(orderNo) {
return request({
baseURL: 'http://localhost:8170',
url: `/api/trade/weixin-pay/create-native/${orderNo}`,
method: 'get'
})
}
}
4、訂單頁面
html:
<el-button :disabled="!agree" type="danger" @click="toPay()">去支付</el-button>
腳本:
methods: {
toPay() {
if (this.agree) {
this.$router.push({ path: '/pay/' + this.order.orderNo })
}
}
}
5、支付頁面
創建pages/pay/_id.vue
<template>
<div class="cart py-container">
<!--主內容-->
<div class="checkout py-container pay">
<div class="checkout-tit" style="width: 1050px; margin: 0 auto; padding: 10px 0;">
<h4 class="fl tit-txt"><span class="success-info">支付申請提交成功,請您及時付款!訂單號:{{ payObj.out_trade_no }}</span>
</h4>
<span class="fr"><em class="sui-lead">應付金額:</em><em class="orange money">¥{{ payObj.total_fee/100 }}</em></span>
<div class="clearfix"/>
</div>
<div class="checkout-steps">
<div class="fl weixin">微信支付</div>
<div class="fl sao">
<div class="fl code">
<!-- <img id="qrious" src="~/assets/img/erweima.png" alt=""> -->
<qriously :value="payObj.code_url" :size="338"/>
</div>
<div style="color: red; text-align:center;">請使用微信掃一掃</div>
</div>
<div class="clearfix"/>
<!-- <p><a href="pay.html" target="_blank"> 其他支付方式</a></p> -->
</div>
</div>
</div>
</template>
<script>
import payApi from '~/api/pay'
export default {
async asyncData(page) {
const response = await payApi.createNative(page.route.params.id)
return {
payObj: response.data
}
}
// 在created中獲取數據,報告Invalid prop: type check failed for prop "value".
// created() {
// payApi.createNative(this.$route.params.id).then(response => {
// this.payObj = response.data
// })
// }
}
</script>