Spring Boot中的微信支付(小程式)

2022-05-27 06:01:08

前言

微信支付是企業級專案中經常使用到的功能,作為後端開發人員,完整地掌握該技術是十分有必要的。

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金鑰是使用者拉起微信支付時後臺所必須獲取的。


四、商戶簽約微信支付產品

商戶可以根據需要簽約微信支付的產品,主要包括有:

  1. JSAPI支付:商戶通過呼叫微信支付提供的JSAPI介面,在支付場景中調起微信支付模組完成收款;
  2. Native支付:商戶系統按微信支付協定生成支付二維條碼,使用者再用微信「掃一掃」完成支付的模式;
  3. 小程式支付:通過好友分享或掃描二維條碼在微信內開啟小程式時,可以呼叫微信支付完成下單購買的流程;
  4. 付款碼支付:使用者出示微信錢包中的條碼、二維條碼,商家通過掃描使用者條碼即可完成收款;
  5. 刷臉支付:使用者在整合微信刷臉支付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工具類
      • 時間格式工具類
      • 第三方登入工具類

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中的微信支付全過程已經分享完成。如有不足,望大家指正。