如何更新 Android 支付应用,以通过 Web Payments API 提供送货地址和付款人的联系信息。
发布时间:2020 年 7 月 17 日;上次更新时间:2025 年 5 月 27 日
通过网页表单输入送货地址和联系信息对客户来说可能是一项繁琐的任务。这可能会导致错误并降低转化率。
因此,Payment Request API 支持请求送货地址和联系信息的功能。这具有多项优势:
- 用户只需点按几下,即可选择正确的地址。
- 系统始终以标准化格式返回地址。
- 提交的地址不太可能不正确。
浏览器可以将收集送货地址和联系信息的任务延迟到付款应用中执行,以提供统一的付款体验。此功能称为委托。
在可能的情况下,Chrome 会将收集客户的送货地址和联系信息的任务委托给调用的 Android 付款应用。这种委托可减少结账过程中的摩擦。
商家网站可以根据客户选择的送货地址和送货选项动态更新运费选项和总价。
如需向现有的 Android 付款应用添加委托支持,请执行以下步骤:
声明支持的委托
浏览器需要知道您的付款应用可以提供的其他信息列表,以便将这些信息的收集工作委托给您的应用。在应用的 AndroidManifest.xml 中将支持的委托声明为 <meta-data>。
<activity
android:name=".PaymentActivity&qu<ot;
…
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" encodin>g<="ut>f-8<"?
resources
string-array name="chromium_payme>nt_su<ppor>ted_delega<tions>"<;
> itempayerEmai&l>t;l/item
< ite>mshippin<gAddress/i>tem
/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 (i&&t != null it instanceof Bundle) {
Bundle shippingOption = (Bundle) it;
}
}
每种配送选项都是一个 Bundle,包含以下键。
id- 配送选项标识符。label- 向用户显示的配送选项标签。amount- 包含currency和value键(具有字符串值)的运费 bundle。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,则此值应为非空软件包。该软件包应具有以下键,这些键表示实体地址的不同部分。countryCodepostalCodesortingCoderegioncitydependentLocalityaddressLineorganizationrecipientphone除addressLine之外的所有键都具有字符串值。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.pu<tStrin>g("countryCode",< ">;CA")
val addressLines: ArrayString =
arrayOfString("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:expor>ted=&<quot;true&quo>t;
in<tent-filter
action android:name="org.chromium.intent.action.>UPDAT<E_PAYMENT_DETA>I<LS"> /
/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 将检查是否存在非空 bundle 和非空 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 bundle 调用 callback.updateWith。该软件包将仅包含 error 键,其值为 "Invalid state"。无效状态的示例包括:
- 当 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- 包含currency和value键的软件包,这两个键都具有字符串值shippingOptions- 配送选项的可封装数组error- 包含一般错误消息的字符串(例如,当changeShippingOption未提供有效的配送选项标识符时)stringifiedPaymentMethodErrors- 表示支付方式验证错误的 JSON 字符串addressErrors- 一个具有可选键的软件包,这些键与送货地址相同,并且具有字符串值。每个键都表示与其对应的送货地址部分相关的验证错误。modifiers- 一个 Parcelable 数组,包含多个 Bundle,每个 Bundle 都包含total和methodData字段,这两个字段也是 Bundle。
如果某个键不存在,则表示相应的值未发生变化。