Découvrez comment adapter votre application de paiement Android pour qu'elle fonctionne avec les paiements Web et offrir une meilleure expérience utilisateur.
L'API Payment Request apporte Web, une interface intégrée au navigateur qui permet aux utilisateurs de saisir les informations de paiement requises l'information plus facilement que jamais. L'API peut également appeler un mode de paiement spécifique à la plate-forme. applications.
Navigateurs pris en charge
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
-
<ph type="x-smartling-placeholder">
Par rapport à l'utilisation d'intents Android, les paiements Web permettent une meilleure intégration avec le navigateur, la sécurité et l'expérience utilisateur:
- L'application de paiement est lancée sous forme de modale, dans le contexte du site Web du marchand.
- L'implémentation vient en complément de votre application de paiement existante. Elle vous permet : pour exploiter au mieux votre base d'utilisateurs.
- La signature de l'application de paiement est vérifiée pour empêcher téléchargement indépendant
- Les applications de paiement sont compatibles avec plusieurs modes de paiement.
- Tous les modes de paiement (cryptomonnaies, virements bancaires, etc.) peuvent être intégrés. Les applications de paiement sur les appareils Android peuvent même intégrer des modes ont besoin d'accéder à la puce matérielle de l'appareil.
Pour implémenter les paiements Web dans une application de paiement Android, quatre étapes sont nécessaires:
- Permettez aux marchands de découvrir votre application de paiement.
- Indiquez au marchand si un client dispose d'un mode de paiement enregistré (par exemple, un avoir carte) prête à payer.
- Permettez à un client d'effectuer un paiement.
- Vérifiez le certificat de signature de l'appelant.
Pour voir une démonstration des paiements Web, consultez la android-web-payment la démo.
Étape 1: Permettez aux marchands de découvrir votre application de paiement
Pour pouvoir utiliser votre application de paiement, le marchand doit utiliser la page Paiement API Request indiquer le mode de paiement que vous acceptez à l'aide du mode de paiement identifiant.
Si l'identifiant de votre mode de paiement est propre à votre application de paiement, vous : configurer votre propre mode de paiement manifeste afin que les navigateurs puissent découvrir votre application.
Étape 2: Indiquez au marchand si un client dispose d'un mode de paiement enregistré et prêt à payer
Le marchand peut appeler hasEnrolledInstrument()
pour demander si le client
peut effectuer un paiement. Vous pouvez
implémenter IS_READY_TO_PAY
en tant que service Android pour répondre à cette requête.
AndroidManifest.xml
Déclarer votre service avec un filtre d'intent avec l'action
org.chromium.intent.action.IS_READY_TO_PAY
<service
android:name=".SampleIsReadyToPayService"
android:exported="true">
<intent-filter>
<action android:name="org.chromium.intent.action.IS_READY_TO_PAY" />
</intent-filter>
</service>
Le service IS_READY_TO_PAY
est facultatif. S'il n'existe aucun gestionnaire d'intent de ce type dans
l'application de paiement, le navigateur Web suppose que l'application peut toujours effectuer
paiements.
AIDL
L'API du service IS_READY_TO_PAY
est définie dans AIDL. Créer deux modèles AIDL
fichiers avec le contenu suivant:
app/src/main/aidl/org/chromium/IsReadyToPayServiceCallback.aidl
package org.chromium;
interface IsReadyToPayServiceCallback {
oneway void handleIsReadyToPay(boolean isReadyToPay);
}
app/src/main/aidl/org/chromium/IsReadyToPayService.aidl
package org.chromium;
import org.chromium.IsReadyToPayServiceCallback;
interface IsReadyToPayService {
oneway void isReadyToPay(IsReadyToPayServiceCallback callback);
}
Mettre en œuvre la directive IsReadyToPayService
L'implémentation la plus simple de IsReadyToPayService
est illustrée ci-dessous.
Exemple:
class SampleIsReadyToPayService : Service() {
private val binder = object : IsReadyToPayService.Stub() {
override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
callback?.handleIsReadyToPay(true)
}
}
override fun onBind(intent: Intent?): IBinder? {
return binder
}
}
Réponse
Le service peut envoyer sa réponse via la méthode handleIsReadyToPay(Boolean)
.
callback?.handleIsReadyToPay(true)
Autorisation
Vous pouvez utiliser Binder.getCallingUid()
pour vérifier qui est l'appelant. Notez que vous devez
devez le faire dans la méthode isReadyToPay
, et non dans la méthode onBind
.
override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
try {
val callingPackage = packageManager.getNameForUid(Binder.getCallingUid())
// …
Consultez Vérifier le certificat de signature de l'appelant pour savoir comment procéder. pour vérifier que le package à l'origine de l'appel a la bonne signature.
Étape 3: Laissez le client effectuer le paiement
Le marchand appelle show()
pour lancer le paiement
application
afin que le client puisse effectuer un paiement. L'application de paiement est appelée via un appel
l'intent PAY
avec les informations de transaction dans les paramètres d'intent ;
L'application de paiement répond avec methodName
et details
, qui sont des applications de paiement
spécifiques et opaques pour le navigateur. Le navigateur convertit details
.
en un objet JavaScript pour le marchand via la désérialisation JSON, mais
n'applique aucune validité au-delà. Le navigateur ne modifie pas
details
; la valeur de ce paramètre est transmise directement au marchand.
AndroidManifest.xml
L'activité avec le filtre d'intent PAY
doit comporter une balise <meta-data>
qui
identifie l'identifiant du mode de paiement par défaut pour le
l'application.
Pour accepter plusieurs modes de paiement, ajoutez une balise <meta-data>
avec un
Ressource <string-array>
.
<activity
android:name=".PaymentActivity"
android:theme="@style/Theme.SamplePay.Dialog">
<intent-filter>
<action android:name="org.chromium.intent.action.PAY" />
</intent-filter>
<meta-data
android:name="org.chromium.default_payment_method_name"
android:value="https://bobbucks.dev/pay" />
<meta-data
android:name="org.chromium.payment_method_names"
android:resource="@array/method_names" />
</activity>
L'élément resource
doit être une liste de chaînes, dont chacune doit être une chaîne valide,
une URL absolue avec un schéma HTTPS, comme indiqué ici.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="method_names">
<item>https://alicepay.com/put/optional/path/here</item>
<item>https://charliepay.com/put/optional/path/here</item>
</string-array>
</resources>
Paramètres
Les paramètres suivants sont transmis à l'activité en tant qu'extras d'intent:
methodNames
methodData
topLevelOrigin
topLevelCertificateChain
paymentRequestOrigin
total
modifiers
paymentRequestId
val extras: Bundle? = intent?.extras
methodNames
Noms des méthodes utilisées. Les éléments sont les clés
Dictionnaire methodData
. Voici les modes compatibles avec l'application de paiement.
val methodNames: List<String>? = extras.getStringArrayList("methodNames")
methodData
Un mappage entre chacun des methodNames
et le
methodData
val methodData: Bundle? = extras.getBundle("methodData")
merchantName
Le contenu de la balise HTML <title>
de la page de paiement du marchand (les
le contexte de navigation de premier niveau du navigateur).
val merchantName: String? = extras.getString("merchantName")
topLevelOrigin
L'origine du marchand sans le schéma (origine sans schéma du
un contexte de navigation de premier niveau). Par exemple, https://mystore.com/checkout
est
transmis en tant que mystore.com
.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
La chaîne de certificats du marchand (chaîne de certificats de l'organisation racine
le contexte de navigation). Null pour localhost et "file on disk", qui sont tous deux sécurisés
sans certificat SSL. Chaque Parcelable
est un bundle avec un
Clé certificate
et une valeur de tableau d'octets.
val topLevelCertificateChain: Array<Parcelable>? =
extras.getParcelableArray("topLevelCertificateChain")
val list: List<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p as Bundle).getByteArray("certificate")
}
paymentRequestOrigin
Origine sans schéma du contexte de navigation iFrame qui a appelé le constructeur new
PaymentRequest(methodData, details, options)
en JavaScript. Si le
a été appelé à partir du contexte de premier niveau, la valeur de ce
est égal à la valeur du paramètre topLevelOrigin
.
val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")
total
Chaîne JSON représentant le montant total de la transaction.
val total: String? = extras.getString("total")
Voici un exemple de contenu de la chaîne:
{"currency":"USD","value":"25.00"}
modifiers
La sortie de JSON.stringify(details.modifiers)
, où details.modifiers
ne contiennent que supportedMethods
et total
.
paymentRequestId
Le champ PaymentRequest.id
indiquant "push-payment" les applications doivent s'associer
l'état de la transaction. Les sites Web marchands utilisent ce champ pour interroger les
"push-payment" applications pour l'état de
la transaction hors bande.
val paymentRequestId: String? = extras.getString("paymentRequestId")
Réponse
L'activité peut renvoyer sa réponse via setResult
avec RESULT_OK
.
setResult(Activity.RESULT_OK, Intent().apply {
putExtra("methodName", "https://bobbucks.dev/pay")
putExtra("details", "{\"token\": \"put-some-data-here\"}")
})
finish()
Vous devez spécifier deux paramètres en tant qu'extras d'intent:
methodName
: nom de la méthode utilisée.details
: chaîne JSON contenant les informations nécessaires pour que le marchand pour finaliser la transaction. Si l'opération réussie esttrue
, alorsdetails
doit être construite de telle sorte queJSON.parse(details)
réussira.
Vous pouvez transmettre RESULT_CANCELED
si la transaction n'a pas été effectuée dans le
application de paiement, par exemple, si l'utilisateur n'a pas réussi à saisir le code
son compte dans l'application de paiement. Le navigateur peut permettre à l'utilisateur de choisir
application de paiement différente.
setResult(RESULT_CANCELED)
finish()
Si le résultat d'activité d'une réponse de paiement reçue du paiement invoqué
l'application est définie sur RESULT_OK
, alors Chrome recherche les valeurs methodName
non vides et
details
dans ses extras. Si la validation échoue, Chrome renvoie une erreur
promesse de request.show()
avec l'une des erreurs suivantes rencontrées par les développeurs :
messages:
'Payment app returned invalid response. Missing field "details".'
'Payment app returned invalid response. Missing field "methodName".'
Autorisation
L'activité peut vérifier l'appelant avec sa méthode getCallingPackage()
.
val caller: String? = callingPackage
La dernière étape consiste à vérifier le certificat de signature de l'appelant afin de confirmer que le le package appelant a la bonne signature.
Étape 4: Vérifier le certificat de signature de l'appelant
Vous pouvez vérifier le nom du package de l'appelant avec Binder.getCallingUid()
dans
IS_READY_TO_PAY
, et avec Activity.getCallingPackage()
dans PAY
. Afin de
vérifier que l'appelant est bien le navigateur que vous avez en tête,
vérifiez son certificat de signature et assurez-vous qu'il correspond au bon
.
Si vous ciblez le niveau d'API 28 ou supérieur et que vous effectuez une intégration avec un navigateur
qui dispose d'un certificat de signature unique, vous pouvez utiliser
PackageManager.hasSigningCertificate()
val packageName: String = … // The caller's package name
val certificate: ByteArray = … // The correct signing certificate.
val verified = packageManager.hasSigningCertificate(
callingPackage,
certificate,
PackageManager.CERT_INPUT_SHA256
)
PackageManager.hasSigningCertificate()
est préférable pour un certificat unique
car il gère correctement la rotation des certificats. (Chrome dispose d'un
certificat de signature unique.) Les applications comportant plusieurs certificats de signature ne peuvent pas
les faire pivoter.
Si vous devez prendre en charge les niveaux d'API 27 et inférieurs, ou si vous devez gérer
des navigateurs avec plusieurs certificats de signature, vous pouvez utiliser
PackageManager.GET_SIGNATURES
val packageName: String = … // The caller's package name
val certificates: Set<ByteArray> = … // The correct set of signing certificates
val packageInfo = getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
val sha256 = MessageDigest.getInstance("SHA-256")
val signatures = packageInfo.signatures.map { sha256.digest(it.toByteArray()) }
val verified = signatures.size == certificates.size &&
signatures.all { s -> certificates.any { it.contentEquals(s) } }