web支付基礎教程

前言

因爲在公司的交易支付部門搬磚,所以To C端的前端支付頁面,基本由我這邊負責javascript

通常來講,一次正常的交易流程,用戶會通過幾個階段:php

  1. 瀏覽商品列表
  2. 查看商品詳情
  3. 點擊購買或加入購物車
  4. 商品結算(確認購買)
  5. 收銀臺(進行支付)
  6. 支付成功

其中收銀臺做爲交易成功的最後一千米,其承擔的職責之重可想而知html

咱們先來看看市面上常見網站的收銀臺:前端

嗶哩嗶哩會員購:java

觸屏端web

pc端ajax

app端json

慕課網:小程序

觸屏端後端

pc端

app端

能夠看出,收銀臺頁面通常要適配3個終端:pc端,觸屏端,app端。所以,主流的第三方支付平臺(微信,支付寶,花唄分期,京東白條)也須要能支持這三種場景的支付

接下來,咱們就來分析下不一樣支付渠道在不一樣終端下,支付的實現方式

因爲支付涉及部門核心業務,所以就不拿公司線上的收銀臺作講解了。支付交互流程,主要參考嗶哩嗶哩會員購慕課網沒有利益相關

<font color="#6495ed">注意:本文只考慮前端支付業務的實現,後端支付業務的實現,暫不考慮</font>

支付寶(花唄分期)

支付寶開發文檔

花唄分期其實就是支付寶的拓展,原理基本一致,就不重複累贅

pc端

交互方式1:

在pc端點擊支付寶支付,網頁新打開一個頁面(window.open()),這個頁面指向的是支付寶官方收銀臺頁面

交互方式2:

在pc端點擊支付寶支付,網頁展現一個二維碼,須要用戶打開支付寶app進行掃碼支付

b站提供了一個很巧妙的思路:把微信,支付寶,qq三個支付二維碼統一成了一個二維碼。(原理後面會講解,本質是調用不一樣容器的JSAPI)

兩種交互方式,點擊支付按鈕時,其實都是把當前的訂單號以及一些相關信息發給後端

const payNum = '123abc';

ajax({ 
  url: '/api/alipay', // 支付api
  type: 'POST',
  data: {
    payNum: payNum, // 訂單號
    other: 'demo', // 其餘參數
}).then((res) => {
  const { payUrl } = res;

  // 交互方法1:
  // payUrl若是是支付寶的收銀臺,則新打開一個頁面
  // payUrl通常是 https://mapi.alipay.com/gateway.do 這種,通常會帶上return_url參數和其餘各類數據,頁面最後被重定向到支付寶收銀臺
  window.open(payUrl);

  // 交互方式2:
  // payUrl若是是支付寶的掃碼地址,則建立一個二維碼彈窗
  // payUrl通常是 https://qr.alipay.com/bax06893swswc4inaknv505d 這種,頁面最後被重定向到支付寶收銀臺,該收銀臺能夠喚起支付寶app
  qrcode({
    width: 175,
    height: 175,
    url: payUrl
  });
}).catch((err) => {
  console.log('提交失敗')
})

因爲支付是異步進行的,因此須要前端去查詢該筆訂單是否支付成功

對於交互方式1,因爲支付頁面已經轉移到支付寶收銀臺,因此在支付寶收銀臺支付成功後,支付寶收銀臺會自動跳轉回return_url(return_url是咱們當初跳到支付寶收銀臺時帶上的參數,通常指向支付成功頁)。

不過因爲咱們使用的是window.open打開的新頁面,因此當用戶回到咱們的收銀臺時,咱們須要打開一個彈框,主動詢問用戶是否支付成功。若是用戶點擊了支付完成,咱們須要查詢該筆訂單是否真正支付成功。

// 打開支付寶收銀臺
window.open(payUrl);

// 在當前頁面打開彈窗,詢問用戶是否支付成功
createFinishWindow()

對於交互方式2,因爲仍然是在當前頁進行掃碼支付,所以建立二維碼彈窗後,咱們立刻就要輪詢進行查詢訂單狀態

const payNum = '123abc'

// 建立一個二維碼彈窗
qrcode({
  width: 175,
  height: 175,
  url: payUrl
});

// 輪詢查詢訂單狀態
function getPayStatus() {
  ajax({ 
    url: '/api/getPayStatus', // 支付狀態api
    data: {
      payNum: payNum, // 訂單號
      other: 'demo', // 其餘參數
    },
    type: 'POST'
  ).then((res) => {
    if (res.payStatus === 0) {
      // 支付成功,跳到成功頁
      window.location.href = `/success/${payNum}`;
      clearTimeout(statusTimeId);
    } else {
      // 還未支付,繼續輪詢
      statusTimeId = setTimeout(getPayStatus, 3000);
    }
  }).catch((err) => {
    // 接口報錯,繼續輪詢
    statusTimeId = setTimeout(getPayStatus, 3000);
  })
}



let statusTimeId = setTimeout(getPayStatus, 3000);

觸屏端

交互方式:

在觸屏端點擊支付寶支付,頁面直接跳轉到支付寶收銀臺,該頁面會嘗試喚起手機上的支付寶app

其實觸屏端原理和pc端基本同樣,只不過在觸屏端,有可能須要本身拼裝一個form表單,而不是直接跳到連接(固然主要看後端的實現)

const payNum = '123abc';

// 模擬表單提交
function formSubmit(formData, url) {
    const form = $('<form method="post" target="_self"></form>');
    form.attr('action', url);
    let input;
    $.each(formData, function (i, v) {
      input = $('<input type="hidden">');
      input.attr("name", i);
      input.attr("value", v);
      form.append(input);
    });
    $(document.body).append(form);
    form.submit();
    form.remove();
  }

ajax({ 
  url: '/api/alipay', // 支付api
  type: 'POST',
  data: {
    payNum: payNum, // 訂單號
    other: 'demo', // 其餘參數
}).then((res) => {
  const { formData, url } = res;
  if (formData) {
    // 須要前端本身構建表單
    formSubmit(formData, url)
  } else {
    // 直接跳轉連接(後端已經拼裝好表單)
    window.location.href = url;
  }
}).catch((err) => {
  console.log('提交失敗')
})

支付成功後,同理支付寶會跳轉到return_url的地址

須要注意:在微信瀏覽器裏,支付寶是不能被喚起的(微信生態圈平常封殺)

解決方法:

方法一:微信環境隱藏支付寶入口

方法二:微信環境,點擊支付寶支付,引導用戶使用其餘瀏覽器打開頁面

JSAPI

若是咱們能誘導用戶使用支付寶客戶端的掃一掃打開咱們觸屏端的收銀臺頁面,那麼其實咱們也可使用支付寶提供的JSAPI喚起收銀臺

這也是b站實現微信,支付寶,qq同一個二維碼都能付款的原理,這三個客戶端都提供了本身的JSAPI,用戶用不一樣的客戶端掃碼,都會進入同一個頁面(b站實現),這個中轉頁根據容器環境,調用不一樣JSAPI的支付功能

支付寶H5開放文檔

關於jsbridge的知識,能夠查看我以前的文章jsbridge初探

JSAPI的簡單示例

function ready(callback) {
  // 若是jsbridge已經注入則直接調用
  if (window.AlipayJSBridge) {
    callback && callback();
  } else {
    // 若是沒有注入則監聽注入的事件
    document.addEventListener('AlipayJSBridgeReady', callback, false);
  }
}
ready(function () {
  // 顯示一個提示框
  AlipayJSBridge.call('toast', {
    content: 'hello'
  });
});

喚起收銀臺須要使用Alipay JSSDK

<script src="https://gw.alipayobjects.com/as/g/h5-lib/alipayjsapi/3.1.1/alipayjsapi.inc.min.js"></script>

<button id="J_btn" class="btn btn-default">支付</button>
<script>
  var btn = document.querySelector('#J_btn');
  btn.addEventListener('click', function(){
    ap.tradePay({
      tradeNO: '201802282100100427058809844'
    }, function(res){
      ap.alert(res.resultCode);
    });
  });
</script>

app端

如今的app基本都是Hybrid App,若是在app端,你的收銀臺頁面不是原生實現的,那麼就能夠直接使用webview加載觸屏端的線上收銀臺便可

手機網站支付產品不建議在APP端使用

這是支付寶官網文檔建議的,所以若是你但願獲得最佳的支付體驗,建議客戶端的開發同窗接入支付寶SDK,固然這部分已經超出了前端的範圍

不過通常在app端中,咱們仍然使用webview加載觸屏端的前端頁面,只不過在app中,咱們的前端代碼,經過jsbridge,調用客戶端的支付方法便可

const payNum = '123abc';

// 支付回調函數
window.ali_pay_callback = function(res) {
  if (res.status === 0) {
    // 支付成功
  } else {
    // 支付失敗
  }
}

// APPSDK是webview注入的全局對象,能夠調用原生方法
APPSDK.invoke('ali_pay', {
  payNum: payNum, // 訂單號
  other: 'demo', // 其餘參數
}, 'ali_pay_callback');

小程序

小程序喚起支付文檔

小程序支付和APP支付的支付流程與體驗基本一致,能夠在當前頁面喚起支付寶收銀臺

const payNum = '123abc';

my.request({
  url: 'https://demo.com/api/alipay',// 須加httpRequest域白名單
  method: 'POST',
  data: { // data裏的key、value是開發者自定義的
    from: '支付寶',
    payNum: payNum, // 訂單號
    other: 'demo', // 其餘參數
  },
  dataType: 'json',
  success: function(res) {
    my.alert({content: 'success'});
  },
  fail: function(res) {
    my.alert({content: 'fail'});
  },
  complete: function(res) {
    my.hideLoading();
    my.alert({content: 'complete'});
  }
});

支付寶支付小結

咱們從pc端,觸屏端,app端三個方面瞭解了支付寶支付的基本原理。能夠看出:支付的前端實現,其實並不複雜,而真正的難點在於後端支付系統的實現。至於最難的支付寶喚起問題,其實支付寶收銀臺自身已經實現了喚起功能,無需咱們實現

微信

微信支付開發文檔

pc端

掃碼支付文檔

交互方式:

因爲微信並無像支付寶提供了pc端的官方收銀臺,因此點擊微信支付,咱們通常都是直接彈出二維碼彈窗,要求用戶進行掃碼支付,用戶掃碼則能夠直接喚起微信支付。彈出二維碼的同時,咱們須要當即輪詢查詢支付狀態。

const payNum = '123abc';

ajax({ 
  url: '/api/weixinpay', // 支付api
  type: 'POST',
  data: {
    payNum: payNum, // 訂單號
    other: 'demo', // 其餘參數
}).then((res) => {
  const { qrUrl } = res;

  // qrUrl是微信的掃碼地址,通常是 weixin://wxpay/bizpayurl?pr=P1oi4x6 ,這段schema經過微信掃一掃能夠喚起微信支付
  qrcode({
    width: 175,
    height: 175,
    url: qrUrl
  });

  // 開始輪詢支付結果
  // 代碼省略,能夠參考以前的支付寶pc端實現
}).catch((err) => {
  console.log('提交失敗')
})

觸屏端

H5支付文檔

交互方式:

在觸屏端點擊微信支付,頁面直接跳轉到微信支付中間頁,該頁面會嘗試喚起微信支付

與支付寶收銀臺不一樣的是,微信支付中間頁在調起微信收銀臺後超過5秒,會自動跳轉回redirect_url,所以沒法保證頁面回跳時,支付流程已結束,因此商戶設置的redirect_url地址不能自動執行查單操做,應讓用戶去點擊按鈕觸發查單操做

// 代碼省略,基本和支付寶的觸屏端同樣

// 微信支付中轉頁通常是這種格式的url地址

// https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx111408048537349a5434e53d1930739300&package=1982317760&redirect_url=https://m.imooc.com/myorder

須要注意:微信支付中轉頁通常不能直接用瀏覽器訪問,由於中轉頁須要判斷referer是不是商戶申請H5時提交的受權域名。若是你直接用瀏覽器訪問,referer爲空,致使頁面並不會加載成功。若是是APP裏調起H5支付,須要在webview中手動設置referer

還有一種取巧的方法,咱們能夠不使用微信中轉頁,直接在當前頁喚起支付

// 後端直接返回一段schema

const schema = `weixin://wap/pay?appid%3Dwxd6841de60b02faef%26noncestr%3D095525b24fc94111a3663068c8dc8a90%26package%3DWAP%26prepayid%3Dwx091027118037832f961440d31092022500%26sign%3D2CF5A14607C6AAEDE382758CA87B973F%26timestamp%3D1591669631`

// 移動端就能喚起微信支付
window.location.href = schema;

不過這種方法,schema容易被第三方app的webveiw攔截,從而調起支付失敗。好比在微博訪問收銀臺,若是使用該方法,就會喚起微信失敗。所以,仍是建議使用微信中轉頁,由中轉頁喚起微信比較保險。固然,支付寶裏無論用啥方法,都沒法進行微信支付(相愛相殺)。

JSAPI

若是咱們的收銀臺頁面是在微信瀏覽器裏打開的,那麼咱們可使用微信提供的JSAPI喚起支付

JSAPI支付文檔

const payNum = '123abc';

function onBridgeReady(wxJsApiParam) {
    window.WeixinJSBridge.invoke(
      'getBrandWCPayRequest',
      wxJsApiParam,//josn串
      function (res) {
        if (res.err_msg == "get_brand_wcpay_request:ok") {
          // 支付成功
          location.href = `/success/${payNum}`;
        } else if (res.err_msg == "get_brand_wcpay_request:fail") {
          // 支付失敗
        }
      }
    );
  }

function weixinPay(wxJsApiParam) {
    if (typeof WeixinJSBridge == "undefined") {
      if (document.addEventListener) {
        document.addEventListener('WeixinJSBridgeReady', function () { onBridgeReady(wxJsApiParam) }, false);
      } else if (document.attachEvent) {
        document.attachEvent('WeixinJSBridgeReady', function () { onBridgeReady(wxJsApiParam) });
        document.attachEvent('onWeixinJSBridgeReady', function () { onBridgeReady(wxJsApiParam) });
      }
    } else {
      onBridgeReady(wxJsApiParam);
    }
  }

ajax({ 
  url: '/api/weixin_jsapi', // 支付api
  type: 'POST',
  data: {
    payNum: payNum, // 訂單號
    other: 'demo', // 其餘參數
}).then((res) => {
  const { jsapiData } = res;
  // jsapiData是一串json字符串,裏面包含了appId,paySign等各類數據,用來調起微信支付
  weixinPay(JSON.parse(jsapiData));
}).catch((err) => {
  console.log('提交失敗')
})

使用JSAPI須要咱們有微信公衆平臺,由於下單必傳的參數openid,須要咱們在公衆平臺設置獲取openid的域名,才能獲取成功

除了使用微信瀏覽器內置的WeixinJSBridge對象,咱們也可使用JSSDK

<script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script>
wx.chooseWXPay({
  timestamp: 0, 
  nonceStr: '', 
  package: '', 
  signType: '', 
  paySign: '',
  success: function (res) {
    // 支付成功後的回調函數
  }
});
</script>

app端

APP支付文檔

H5支付不建議在APP端使用,如須要在APP中使用微信支付,請接APP支付

微信官方文檔一樣不建議在APP端使用觸屏端的支付方式,所以最好接入微信SDK。前端一樣可使用jsbridge調用客戶端的微信支付方法,能夠參考前面支付寶的app端方式。

小程序

小程序支付文檔

小程序支付其實和微信JSAPI支付很是相似,都須要先獲取到Openid,調用相同的API

wx.requestPayment({
  timeStamp: '',
  nonceStr: '',
  package: '',
  signType: 'MD5',
  paySign: '',
  success (res) { },
  fail (res) { }
})

微信支付小結

微信支付在JSAPI和小程序的流程上比較複雜些,由於涉及到公衆號access_token openid 等一系列權限的獲取。不過總的來講,複雜難度主要仍是在後端方面。

其餘第三方支付

除了主流的微信支付和支付寶支付,咱們有可能還須要對接一些其餘第三方支付平臺,好比:QQ,PayPal, 銀聯,京東白條,各大銀行等等,固然原理也是大同小異。

同時,咱們也可使用第三方聚合支付平臺,好比度小滿支付,這些平臺已經集成好了各大銀行信用卡和存儲卡支付功能,咱們能夠很容易的接入sdk,節約開發成本。

總結

web支付因爲開發條件要求很高(至少要有註冊公司),所以大部分同窗平常工做接觸並非不少。固然本文也僅僅是回顧了下平常開發中,前端在web支付中的一些常見套路。

真正實際項目裏,咱們仍然會面臨不少問題和難點,這時就須要咱們見招拆招了。

參考

  1. web開發中的支付寶支付和微信支付
  2. 微信支付文檔
  3. 支付寶文檔
相關文章
相關標籤/搜索