Cómo actualizar tu app de pagos de Android para proporcionar la dirección de envío y la información de contacto del pagador con las API de pagos web
Ingresar la dirección de envío y la información de contacto a través de un formulario web puede ser una experiencia engorrosa para los clientes. Puede provocar errores y un porcentaje de conversiones más bajo.
Es por eso que la API de Payment Request admite una función para solicitar la dirección de envío y la información de contacto. Esto proporciona varios beneficios:
- Los usuarios pueden elegir la dirección correcta con solo unos toques.
- La dirección siempre se muestra en el formato estandarizado.
- Es menos probable que envíes una dirección incorrecta.
Los navegadores pueden diferir la recopilación de la dirección de envío y la información de contacto a una app de pagos para proporcionar una experiencia de pago unificada. Esta funcionalidad se denomina delegación.
Siempre que sea posible, Chrome delega la recopilación de la dirección de envío y la información de contacto de un cliente a la app de pagos de Android invocada. La delegación reduce los inconvenientes durante la confirmación de la compra.
El sitio web del comercio puede actualizar de forma dinámica las opciones de envío y el precio total según la elección del cliente de la dirección y la opción de envío.
Para agregar compatibilidad con la delegación a una app de pagos de Android existente, implementa los siguientes pasos:
- Declara las delegaciones admitidas.
- Analiza los extras de intent
PAY
para las opciones de pago requeridas. - Proporciona la información necesaria en la respuesta de pago.
- [Opcional] Compatibilidad con el flujo dinámico:
Declara delegaciones admitidas
El navegador debe conocer la lista de información adicional que puede proporcionar tu app de pagos para poder delegar la recopilación de esa información a la app. Declara las delegaciones compatibles como un <meta-data>
en el archivo AndroidManifest.xml de tu app.
<activity
android:name=".PaymentActivity"
…
<meta-data
android:name="org.chromium.payment_supported_delegations"
android:resource="@array/supported_delegations" />
</activity>
<resource>
debe ser una lista de cadenas elegidas entre los siguientes valores válidos:
[ "payerName", "payerEmail", "payerPhone", "shippingAddress" ]
En el siguiente ejemplo, solo se puede proporcionar una dirección de envío y la dirección de correo electrónico del pagador.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="supported_delegations">
<item>payerEmail</item>
<item>shippingAddress</item>
</string-array>
</resources>
Analiza los extras de intent PAY
para las opciones de pago requeridas
El comercio puede especificar la información obligatoria adicional con el diccionario paymentOptions
. Chrome proporcionará la lista de opciones obligatorias que tu app puede proporcionar pasando los siguientes parámetros a la actividad PAY
como adicionales de intent.
paymentOptions
paymentOptions
es el subconjunto de opciones de pago especificadas por el comercio para las que
tu app declaró la compatibilidad con la delegación.
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")
Puede incluir los siguientes parámetros:
requestPayerName
: Es un valor booleano que indica si el nombre del pagador es obligatorio o no.requestPayerPhone
: Es un valor booleano que indica si es necesario o no el teléfono del pagador.requestPayerEmail
: Es un valor booleano que indica si el correo electrónico del pagador es obligatorio o no.requestShipping
: Es el valor booleano que indica si se requiere o no la información de envío.shippingType
: Es la cadena que muestra el tipo de envío. El tipo de envío puede ser"shipping"
,"delivery"
o"pickup"
. Tu app puede usar esta sugerencia en su IU cuando solicite la dirección del usuario o la elección de opciones de envío.
shippingOptions
shippingOptions
es el array parcelable de las opciones de envío especificadas por el comercio. Este parámetro solo existirá cuando paymentOptions.requestShipping ==
true
.
val shippingOptions: List<ShippingOption>? =
extras.getParcelableArray("shippingOptions")?.mapNotNull {
p -> from(p as Bundle)
}
Cada opción de envío es un Bundle
con las siguientes claves.
id
: Es el identificador de la opción de envío.label
: Es la etiqueta de opción de envío que se muestra al usuario.amount
: Es el paquete de costo de envío que contiene las clavescurrency
yvalue
con valores de cadena.currency
muestra la moneda del costo de envío como un código alfabético de 3 letras ISO4217 bien formado.value
muestra el valor del costo de envío como un valor monetario decimal válido.
selected
: Indica si se debe seleccionar o no la opción de envío cuando la app de pagos muestra las opciones de envío.
Todas las claves que no sean selected
tienen valores de cadena. selected
tiene un valor booleano.
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)
Proporciona la información necesaria en una respuesta de pago
Tu app debe incluir la información adicional requerida en su respuesta a la actividad PAY
.
Para ello, se deben especificar los siguientes parámetros como extras de intent:
payerName
: Es el nombre completo del pagador. Debe ser una string que no esté vacía cuandopaymentOptions.requestPayerName
es verdadero.payerPhone
: Es el número de teléfono del pagador. Debe ser una string que no esté vacía cuandopaymentOptions.requestPayerPhone
es verdadero.payerEmail
: Es la dirección de correo electrónico del pagador. Debe ser una string que no esté vacía cuandopaymentOptions.requestPayerEmail
sea verdadero.shippingAddress
: Es la dirección de envío proporcionada por el usuario. Este debe ser un paquete no vacío cuandopaymentOptions.requestShipping
es verdadero. El paquete debe tener las siguientes claves, que representan diferentes partes de una dirección física.city
countryCode
dependentLocality
organization
phone
postalCode
recipient
region
sortingCode
addressLine
Todas las claves que no seanaddressLine
tienen valores de cadena. LaaddressLine
es un array de cadenas.
shippingOptionId
: Es el identificador de la opción de envío que seleccionó el usuario. Debe ser una string que no esté vacía cuandopaymentOptions.requestShipping
es verdadero.
Valida la respuesta de pago
Si el resultado de la actividad de una respuesta de pago recibida de la app de pagos invocada se establece en RESULT_OK
, Chrome buscará en los extras la información adicional requerida. Si la validación falla, Chrome mostrará una promesa
rechazada de request.show()
con uno de los siguientes mensajes de error
para el desarrollador:
'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".'
La siguiente muestra de código es un ejemplo de una respuesta válida:
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")
}
}
Opcional: Compatibilidad con el flujo dinámico
A veces, aumenta el costo total de una transacción, por ejemplo, cuando el usuario elige la opción de envío exprés, o cuando la lista de opciones de envío disponibles o sus precios cambian cuando el usuario elige una dirección de envío internacional. Cuando tu app proporciona la opción o dirección de envío seleccionada por el usuario, debería poder notificar al comercio sobre cualquier cambio en la dirección o en la opción de envío y mostrarle al usuario los detalles del pago actualizados (proporcionados por el comercio).
AIDL
Para notificar al comercio sobre los cambios nuevos, usa el servicio PaymentDetailsUpdateService
declarado en el archivo AndroidManifest.xml de Chrome. Para usar este servicio, crea dos archivos AIDL con el siguiente contenido:
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);
}
Notificar al comercio sobre los cambios en la forma de pago, la dirección de envío o la opción de envío que seleccionó el usuario
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
}
}
}
El callingPackageName
que se usa para el intent de inicio del servicio puede tener uno de los siguientes valores según el navegador que inició la solicitud de pago.
Canal de Chrome | Nombre del paquete |
---|---|
Estable |
"com.android.chrome"
|
Beta |
"com.chrome.beta"
|
Desarrollo |
"com.chrome.dev"
|
Canary |
"com.chrome.canary"
|
Chromium |
"org.chromium.chrome"
|
Cuadro de búsqueda rápida de Google (un insertador de WebLayer) |
"com.google.android.googlequicksearchbox"
|
changePaymentMethod
Notifica al comercio acerca de los cambios en la forma de pago seleccionada por el usuario. El paquete paymentHandlerMethodData
contiene claves methodName
y details
opcionales, ambas con valores de cadena. Chrome verificará si hay un paquete que no esté vacío con un
methodName
que no esté vacío y enviará un updatePaymentDetails
con uno de los
siguientes mensajes de error a través de callback.updateWith
si falla la validación.
'Method data required.'
'Method name required.'
changeShippingOption
Notifica al comercio acerca de los cambios en la opción de envío que seleccionó el usuario.
shippingOptionId
debe ser el identificador de una de las opciones de envío
especificadas por el comercio. Chrome buscará un objeto shippingOptionId
que no esté vacío y enviará
una updatePaymentDetails
con el siguiente mensaje de error a través de
callback.updateWith
si falla la validación.
'Shipping option identifier required.'
changeShippingAddress
Notifica al comercio sobre los cambios en la dirección de envío que proporcionó el usuario. Chrome buscará un paquete shippingAddress
que no esté vacío con un countryCode
válido y enviará un updatePaymentDetails
con el siguiente mensaje de error a través de callback.updateWith
si falla la validación.
'Payment app returned invalid shipping address in response.'
Mensaje de error de estado no válido
Si Chrome encuentra un estado no válido cuando reciba cualquiera de las solicitudes de cambio, llamará a callback.updateWith
con un paquete updatePaymentDetails
oculto. El paquete solo contendrá la clave error
con "Invalid state"
.
Estos son algunos ejemplos de estado no válido:
- Cuando Chrome todavía esté esperando la respuesta del comercio a un cambio anterior (como un evento de cambio en curso)
- El identificador de la opción de envío que proporciona la app de pago no pertenece a ninguna de las opciones de envío especificadas por el comercio.
Recibir los detalles del pago actualizados del comercio
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
es el paquete equivalente al diccionario
PaymentRequestDetailsUpdate
WebIDL (después de ocultar el campo
modifiers
) y contiene las siguientes claves opcionales:
total
: Es un paquete que contiene las clavescurrency
yvalue
. Ambas claves tienen valores de cadena.shippingOptions
: Es el array parcelable de opciones de envío.error
: Es una cadena que contiene un mensaje de error genérico (p. ej., cuandochangeShippingOption
no proporciona un identificador de opción de envío válido).stringifiedPaymentMethodErrors
: Es una cadena JSON que representa errores de validación para la forma de pago.addressErrors
: Es un paquete con claves opcionales idénticas a shipping address y valores de cadena. Cada clave representa un error de validación relacionado con su parte correspondiente de la dirección de envío.
Si una clave está ausente, significa que su valor no ha cambiado.