Dowiedz się, jak dostosować aplikację do płatności na Androida, aby współpracowała z płatnościami internetowymi i zapewniła klientom większą wygodę.
Payment Request API to interfejs wbudowany w przeglądarkę, który pozwala użytkownikom łatwiej niż kiedykolwiek wcześniej wpisywać wymagane informacje o płatności. Może też wywoływać aplikacje płatnicze na konkretnej platformie.
W porównaniu do korzystania tylko z intencji na Androida płatności internetowe umożliwiają lepszą integrację z przeglądarką, zabezpieczeniami i wygodą użytkowników:
- Aplikacja płatnicza jest uruchamiana w oknie modalnym na stronie sprzedawcy.
- Uzupełnia ona Twoją istniejącą aplikację płatniczą i umożliwia Ci wykorzystanie potencjału użytkowników.
- Podpis aplikacji płatniczej jest sprawdzany, aby zapobiec instalowaniu z nieoficjalnych źródeł.
- Aplikacje do płatności mogą obsługiwać wiele form płatności.
- Integracja może obejmować wszystkie formy płatności, takie jak kryptowaluty, przelewy bankowe itp. Aplikacje do płatności na urządzeniach z Androidem mogą też integrować metody, które wymagają dostępu do układu sprzętowego urządzenia.
Wdrożenie płatności internetowych w aplikacji do płatności na Androida wymaga wykonania 4 kroków:
- Ułatw sprzedawcom znalezienie Twojej aplikacji płatniczej.
- Poinformuj sprzedawcę, czy klient ma zarejestrowany instrument (np. kartę kredytową), który może już zapłacić.
- Umożliwienie klientom dokonania płatności.
- Sprawdź certyfikat podpisywania elementu wywołującego.
Aby zobaczyć, jak działa płatność internetowa, zapoznaj się z prezentacją android-web-payment.
Krok 1. Ułatw sprzedawcom znalezienie Twojej aplikacji płatniczej
Aby sprzedawca mógł skorzystać z Twojej aplikacji płatniczej, musi użyć interfejsu PaymentRequest API i określić obsługiwaną formę płatności za pomocą identyfikatora formy płatności.
Jeśli masz identyfikator formy płatności, który jest unikalny dla Twojej aplikacji płatniczej, możesz skonfigurować własny plik manifestu, aby przeglądarki mogły wykrywać Twoją aplikację.
Krok 2. Poinformuj sprzedawcę, czy klient ma zarejestrowany instrument, który może już zapłacić
Sprzedawca może wywołać hasEnrolledInstrument()
, aby zapytać, czy klient może dokonać płatności. Aby odpowiedzieć na to zapytanie, możesz zaimplementować IS_READY_TO_PAY
jako usługę na Androida.
AndroidManifest.xml
Zadeklaruj usługę za pomocą filtra intencji, używając działania 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>
Usługa IS_READY_TO_PAY
jest opcjonalna. Jeśli w aplikacji płatniczej nie ma takiego modułu obsługi intencji, przeglądarka przyjmuje, że aplikacja zawsze może dokonywać płatności.
AIDL
Interfejs API dla usługi IS_READY_TO_PAY
jest zdefiniowany w AIDL. Utwórz 2 pliki AIDL o tej treści:
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);
}
Stosowanie dyrektywy IsReadyToPayService
Najprostszą implementację obiektu IsReadyToPayService
przedstawiono w tym przykładzie:
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
}
}
Odpowiedź
Usługa może wysłać odpowiedź za pomocą metody handleIsReadyToPay(Boolean)
.
callback?.handleIsReadyToPay(true)
Uprawnienia
Za pomocą metody Binder.getCallingUid()
możesz sprawdzić, kto wywołuje użytkownika. Pamiętaj, że trzeba to zrobić w metodzie isReadyToPay
, a nie w metodzie onBind
.
override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
try {
val callingPackage = packageManager.getNameForUid(Binder.getCallingUid())
// …
Informacje o tym, jak sprawdzić, czy pakiet wywołujący ma właściwy podpis, znajdziesz w sekcji Sprawdzanie certyfikatu podpisywania elementu wywołującego.
Krok 3. Pozwól klientowi dokonać płatności
Sprzedawca wywołuje show()
, aby uruchomić aplikację płatniczą, aby klient mógł dokonać płatności. Aplikacja płatnicza jest wywoływana za pomocą intencji Androida PAY
z informacjami o transakcji w parametrach intencji.
Aplikacja płatnicza w odpowiedzi wysyła w odpowiedzi methodName
i details
, które są związane z aplikacją płatniczą i są nieprzezroczyste dla przeglądarki. Przeglądarka konwertuje ciąg znaków details
na obiekt JavaScript dla sprzedawcy za pomocą deserializacji JSON, ale nie egzekwuje żadnej innej wartości. Przeglądarka nie modyfikuje parametru details
. Wartość tego parametru jest przekazywana bezpośrednio do sprzedawcy.
AndroidManifest.xml
Aktywność z filtrem intencji PAY
powinna zawierać tag <meta-data>
identyfikujący domyślny identyfikator formy płatności dla aplikacji.
Aby obsługiwać wiele form płatności, dodaj tag <meta-data>
z zasobem <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>
Element resource
musi być listą ciągów tekstowych, z których każdy musi być prawidłowym, bezwzględnym adresem URL o schemacie HTTPS, jak pokazano tutaj.
<?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>
Parametry
Te parametry są przekazywane do działania jako dodatki do intencji:
methodNames
methodData
topLevelOrigin
topLevelCertificateChain
paymentRequestOrigin
total
modifiers
paymentRequestId
val extras: Bundle? = intent?.extras
methodNames
Nazwy stosowanych metod. Elementy to klucze w słowniku methodData
. Te formy są obsługiwane przez aplikację płatniczą.
val methodNames: List<String>? = extras.getStringArrayList("methodNames")
methodData
Mapowanie z każdego obiektu methodNames
na methodData
.
val methodData: Bundle? = extras.getBundle("methodData")
merchantName
Zawartość tagu HTML <title>
na stronie płatności sprzedawcy (Kontekst przeglądania najwyższego poziomu w przeglądarce).
val merchantName: String? = extras.getString("merchantName")
topLevelOrigin
Pochodzenie sprzedawcy bez schematu (bez schematów pochodzenie kontekstu przeglądania najwyższego poziomu). Na przykład wartość https://mystore.com/checkout
jest przekazywana jako mystore.com
.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
Łańcuch certyfikatów sprzedawcy (łańcuch certyfikatów kontekstu przeglądania najwyższego poziomu). Wartość null w przypadku hosta lokalnego i pliku na dysku, które są bezpiecznymi kontekstami bez certyfikatów SSL. Każdy element Parcelable
to pakiet z kluczem certificate
i wartością w tablicy bajtów.
val topLevelCertificateChain: Array<Parcelable>? =
extras.getParcelableArray("topLevelCertificateChain")
val list: List<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p as Bundle).getByteArray("certificate")
}
paymentRequestOrigin
Bez schematu pochodzenie kontekstu przeglądania iframe, które wywołało konstruktor new
PaymentRequest(methodData, details, options)
w JavaScripcie. Jeśli konstruktor został wywołany z kontekstu najwyższego poziomu, wartość tego parametru jest równa wartości parametru topLevelOrigin
.
val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")
total
Ciąg znaków JSON reprezentujący łączną kwotę transakcji.
val total: String? = extras.getString("total")
Oto przykładowa zawartość tego ciągu znaków:
{"currency":"USD","value":"25.00"}
modifiers
Dane wyjściowe funkcji JSON.stringify(details.modifiers)
, gdzie details.modifiers
zawiera tylko wartości supportedMethods
i total
.
paymentRequestId
Pole PaymentRequest.id
, które aplikacje typu „push” powinny powiązać ze stanem transakcji. Strony sprzedawców będą używać tego pola do wysyłania zapytań dotyczących aplikacji typu „push” na potrzeby stanu transakcji.
val paymentRequestId: String? = extras.getString("paymentRequestId")
Odpowiedź
Aktywność może wysłać odpowiedź przez setResult
za pomocą RESULT_OK
.
setResult(Activity.RESULT_OK, Intent().apply {
putExtra("methodName", "https://bobbucks.dev/pay")
putExtra("details", "{\"token\": \"put-some-data-here\"}")
})
finish()
Musisz określić 2 parametry jako dodatki do intencji:
methodName
: nazwa używanej metody.details
: ciąg znaków JSON zawierający informacje niezbędne sprzedawcy do zrealizowania transakcji. Jeśli sukcesem jesttrue
, musisz utworzyć kolumnędetails
w taki sposób, aby parametrJSON.parse(details)
działał.
Możesz przekazać RESULT_CANCELED
, jeśli transakcja nie została zrealizowana w aplikacji płatniczej, na przykład wtedy, gdy użytkownik nie podał prawidłowego kodu PIN do swojego konta w aplikacji płatniczej. Przeglądarka może pozwolić użytkownikowi wybrać inną aplikację płatniczą.
setResult(RESULT_CANCELED)
finish()
Jeśli wynik aktywności odpowiedzi na płatność otrzymaną z wywołanej aplikacji płatniczej jest ustawiony na RESULT_OK
, Chrome sprawdzi, czy w dodatkach nie ma pustych elementów methodName
i details
. Jeśli weryfikacja się nie powiedzie, Chrome zwróci od request.show()
komunikat o odrzuceniu z jednym z tych komunikatów o błędzie:
'Payment app returned invalid response. Missing field "details".'
'Payment app returned invalid response. Missing field "methodName".'
Uprawnienia
Aktywność może sprawdzać element wywołujący za pomocą metody getCallingPackage()
.
val caller: String? = callingPackage
Ostatnim krokiem jest sprawdzenie certyfikatu podpisywania elementu wywołującego, aby potwierdzić, że pakiet wywołań ma właściwy podpis.
Krok 4. Sprawdź certyfikat podpisywania elementu wywołującego
Nazwę pakietu rozmówcy możesz sprawdzić w aplikacji Binder.getCallingUid()
w systemie IS_READY_TO_PAY
, a w PAY
– w Activity.getCallingPackage()
. Aby rzeczywiście upewnić się, że obiekt wywołujący jest przeglądarką, o które Ci chodzi, sprawdź jego certyfikat podpisywania i upewnij się, że jest zgodny z prawidłową wartością.
Jeśli kierujesz aplikację na poziom API 28 lub wyższy i przeprowadzasz integrację z przeglądarką, która ma jeden certyfikat podpisywania, możesz użyć 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
)
Typ PackageManager.hasSigningCertificate()
jest preferowany w przypadku przeglądarek z jednym certyfikatem, ponieważ prawidłowo obsługuje rotację certyfikatów. (Chrome ma
jeden certyfikat podpisywania). Aplikacje, które mają wiele certyfikatów podpisywania,
nie mogą ich obracać.
Jeśli chcesz obsługiwać starsze interfejsy API 27 i niższe lub jeśli chcesz obsługiwać przeglądarki z wieloma certyfikatami podpisywania, możesz użyć 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) } }