透過 Android 付款應用程式提供運送和聯絡資訊

瞭解如何更新 Android 付款應用程式,透過 Web Payments API 提供運送地址和付款人聯絡資訊。

Sahel Sharify
Sahel Sharify

發布日期:2020 年 7 月 17 日,上次更新時間:2025 年 5 月 27 日

透過網路表單輸入運送地址和聯絡資訊,對消費者來說可能相當麻煩。這可能會導致錯誤,並降低轉換率。

因此,Payment Request API 支援要求運送地址和聯絡資訊的功能。這項功能有許多優點:

  • 使用者只要輕觸幾下,就能選取正確地址。
  • 地址一律會以標準化格式傳回。
  • 減少提交錯誤地址的機率。

瀏覽器可將運送地址和聯絡資訊的收集作業延後至付款應用程式,提供統一的付款體驗。這項功能稱為「委派」

Chrome 會盡可能將收集顧客運送地址和聯絡資訊的作業,委派給叫用的 Android 付款應用程式。這項委派作業可減少結帳過程中的阻礙。

商家網站可根據顧客選擇的運送地址和運送選項,動態更新運送選項和總價。

運送選項和運送地址變更作業正在進行中。查看這項功能如何動態影響運送選項和總價。

如要為現有的 Android 付款應用程式新增委派支援,請按照下列步驟操作:

  1. 宣告支援的委派
  2. 剖析 PAY 意圖額外資訊,取得必要付款選項
  3. 在付款回應中提供必要資訊
  4. 選用:支援動態流程
    1. 通知商家使用者選取的付款方式、運送地址或運送選項有變更
    2. 接收商家提供的最新付款詳細資料 (例如根據所選運送選項的費用調整總金額)

宣告支援的委派項目

瀏覽器需要知道付款應用程式可提供的額外資訊清單,才能將收集該資訊的作業委派給您的應用程式。請在應用程式的 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>,其中包含下列所有或部分值:

  • payerName
  • payerEmail
  • payerPhone
  • shippingAddress

以下範例只能提供運送地址和付款人的電子郵件地址。

<?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 extras 傳遞至 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 - 運費組合,包含 currencyvalue 鍵,以及字串值。
  • 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,則此值不得為空白套裝組合。這個組合應包含下列鍵,代表實體地址的不同部分。
    • countryCode
    • postalCode
    • sortingCode
    • region
    • city
    • dependentLocality
    • addressLine
    • organization
    • recipient
    • phone 除了 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。組合只會包含索引為 "Invalid state"error 鍵。無效狀態的例子包括:

  • 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 - 包含 currencyvalue 鍵的組合,兩個鍵都有字串值
  • shippingOptions - shipping options 的可封送陣列
  • error - 包含一般錯誤訊息的字串 (例如,當 changeShippingOption 未提供有效的運送選項 ID 時)
  • stringifiedPaymentMethodErrors:代表付款方式驗證錯誤的 JSON 字串
  • addressErrors - 組合,內含與 shipping address 相同的選用鍵和字串值。每個鍵都代表與運送地址相應部分相關的驗證錯誤。
  • modifiers - 包含多個可封送處理的 Bundle 的陣列,每個 Bundle 都含有 totalmethodData 欄位,這些欄位也是 Bundle。

如果缺少鍵,表示值未變更。