微信支付—微信H5支付「微信內部瀏覽器」


前言

微信支付-微信H5外部瀏覽器支付
微信支付-微信H5內部瀏覽器支付本文
微信支付-PC端掃碼支付

本篇是微信支付系列的第二篇、微信H5內部瀏覽器支付,關於微信H5外部瀏覽器喚起微信APP支付,請參考上一篇文章。

開發環境:Java + SpringBoot + Vue +WxJava(開源SDK)

掃盲補充:關於微信內部瀏覽器支付,支付時會直接調起微信支付,不同於外部瀏覽器支付,內部瀏覽器支付首先需要獲得當前支付用戶對該公眾號的唯一標識 openId「是否關注都是唯一的」,拿到 openId 后,結合后端其他參數調用微信預支付接口,獲得預支付id,然后交由前端發起微信支付,支付成功后回調后端接口。

如下是正文部分。

Java支付項目實戰教程,包括支付寶,微信等支付方式,不看虧!

1、獲取Code

要想獲得用戶唯一標識 openid,首先需要辦的事就是獲得 code。

code 部分在本文中交由前端去獲取「調用微信authorize授權方法」,拿到 code 后傳遞給后端換取 openid「用戶唯一標識」;通常這個操作都是在用戶登錄時去實現的,登錄成功后同時拿到 openid,而且還可以存(更新)到該用戶的數據庫方便后面使用。

前端獲取code,具體如下:

let ua = navigator.userAgent.toLowerCase()
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
    if (!this.GetQueryString('code')) {
        alert("跳轉");
        // this.$vux.toast.text('微信授權中……', 'default')
        let currentUrl = encodeURIComponent(window.location.href)
        window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=我是appid&redirect_uri='+currentUrl+'&response_type=code&scope=snsapi_base&state=STATE&connect_redirect=1#wechat_redirect'
    } else {
        let code = this.GetQueryString('code')
        // 此處調用后端方法,通過 code 換取 openid
    }
}

補充:授權鏈接中的 scope 參數分為 snsapi_base、snsapi_userinfo,snsapi_base 可以獲得用戶的唯一標識 openid,snsapi_userinfo 則在此基礎上獲得用戶資料「昵稱、頭像等」

上述方法中 ua.match(/MicroMessenger/i) 是用來判斷是否是微信環境的, GetQueryString 方法用來獲取微信中的 code,如果當前瀏覽器 url 並沒有附帶 code 參數,那么就會調用微信的 authorize 方法進行授權,授權后獲得 code,該方法具體如下:

GetQueryString (name) {
    let url = new RegExp('(^|&)' + name + '=([^&]*)(&|$)')
    let newUrl = window.location.search.substr(1).match(url)
    if (newUrl != null) {
        return unescape(newUrl[2])
    } else {
        return false
    }
},

2、換取openid

拿到 code 后,下一步就是調用后端接口換取 openid 了, 簡單看一下換取 openid 的后端方法:

try {
    String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=我是appid&secret=我是secret&grant_type=authorization_code"+
        "&code=" + loginRequest.getCode();
    String body = RestTemplateUtils.get(url,new JSONObject());
    JSONObject jsonObject = JSONObject.parseObject(body);
    Integer errcode = jsonObject.getInteger("errcode");
    if (errcode == null || errcode == 0) {
        String openId = jsonObject.getString("openid");
        //將此次登錄的openId,暫且放入user的域里面,支付的時候會用到
        System.out.println("openId:"+openId);
        loginRequest.setOpenId(openId);
        return ResultUtil.success(userService.login(loginRequest));
    }else{
        logger.error("[微信第三方登錄] 異常”);
        拋出自定義異常
        throw new CommonException("微信第三方登錄異常","");
    }
} catch (Exception e) {
    logger.error("[微信第三方登錄] 異常", e);
    拋出自定義異常
    throw new CommonException("微信第三方登錄異常","");
}

簡單說一下該方法,前端傳遞 code 致后端方法,后端拿到 code 后,調用 access_token 接口獲取 openid,同時完成登錄操作。

至此,已經成功登錄並拿到用戶 openid 了,接下來就是調用支付接口。

3、預支付接口

上邊已經提到了,內部瀏覽器支付是交由前端發起的,但是又依賴於后端的 預支付接口,所以先來看一下后端預支付接口:

/**
 * 生成訂單「微信內部瀏覽器」
 * @return
 */

@Transactional
public Object wxPrepay(Orders orders,String openId) {
    Object result = null;
    try {
        WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
        orderRequest.setOutTradeNo(orders.getOrderId());
        orderRequest.setOpenid(openId);
        orderRequest.setBody(“我是商品描述信息");
        orderRequest.setTotalFee(orders.getAmount().multiply(new BigDecimal("
100")).intValue());
        orderRequest.setSpbillCreateIp(DispatchParams.getInstance().getWechatSpbillCreateIp());
        orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
        result = wxPayService.createOrder(orderRequest);
        if (result instanceof WxPayMpOrderResult) {
            String prepayId = ((WxPayMpOrderResult)result).getPackageValue();
            String paySign = ((WxPayMpOrderResult) result).getPaySign();
            prepayId = prepayId.replace("
prepay_id=", "");
            orders.setPrepayId(prepayId);
            orders.setSign(paySign);
            ordersDao.updateOrders(orders);
        }
    } catch (WxPayException e) {
        logger.error("
[微信支付] 異常", e);
        拋出自定義全局異常
        throw new CommonException(WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorMsg()+"
':微信支付異常", WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorCode());
    } catch (Exception e) {
        logger.error("[預付款異常]", e);
        拋出自定義全局異常
        throw new CommonException(WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorMsg()+"'
:預付款異常", WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorCode());
    }
    return result;
}

簡單說一下預支付方法,首先是根據自己情況創建訂單記錄,然后就是通過 openid 調用 wxPayService.createOrder 方法「WxJava」獲取預支付id,該方法返回的實體為 WxPayMpOrderResult,實體參數為前端調起微信支付的必要參數,具體如下:

private String appId;
private String timeStamp;
private String nonceStr;
@XStreamAlias("package")
private String packageValue;
private String signType;
private String paySign;

為啥獲得的預支付id沒有用到呀?上方返回的參數並沒有看到呀!

其實不然,屬性 packageValue 的值為 prepay_id=預支付id ,該參數是必須的。

4、前端調用,發起支付

至此,后端基本完成了,我們將參數傳遞給前端調用,直接模擬返回后的數據:

假設下方是調用接口返回的數據

console.log(“我是后端返回的數據 - res:"+JSON.stringify(res))

const payParam = {
    appId: res.appId,
    nonceStr: res.nonceStr,
    package: res.packageValue,
    timeStamp: res.timeStamp,
    signType: res.signType,
    paySign: res.paySign,
}


if (typeof WeixinJSBridge === 'undefined') {
    if (document.addEventListener) {
        document.addEventListener('WeixinJSBridgeReady', this.onBridgeReady(payParam), false)
    } else if (document.attachEvent) {
        document.attachEvent('WeixinJSBridgeReady', this.onBridgeReady(payParam))
        document.attachEvent('onWeixinJSBridgeReady', this.onBridgeReady(payParam))
    }
} else {
    this.onBridgeReady(payParam)
}

發起支付的 onBridgeReady 方法:

onBridgeReady(res){
    alert("發起請求:"+JSON.stringify(res));
    WeixinJSBridge.invoke(
        'getBrandWCPayRequest', {
            "appId":res.appId,     //公眾號名稱,由商戶傳入
            "timeStamp":res.timeStamp, //時間戳,自1970年以來的秒數
            "nonceStr":res.nonceStr, //隨機串
            "package":res.package, // prepay_id=xxx
            "signType":res.signType, //微信簽名方式:
            "paySign":res.paySign //微信簽名
        },
        function(res){
            alert(JSON.stringify("我是支付返回的信息:\n"+res));
            alert("我是支付返回的信息:\n"+res.err_msg);
            if(res.err_msg == "get_brand_wcpay_request:ok" ){
                // 使用以上方式判斷前端返回,微信團隊鄭重提示:
                //res.err_msg將在用戶支付成功后返回ok,但並不保證它絕對可靠。
                alert("支付成功了");
            }
        }
    );
}

5、效果截圖

再來簡單總結一下,首先由前端獲取 code,獲取 code 后傳遞給后端換取 openid,openid 是預支付必須的參數,前端發起支付時,需要6個參數,此時調用后端預支付接口獲取「wxPayService.createOrder」,前端支付成功后同樣微信會自動回調后端 notify 接口,具體如下「代碼僅供參考」:

@RequestMapping(value = "/notify")
@ResponseBody
public String notify(@RequestBody String body) throws Exception {

        WxPayOrderNotifyResult result = null;
        try {
            result = wxPayService.parseOrderNotifyResult(body);
        } catch (WxPayException e) {
            logger.error("[微信解析回調請求] 異常", e);
            return WxPayNotifyResponse.fail(e.getMessage());
        }
        logger.info("處理微信支付平台的訂單支付");
        logger.info(JSONObject.toJSONString(result));


        String appid = result.getAppid();//應用ID
        String attach = result.getAttach();//商家數據包
        String bank_type =result.getBankType();//付款銀行
        Integer cash_fee = result.getCashFee();//現金支付金額
        String fee_type = result.getFeeType();//貨幣種類
        String is_subscribe = result.getIsSubscribe();//是否關注公眾賬號
        String mch_id = result.getMchId();//商戶號
        String nonce_str = result.getNonceStr();//隨機字符串
        String openid = result.getOpenid();//用戶標識
        String out_trade_no = result.getOutTradeNo();// 獲取商戶訂單號
        String result_code = result.getResultCode();// 業務結果
        String return_code = result.getReturnCode();// SUCCESS/FAIL
        String sign = result.getSign();// 獲取簽名
        String time_end = result.getTimeEnd();//支付完成時間
        Integer total_fee = result.getTotalFee();// 獲取訂單金額
        String trade_type = result.getTradeType();//交易類型
        String transaction_id = result.getTransactionId();//微信支付訂單號


        //如果成功寫入數據庫
        if("SUCCESS".equals(return_code)) {// 如果微信返回的結果是success,則修改訂單狀態
            Orders orders = ordersDao.selectByOrderId(out_trade_no);
            // 驗證簽名
            if(orders != null){
                if(!"1".equals(orders.getOrderStatus())){//判斷是否訂單已經完成了
                    // 判斷金額是否跟數據庫訂單金額一致,放置人為修改
                    if(orders.getAmount().multiply(new BigDecimal("100")).compareTo(new BigDecimal(total_fee)) == 0){
                        //更新訂單狀態
                        業務邏輯處理部分...
                        return WxPayNotifyResponse.success("訂單已經處理成功!");
                    }else{
                        logger.error("微信:金額不一致!");
                        return WxPayNotifyResponse.fail("訂單金額不一致");
                    }
                }else {
                    return WxPayNotifyResponse.success("訂單已經處理成功!");
                }
            }else{
                return WxPayNotifyResponse.fail("商戶訂單號不匹配");
            }
        }
        System.out.println("回調成功");
        System.out.println("----返回給微信的xml:" + result);
        return WxPayNotifyResponse.success("支付成功!");
}

最后

博客地址:https://www.cnblogs.com/niceyoo

如果覺得這篇文章有丶東西,不放關注一下我,關注是對我最大的鼓勵~

18年專科畢業后,期間一度迷茫,最近我創建了一個公眾號用來記錄自己的成長。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM