通过 Android 付款应用提供送货和联系信息

如何更新 Android 付款应用,以便使用 Web Payments API 提供送货地址和付款方的联系信息。

Sahel Sharify
Sahel Sharify

发布时间:2020 年 7 月 17 日;上次更新时间:2025 年 5 月 27 日

通过网络表单输入送货地址和联系信息对客户来说可能很麻烦。这可能会导致错误并降低转化率。

因此,Payment Request API 支持请求送货地址和联系信息的功能。这有诸多好处:

  • 用户只需点按几次,即可选择正确的地址。
  • 地址始终以标准格式返回。
  • 提交错误地址的可能性较小。

浏览器可以将送货地址和联系信息的收集工作推迟到付款应用,以提供统一的付款体验。此功能称为委托

Chrome 会尽可能将收集客户送货地址和联系信息的任务委托给调用的 Android 付款应用。这种委托可减少结账过程中的摩擦。

商家网站可以根据客户选择的送货地址和配送方式,动态更新配送选项和总价。

运费选项和送货地址更改操作。查看它对配送方式和总价的动态影响。

如需向现有的 Android 付款应用添加委托支持,请实现以下步骤:

  1. 声明支持的委托
  2. 解析 PAY intent extra 以获取必需的付款方式
  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 intent extra 以获取所需的付款方式

商家可以使用 paymentOptions 字典指定其他必需信息。Chrome 会通过将 paymentOptions intent extra 传递给 PAY activity,提供您的应用可以提供的必需选项列表。

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"。当应用请求用户提供地址或选择配送方式时,可以在界面中使用此提示。

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 - 运费选项标识符。
  • label - 向用户显示的配送方式标签。
  • amount - 包含 currencyvalue 键(值为字符串)的运费软件包。
    • currency 显示运费的币种,以 ISO4217 格式良好的 3 个字母的字母代码表示
    • 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 activity 的响应中包含所需的其他信息。

为此,必须将以下参数指定为 intent extra:

  • 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
    • phoneaddressLine 之外的所有键都有字符串值。addressLine 是字符串数组。
  • shippingOptionId - 用户选择的配送方式的标识符。如果 paymentOptions.requestShipping 为 true,则此值应为非空字符串。

验证付款响应

如果从调用的付款应用收到的付款响应的 activity 结果设为 RESULT_OK,则 Chrome 会在其 extras 中检查是否有必要的其他信息。如果验证失败,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 intent 过滤器声明该接口。

调用 PAY intent 后,Chrome 会立即连接到与 PAY intent 在同一软件包中的 UPDATE_PAYMENT_DETAILS 服务(如果存在),并调用 setPaymentDetailsUpdateService(service) 以向您的付款应用提供 IPaymentDetailsUpdateService 端点,以便在用户的付款方式、配送方式或送货地址发生变化时进行通知。

在接收进程间通信 (IPC) 时,使用 packageManager.getPackagesForUid(Binder.getCallingUid()) 来验证调用了 PAY intent 的应用与调用了 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 应为商家指定的配送方式之一的标识符。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 仍在等待商家对之前的更改(例如正在进行的更改事件)做出回应。
  • 付款应用提供的配送方式标识符不属于商家指定的任何配送方式。

接收商家提供的更新版付款信息

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 - 配送方式的 Parcelable 数组
  • error - 包含通用错误消息的字符串(例如,当 changeShippingOption 未提供有效的配送方式标识符时)
  • stringifiedPaymentMethodErrors - 表示付款方式验证错误的 JSON 字符串
  • addressErrors - 一个软件包,其中包含与送货地址相同的可选键和字符串值。每个键都代表与送货地址的相应部分相关的验证错误。
  • modifiers - 一个由 Bundle 组成的 Parcelable 数组,每个 Bundle 都包含一个 total 和一个 methodData 字段,这两个字段也是 Bundle。

键不存在表示其值未更改。