Fornire dati di contatto e di spedizione da un'app per pagamenti Android

Come aggiornare l'app per i pagamenti Android in modo da fornire l'indirizzo di spedizione e le informazioni di contatto del pagatore con le API dei pagamenti web.

Sahel Sharify
Sahel Sharify

Inserire l'indirizzo di spedizione e i dati di contatto tramite un modulo web può essere un'esperienza difficile per i clienti. Può causare errori e ridurre il tasso di conversione.

L'API Payment Request supporta una funzionalità per richiedere l'indirizzo di spedizione e i dati di contatto. Questo offre diversi vantaggi:

  • Gli utenti possono scegliere l'indirizzo giusto con pochi tocchi.
  • L'indirizzo viene sempre restituito nel formato standardizzato.
  • È meno probabile che tu invii un indirizzo errato.

I browser possono posticipare la raccolta dell'indirizzo di spedizione e dei dati di contatto a un'app per i pagamenti per fornire un'esperienza di pagamento unificata. Questa funzionalità è chiamata delega.

Quando possibile, Chrome delega la raccolta dell'indirizzo di spedizione e delle informazioni di contatto di un cliente all'app per pagamenti Android richiamata. La delega riduce gli ostacoli durante il pagamento.

Il sito web del commerciante può aggiornare in modo dinamico le opzioni di spedizione e il prezzo totale a seconda della scelta del cliente in merito all'indirizzo di spedizione e all'opzione di spedizione.

Modifica dell'opzione di spedizione e dell'indirizzo di spedizione in azione. Scopri come influisce sulle opzioni di spedizione e sul prezzo totale in modo dinamico.

Per aggiungere il supporto della delega a un'app per pagamenti Android già esistente, implementa i seguenti passaggi:

  1. Dichiarare le deleghe supportate.
  2. Analizza gli extra per l'intent PAY per le opzioni di pagamento richieste.
  3. Fornisci le informazioni richieste nella risposta al pagamento.
  4. [Facoltativo] Supporto del flusso dinamico:
    1. Informa il commerciante delle modifiche relative al metodo di pagamento, all'indirizzo di spedizione o all'opzione di spedizione selezionati dall'utente.
    2. Ricevere dal commerciante i dettagli di pagamento aggiornati (ad esempio, l'importo totale rettificato in base al costo dell'opzione di spedizione selezionata).

Dichiara le deleghe supportate

Il browser deve conoscere l'elenco di informazioni aggiuntive che la tua app di pagamento può fornire per poter delegare la raccolta di queste informazioni alla tua app. Dichiara le deleghe supportate come <meta-data> nel file AndroidManifest.xml della tua app.

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

<resource> deve essere un elenco di stringhe scelte tra i seguenti valori validi:

[ "payerName", "payerEmail", "payerPhone", "shippingAddress" ]

L'esempio seguente può fornire solo un indirizzo di spedizione e l'indirizzo email del pagatore.

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string-array name="supported_delegations">
    <item>payerEmail</item>
    <item>shippingAddress</item>
  </string-array>
</resources>

Analizza PAY extra per intenzione per le opzioni di pagamento richieste

Il commerciante può specificare ulteriori informazioni richieste utilizzando il dizionario paymentOptions. Chrome fornirà l'elenco delle opzioni obbligatorie che la tua app può fornire passando i seguenti parametri all'attività PAY come elementi aggiuntivi per intent.

paymentOptions

paymentOptions è il sottoinsieme di opzioni di pagamento specificate dal commerciante per cui la tua app ha dichiarato il supporto della delega.

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

Può includere i seguenti parametri:

  • requestPayerName: il valore booleano che indica se il nome del pagatore è obbligatorio o meno.
  • requestPayerPhone: il valore booleano che indica se è richiesto o meno il telefono del pagatore.
  • requestPayerEmail: il valore booleano che indica se l'indirizzo email del pagatore è obbligatorio o meno.
  • requestShipping. Il valore booleano che indica se sono necessarie o meno informazioni di spedizione.
  • shippingType: la stringa che mostra il tipo di spedizione. Il tipo di spedizione può essere "shipping", "delivery" o "pickup". La tua app può usare questo suggerimento nell'UI quando chiedi l'indirizzo dell'utente o la scelta delle opzioni di spedizione.

shippingOptions

shippingOptions è l'array packing delle opzioni di spedizione specificate dal commerciante. Questo parametro esiste solo quando paymentOptions.requestShipping == true.

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

Ogni opzione di spedizione è un Bundle con le seguenti chiavi.

  • id: l'identificatore dell'opzione di spedizione.
  • label: l'etichetta dell'opzione di spedizione mostrata all'utente.
  • amount: il pacchetto del costo di spedizione contenente le chiavi currency e value con valori stringa.
    • currency mostra la valuta del costo di spedizione, espressa sotto forma di codice alfabetico di tre lettere ISO4217 ben formato
    • value mostra il valore del costo di spedizione sotto forma di valore monetario decimale valido
  • selected - Indica se selezionare o meno l'opzione di spedizione quando l'app per i pagamenti mostra le opzioni di spedizione.

Tutte le chiavi diverse da selected hanno valori stringa. selected ha un valore 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)

Fornire le informazioni richieste in una risposta al pagamento

La tua app deve includere le informazioni aggiuntive richieste nella sua risposta all'attività PAY.

A questo scopo, è necessario specificare i seguenti parametri come extra di intent:

  • payerName: nome completo del pagatore. Deve essere una stringa non vuota quando paymentOptions.requestPayerName è true.
  • payerPhone: numero di telefono di chi paga. Deve essere una stringa non vuota quando paymentOptions.requestPayerPhone è true.
  • payerEmail: l'indirizzo email del pagatore. Deve essere una stringa non vuota quando paymentOptions.requestPayerEmail è true.
  • shippingAddress: l'indirizzo di spedizione fornito dall'utente. Deve essere un bundle non vuoto quando paymentOptions.requestShipping è true. Il bundle deve avere le seguenti chiavi, che rappresentano parti diverse di un indirizzo fisico.
    • city
    • countryCode
    • dependentLocality
    • organization
    • phone
    • postalCode
    • recipient
    • region
    • sortingCode
    • addressLine Tutte le chiavi diverse da addressLine hanno valori stringa. addressLine è un array di stringhe.
  • shippingOptionId: l'identificatore dell'opzione di spedizione selezionata dall'utente. Deve essere una stringa non vuota quando paymentOptions.requestShipping è true.

Convalida la risposta al pagamento

Se il risultato dell'attività di una risposta di pagamento ricevuta dall'app di pagamento richiamata è impostato su RESULT_OK, Chrome controllerà le informazioni aggiuntive richieste nei suoi extra. Se la convalida non va a buon fine, Chrome restituirà una promessa rifiutata da request.show() con uno dei seguenti messaggi di errore rivolti agli sviluppatori:

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

Il seguente esempio di codice è un esempio di risposta valida:

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

(Facoltativo) Supporto del flusso dinamico

A volte il costo totale di una transazione aumenta, ad esempio quando l'utente sceglie l'opzione di spedizione espressa oppure quando l'elenco delle opzioni di spedizione disponibili o i relativi prezzi cambiano quando l'utente sceglie un indirizzo di spedizione internazionale. Quando la tua app fornisce l'opzione o l'indirizzo di spedizione selezionati dall'utente, deve essere in grado di comunicare al commerciante eventuali modifiche all'indirizzo di spedizione o all'opzione e mostrare all'utente i dettagli di pagamento aggiornati (forniti dal commerciante).

AIDL

Per informare il commerciante delle nuove modifiche, utilizza il servizio PaymentDetailsUpdateService dichiarato nel file AndroidManifest.xml di Chrome. Per utilizzare questo servizio, crea due file AIDL con i contenuti seguenti:

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

Avvisa il commerciante in merito a modifiche relative al metodo di pagamento, all'indirizzo di spedizione o all'opzione di spedizione selezionati dall'utente

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

Il callingPackageName utilizzato per l'intent di avvio del servizio può avere uno dei seguenti valori, a seconda del browser che ha avviato la richiesta di pagamento.

Canale Chrome Nome pacchetto
Stabile "com.android.chrome"
Beta "com.chrome.beta"
Sviluppatori "com.chrome.dev"
Canarie "com.chrome.canary"
Chromium "org.chromium.chrome"
Casella di ricerca rapida Google (un incorporamento di Web Layer) "com.google.android.googlequicksearchbox"

changePaymentMethod

Comunica al commerciante le modifiche al metodo di pagamento selezionato dall'utente. Il bundle paymentHandlerMethodData contiene chiavi methodName e details facoltative, entrambe con valori stringa. Chrome verifica la presenza di un bundle non vuoto con un valore methodName non vuoto e invia un updatePaymentDetails con uno dei seguenti messaggi di errore tramite callback.updateWith se la convalida non va a buon fine.

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

changeShippingOption

Comunica al commerciante le modifiche all'opzione di spedizione selezionata dall'utente. shippingOptionId deve essere l'identificatore di una delle opzioni di spedizione specificate dal commerciante. Chrome verifica la presenza di un valore shippingOptionId non vuoto e invia un updatePaymentDetails con il seguente messaggio di errore tramite callback.updateWith se la convalida non va a buon fine.

'Shipping option identifier required.'

changeShippingAddress

Comunica al commerciante le modifiche all'indirizzo di spedizione fornito dall'utente. Chrome verifica la presenza di un bundle shippingAddress non vuoto con un valore countryCode valido e invia updatePaymentDetails con il seguente messaggio di errore tramite callback.updateWith se la convalida non va a buon fine.

'Payment app returned invalid shipping address in response.'

Messaggio di errore di stato non valido

Se Chrome rileva uno stato non valido dopo aver ricevuto una delle richieste di modifica, chiamerà callback.updateWith con un bundle updatePaymentDetails oscurato. Il bundle conterrà solo la chiave error con "Invalid state". Esempi di stato non valido sono:

  • Quando Chrome è ancora in attesa della risposta del commerciante a una modifica precedente (ad esempio un evento di modifica in corso).
  • L'identificatore dell'opzione di spedizione fornita dall'app di pagamento non appartiene ad alcuna opzione di spedizione specificata dal commerciante.

Ricevi i dettagli di pagamento aggiornati dal commerciante

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 è il bundle equivalente al dizionario WebIDL PaymentRequestDetailsUpdate (dopo aver oscurato il campo modifiers) e contiene le seguenti chiavi facoltative:

  • total: un bundle contenente chiavi currency e value, entrambe le chiavi hanno valori stringa
  • shippingOptions: l'array parcellabile di opzioni di spedizione
  • error: una stringa contenente un messaggio di errore generico (ad es. quando changeShippingOption non fornisce un identificatore di opzione di spedizione valido)
  • stringifiedPaymentMethodErrors: una stringa JSON che rappresenta gli errori di convalida per il metodo di pagamento
  • addressErrors: un set con chiavi facoltative identiche ai valori di indirizzo di spedizione e stringa. Ogni chiave rappresenta un errore di convalida relativo alla parte corrispondente dell'indirizzo di spedizione.

Se una chiave non è presente, il suo valore non è cambiato.