如何更新 Android 付款應用程式,以便使用 Web Payments API 提供運送地址和付款人聯絡資訊。
透過網路表單輸入運送地址和聯絡資訊,可能會讓客戶感到不便。這可能會造成錯誤及降低轉換率。
因此,Payment Request API 支援要求運送地址和聯絡資訊的功能。這麼做有許多好處:
- 使用者只要輕觸幾下,就能選擇正確的地址。
- 系統一律會以標準化格式傳回地址。
- 提交錯誤的地址較不容易。
瀏覽器可將運送地址和聯絡資訊延遲收集至付款應用程式,提供一致的付款體驗。這項功能稱為「委派」。
Chrome 會盡可能將客戶運送地址和聯絡資訊的收集作業委派給叫用的 Android 付款應用程式。委派功能可以減少結帳過程中的不便。
商家網站可以根據消費者選擇的運送地址和運送選項,動態更新運送選項和總價。
如要為現有的 Android 付款應用程式新增委派支援,請執行下列步驟:
宣告支援的委派
瀏覽器必須知道付款應用程式可提供的其他資訊清單,才能將這些資訊的集合委派給應用程式。在應用程式的 AndroidManifest.xml 中,將支援的委派宣告宣告為 <meta-data>
。
<activity
android:name=".PaymentActivity"
…
<meta-data
android:name="org.chromium.payment_supported_delegations"
android:resource="@array/supported_delegations" />
</activity>
<resource>
必須是從下列有效值中選取的字串清單:
[ "payerName", "payerEmail", "payerPhone", "shippingAddress" ]
以下範例只能提供運送地址和付款人的電子郵件地址。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="supported_delegations">
<item>payerEmail</item>
<item>shippingAddress</item>
</string-array>
</resources>
剖析 PAY
意圖額外項目做為必要付款方式
商家可以使用 paymentOptions
字典指定額外的必要資訊。Chrome 將透過意圖額外資訊的形式將下列參數傳送至 PAY
活動,為應用程式提供必要選項清單。
paymentOptions
paymentOptions
是商家指定付款選項的子集,而您的應用程式已宣告這些付款方式的支援。
val paymentOptions: Bundle? = extras.getBundle("paymentOptions")
val requestPayerName: Boolean? = paymentOptions?.getBoolean("requestPayerName")
val requestPayerPhone: Boolean? = paymentOptions?.getBoolean("requestPayerPhone")
val requestPayerEmail: Boolean? = paymentOptions?.getBoolean("requestPayerEmail")
val requestShipping: Boolean? = paymentOptions?.getBoolean("requestShipping")
val shippingType: String? = paymentOptions?.getString("shippingType")
可包含以下參數:
requestPayerName
- 這個布林值表示是否需要付款人姓名。requestPayerPhone
- 這個布林值表示是否需要付款人手機。requestPayerEmail
- 這個布林值表示是否需要付款人的電子郵件地址。requestShipping
- 這個布林值表示是否需要運送資訊。shippingType
- 用於表示運送類型的字串。運送類型可以是"shipping"
、"delivery"
或"pickup"
。應用程式要求使用者的地址或選擇運送選項時,可在使用者介面中使用這項提示。
shippingOptions
shippingOptions
是商家指定運送選項的可包裝陣列。這個參數只會在 paymentOptions.requestShipping ==
true
時存在。
val shippingOptions: List<ShippingOption>? =
extras.getParcelableArray("shippingOptions")?.mapNotNull {
p -> from(p as Bundle)
}
每個運送選項都是含有下列索引鍵的 Bundle
。
id
:運送選項 ID。label
:向使用者顯示的運送選項標籤。amount
- 運費組合,內含currency
和value
鍵以及字串值。selected
- 付款應用程式顯示運送選項時,是否應選取運送選項。
selected
以外的所有索引鍵都有字串值。selected
具有布林值。
val id: String = bundle.getString("id")
val label: String = bundle.getString("label")
val amount: Bundle = bundle.getBundle("amount")
val selected: Boolean = bundle.getBoolean("selected", false)
在付款回應中提供必要資訊
應用程式應在對 PAY
活動的回應中加入必要額外資訊。
方法是將下列參數指定為意圖額外項目:
payerName
- 付款人的全名。當paymentOptions.requestPayerName
為 true 時,這應該是非空白字串。payerPhone
- 付款人的電話號碼。當paymentOptions.requestPayerPhone
為 true 時,這應該是非空白字串。payerEmail
- 付款人的電子郵件地址。當paymentOptions.requestPayerEmail
為 true 時,這應該是非空白字串。shippingAddress
- 使用者提供的運送地址。當paymentOptions.requestShipping
為 true 時,這應該是非空白的套件。組合應具備下列鍵,代表實際位址中不同部分。city
countryCode
dependentLocality
organization
phone
postalCode
recipient
region
sortingCode
addressLine
addressLine
以外的所有鍵都有字串值。addressLine
是字串陣列。
shippingOptionId
:使用者所選運送選項的 ID。當paymentOptions.requestShipping
為 true 時,這應該是非空白字串。
驗證付款回應
如果所叫用付款應用程式收到的付款回應活動結果設為 RESULT_OK
,Chrome 會在額外項目中檢查必要資訊。如果驗證失敗,Chrome 會從 request.show()
傳回遭拒的承諾,並顯示下列其中一項向開發人員顯示的錯誤訊息:
'Payment app returned invalid response. Missing field "payerEmail".'
'Payment app returned invalid response. Missing field "payerName".'
'Payment app returned invalid response. Missing field "payerPhone".'
'Payment app returned invalid shipping address in response.'
'... is not a valid CLDR country code, should be 2 upper case letters [A-Z]'
'Payment app returned invalid response. Missing field "shipping option".'
以下程式碼範例為有效回應的範例:
fun Intent.populateRequestedPaymentOptions() {
if (requestPayerName) {
putExtra("payerName", "John Smith")
}
if (requestPayerPhone) {
putExtra("payerPhone", "4169158200")
}
if (requestPayerEmail) {
putExtra("payerEmail", "john.smith@gmail.com")
}
if(requestShipping) {
val address: Bundle = Bundle()
address.putString("countryCode", "CA")
val addressLines: Array<String> =
arrayOf<String>("111 Richmond st. West")
address.putStringArray("addressLines", addressLines)
address.putString("region", "Ontario")
address.putString("city", "Toronto")
address.putString("postalCode", "M5H2G4")
address.putString("recipient", "John Smith")
address.putString("phone", "4169158200")
putExtra("shippingAddress", address)
putExtra("shippingOptionId", "standard")
}
}
選用:支援動態流程
有時交易的總費用會增加,例如當使用者選擇快速運送選項,或使用者選擇國際運送地址時,可用的運送選項清單或價格清單有所變更。應用程式向使用者提供指定運送地址或選項時,其應能通知商家任何運送地址或選項異動,並向使用者顯示更新後的付款資料 (由商家提供)。
AIDL
如要通知商家有新的變更,請使用 Chrome 在 AndroidManifest.xml 中宣告的 PaymentDetailsUpdateService
服務。如要使用此服務,請建立含有以下內容的兩個 AIDL 檔案:
app/src/main/aidl/org/chromium/components/payments/IPaymentDetailsUpdateService
package org.chromium.components.payments;
import android.os.Bundle;
interface IPaymentDetailsUpdateServiceCallback {
oneway void updateWith(in Bundle updatedPaymentDetails);
oneway void paymentDetailsNotUpdated();
}
app/src/main/aidl/org/chromium/components/payments/IPaymentDetailsUpdateServiceCallback
package org.chromium.components.payments;
import android.os.Bundle;
import org.chromium.components.payments.IPaymentDetailsUpdateServiceCallback;
interface IPaymentDetailsUpdateService {
oneway void changePaymentMethod(in Bundle paymentHandlerMethodData,
IPaymentDetailsUpdateServiceCallback callback);
oneway void changeShippingOption(in String shippingOptionId,
IPaymentDetailsUpdateServiceCallback callback);
oneway void changeShippingAddress(in Bundle shippingAddress,
IPaymentDetailsUpdateServiceCallback callback);
}
在使用者選取的付款方式、運送地址或運送選項有所異動時通知商家
private fun bind() {
// The action is introduced in Chrome version 92, which supports the service in Chrome
// and other browsers (e.g., WebLayer).
val newIntent = Intent("org.chromium.intent.action.UPDATE_PAYMENT_DETAILS")
.setPackage(callingBrowserPackage)
if (packageManager.resolveService(newIntent, PackageManager.GET_RESOLVED_FILTER) == null) {
// Fallback to Chrome-only approach.
newIntent.setClassName(
callingBrowserPackage,
"org.chromium.components.payments.PaymentDetailsUpdateService")
newIntent.action = IPaymentDetailsUpdateService::class.java.name
}
isBound = bindService(newIntent, connection, Context.BIND_AUTO_CREATE)
}
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val service = IPaymentDetailsUpdateService.Stub.asInterface(service)
try {
if (isOptionChange) {
service?.changeShippingOption(selectedOptionId, callback)
} else (isAddressChange) {
service?.changeShippingAddress(selectedAddress, callback)
} else {
service?.changePaymentMethod(methodData, callback)
}
} catch (e: RemoteException) {
// Handle the remote exception
}
}
}
用於服務啟動意圖的 callingPackageName
可具有下列其中一個值,視發出付款要求的瀏覽器而定。
Chrome 版本 | 套件名稱 |
---|---|
穩定 |
"com.android.chrome"
|
Beta 版 |
"com.chrome.beta"
|
開發 |
"com.chrome.dev"
|
Canary 版 |
"com.chrome.canary"
|
Chromium |
"org.chromium.chrome"
|
Google 快速搜尋框 (WebLayer 嵌入器) |
"com.google.android.googlequicksearchbox"
|
changePaymentMethod
通知商家使用者所選付款方式有異動。paymentHandlerMethodData
組合包含 methodName
和選用的 details
鍵,兩者皆含字串值。Chrome 會檢查是否有包含非空白 methodName
的非空白組合,並在驗證失敗時透過 callback.updateWith
傳送包含下列其中一則錯誤訊息的 updatePaymentDetails
。
'Method data required.'
'Method name required.'
changeShippingOption
通知商家使用者所選運送選項有異動。
shippingOptionId
應為商家指定的其中一個運送選項 ID。Chrome 會檢查非空白的 shippingOptionId
,如果驗證失敗,則會透過 callback.updateWith
傳送包含以下錯誤訊息的 updatePaymentDetails
。
'Shipping option identifier required.'
changeShippingAddress
在使用者提供的運送地址有變動時通知商家。Chrome 會檢查是否有包含有效 countryCode
的非空白 shippingAddress
組合。如果驗證失敗,則透過 callback.updateWith
傳送含有以下錯誤訊息的 updatePaymentDetails
。
'Payment app returned invalid shipping address in response.'
無效的狀態錯誤訊息
如果 Chrome 在收到任何變更要求時遇到無效狀態,就會使用遮蓋的 updatePaymentDetails
軟體包呼叫 callback.updateWith
。組合只會包含有 "Invalid state"
的 error
鍵。無效狀態的例子包括:
- Chrome 仍在等待商家對先前變更的回應 (例如進行中的變更事件)。
- 付款應用程式提供的運送選項 ID 不屬於任何商家指定的運送選項。
接收商家更新的付款資料
private fun unbind() {
if (isBound) {
unbindService(connection)
isBound = false
}
}
private val callback: IPaymentDetailsUpdateServiceCallback =
object : IPaymentDetailsUpdateServiceCallback.Stub() {
override fun paymentDetailsNotUpdated() {
// Payment request details have not changed.
unbind()
}
override fun updateWith(updatedPaymentDetails: Bundle) {
newPaymentDetails = updatedPaymentDetails
unbind()
}
}
updatePaymentDetails
是相當於 PaymentRequestDetailsUpdate
WebIDL 字典 (在遮蓋 modifiers
欄位之後) 的組合,並包含下列選用鍵:
total
- 包含currency
和value
鍵的組合,兩個鍵都有字串值shippingOptions
:運送選項的可包裝陣列error
- 包含一般錯誤訊息的字串 (例如changeShippingOption
未提供有效的運送選項 ID 時)stringifiedPaymentMethodErrors
- 代表付款方式驗證錯誤的 JSON 字串addressErrors
- 具有選用鍵的組合,與運送地址和字串值相同。每個金鑰都代表一個與運送地址中對應部分相關的驗證錯誤。
如果缺少索引鍵,則代表該值未變更。