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

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

Sahel Sharify
Sahel Sharify

通过网络表单输入收货地址和联系信息 为客户带来繁琐的体验这可能会导致错误并降低转化率 。

正因如此,Payment Request API 支持请求运费的功能 地址和联系信息这样做有诸多好处:

  • 用户只需点按几下就能选择正确的地址。
  • 地址始终以标准化 格式
  • 提交错误的地址不太可能会发生。

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

Chrome 会尽可能委托客户收取运费 调用的 Android 付款应用的地址和联系信息。通过 委托可减少结账过程中的不便

商家网站可以动态更新运费选项和总价 具体取决于客户选择的送货地址和送货方式 选项。

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
正在更改配送选项和送货地址。查看它对运费选项和总价有何影响。

要为现有的 Android 付款应用添加委托支持,请按以下步骤操作: 实现以下步骤:

  1. 声明支持的委托
  2. 解析了所需付款的 PAY intent extra 选项
  3. 提供付款所需的信息 响应
  4. [可选] 支持动态流程: <ph type="x-smartling-placeholder">
      </ph>
    1. 通知商家用户所选付款方式的变化。 送货地址或送货地址 选项
    2. 从商家接收更新后的付款信息(例如 已根据所选运费选项调整了总金额 费用)

声明支持的委托

浏览器需要知道您的付款 以便其将收集该信息的权限委托给 应用。在应用的<meta-data> AndroidManifest.xml

<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 intent extra 以了解必需的付款方式

商家可以使用 paymentOptions 字典。Chrome 将提供一系列必需选项, 将以下参数作为 intent 传递给 PAY activity 来提供 extra

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 是商家指定运费的 Parcelable 数组 选项。只有当 paymentOptions.requestShipping == true 时,此参数才存在。

val shippingOptions: List<ShippingOption>? =
    extras.getParcelableArray("shippingOptions")?.mapNotNull {
        p -> from(p as Bundle)
    }

每个运费选项都是一个 Bundle,其中包含以下键。

  • id - 送货方式标识符。
  • label - 向用户显示的配送选项标签。
  • amount - 运费捆绑包包含 currencyvalue 键, 字符串值。
  • 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 activity。

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

  • payerName - 付款人的全名。在下列情况下,它应该是非空字符串 paymentOptions.requestPayerName 为 true。
  • payerPhone - 付款人的电话号码。在下列情况下,它应该是非空字符串 paymentOptions.requestPayerPhone 为 true。
  • payerEmail - 付款人的电子邮件地址。它应该是非空字符串 当 paymentOptions.requestPayerEmail 为 true 时。
  • shippingAddress - 用户提供的送货地址。它应该是 当 paymentOptions.requestShipping 为 true 时,非空 bundle。套装 应包含以下键,这些键分别代表物理 地址
    • city
    • countryCode
    • dependentLocality
    • organization
    • phone
    • postalCode
    • recipient
    • region
    • sortingCode
    • addressLineaddressLine 之外的所有键都具有字符串值。addressLine 是一个字符串数组。
  • shippingOptionId - 用户选择的运费选项的标识符。这个 当 paymentOptions.requestShipping 为 true 时,它应该是非空字符串。

验证付款响应

如果从调用的付款收到付款响应的活动结果 应用设置为 RESULT_OK,则 Chrome 会检查是否有必需的额外 。如果验证失败,Chrome 会返回“已拒绝” promise 中包含下列面向开发者的以下错误之一: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

如需通知商家有关新更改的信息,请使用PaymentDetailsUpdateService Chrome 的 AndroidManifest.xml 中声明的服务。要使用此服务,请创建两个 包含以下内容的 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
        }
    }
}

用于服务启动 intent 的 callingPackageName 可以具有下列其中一项: 以下值,具体取决于发起付款的浏览器 请求。

Chrome 渠道 软件包名称
稳定 "com.android.chrome"
测试版 "com.chrome.beta"
Dev "com.chrome.dev"
Canary 版 "com.chrome.canary"
Chromium "org.chromium.chrome"
Google 快速搜索框(一种 WebLayer 嵌入器) "com.google.android.googlequicksearchbox"

changePaymentMethod

在用户选择的付款方式发生变化时通知商家。通过 “paymentHandlerMethodData”套装包含“methodName”和可选的details 键和字符串值。Chrome 会检查是否有包含 非空 methodName,并发送 updatePaymentDetails,其中包含其中一个 通过 callback.updateWith 发送以下错误消息。

'Method data required.'
'Method name required.'

changeShippingOption

通知商家用户选择的配送选项的变化。 shippingOptionId 应该是某个商家指定的 运费选项。Chrome 将检查是否有非空 shippingOptionId 并发送 通过updatePaymentDetails 如果验证失败,则返回 callback.updateWith

'Shipping option identifier required.'

changeShippingAddress

通知商家用户提供的送货地址的变化。Chrome 浏览器 将检查是否有包含有效 countryCode 的非空 shippingAddress bundle 并通过updatePaymentDetails 如果验证失败,则返回 callback.updateWith

'Payment app returned invalid shipping address in response.'

“状态无效”错误消息

Chrome 在收到任何更改请求时遇到无效状态 它将调用 callback.updateWith,其中 updatePaymentDetails 已隐去 软件包。该 bundle 将仅包含具有 "Invalid state"error 键。 无效状态的示例如下:

  • Chrome 仍在等待商家对上一次更改做出的响应时 (例如进行中的更改事件)。
  • 付款应用提供的配送选项标识符不属于以下任何一项: 商家指定的运费选项。

从商家接收更新后的付款信息

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 字段),并包含以下可选键:

如果缺少键,则意味着该键的值未更改。