
微信支付是企業級項目中經常使用到的功能,作為後端開發人員,完整地掌握該技術是十分有必要的。
前言
微信支付是企業級項目中經常使用到的功能,作為後端開發人員,完整地掌握該技術是十分有必要的。
logo
一、申請流程和步驟
圖1-1
- 注冊微信支付賬号
- 擷取微信小程式APPID
- 擷取微信商家的商戶ID
- 擷取微信商家的API私鑰
- 配置微信支付回調位址
- 綁定微信小程式和微信支付的關系
- 搭建SpringBoot工程編寫背景支付接口
- 釋出部署接口服務項目
- 使用微信小程式或者UniAPP調用微信支付功能
- 支付接口的封裝
- 配置jwt或者openid的token派發
- 原生微信小程式完成支付對接
二、注冊商家
2.1商戶平台
商家或者企業想要通過微信支付來進行商品的銷售,必須先通過微信平台(pay.weixin.qq.com)去将商家進行注冊。注冊成功後将會有商戶資訊等界面,包括商家的賬戶資訊、企業資訊等等。如圖2-1所示:
圖2-1
2.2商戶id
商戶id是項目開發中的唯一辨別,是微信支付給予每個商戶或者企業的唯一id。也是客戶(消費者)在拉起微信支付時的憑據之一,在圖2-1中的“微信支付商戶号”就是商戶id。
三、API私鑰(支付密鑰)
在注冊商戶成功後,同樣在微信平台(pay.weixin.qq.com)可以對API私鑰進行設定。如圖3-1所示:
圖3-1
API私鑰也稱為支付密鑰,商戶id和API密鑰是使用者拉起微信支付時背景所必須擷取的。
四、商戶簽約微信支付産品
商戶可以根據需要簽約微信支付的産品,主要包括有:
- JSAPI支付:商戶通過調用微信支付提供的JSAPI接口,在支付場景中調起微信支付子產品完成收款;
- Native支付:商戶系統按微信支付協定生成支付二維碼,使用者再用微信“掃一掃”完成支付的模式;
- 小程式支付:通過好友分享或掃描二維碼在微信内打開小程式時,可以調用微信支付完成下單購買的流程;
- 付款碼支付:使用者出示微信錢包中的條碼、二維碼,商家通過掃描使用者條碼即可完成收款;
- 刷臉支付:使用者在內建微信刷臉支付SDK的線下機具上"刷臉"即可完成支付。
如圖4-1所示:
圖4-1
五、配置回調位址
支付回調位址是微信支付伺服器傳回給使用者支付資訊(通知)的位址。如果商戶簽約的是微信小程式産品,那麼支付回調位址可以配置也可以不進行配置。該位址為公司的域名,或者不加以配置。
六、小程式擷取APPID
首先要登入申請官網進行微信小程式的注冊:https://mp.weixin.qq.com/,如圖6-1所示:
圖6-1
注冊成功後即可獲得小程式唯一的APPID。如圖6-2所示:
圖6-2
七、微信支付與小程式綁定
在微信支付平台對APPID進行綁定即可。如圖7-1所示:
圖7-1
八、實戰部分
8.1SpringBoot架構搭建
- 首先建立一個初始化SpringBoot項目;
- 在項目/子產品的resources檔案夾下,編寫properties/yml配置檔案;
- 配置檔案中需隔離dev環境與prod環境;
- 配置檔案中還包括了server、資料庫、spring、token、日志、時區、json格式、mybatis-plus、swagger、redis、伺服器配置、微信小程式配置(包括支付相關)等等全局統一配置。
- 項目基本架構(SSM:Spring+SpringMVC+MyBatis)
- controller
- 後端接口,與前端資料互動
- config
- 雲伺服器配置
- Swagger配置
- 接口攔截器(路由)
- 微信支付配置(引入微信相關服務)
- common
- constant
- enums
- Ajaxresult
- BaseController
- BaseEntity
- Page(Page與Table傳回參數)
- domain
- 接口與頁面所需參數(DTO、entity、req、res、VO等)
- mapper
- mapper檔案接口(SQL方法定義)
- service
- Service:承接Controller層的接口方法定義
- Impl(接口實作類):接口的具體實作邏輯
- utils
- 檔案工具類
- Json工具類
- 時間格式工具類
- 第三方登入工具類
- controller
8.2微信支付相關接口
8.2.1小程式使用者登入接口
使用者首先需要在小程式端進行微信使用者登入授權,需調用接口擷取登入憑證(code)。通過憑證進而換取使用者登入态資訊,包括使用者在目前小程式的唯一辨別(openid)、微信開放平台帳号下的唯一辨別(unionid,若目前小程式已綁定到微信開放平台帳号)及本次登入的會話密鑰(session_key)等。
具體的登入流程如圖8-1所示:
圖8-1
此時調用服務端接口,請求參數如圖8-2所示:
圖8-2
使用者登入後的傳回參數,如圖8-3所示:
圖8-3
8.2.2統一下單接口
使用者登入小程式後,在小程式頁面拉起支付請求時,會調用統一的下單接口。
在拉起支付請求時,下單接口參數需要兩部分:一是商戶、小程式相關的openid,appid等,二是需要商品相關的價格,名稱,數量等參數。
以下将用代碼來對微信支付接口做詳細的講解,代碼以REST風格API接口的形式編寫。
統一下單接口
@ApiOperation(value = "統一下單接口")
@RequestMapping(value = "/unifiedOrder",method = RequestMethod.POST)
public AjaxResult unifiedOrder(HttpServletRequest req,@RequestBody){
//校驗小程式(微信)使用者登入
//查詢資料庫訂單資訊(狀态)
//隻有未支付訂單才能發起支付
//0元購買不調支付
/**
* 設定商戶、小程式相關請求參數
* */
//擷取小程式的appId
String appId = WxMaUtil.getAppId(request);
WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
wxPayUnifiedOrderRequest.setAppid(appId);
//商品名稱
String body = sysOrdersCourse.getCourseName();
body =body.length() > 40 ? body.substring(0,39) : body;
wxPayUnifiedOrderRequest.setBody(body);
//訂單編号
wxPayUnifiedOrderRequest.setOutTradeNo(sysOrders.getOrderNo());
//訂單金額
wxPayUnifiedOrderRequest.setTotalFee(sysOrders.getOrderAmount().multiply(new BigDecimal(100)).intValue());
//交易類型
wxPayUnifiedOrderRequest.setTradeType("JSAPI");
//支付回調位址
wxPayUnifiedOrderRequest.setNotifyUrl(env.getProperty("notify-host")+"/wx/api/order/notify-order");
wxPayUnifiedOrderRequest.setSpbillCreateIp("127.0.0.1");
//使用者在目前小程式中的唯一辨別
wxPayUnifiedOrderRequest.setOpenid(wxUser.getOpenId());
//調用微信服務類
WxPayService wxPayService = WxPayConfiguration.getPayService();
return AjaxResult.success(JSONUtils.parse(wxPayService.createOrder(wxPayUnifiedOrderRequest)));
}
8.2.3建立訂單接口
建立訂單接口
使用者可以在商品頁面對某個商品進行下單,此時需要建立該使用者購買某個商品的訂單。
該過程主要是通過接口去請求使用者資訊、商品資訊等參數,經過邏輯判斷(是否存在已購買的訂單)後建立(資料庫插入相關資訊)新的訂單,最後傳回該訂單的所需資料。
@ApiOperation(value = "建立訂單")
@RequestMapping(value = "/create",method = RequestMethod.POST)
public AjaxResult create(@RequestBody WxOrderRequest wxOrderRequest){
//微信使用者資訊
//判斷是否購買過相同商品,若已經購買過,則無法建立新的訂單
//滿足條件後将資料插入資料庫
WxOrderResponse wxOrderResponse = orderService.add(wxOrderRequest);
//如果新增失敗,則傳回提示
if (wxOrderResponse == null){
return AjaxResult.error("訂單建立失敗");
}
return AjaxResult.success(wxOrderResponse);
}
8.2.4取消訂單接口
取消訂單接口
當使用者拉起微信支付時,如果在付款界面點選×取消付款(但此時訂單已經建立),則該操作視為取消訂單支付,同時在訂單頁面應當顯示此時該訂單的支付狀态。
該接口可以通過訂單id來作為請求參數,首先判斷資料庫中是否存在該訂單資訊,接着對可以取消支付的訂單類型進行限制,最後更新資料庫表(訂單、商品和聲明周期)狀态。
@ApiOperation(value = "取消訂單")
@RequestMapping(value = "/cancel/{orderId}",method = RequestMethod.PUT)
public AjaxResult cancel(@PathVariable Long orderId){
//判斷訂單是否存在
SysOrders sysOrders = sysOrdersService.getById(orderId);
if(sysOrders==null){
return AjaxResult.error("訂單不存在");
}
//隻有未支付的訂單能取消
if(!CommonConstants.NO.equals(sysOrders.getIsPay())){
return AjaxResult.error(MyReturnCode.ERR_70001.getCode(), MyReturnCode.ERR_70001.getMsg());
}
sysOrdersService.orderCancel(sysOrders);
return AjaxResult.success();
}
8.2.5訂單詳情接口
訂單詳情接口
當訂單生成後,使用者可以在頁面檢視該商品訂單的詳情。
該接口通過訂單id即可從資料庫擷取詳情資訊,而該頁面展示的資料由業務需求确定。
@ApiOperation(value = "訂單詳情")
@RequestMapping(value = "/myOrderDetail/{orderId}",method = RequestMethod.GET)
@ApiImplicitParams({@ApiImplicitParam(name = "orderId", value = "訂單Id")})
public AjaxResult myOrderDetail(@PathVariable Long orderId){
//從資料庫擷取詳情參數
SysOrderDetailResponse detailResponse = sysOrdersService.getDetail(orderId);
return AjaxResult.success(detailResponse);
}
訂單詳情邏輯
/**
* 訂單詳情頁面參數處理
* @param orderId
* @return sysOrderDetailResponse
*/
@Override
public SysOrderDetailResponse getDetail(Long orderId) {
SysOrderDetailResponse sysOrderDetailResponse = orderMapper.getDetail(orderId);
//對頁面的手機号做處理
String phoneNum = sysOrderDetailResponse.getMobile();
sysOrderDetailResponse.setMobile(phoneNum.substring(0,3) + "****" + phoneNum.substring(7,phoneNum.length()));
//根據訂單狀态不同,顯示不同的資料
//計算剩餘待支付時間并展示
return sysOrderDetailResponse;
}
8.2.6支付回調接口
微信支付回調是使用者在對商品進行支付操作後,将資料發送至微信伺服器,微信伺服器再将支付的結果傳回(通知)給使用者和商家的過程。
其中,主要關注的是使用者支付-微信回調判斷-修改資料庫這個過程。
支付回調接口
@ApiOperation("支付回調")
@RequestMapping(value = "notify-order", method = RequestMethod.POST)
public String notifyOrder(@RequestBody String xmlData) throws WxPayException {
log.info("支付回調:" + xmlData);
//微信支付服務
WxPayService wxPayService = WxPayConfiguration.getPayService();
WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
log.info("支付回調解析結果" + notifyResult);
//對建立的訂單進行支付
SysOrders sysOrders = orderService.getOne(notifyResult.getOutTradeNo());
if (sysOrders != null) {
if (sysOrders.getOrderAmount().multiply(new BigDecimal(100)).intValue() == notifyResult.getTotalFee()){
String timeEnd = notifyResult.getTimeEnd();
LocalDateTime paymentTime = LocalDateTimeUtils.parse(timeEnd);
//支付時間
sysOrders.setPaymentTime(paymentTime);
sysOrders.setOrderAmount(sysOrders.getOrderAmount());
//微信訂單編号
sysOrders.setTransactionId(notifyResult.getTransactionId());
//更新資料庫
orderService.notifyOrder(sysOrders);
log.info("支付回調成功通知:" + sysOrders.getOrderNo());
return WxPayNotifyResponse.success("支付成功");
} else {
return WxPayNotifyResponse.fail("付款金額與訂單金額不符");
}
} else {
return WxPayNotifyResponse.fail("無此訂單");
}
}
其中,notifyOrder方法的作用是在支付成功後在資料庫更新訂單的狀态。
notifyOrder(SysOrders sysOrders)方法
@Override
@Transactional(rollbackFor = Exception.class)
public void notifyOrder(SysOrders sysOrders) {
//隻有未支付訂單能操作,即is_pay字段為0的訂單
if(CommonConstants.NO.equals(sysOrders.getIsPay())) {
//更新訂單支付狀态
sysOrders.setIsPay(CommonConstants.YES);
sysOrders.setStatus(OrderInfoEnum.STATUS_1.getValue());
sysOrdersMapper.updateById(sysOrders);
//更新商品表狀态
//更新訂單生命周期表
}
}
至此,Spring Boot中的微信支付全過程已經分享完成。如有不足,望大家指正。