Erfahren Sie, wie Sie Ihre Android-Zahlungsanwendung an die Arbeit mit Web Payments anpassen und Kunden eine bessere Benutzererfahrung bieten.
das Zahlungsanforderungs-API bringt eine integrierte browserbasierte Oberfläche ins Web, mit der Benutzer die erforderlichen Zahlungsinformationen einfacher als je zuvor eingeben können. Die API kann auch plattformspezifische Zahlungsanwendungen aufrufen.
Im Vergleich zur Verwendung von nur Android Intents ermöglichen Webzahlungen eine bessere Browserintegration, Sicherheit und Benutzererfahrung:
- Die Zahlungsanwendung wird modal im Kontext der Website des Händlers gestartet.
- Die Implementierung ergänzt Ihre bestehende Zahlungsanwendung und ermöglicht es Ihnen, Ihre Benutzerbasis zu nutzen.
- Die Signatur der Zahlungs-App wird überprüft, um dies zu vermeiden
seitliche Belastung. - Zahlungs-Apps können mehrere Zahlungsmethoden unterstützen.
- Jede Zahlungsmethode kann integriert werden, z. B. Kryptowährungen, Banküberweisungen und mehr. Zahlungsanwendungen auf Android-Geräten können sogar Methoden integrieren, die den Zugriff auf den Hardware-Chip im Gerät erfordern.
Es sind vier Schritte erforderlich, um Webzahlungen in einer Android-Zahlungsanwendung zu implementieren:
- Lassen Sie Händler Ihre Zahlungsanwendung entdecken.
- Informieren Sie einen Händler, wenn ein Kunde über ein registriertes Instrument (z. B. eine Kreditkarte) verfügt, dass er zur Zahlung bereit ist.
- Lassen Sie einen Kunden die Zahlung leisten.
- Überprüfen Sie das Signaturzertifikat des Anrufers.
Informationen zum Anzeigen von Webzahlungen in Aktion finden Sie unter
Zahlung-Web-Android
Manifestation.
Schritt 1: Lassen Sie Händler Ihre Zahlungs-App entdecken
Damit ein Händler Ihre Zahlungsanwendung verwenden kann, muss er die verwenden Zahlungsanforderungs-API und geben Sie die Zahlungsmethode an, die Sie mit dem unterstützen Kennung der Zahlungsmethode.
Wenn Sie eine eindeutige Zahlungsmethoden-ID für Ihre Zahlungs-App haben, können Sie Ihre festlegen Zahlungsmethode manifest damit Browser Ihre Anwendung erkennen können.
Schritt 2: Informieren Sie einen Händler, wenn ein Kunde ein registriertes Instrument hat, dass er bereit ist zu zahlen
Der Händler kann anrufen hasEnrolledInstrument ()
um zu sehen, ob der Kunde eine Zahlung leisten kann. Sie können implementieren IS_READY_TO_PAY
als Android-Dienst zur Beantwortung dieser Frage.
AndroidManifest.xml
Deklarieren Sie Ihren Service mit einem Absichtsfilter mit Aktion
org.chromium.intent.action.IS_READY_TO_PAY
.
<Bedienung
android:Süßkartoffel=".SampleIsReadyToPayService"
android:exported="wahr">
<intent-filter>
<Aktion android:Süßkartoffel="org.chromium.intent.action.IS_READY_TO_PAY" />
</intent-filter>
</Bedienung>
das IS_READY_TO_PAY
Der Service ist optional. Wenn die Zahlungsanwendung keinen solchen Intent-Handler enthält, geht der Webbrowser davon aus, dass die Anwendung jederzeit Zahlungen ausführen kann.
AIDL
Die API für IS_READY_TO_PAY
Der Dienst ist in AIDL definiert. Erstellen Sie zwei AIDL-Dateien mit folgendem Inhalt:
app / src / main / aidl / org / chrom / IsReadyToPayServiceCallback.aidl
package org.chromium;
interface IsReadyToPayServiceCallback {
oneway void handleIsReadyToPay(boolean isReadyToPay);
}
app / src / main / aidl / org / chrom / IsReadyToPayService.aidl
package org.chromium;
importieren org.chromium.IsReadyToPayServiceCallback;interface IsReadyToPayService {
oneway void isReadyToPay(IsReadyToPayServiceCallback callback);
}
Implementieren IsReadyToPayService
Die einfachste Implementierung von IsReadyToPayService
im folgenden Beispiel gezeigt:
Klasse SampleIsReadyToPayService : Service() {
private val binder = object : IsReadyToPayService.Stub() {
override fun isReadyToPay(Rückrufen: IsReadyToPayServiceCallback?) {
Rückrufen?.handleIsReadyToPay(wahr)
}
}override fun onBind(Ich habe es versucht: Absicht?): IBinder? {
Rückkehr binder
}
}
Parameter
Übergeben Sie die folgenden Parameter an onBind
als Absichts-Extras:
methodNames
methodData
topLevelOrigin
topLevelCertificateChain
topLevelCertificateChain
paymentRequestOrigin
override fun onBind(Ich habe es versucht: Absicht?): IBinder? {
val extras: Bündeln? = Ich habe es versucht?.extras
}
methodNames
Die Namen der abgefragten Methoden. Die Elemente sind die Schlüssel in der
methodData
Wörterbuch und geben Sie die Methoden an, die die Zahlungsanwendung unterstützt.
val methodNames: Liste<String>? = extras.getStringArrayList("methodNames")
methodData
Eine Zuordnung jeder Eingabe von methodNames
zum
methodData
.
val methodData: Bündeln? = extras.getBundle("methodData")
topLevelOrigin
Der Ursprung des Händlers ohne das Schema (der Ursprung ohne das Schema des Navigationskontexts der obersten Ebene). Zum Beispiel, https://mystore.com/checkout
es wird wie vergehen mystore.com
.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
Die Zertifikatkette des Händlers (die Zertifikatkette aus dem Navigationskontext der obersten Ebene). Null für localhost und Datei auf der Festplatte, die sichere Kontexte ohne SSL-Zertifikate sind. Die Zertifikatskette ist erforderlich, da für einen Zahlungsantrag möglicherweise unterschiedliche Vertrauensanforderungen für Websites gelten.
val topLevelCertificateChain: Array<Paketierbar>? =
extras.getParcelableArray("topLevelCertificateChain")
Jeder Paketierbar
ist ein Bündeln
mit einer "Zertifikat"
Schlüssel und ein Byte-Array-Wert.
val list: Liste<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p As Bündeln).getByteArray("Zertifikat")
}
paymentRequestOrigin
Die Nicht-Schema-Quelle des aufgerufenen Iframe-Navigationskontexts neue PaymentRequest (methodData, Details, Optionen)
Konstruktor in JavaScript. Wenn der Konstruktor aus dem Kontext der obersten Ebene aufgerufen wurde, entspricht der Wert dieses Parameters dem Wert von topLevelOrigin
Parameter.
val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")
Antworten
Der Dienst kann seine Antwort durch senden handleIsReadyToPay (Boolean)
Methode.
Rückrufen?.handleIsReadyToPay(wahr)
Entschuldigen Sie mich
Sie können verwenden Binder.getCallingUid ()
um zu überprüfen, wer der Anrufer ist. Beachten Sie, dass Sie dies in der tun müssen isReadyToPay
Methode, nicht die onBind
Methode.
override fun isReadyToPay(Rückrufen: IsReadyToPayServiceCallback?) {
try {
val callingPackage = packageManager.getNameForUid(Binder.getCallingUid())
Informationen zum Überprüfen, ob das anrufende Paket die richtige Signatur hat, finden Sie unter Überprüfen des Signaturzertifikats des Anrufers.
Schritt 3: Lassen Sie einen Kunden auschecken
Der Händler ruft an Show()
um den Zahlungsantrag zu starten, damit der Kunde eine Zahlung leisten kann. Die kostenpflichtige App wird über eine Android-Absicht aufgerufen ZAHLEN
mit Transaktionsinformationen in den Intent-Parametern.
Der Zahlungsantrag antwortet mit Methodenname
y Einzelheiten
, die spezifisch für die Zahlungsanwendung sind und für den Browser undurchsichtig sind. Der Browser konvertiert die Einzelheiten
Zeichenfolge in einem JavaScript-Objekt für den Händler über JSON-Deserialisierung, aber darüber hinaus keine Gültigkeit. Der Browser ändert das nicht
Einzelheiten
;; Der Wert dieses Parameters wird direkt an den Händler übergeben.
AndroidManifest.xml
Die Aktivität mit dem ZAHLEN
Der Absichtsfilter muss a haben Ein Tag, das die Standardkennung der Zahlungsmethode für die Anwendung angibt.
Fügen Sie a hinzu, um mehrere Zahlungsmethoden zu unterstützen Etikett mit a
Ressource.
<activity
android:Süßkartoffel=".PaymentActivity"
android:Thema="@style/Theme.SamplePay.Dialog">
<intent-filter>
<Aktion android:Süßkartoffel="org.chromium.intent.action.PAY" />
</intent-filter><meta-data
android:Süßkartoffel="org.chromium.default_payment_method_name"
android:Wert="https://bobpay.xyz/pay" />
<meta-data
android:Süßkartoffel="org.chromium.payment_method_names"
android:Ressource="@array/method_names" />
</activity>
das Ressource
muss eine Liste von Zeichenfolgen sein, von denen jede eine gültige absolute URL mit einem HTTPS-Schema sein muss, wie hier gezeigt.
<?xml version="1.0" encoding="utf-8"?>
<Ressourcen>
<string-array Süßkartoffel="method_names">
<Artikel>https://alicepay.com/put/optional/path/here</Artikel>
<Artikel>https://charliepay.com/put/optional/path/here</Artikel>
</string-array>
</Ressourcen>
Parameter
Die folgenden Parameter werden als Absichts-Extras an die Aktivität übergeben:
methodNames
methodData
topLevelOrigin
topLevelCertificateChain
paymentRequestOrigin
gesamt
Modifikatoren
paymentRequestId
val extras: Bündeln? = Ich habe es versucht?.extras
methodNames
Die Namen der verwendeten Methoden. Die Elemente sind die Schlüssel in der
methodData
Wörterbuch. Dies sind die Methoden, die die Zahlungsanwendung unterstützt.
val methodNames: Liste<String>? = extras.getStringArrayList("methodNames")
methodData
Eine Zuordnung von jedem der methodNames
zum
methodData
.
val methodData: Bündeln? = extras.getBundle("methodData")
Händlername
Der Inhalt der
HTML-Tag der Händler-Checkout-Seite (der Navigationskontext der obersten Ebene des Browsers).
val merchantName: String? = extras.getString("merchantName")
topLevelOrigin
Der Ursprung des Händlers ohne das Schema (Der Ursprung ohne das Schema des Navigationskontexts der obersten Ebene). Zum Beispiel, https://mystore.com/checkout
es passiert wie mystore.com
.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
Die Zertifikatkette des Händlers (die Zertifikatkette aus dem Navigationskontext der obersten Ebene). Null für localhost und Datei auf der Festplatte, die sichere Kontexte ohne SSL-Zertifikate sind. Jeder Paketierbar
Es ist ein Paket mit einem
Zertifikat
Schlüssel und ein Byte-Array-Wert.
val topLevelCertificateChain: Array<Paketierbar>? =
extras.getParcelableArray("topLevelCertificateChain")
val list: Liste<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p As Bündeln).getByteArray("Zertifikat")
}
paymentRequestOrigin
Die Nicht-Schema-Quelle des aufgerufenen Iframe-Navigationskontexts neue PaymentRequest (methodData, Details, Optionen)
Konstruktor in JavaScript. Wenn der Konstruktor aus dem Kontext der obersten Ebene aufgerufen wurde, entspricht der Wert dieses Parameters dem Wert von topLevelOrigin
Parameter.
val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")
gesamt
Die JSON-Zeichenfolge, die den Gesamtbetrag der Transaktion darstellt.
val gesamt: String? = extras.getString("total")
Hier ist ein Beispiel für den Inhalt von Zeichenfolgen:
{"currency":"USD","value":"25.00"}
Modifikatoren
Die Ausgabe von JSON.stringify (details.modifiers)
, wo details.modifiers
nur enthalten unterstützte Methoden
y gesamt
.
paymentRequestId
das PaymentRequest.id
Feld, das Anwendungen für die automatische Zahlung mit dem Status der Transaktion verknüpfen sollen. Händler-Websites verwenden dieses Feld, um "Auto Pay" -Anwendungen nach dem Out-of-Band-Transaktionsstatus abzufragen.
val paymentRequestId: String? = extras.getString("paymentRequestId")
Antworten
Die Aktivität kann ihre Antwort durch senden setResult
mit RESULT_OK
.
setResult(Activity.RESULT_OK, Absicht().apply {
putExtra("methodName", "https://bobpay.xyz/pay")
putExtra("details", "{"token": "put-some-data-here"}")
})
finish()
Sie müssen zwei Parameter als Absichts-Extras angeben:
Methodenname
: Der Name der verwendeten Methode.Einzelheiten
: JSON-Zeichenfolge mit den Informationen, die der Händler zum Abschließen der Transaktion benötigt. Wenn Erfolg istwahr
, dannEinzelheiten
muss so konstruiert sein, dassJSON.parse (Details)
es könnte passieren.
Du kannst passieren RESULT_CANCELED
Wenn die Transaktion in der Zahlungsanwendung nicht abgeschlossen wurde, z. B. wenn der Benutzer in der Zahlungsanwendung nicht den richtigen PIN-Code für sein Konto eingegeben hat. Der Browser kann es dem Benutzer ermöglichen, eine andere Zahlungsanwendung auszuwählen.
setResult(RESULT_CANCELED)
finish()
Wenn das Aktivitätsergebnis einer von der aufgerufenen Zahlungsanwendung empfangenen Zahlungsantwort auf gesetzt ist RESULT_OK
Dann prüft Chrome, ob es nicht leer ist Methodenname
y
Einzelheiten
in Ihren Extras. Wenn die Validierung fehlschlägt, gibt Chrome ein abgelehntes Versprechen von zurück request.show ()
mit einer der folgenden Fehlermeldungen für Entwickler:
'Payment app returned invalid response. Missing field "details".'
'Payment app returned invalid response. Missing field "methodName".'
Entschuldigen Sie mich
Die Aktivität kann den Anrufer mit ihrem überprüfen getCallingPackage ()
Methode.
val caller: String? = callingPackage
Der letzte Schritt besteht darin, das Signaturzertifikat des Anrufers zu überprüfen, um zu bestätigen, dass das anrufende Paket die richtige Signatur hat.
Schritt 4: Überprüfen Sie das Signaturzertifikat des Anrufers
Sie können den Paketnamen des Anrufers mit überprüfen Binder.getCallingUid ()
im
IS_READY_TO_PAY
, und mit Activity.getCallingPackage ()
im ZAHLEN
. Um wirklich zu überprüfen, ob der Anrufer der Browser ist, an den Sie denken, müssen Sie das Signaturzertifikat überprüfen und sicherstellen, dass es mit dem richtigen Wert übereinstimmt.
Wenn Sie auf API-Level 28 und höher abzielen und in einen Browser mit einem einzigen Signaturzertifikat integrieren, können Sie diesen verwenden
PackageManager.hasSigningCertificate ()
.
val packageName: String = …
val Zertifikat: ByteArray = …
val verified = packageManager.hasSigningCertificate(
callingPackage,
Zertifikat,
PackageManager.CERT_INPUT_SHA256
)
PackageManager.hasSigningCertificate ()
Es wird für einzelne Zertifikatbrowser bevorzugt, da die Zertifikatrotation korrekt ausgeführt wird. (Chrome hat nur ein Signaturzertifikat.) Anwendungen mit mehreren Signaturzertifikaten können diese nicht drehen.
Wenn Sie API-Levels unterstützen müssen, die älter als 27 Jahre oder niedriger sind, oder wenn Sie Browser mit mehreren Signaturzertifikaten verarbeiten müssen, können Sie diese verwenden
PackageManager.GET_SIGNATURES
.
val packageName: String = …
val certificates: einstellen<ByteArray> = … val packageInfo = getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
val sha256 = MessageDigest.getInstance("SHA-256")
val signatures = packageInfo.signatures.map { sha256.digest(Artikel.toByteArray()) }
val verified = signatures.Größe == certificates.Größe &&
signatures.Dort { s -> certificates.any { Artikel.contentEquals(s) } }