? 你可能還不知道的 Web 支付流程標準化

JS菌發表於2019-03-27

20190327101136.png

PaymentRequest API 是一種跨瀏覽器的標準 API,主要的目的是以瀏覽器充當中介,儘可能標準化支付通訊的流程。 ?

整個流程主要是建立 PaymentRequest,將購買貨物的詳細資訊傳遞給瀏覽器,在 UI 層面顯示支付的 UI,使用者填入支付資訊或從快取中一鍵填充並確認支付。

他的最大的優勢是信用卡、收貨地址等支付資訊都統一儲存在瀏覽器,如果網站都能夠使用該 API,那麼就不再需要重複填寫支付資訊。

⚠️ 使用 PaymentRequest 非常簡單,但由於相容性問題,目前不要在生產環境中使用,API 層面亦會有可能改動。

建立 PaymentRequest 例項

第一步是通過呼叫 PaymentRequest 建構函式建立一個 PaymentRequest 物件

建構函式需要三個引數分別是 methodData、details 和可選的 options

methodData 支付方式

首先需要設定支付方式,傳入賣家支援的支付手段,如 visa 或 mastercard 等信用卡或其他方式:

methodData 是一個陣列,陣列中每一項應為一個物件,物件內包含兩個屬性:

  • supportedMethods 付款方式識別符
  • data 額外資訊
    • supportedNetworks 支付網路

supportedMethods 需要填寫 付款方式識別符,一般填寫 basic-card,也可填寫 url 的識別符如:

20190326151631.png

如果是 google pay url 付款識別符,那麼呼叫的時候長這樣:

20190326154954.png

這裡以 basic-card 為例,那麼 data 屬性則需要填寫一些額外的資訊,如果是 basic-card 方式,那麼還可以選擇配置 supportedNetworks,這個選項指定了 card networks

card networks 是一個陣列,如支援 visa、mastercard 等

['visa', 'mastercard', 'amex', 'jcb', 'diners', 'discover', 'mir', 'unionpay']
複製程式碼

什麼是 card networks 見這篇文章:www.cardinalcommerce.com/startups/on…

根據規範,我們編寫以下程式碼 ?

const methodData = [{
    supportedMethods: 'basic-card',
    data: {
        supportedNetworks: ['visa', 'mastercard', 'amex', 'jcb', 'diners', 'discover', 'mir', 'unionpay']
    }
}]
複製程式碼

在呼叫的時候長這樣:

20190326155015.png

20190326155030.png

另外,Apple Pay 也支援該特性,詳細見文件:webkit.org/blog/8182/i…

details 交易詳情

details 儲存的是交易詳情,主要有以下欄位:

  • total 需要支付的總額
  • id 交易 ID,如果不填寫瀏覽器自動生成
  • displayItems 主要是一些貨品資訊、稅費、運費等詳細清單
  • shippingOptions 則是運輸相關的選項,有事件監聽如果不能送達,則應在 UI 層面給使用者提示
  • modifier 主要是針對使用者付款方式,修改交易詳情,比方說針對某種支付手段給予優惠,展示不同金額等
    • additionalDisplayItems 額外需要展示的新增訂單專案
    • data 額外資訊
    • total 修改後的總價

total

這一欄位需要填寫支付總額,API 不會自動計算,需要計算後填入 total 欄位需要滿足 PaymentItem 規範

20190326160854.png

也就是說至少需要一個 label 字串和一個 PaymentCurrencyAmount 金額,另外還有一個可選項 pending 用來表示是否為最終金額,預設為 false:

PaymentCurrencyAmount 則需要兩個屬性,一個是 currency 一個是 value,兩個都是字串:

const details = {
    total: {
        label: '合計總金額 ? ',
        amount: { currency: 'CNY', value: '100.00' }
    }
}
複製程式碼

增加 details 物件,編寫程式碼如上所示

效果如下:

20190326161255.png

id

id 則代表了交易 ID,可以自定義,字串格式:

const details = {
    id: 'Order-Funny-ID-000-001',
    total: {
複製程式碼

displayItems

訂單詳情,每個單個條目最終是否展示取決於瀏覽器。這是一個陣列,陣列內每一個物件都是一個 PaymentItem,因此陣列內每一項的規範參照 total:

const details = {
    id: 'Order-Funny-ID-000-001',
    displayItems: [{
        label: '西瓜 ?',
        amount: { currency: 'CNY', value: '20.00' }
    }, {
        label: '草莓 ?',
        amount: { currency: 'CNY', value: '90.00' }
    }, {
        label: 'VIP 會員 ?',
        amount: { currency: 'CNY', value: '-10.00' },
        pending: true
    }],
    total: {
        label: '合計總金額 ? ',
        amount: { currency: 'CNY', value: '100.00' }
    }
}
複製程式碼

上面程式碼執行效果長這樣:

20190326162727.png

shippingOptions

根據規範 shippingOptions 應該有三個必選引數,id、label、amount(同樣,amount 必須符合 PaymentCurrencyAmount),selected 預設為 false,是可選引數:

20190326174021.png

const details = {
    id: 'Order-Funny-ID-000-001',
    displayItems: [{
    }],
    total: {
    },
    shippingOptions: [{
        id: '標準快遞',
        label: '? 免費普通快遞 1 天全國範圍內',
        amount: {
            currency: 'CNY',
            value: '0.00'
        },
        selected: true
    }, {
        id: '東風快遞',
        label: '? 超快速遞 3 小時全球範圍內',
        amount: {
            currency: 'CNY',
            value: '100.00'
        }
    }]
}

const options = {
    requestShipping: true // 別忘記這裡需要設定為 true
}

let request = new PaymentRequest(
    methodData, // 支付方式
    details, // 交易資訊
    options // 其他額外資訊
)
複製程式碼

按上述規範配置 shippingOptions,並監聽配送選項改變或地址改變動態調整收費價格標準

20190327081500.png

// 監聽配送選項改變,動態修改收費標準
request.onshippingoptionchange = function (e) {
    console.log('快遞選項改變,重新計算價格')

    e.updateWith(Promise.resolve(updateDetail(details, request.shippingOption))) // 接收 promise
}
// 監聽地址選項改變,動態修改收費標準
request.onshippingaddresschange = function (e) {
    console.log('地址選項改變,重新計算價格')

    e.updateWith(Promise.resolve(updateDetail(details, request.shippingOption))) // 接收 promise
}

function updateDetail(details, shippingOpts) {
    console.log({ details, shippingOpts }) // shippingOpts 代表選擇的快遞 id
    // fetch 後臺資料
    // 各種判斷
    // 修改 details 最後 return 出去
    // if (shippingOpts) {} else {}
    // 這裡僅作演示沒修改資料
    return details
}
複製程式碼

modifier

用於修改賬單,這裡以 visa 卡為例,使用此型別信用卡會在賬單中增加一條 additionalDisplayItems,並通過 total 修改賬單總額

modifier 需要一個 supportedMethods 為必選引數:

20190327085355.png

modifiers: [
    {
        additionalDisplayItems: [{
            label: 'visa 手續費',
            amount: { currency: 'CNY', value: '10.00' }
        }],
        supportedMethods: "basic-card",
        total: {
            label: "Total due",
            amount: { currency: "USD", value: "110.00" },
        },
        data: {
            supportedNetworks: ['visa'],
        },
    },
]
複製程式碼

效果如下:

2019年3月27日08點52分.gif

options

主要有六個引數可供配置,分別是 requestPayerNamerequestPayerEmailrequestPayerPhonerequestShippingrequestBillingAddressshippingType,前五項預設為 false,最後一項可設定為 shippingdeliverypickup,三者根據語境選擇合適的 UI 層面對“快遞”的描述,這三個單詞在中國大陸分別代表送貨遞送取貨

request 例項的屬性和方法

上文提到的 shippingaddresschangeshippingoptionchange 就是該例項的兩個事件,除此之外還有 paymentmethodchangemerchantvalidation。除事件之外,例項的四個屬性分別是 idshippingAddressshippingOptionshippingType 可以訪問到這個支付請求的相關配置。該例項的三個方法比較重要,用來鑑定是否能夠發起支付的 canMakePayment、調起 UI 支付介面的 show 以及放棄支付的 abort

canMakePayment

檢測是否支援此種支付方式,在使用之前必須先要判斷瀏覽器是否支援 canMakePayment 本身:

if (request.canMakePayment) {

} else {
+   ;(() => { console.log('瀏覽器不支援 canMakePayment 特性') })()
}
複製程式碼

然後呼叫方法檢測支付:

if (request.canMakePayment) {
+   request.canMakePayment().then(res => {

+   }).catch(console.error)
} else {
    ; (() => { console.log('瀏覽器不支援 canMakePayment 特性') })()
}
複製程式碼

根據檢測結果判斷是否進一步呼叫 show,發起支付:

if (request.canMakePayment) {
    request.canMakePayment().then(res => {
+       if (res) {
+           request.show() // true 如果支援
+       } else {
+           console.log('未能發起支付')
+       }
    }).catch(console.error)
} else {
    ; (() => { console.log('瀏覽器不支援 canMakePayment 特性') })()
}
複製程式碼

show

最終獲取支付成功的 response 是通過 show 方法返回的,最後確認無誤後,呼叫 response 的 complete 方法,並傳入 successfailunknow 欄位來結束支付:

if (request.canMakePayment) {
    request.canMakePayment().then(res => {
        if (res) {
+           request.show().then(response => {
+               console.log(response)
+               setTimeout(() => { // 模擬傳送支付資訊到服務端,並呼叫 response 的 complete 方法完成支付
+                   response.complete()
+               }, 2000)
            })
        } else {
            console.log('未能發起支付')
        }
    }).catch(console.error)
} else {
    ; (() => { console.log('瀏覽器不支援 canMakePayment 特性') })()
}
複製程式碼

此外 response 還有 retry 方法,可以在遇到支付 response 出現錯誤的時候重新發起支付

關於 response 的屬性和方法見如下截圖:

20190327095139.png

附上用於測試的信用卡卡號

最後附上用於測試的信用卡卡號,日期隨便填,CVC 隨便填

Test Credit Card Account Numbers www.blogjava.net/sealyu/arch…

最後程式碼在此:codepen.io/oliyg/pen/P…

? 你可能還不知道的 Web 支付流程標準化

相關文章