Proporciona información de contacto y de envío desde una app de pagos de Android

Cómo actualizar tu app de pagos para Android para proporcionar la dirección de envío y la información de contacto del pagador con las APIs de Web Payments

Sahel Sharify
Sahel Sharify

Fecha de publicación: 17 de julio de 2020; Última actualización: 27 de mayo de 2025

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 generar errores y una tasa de conversiones más baja.

Por eso, la API de Payment Request admite una función para solicitar la dirección de envío y la información de contacto. Esto ofrece 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 aplazar 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 llama 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 pago 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 dirección de envío y la opción de envío que elija el cliente.

Cambio de la opción y la dirección de envío en acción. Consulta cómo afecta de forma dinámica las opciones de envío y el precio total.

Para agregar compatibilidad con la delegación a una app de pagos para Android existente, implementa los siguientes pasos:

  1. Declara las delegaciones compatibles.
  2. Analiza los extras del intent PAY para las opciones de pago obligatorias.
  3. Proporciona la información requerida en la respuesta de pago.
  4. [Opcional] Admite flujo dinámico:
    1. Notifica 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.
    2. Recibir detalles de pago actualizados del comercio (por ejemplo, el importe total ajustado según el costo de la opción de envío seleccionada)

Cómo declarar delegaciones compatibles

El navegador necesita conocer la lista de información adicional que puede proporcionar tu app de pago para poder delegar la recopilación de esa información a tu app. Declara las delegaciones admitidas como <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/chromium_payment_supported_delegations" />
</activity>

android:resource debe apuntar a un <string-array> que contenga todos o un subconjunto de los siguientes valores:

  • 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="chromium_payment_supported_delegations">
    <item>payerEmail</item>
    <item>shippingAddress</item>
  </string-array>
</resources>

Analiza los extras del intent PAY para las opciones de pago requeridas

El comercio puede especificar información obligatoria adicional con el diccionario paymentOptions. Chrome proporcionará la lista de opciones requeridas que puede proporcionar tu app pasando los paymentOptions elementos adicionales del intent a la actividad PAY.

paymentOptions

paymentOptions es el subconjunto de opciones de pago especificadas por el comercio para las que tu app declaró compatibilidad con la delegación.

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");
}

Puede incluir los siguientes parámetros:

  • requestPayerName: Es el valor booleano que indica si se requiere o no el nombre del pagador.
  • requestPayerPhone: Es el valor booleano que indica si se requiere o no el teléfono del pagador.
  • requestPayerEmail: Es el valor booleano que indica si se requiere o no el correo electrónico del pagador.
  • 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 particionable de opciones de envío especificadas por el comercio. Este parámetro solo existirá cuando 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;
  }
}

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 la opción de envío que se muestra al usuario.
  • amount: Es el paquete de costos de envío que contiene las claves currency y value con valores de cadena.
    • currency muestra la moneda del costo de envío como un código alfabético de 3 letras bien formado según ISO4217.
    • 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, excepto selected, tienen valores de cadena. selected tiene un valor booleano.

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);

Proporciona la información obligatoria 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 cadena que no esté vacía cuando paymentOptions.requestPayerName sea verdadero.
  • payerPhone: Es el número de teléfono del pagador. Debe ser una cadena que no esté vacía cuando paymentOptions.requestPayerPhone sea verdadero.
  • payerEmail: Es la dirección de correo electrónico del pagador. Debe ser una cadena que no esté vacía cuando paymentOptions.requestPayerEmail sea verdadero.
  • shippingAddress: Es la dirección de envío que proporcionó el usuario. Debe ser un paquete que no esté vacío cuando paymentOptions.requestShipping sea verdadero. El paquete debe tener las siguientes claves que representan diferentes partes de una dirección física.
    • countryCode
    • postalCode
    • sortingCode
    • region
    • city
    • dependentLocality
    • addressLine
    • organization
    • recipient
    • phone Todas las claves, excepto addressLine, tienen valores de cadena. addressLine es un array de cadenas.
  • shippingOptionId: Es el identificador de la opción de envío que seleccionó el usuario. Esta debería ser una cadena que no esté vacía cuando paymentOptions.requestShipping sea verdadero.

Valida la respuesta de pago

Si el resultado de la actividad de una respuesta de pago recibida de la app de pago invocada se establece en RESULT_OK, Chrome buscará la información adicional requerida en sus elementos adicionales. Si la validación falla, Chrome mostrará una promesa rechazada de request.show() con uno de los siguientes mensajes de error para desarrolladores:

'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".'

El siguiente ejemplo de código es un ejemplo de una respuesta válida:

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;
}

Opcional: Admite el flujo dinámico

A veces, el costo total de una transacción aumenta, por ejemplo, cuando el usuario elige la opción de envío exprés o cuando cambia la lista de opciones de envío disponibles o sus precios cuando el usuario elige una dirección de envío internacional. Cuando tu app proporciona la dirección de envío o la opción que seleccionó el usuario, debería poder notificar al comercio sobre cualquier cambio en la dirección de envío o la opción, y mostrarle al usuario los detalles de pago actualizados (proporcionados por el comercio).

Para notificar al comercio sobre los cambios nuevos, implementa la interfaz IPaymentDetailsUpdateServiceCallback y declárala en tu AndroidManifest.xml con el filtro de intents UPDATE_PAYMENT_DETAILS.

Inmediatamente después de invocar el intent PAY, Chrome se conectará al servicio UPDATE_PAYMENT_DETAILS (si existe) en el mismo paquete que el intent PAY y llamará a setPaymentDetailsUpdateService(service) para proporcionarle a tu app de pagos el extremo IPaymentDetailsUpdateService para notificar sobre los cambios en la forma de pago, la opción de envío o la dirección de envío del usuario.

Usa packageManager.getPackagesForUid(Binder.getCallingUid()) cuando recibas la comunicación entre procesos (IPC) para validar que la app que invocó el intent PAY tenga el mismo nombre de paquete que la app que invocó los métodos IPaymentDetailsUpdateServiceCallback.

AIDL

Crea dos archivos AIDL con el siguiente contenido:

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);
}

Servicio

Implementa el servicio 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

Expón el servicio para IPaymentDetailsUpdateServiceCallback en tu AndroidManifest.xml.

<service
    android:name=".SampleUpdatePaymentDetailsCallbackService"
    android:exported="true">
    <intent-filter>
        <action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS" />
    </intent-filter>
</service>

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

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

Notifica al comercio sobre los cambios en la forma de pago que seleccionó el usuario. El paquete paymentHandlerMethodData contiene claves methodName y details opcionales, ambas con valores de cadena. Chrome buscará un paquete no vacío con un methodName no vacío y enviará un updatePaymentDetails con uno de los siguientes mensajes de error a través de callback.updateWith si la validación falla.

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

changeShippingOption

Notifica al comercio sobre 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 shippingOptionId no vacío y enviará un updatePaymentDetails con el siguiente mensaje de error a través de callback.updateWith si la validación falla.

'Shipping option identifier required.'

changeShippingAddress

Notifica al comercio sobre los cambios en la dirección de envío que proporcionó el usuario. Chrome verificará si hay un paquete shippingAddress no vacío con un countryCode válido y enviará un updatePaymentDetails con el siguiente mensaje de error a través de callback.updateWith si la validación falla.

'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 recibe alguna de las solicitudes de cambios, llamará a callback.updateWith con un paquete updatePaymentDetails ocultado. El paquete solo contendrá la clave error con "Invalid state". Estos son algunos ejemplos de estados no válidos:

  • Cuando Chrome aún 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 proporcionado por la app de pagos no pertenece a ninguna de las opciones de envío especificadas por el comercio.

Cómo recibir detalles de pago actualizados del comercio

Kotlin

override fun updateWith(updatedPaymentDetails: Bundle) {}

override fun paymentDetailsNotUpdated() {}

Java

@Override
public void updateWith(Bundle updatedPaymentDetails) {}

@Override
public void paymentDetailsNotUpdated() {}

updatedPaymentDetails es el paquete equivalente al diccionario WebIDL PaymentRequestDetailsUpdate y contiene las siguientes claves opcionales:

  • total: Es un paquete que contiene claves currency y value. 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., cuando changeShippingOption 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 la parte correspondiente de la dirección de envío.
  • modifiers: Es un array parcelable de paquetes, cada uno con un campo total y un campo methodData, que también son paquetes.

Una clave ausente significa que su valor no cambió.