Fornecer informações de frete e contato de um app de pagamento Android

Como atualizar seu app de pagamento para Android e fornecer endereço de entrega e informações de contato do pagador com as APIs Web Payments.

Sahel Sharify
Sahel Sharify

Publicação: 17 de julho de 2020, última atualização: 27 de maio de 2025

Inserir o endereço de entrega e as informações de contato em um formulário da Web pode ser uma experiência complicada para os clientes. Isso pode causar erros e diminuir a taxa de conversão.

Por isso, a API Payment Request oferece suporte a um recurso para solicitar o endereço de entrega e as informações de contato. Isso oferece vários benefícios:

  • Os usuários podem escolher o endereço certo com apenas alguns toques.
  • O endereço é sempre retornado no formato padronizado.
  • É menos provável que você envie um endereço incorreto.

Os navegadores podem adiar a coleta de endereço de entrega e informações de contato para um app de pagamento e oferecer uma experiência unificada. Essa funcionalidade é chamada de delegação.

Sempre que possível, o Chrome delega a coleta do endereço de entrega e das informações de contato de um cliente ao app de pagamento do Android invocado. A delegação reduz o atrito durante a finalização da compra.

O site do comerciante pode atualizar dinamicamente as opções de envio e o preço total dependendo da escolha do cliente em relação ao endereço e à opção de entrega.

Opção de frete e mudança de endereço de entrega em ação. Veja como isso afeta as opções de envio e o preço total de forma dinâmica.

Para adicionar suporte à delegação a um app de pagamentos Android já existente, siga estas etapas:

  1. Declare delegações compatíveis.
  2. Analise os extras de intent PAY para opções de pagamento obrigatórias.
  3. Forneça as informações necessárias na resposta de pagamento.
  4. Opcional: suporte ao fluxo dinâmico:
    1. Notifique o comerciante sobre mudanças na forma de pagamento selecionada pelo usuário, no endereço de entrega ou na opção de envio.
    2. Receber detalhes de pagamento atualizados do comerciante (por exemplo, o valor total ajustado com base no custo da opção de envio selecionada).

Declarar delegações compatíveis

O navegador precisa saber a lista de informações adicionais que seu app de pagamento pode fornecer para delegar a coleta dessas informações ao seu app. Declare as delegações compatíveis como um <meta-data> no AndroidManifest.xml do app.

<activity
  android:name=".PaymentActivity"
    <meta-data
    android:name="org.chromium.payment_supported_delegations"
    android:resource="@array/chromium_payment_supported_delegations" />
</activity>

android:resource precisa apontar para um <string-array> que contenha todos ou um subconjunto dos seguintes valores:

  • payerName
  • payerEmail
  • payerPhone
  • shippingAddress

O exemplo a seguir só pode fornecer um endereço de entrega e o endereço de e-mail do 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>

Analisar extras de intent PAY para opções de pagamento obrigatórias

O comerciante pode especificar outras informações obrigatórias usando o dicionário paymentOptions. O Chrome vai fornecer a lista de opções necessárias que seu app pode oferecer transmitindo os paymentOptions extras de intent para a atividade PAY.

paymentOptions

paymentOptions é o subconjunto de opções de pagamento especificadas pelo comerciante para as quais seu app declarou suporte à delegação.

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

Ele pode incluir os seguintes parâmetros:

  • requestPayerName: o booleano que indica se o nome do pagador é obrigatório ou não.
  • requestPayerPhone: o booleano que indica se o telefone do pagador é obrigatório ou não.
  • requestPayerEmail: o booleano que indica se o e-mail do pagador é obrigatório.
  • requestShipping: o booleano que indica se as informações de frete são necessárias ou não.
  • shippingType: a string que mostra o tipo de frete. O tipo de frete pode ser "shipping", "delivery" ou "pickup". Seu app pode usar essa dica na interface ao pedir o endereço do usuário ou a escolha de opções de frete.

shippingOptions

shippingOptions é a matriz parcelável de opções de frete especificadas pelo comerciante. Esse parâmetro só vai existir quando 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 opção de frete é um Bundle com as seguintes chaves.

  • id: o identificador da opção de frete.
  • label: o rótulo da opção de frete mostrado ao usuário.
  • amount: o pacote de custo de frete que contém as chaves currency e value com valores de string.
  • selected: indica se a opção de frete deve ser selecionada quando o app de pagamento mostra as opções de frete.

Todas as chaves, exceto selected, têm valores de string. selected tem um 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);

Fornecer as informações necessárias em uma resposta de pagamento

O app precisa incluir as informações adicionais necessárias na resposta à atividade de PAY.

Para isso, os seguintes parâmetros precisam ser especificados como extras de intent:

  • payerName: o nome completo do pagador. Precisa ser uma string não vazia quando paymentOptions.requestPayerName for verdadeiro.
  • payerPhone: o número de telefone do pagador. Precisa ser uma string não vazia quando paymentOptions.requestPayerPhone for verdadeiro.
  • payerEmail: o endereço de e-mail do pagador. Precisa ser uma string não vazia quando paymentOptions.requestPayerEmail for verdadeiro.
  • shippingAddress: o endereço de entrega fornecido pelo usuário. Precisa ser um pacote não vazio quando paymentOptions.requestShipping for verdadeiro. O pacote precisa ter as seguintes chaves, que representam diferentes partes de um endereço físico.
    • countryCode
    • postalCode
    • sortingCode
    • region
    • city
    • dependentLocality
    • addressLine
    • organization
    • recipient
    • phone Todas as chaves, exceto addressLine, têm valores de string. O addressLine é uma matriz de strings.
  • shippingOptionId: o identificador da opção de envio selecionada pelo usuário. Precisa ser uma string não vazia quando paymentOptions.requestShipping é verdadeiro.

Validar resposta de pagamento

Se o resultado da atividade de uma resposta de pagamento recebida do app de pagamento invocado for definido como RESULT_OK, o Chrome vai verificar se há informações adicionais necessárias nos extras. Se a validação falhar, o Chrome vai retornar uma promessa rejeitada de request.show() com uma das seguintes mensagens de erro para desenvolvedores:

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

Confira a seguir um exemplo de resposta 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: oferecer suporte ao fluxo dinâmico

Às vezes, o custo total de uma transação aumenta, como quando o usuário escolhe a opção de frete expresso ou quando a lista de opções de frete disponíveis ou os preços mudam quando o usuário escolhe um endereço de entrega internacional. Quando o app fornece o endereço ou a opção de entrega selecionada pelo usuário, ele precisa notificar o comerciante sobre qualquer mudança no endereço ou na opção de entrega e mostrar ao usuário os detalhes de pagamento atualizados (fornecidos pelo comerciante).

Para notificar o comerciante sobre novas mudanças, implemente a interface IPaymentDetailsUpdateServiceCallback e declare-a no seu AndroidManifest.xml com o filtro de intent UPDATE_PAYMENT_DETAILS.

Imediatamente após invocar a intent PAY, o Chrome se conectará ao serviço UPDATE_PAYMENT_DETAILS (se ele existir) no mesmo pacote da intent PAY e chamará setPaymentDetailsUpdateService(service) para fornecer ao app de pagamento o endpoint IPaymentDetailsUpdateService e notificar sobre mudanças na forma de pagamento, na opção de frete ou no endereço de entrega do usuário.

Use packageManager.getPackagesForUid(Binder.getCallingUid()) ao receber comunicação entre processos (IPC, na sigla em inglês) para validar se o app que invocou a intent PAY tem o mesmo nome de pacote do app que invocou os métodos IPaymentDetailsUpdateServiceCallback.

AIDL

Crie dois arquivos AIDL com o seguinte conteúdo:

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

Serviço

Implemente o serviço 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

Exponha o serviço para IPaymentDetailsUpdateServiceCallback no 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 o comerciante sobre mudanças na forma de pagamento, no endereço de entrega ou na opção de envio selecionados pelo usuário

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 o comerciante sobre mudanças na forma de pagamento selecionada pelo usuário. O pacote paymentHandlerMethodData contém methodName e chaves details opcionais, ambas com valores de string. O Chrome vai verificar se há um pacote não vazio com um methodName não vazio e enviar um updatePaymentDetails com uma das seguintes mensagens de erro usando callback.updateWith se a validação falhar.

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

changeShippingOption

Notifica o comerciante sobre mudanças na opção de envio selecionada pelo usuário. shippingOptionId precisa ser o identificador de uma das opções de frete especificadas pelo comerciante. O Chrome vai verificar se há um shippingOptionId não vazio e enviar um updatePaymentDetails com a seguinte mensagem de erro usando callback.updateWith se a validação falhar.

'Shipping option identifier required.'

changeShippingAddress

Notifica o comerciante sobre mudanças no endereço de entrega fornecido pelo usuário. O Chrome vai verificar um pacote shippingAddress não vazio com um countryCode válido e enviar um updatePaymentDetails com a seguinte mensagem de erro usando callback.updateWith se a validação falhar.

'Payment app returned invalid shipping address in response.'

Mensagem de erro de estado inválido

Se o Chrome encontrar um estado inválido ao receber qualquer uma das solicitações de mudança, ele vai chamar callback.updateWith com um pacote updatePaymentDetails redigido. O pacote vai conter apenas a chave error com "Invalid state". Exemplos de estados inválidos:

  • Quando o Chrome ainda está aguardando a resposta da loja a uma mudança anterior (como um evento de mudança em andamento).
  • O identificador da opção de envio fornecido pelo app de pagamento não pertence a nenhuma das opções de envio especificadas pelo comerciante.

Receber detalhes de pagamento atualizados do comerciante

Kotlin

override fun updateWith(updatedPaymentDetails: Bundle) {}

override fun paymentDetailsNotUpdated() {}

Java

@Override
public void updateWith(Bundle updatedPaymentDetails) {}

@Override
public void paymentDetailsNotUpdated() {}

updatedPaymentDetails é o equivalente do pacote ao dicionário PaymentRequestDetailsUpdate WebIDL e contém as seguintes chaves opcionais:

  • total: um pacote que contém as chaves currency e value. Ambas têm valores de string.
  • shippingOptions: a matriz parcelable de opções de frete
  • error: uma string que contém uma mensagem de erro genérica (por exemplo, quando changeShippingOption não fornece um identificador de opção de frete válido).
  • stringifiedPaymentMethodErrors: uma string JSON que representa erros de validação do método de pagamento.
  • addressErrors: um pacote com chaves opcionais idênticas a shipping address e valores de string. Cada chave representa um erro de validação relacionado à parte correspondente do endereço de entrega.
  • modifiers: uma matriz parcelável de pacotes, cada um com um campo total e methodData, que também são pacotes.

Uma chave ausente significa que o valor dela não mudou.