瞭解如何更新 Android 付款應用程式,透過 Web Payments API 提供運送地址和付款人聯絡資訊。
發布日期:2020 年 7 月 17 日,上次更新時間:2025 年 5 月 27 日
透過網路表單輸入運送地址和聯絡資訊,對消費者來說可能相當麻煩。這可能會導致錯誤,並降低轉換率。
因此,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/chromium_payment_supported_delegations" />
</activity>
android:resource 必須指向 <string-array>,其中包含下列所有或部分值:
payerNamepayerEmailpayerPhoneshippingAddress
以下範例只能提供運送地址和付款人的電子郵件地址。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="chromium_payment_supported_delegations">
<item>payerEmail</item>
<item>shippingAddress</item>
</string-array>
</resources>
剖析 PAY 意圖額外項目,取得必要付款方式
商家可以使用 paymentOptions 字典指定其他必要資訊。Chrome 會傳遞 paymentOptions
Intent 額外資訊至 PAY 活動,提供應用程式可提供的必要選項清單。
paymentOptions
paymentOptions 是商家指定的付款選項子集,您的應用程式已聲明支援委派。
Kotlin
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")
Java
Bundle paymentOptions = extras.getBundle("paymentOptions");
if (paymentOptions != null) {
Boolean requestPayerName = paymentOptions.getBoolean("requestPayerName");
Boolean requestPayerPhone = paymentOptions.getBoolean("requestPayerPhone");
Boolean requestPayerEmail = paymentOptions.getBoolean("requestPayerEmail");
Boolean requestShipping = paymentOptions.getBoolean("requestShipping");
String shippingType = paymentOptions.getString("shippingType");
}
可包含下列參數:
requestPayerName- 布林值,指出是否需要付款人姓名。requestPayerPhone- 布林值,指出是否需要付款人的手機。requestPayerEmail- 布林值,指出是否需要付款人的電子郵件地址。requestShipping- 布林值,指出是否需要運送資訊。shippingType:顯示運送類型的字串。運送類型可為"shipping"、"delivery"或"pickup"。應用程式可以在要求使用者提供地址或選擇運送選項時,在 UI 中使用這項提示。
shippingOptions
shippingOptions 是商家指定運送選項的可封裝陣列。只有在 paymentOptions.requestShipping ==
true 時,才會出現這項參數。
Kotlin
val shippingOptions: List<ShippingOption>? =
extras.getParcelableArray("shippingOptions")?.mapNotNull {
p -> from(p as Bundle)
}
Java
Parcelable[] shippingOptions = extras.getParcelableArray("shippingOptions");
for (Parcelable it : shippingOptions) {
if (it != null && it instanceof Bundle) {
Bundle shippingOption = (Bundle) it;
}
}
每個運送選項都是 Bundle,並包含下列鍵。
id- 運送選項 ID。label- 向使用者顯示的運送選項標籤。amount- 運費組合,包含currency和value鍵,以及字串值。selected- 支付應用程式顯示運送選項時,是否應選取運送選項。
除了 selected 以外,所有鍵都有字串值。selected 具有布林值。
Kotlin
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)
Java
String id = bundle.getString("id");
String label = bundle.getString("label");
Bundle amount = bundle.getBundle("amount");
Boolean selected = bundle.getBoolean("selected", false);
在付款回覆中提供必要資訊
應用程式應在對 PAY 活動的回應中加入必要額外資訊。
如要執行這項操作,必須將下列參數指定為 Intent 額外資訊:
payerName- 付款人全名。如果paymentOptions.requestPayerName為 true,則這個值不得為空白字串。payerPhone- 付款人的電話號碼。如果paymentOptions.requestPayerPhone為 true,則這個值不得為空白字串。payerEmail- 付款人的電子郵件地址。如果paymentOptions.requestPayerEmail為 true,則此值不得為空字串。shippingAddress- 使用者提供的運送地址。如果paymentOptions.requestShipping為 true,這個值應為非空白的套裝組合。這個組合應包含下列鍵,代表實體地址的不同部分。countryCodepostalCodesortingCoderegioncitydependentLocalityaddressLineorganizationrecipientphone除了addressLine以外的所有鍵都有字串值。addressLine是字串陣列。
shippingOptionId- 使用者選取的運送選項 ID。如果paymentOptions.requestShipping為 true,這個值應為非空白字串。
驗證付款回應
如果從叫用的付款應用程式收到的付款回應活動結果設為 RESULT_OK,Chrome 會檢查額外資訊中是否包含必要資訊。如果驗證失敗,Chrome 會從 request.show() 傳回遭拒的 Promise,並顯示下列其中一則開發人員適用的錯誤訊息:
'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".'
以下是有效回應的程式碼範例:
Kotlin
fun Intent.populateRequestedPaymentOptions() {
if (requestPayerName) {
putExtra("payerName", "John Smith")
}
if (requestPayerPhone) {
putExtra("payerPhone", "5555555555")
}
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", "5555555555")
putExtra("shippingAddress", address)
putExtra("shippingOptionId", "standard")
}
}
Java
private Intent populateRequestedPaymentOptions() {
Intent result = new Intent();
if (requestPayerName) {
result.putExtra("payerName", "John Smith");
}
if (requestPayerPhone) {
presult.utExtra("payerPhone", "5555555555");
}
if (requestPayerEmail) {
result.putExtra("payerEmail", "john.smith@gmail.com");
}
if (requestShipping) {
Bundle address = new Bundle();
address.putExtra("countryCode", "CA");
address.putExtra("postalCode", "M5H2G4");
address.putExtra("region", "Ontario");
address.putExtra("city", "Toronto");
String[] addressLines = new String[] {"111 Richmond st. West"};
address.putExtra("addressLines", addressLines);
address.putExtra("recipient", "John Smith");
address.putExtra("phone", "5555555555");
result.putExtra("shippingAddress", address);
result.putExtra("shippingOptionId", "standard");
}
return result;
}
選用:支援動態流程
交易總費用有時會增加,例如使用者選擇快遞運送選項,或是使用者選擇國際運送地址時,可用的運送選項或價格清單有所變更。應用程式提供使用者選取的運送地址或選項時,應能通知商家運送地址或選項的任何變更,並向使用者顯示更新後的付款詳細資料 (由商家提供)。
如要通知商家新異動,請實作 IPaymentDetailsUpdateServiceCallback 介面,並在 AndroidManifest.xml 中使用 UPDATE_PAYMENT_DETAILS 意圖篩選器宣告。
叫用 PAY 意圖後,Chrome 會立即連線至與 PAY 意圖位於相同套件中的 UPDATE_PAYMENT_DETAILS 服務 (如果存在),並呼叫 setPaymentDetailsUpdateService(service),向付款應用程式提供 IPaymentDetailsUpdateService 端點,以便在使用者付款方式、運送選項或運送地址有變更時通知應用程式。
接收跨程序通訊 (IPC) 時,請使用 packageManager.getPackagesForUid(Binder.getCallingUid()) 驗證叫用 PAY 意圖的應用程式,是否與叫用 IPaymentDetailsUpdateServiceCallback 方法的應用程式具有相同的套件名稱。
AIDL
建立兩個 AIDL 檔案,並加入以下內容:
org/chromium/components/payments/IPaymentDetailsUpdateServiceCallback.aidl
package org.chromium.components.payments;
import android.os.Bundle;
import org.chromium.components.payments.IPaymentDetailsUpdateService;
interface IPaymentDetailsUpdateServiceCallback {
oneway void updateWith(in Bundle updatedPaymentDetails);
oneway void paymentDetailsNotUpdated();
oneway void setPaymentDetailsUpdateService(IPaymentDetailsUpdateService service);
}
org/chromium/components/payments/IPaymentDetailsUpdateService.aidl
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);
}
服務
實作 IPaymentDetailsUpdateServiceCallback 服務。
Kotlin
class SampleUpdatePaymentDetailsCallbackService : Service() {
private val binder = object : IPaymentDetailsUpdateServiceCallback.Stub() {
override fun updateWith(updatedPaymentDetails: Bundle) {}
override fun paymentDetailsNotUpdated() {}
override fun setPaymentDetailsUpdateService(service: IPaymentDetailsUpdateService) {}
}
override fun onBind(intent: Intent?): IBinder? {
return binder
}
}
Java
import org.chromium.components.paymsnts.IPaymentDetailsUpdateServiceCallback;
public class SampleUpdatePaymentDetailsCallbackService extends Service {
private final IPaymentDetailsUpdateServiceCallback.Stub mBinder =
new IPaymentDetailsUpdateServiceCallback.Stub() {
@Override
public void updateWith(Bundle updatedPaymentDetails) {}
@Override
public void paymentDetailsNotUpdated() {}
@Override
public void setPaymentDetailsUpdateService(IPaymentDetailsUpdateService service) {}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
AndroidManifest.xml
在 AndroidManifest.xml 中公開 IPaymentDetailsUpdateServiceCallback 的服務。
<service
android:name=".SampleUpdatePaymentDetailsCallbackService"
android:exported="true">
<intent-filter>
<action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS" />
</intent-filter>
</service>
通知商家使用者選取的付款方式、運送地址或運送選項有異動
Kotlin
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
}
Java
if (service == null) {
return;
}
try {
if (isOptionChange) {
service.changeShippingOption(selectedOptionId, callback);
} else (isAddressChange) {
service.changeShippingAddress(selectedAddress, callback);
} else {
service.changePaymentMethod(methodData, callback);
}
} catch (RemoteException e) {
// Handle the remote exception
}
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。組合只會包含 error 鍵和 "Invalid state"。
無效狀態的例子包括:
- Chrome 仍在等待商家對先前的變更做出回應 (例如進行中的變更事件)。
- 付款應用程式提供的運送選項 ID 不屬於任何商家指定的運送選項。
接收商家提供的最新付款詳細資料
Kotlin
override fun updateWith(updatedPaymentDetails: Bundle) {}
override fun paymentDetailsNotUpdated() {}
Java
@Override
public void updateWith(Bundle updatedPaymentDetails) {}
@Override
public void paymentDetailsNotUpdated() {}
updatedPaymentDetails 是與 PaymentRequestDetailsUpdate
WebIDL 字典對應的組合,包含下列選用鍵:
total- 包含currency和value鍵的組合,兩個鍵都有字串值shippingOptions- 運送選項的可打包陣列error- 包含一般錯誤訊息的字串 (例如changeShippingOption未提供有效的運送選項 ID 時)stringifiedPaymentMethodErrors- 代表付款方式驗證錯誤的 JSON 字串addressErrors- 組合,內含與 shipping address 相同的選用鍵和字串值。每個鍵代表與運送地址相應部分相關的驗證錯誤。modifiers- 一組可封送的 Bundle 陣列,每個 Bundle 都包含total和methodData欄位,這兩個欄位也是 Bundle。
如果缺少鍵,表示值未變更。