Fournir des coordonnées et des informations de livraison depuis une application de paiement Android

Mettre à jour votre application de paiement Android pour fournir une adresse de livraison et les coordonnées du payeur avec les API Web Payments

Sahel Sharify
Sahel Sharify

Saisir une adresse de livraison et des coordonnées via un formulaire Web peut s'avérer fastidieux pour les clients. Cela peut entraîner des erreurs et réduire le taux de conversion.

C'est pourquoi l'API Payment Request propose une fonctionnalité permettant de demander une adresse de livraison et des coordonnées. Cela présente plusieurs avantages:

  • Les utilisateurs peuvent choisir la bonne adresse en quelques gestes.
  • L'adresse est toujours renvoyée au format standardisé.
  • Il est moins probable que vous indiquiez une adresse incorrecte.

Les navigateurs peuvent reporter la collecte de l'adresse de livraison et des coordonnées à une application de paiement afin de fournir une expérience de paiement unifiée. Cette fonctionnalité est appelée délégation.

Dans la mesure du possible, Chrome délègue la collecte de l'adresse de livraison et des coordonnées d'un client à l'application de paiement Android appelée. Cette délégation simplifie la procédure de paiement.

Le site Web du marchand peut mettre à jour de manière dynamique les options de livraison et le prix total en fonction de l'adresse de livraison et de l'option de livraison choisies par le client.

Changement d'option de livraison et d'adresse de livraison en action. Découvrez son impact dynamique sur les options de livraison et le prix total.

Pour ajouter la délégation à une application de paiement Android existante, procédez comme suit:

  1. Déclarer les délégations acceptées.
  2. Analysez les extras d'intent PAY pour les options de paiement requises.
  3. Fournissez les informations requises dans votre réponse au paiement.
  4. [Facultatif] Prendre en charge le flux dynamique :
    1. Informez le marchand des modifications apportées au mode de paiement, à l'adresse de livraison ou à l'option de livraison sélectionné par l'utilisateur.
    2. Recevez de la part du marchand les détails du mode de paiement mis à jour (par exemple, le montant total ajusté en fonction des coûts de l'option de livraison sélectionnée).

Déclarer les délégations acceptées

Le navigateur doit connaître la liste des informations supplémentaires que votre application de paiement peut fournir pour déléguer la collecte de ces informations à votre application. Déclarez les délégations compatibles en tant que <meta-data> dans le fichier AndroidManifest.xml de votre application.

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

<resource> doit être une liste de chaînes choisies parmi les valeurs valides suivantes:

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

Dans l'exemple suivant, vous ne pouvez fournir qu'une adresse de livraison et l'adresse e-mail du payeur.

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

Analyser les extras d'intent PAY pour les options de paiement requises

Le marchand peut spécifier des informations supplémentaires requises à l'aide du dictionnaire paymentOptions. Chrome fournit la liste des options requises que votre application peut fournir en transmettant les paramètres suivants à l'activité PAY en tant qu'extras d'intent.

paymentOptions

paymentOptions est le sous-ensemble des options de paiement spécifiées par le marchand pour lesquelles votre application a déclaré la prise en charge de la délégation.

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

Il peut inclure les paramètres suivants:

  • requestPayerName : valeur booléenne indiquant si le nom du payeur est requis ou non.
  • requestPayerPhone : valeur booléenne indiquant si le numéro de téléphone du payeur est requis ou non.
  • requestPayerEmail : valeur booléenne indiquant si l'adresse e-mail du payeur est requise ou non.
  • requestShipping : valeur booléenne indiquant si les informations de livraison sont requises.
  • shippingType : chaîne indiquant le type de livraison. Le type de livraison peut être "shipping", "delivery" ou "pickup". Votre application peut utiliser cet indice dans son UI lorsqu'elle demande l'adresse de l'utilisateur ou les options de livraison disponibles.

shippingOptions

shippingOptions est le tableau parcelable des options de livraison spécifiées par le marchand. Ce paramètre n'existe que lorsque paymentOptions.requestShipping == true.

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

Chaque option de livraison est un Bundle avec les clés suivantes.

  • id : identifiant de l'option de livraison.
  • label : libellé de l'option de livraison présenté à l'utilisateur.
  • amount : lot de frais de port contenant des clés currency et value avec des valeurs de chaîne.
    • currency indique la devise des frais de port sous la forme d'un code alphabétique ISO4217 à trois lettres au format correct.
    • value indique la valeur des frais de port sous la forme d'une valeur monétaire décimale valide.
  • selected : indique si l'option de livraison doit être sélectionnée ou non lorsque l'application de paiement affiche les options de livraison.

Toutes les clés autres que selected ont des valeurs de chaîne. selected a une valeur booléenne.

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)

Fournir les informations requises dans une réponse au paiement

Votre application doit inclure les informations supplémentaires requises dans sa réponse à l'activité PAY.

Pour ce faire, les paramètres suivants doivent être spécifiés en tant qu'extras d'intent:

  • payerName : nom complet du payeur. Il doit s'agir d'une chaîne non vide lorsque paymentOptions.requestPayerName est "true".
  • payerPhone : numéro de téléphone du payeur. Il doit s'agir d'une chaîne non vide lorsque paymentOptions.requestPayerPhone est "true".
  • payerEmail : adresse e-mail du payeur. Il doit s'agir d'une chaîne non vide lorsque paymentOptions.requestPayerEmail est défini sur "true".
  • shippingAddress : adresse de livraison fournie par l'utilisateur. Il doit s'agir d'un bundle non vide lorsque paymentOptions.requestShipping est défini sur "true". Le bundle doit comporter les clés suivantes, qui représentent différentes parties d'une adresse physique.
    • city
    • countryCode
    • dependentLocality
    • organization
    • phone
    • postalCode
    • recipient
    • region
    • sortingCode
    • addressLineToutes les clés autres que addressLine ont des valeurs de chaîne. Le addressLine est un tableau de chaînes.
  • shippingOptionId : identifiant de l'option de livraison sélectionnée par l'utilisateur. Il doit s'agir d'une chaîne non vide lorsque paymentOptions.requestShipping est défini sur "true".

Valider la réponse au paiement

Si le résultat d'activité d'une réponse de paiement reçue de l'application de paiement appelée est défini sur RESULT_OK, Chrome recherche les informations supplémentaires requises dans ses extras. Si la validation échoue, Chrome renvoie une promesse refusée de request.show() avec l'un des messages d'erreur suivants destinés aux développeurs:

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

L'exemple de code suivant est un exemple de réponse valide:

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

Facultatif: Assurer la compatibilité avec le flux dynamique

Parfois, le coût total d'une transaction augmente (par exemple, lorsque l'utilisateur choisit l'option de livraison express, ou lorsque la liste des options de livraison disponibles ou ses prix change lorsque l'utilisateur choisit une adresse de livraison internationale). Lorsque votre application fournit l'adresse ou l'option de livraison sélectionnée par l'utilisateur, elle doit pouvoir informer le marchand de toute modification d'adresse ou d'option de livraison, et lui montrer les détails de paiement mis à jour (fournis par le marchand).

AIDL

Pour informer le marchand de nouvelles modifications, utilisez le service PaymentDetailsUpdateService déclaré dans le fichier AndroidManifest.xml de Chrome. Pour utiliser ce service, créez deux fichiers AIDL avec le contenu suivant:

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

Informer le marchand des modifications apportées au mode de paiement, à l'adresse de livraison ou à l'option de livraison sélectionnés par l'utilisateur

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

Le callingPackageName utilisé pour l'intent de démarrage du service peut avoir l'une des valeurs suivantes en fonction du navigateur qui a initié la demande de paiement.

Version de Chrome Nom du package
Stable "com.android.chrome"
Bêta "com.chrome.beta"
Dév "com.chrome.dev"
Canary "com.chrome.canary"
Chromium "org.chromium.chrome"
Champ de recherche rapide Google (un outil d'intégration WebLayer) "com.google.android.googlequicksearchbox"

changePaymentMethod

Informe le marchand des modifications apportées au mode de paiement sélectionné par l'utilisateur. Le bundle paymentHandlerMethodData contient des clés methodName et des clés details facultatives avec toutes deux des valeurs de chaîne. Chrome recherche un bundle non vide avec un methodName non vide et envoie une updatePaymentDetails avec l'un des messages d'erreur suivants via callback.updateWith si la validation échoue.

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

changeShippingOption

Informe le marchand des modifications apportées à l'option de livraison sélectionnée par l'utilisateur. shippingOptionId doit être l'identifiant de l'une des options de livraison spécifiées par le marchand. Chrome recherche un shippingOptionId non vide et envoie un updatePaymentDetails avec le message d'erreur suivant via callback.updateWith si la validation échoue.

'Shipping option identifier required.'

changeShippingAddress

Informe le marchand des modifications apportées à l'adresse de livraison fournie par l'utilisateur. Chrome recherche un bundle shippingAddress non vide avec un countryCode valide et envoie une updatePaymentDetails avec le message d'erreur suivant via callback.updateWith si la validation échoue.

'Payment app returned invalid shipping address in response.'

Message d'erreur d'état non valide

Si Chrome rencontre un état non valide à la réception de l'une des requêtes de modification, il appelle callback.updateWith avec un bundle updatePaymentDetails masqué. Le bundle ne contiendra que la clé error avec "Invalid state". Voici quelques exemples d'état non valide:

  • Lorsque Chrome attend toujours la réponse du marchand à une modification précédente (par exemple, un événement de modification en cours).
  • L'identifiant de l'option de livraison fourni par l'application de paiement n'appartient à aucune des options de livraison spécifiées par le marchand.

Recevoir les détails du paiement à jour de la part du marchand

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 est l'équivalent du bundle du dictionnaire PaymentRequestDetailsUpdate WebIDL (après avoir masqué le champ modifiers) et contient les clés facultatives suivantes:

  • total : un bundle contenant des clés currency et value. Les deux clés ont des valeurs de chaîne.
  • shippingOptions : tableau parcelable des options de livraison
  • error : chaîne contenant un message d'erreur générique (par exemple, lorsque changeShippingOption ne fournit pas d'identifiant d'option de livraison valide).
  • stringifiedPaymentMethodErrors : chaîne JSON représentant les erreurs de validation du mode de paiement.
  • addressErrors : lot avec des clés facultatives identiques aux valeurs de l'adresse de livraison et des chaînes. Chaque clé représente une erreur de validation liée à la partie correspondante de l'adresse de livraison.

Une clé absente signifie que sa valeur n'a pas changé.