Pelajari cara menyesuaikan aplikasi pembayaran Android Anda agar berfungsi dengan Pembayaran Web dan memberikan pengalaman pengguna yang lebih baik bagi pelanggan.
Payment Request API mengarahkan ke web antarmuka bawaan berbasis browser yang memungkinkan pengguna memasukkan pembayaran yang diperlukan informasi menjadi lebih mudah. API juga dapat memanggil pembayaran khusus platform aplikasi.
Dibandingkan hanya menggunakan Intent Android, Pembayaran Web memungkinkan integrasi yang lebih baik dengan browser, keamanan, dan pengalaman pengguna:
- Aplikasi pembayaran diluncurkan sebagai modal, dalam konteks situs penjual.
- Penerapan ini hanya tambahan untuk aplikasi pembayaran yang sudah ada, sehingga Anda dapat memanfaatkan basis pengguna Anda.
- Tanda tangan aplikasi pembayaran diperiksa untuk mencegah melakukan sideload.
- Aplikasi pembayaran dapat mendukung beberapa metode pembayaran.
- Metode pembayaran apa pun, seperti mata uang kripto, transfer bank, dan lainnya, dapat terintegrasi. Aplikasi pembayaran di perangkat Android bahkan dapat mengintegrasikan metode yang memerlukan akses ke {i>chip<i} perangkat keras pada perangkat.
Dibutuhkan empat langkah untuk menerapkan Pembayaran Web di aplikasi pembayaran Android:
- Permudah penjual menemukan aplikasi pembayaran Anda.
- Beri tahu penjual jika pelanggan memiliki instrumen yang terdaftar (seperti saldo ) yang siap digunakan untuk membayar.
- Mengizinkan pelanggan melakukan pembayaran.
- Verifikasi sertifikat penandatanganan penelepon.
Untuk melihat cara kerja Pembayaran Web, lihat android-web-payment demo.
Langkah 1: Biarkan penjual menemukan aplikasi pembayaran Anda
Agar penjual dapat menggunakan aplikasi pembayaran Anda, mereka harus menggunakan metode Pembayaran Request API dan tentukan metode pembayaran yang Anda dukung menggunakan metode pembayaran ID.
Jika Anda memiliki ID metode pembayaran yang unik untuk aplikasi pembayaran Anda, dapat menyiapkan metode pembayaran Anda sendiri manifes sehingga browser dapat menemukan aplikasi Anda.
Langkah 2: Beri tahu penjual jika pelanggan memiliki instrumen terdaftar yang siap untuk melakukan pembayaran
Penjual dapat memanggil hasEnrolledInstrument()
untuk meminta informasi apakah pelanggan
bisa melakukan pembayaran. Anda dapat
menerapkan IS_READY_TO_PAY
sebagai layanan Android untuk menjawab kueri ini.
AndroidManifest.xml
Mendeklarasikan layanan Anda dengan filter intent dengan tindakan
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>
Layanan IS_READY_TO_PAY
bersifat opsional. Jika tidak ada pengendali intent seperti itu di
aplikasi pembayaran, maka browser web
berasumsi bahwa aplikasi selalu bisa melakukan
pembayaran.
AIDL
API untuk layanan IS_READY_TO_PAY
ditentukan dalam AIDL. Membuat dua AIDL
file dengan konten berikut:
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);
}
Menerapkan IsReadyToPayService
Implementasi paling sederhana dari IsReadyToPayService
ditunjukkan dalam contoh berikut
contoh:
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
}
}
Respons
Layanan dapat mengirimkan responsnya melalui metode handleIsReadyToPay(Boolean)
.
callback?.handleIsReadyToPay(true)
Izin
Anda dapat menggunakan Binder.getCallingUid()
untuk memeriksa siapa pemanggil. Perlu diketahui bahwa Anda
harus melakukannya dalam metode isReadyToPay
, bukan di metode onBind
.
override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
try {
val callingPackage = packageManager.getNameForUid(Binder.getCallingUid())
// …
Lihat Memverifikasi sertifikat penandatanganan penelepon untuk mengetahui caranya untuk memverifikasi bahwa paket panggilan memiliki tanda tangan yang benar.
Langkah 3: Izinkan pelanggan melakukan pembayaran
Penjual memanggil show()
untuk meluncurkan pembayaran
aplikasi
agar pelanggan dapat melakukan pembayaran. Aplikasi pembayaran dipanggil melalui Android
PAY
intent dengan informasi transaksi dalam parameter intent.
Aplikasi pembayaran merespons dengan methodName
dan details
, yang merupakan aplikasi pembayaran
spesifik dan tidak transparan terhadap browser. Browser mengonversi details
ke dalam objek JavaScript untuk penjual melalui deserialisasi JSON, tetapi
tidak memberlakukan validitas apa pun di luar itu. Browser tidak mengubah
details
; nilai parameter tersebut diteruskan langsung ke penjual.
AndroidManifest.xml
Aktivitas dengan filter intent PAY
harus memiliki tag <meta-data>
yang
akan mengidentifikasi ID metode pembayaran default untuk
aplikasi.
Untuk mendukung beberapa metode pembayaran, tambahkan tag <meta-data>
dengan
Resource <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>
resource
harus berupa daftar string, yang masing-masing harus valid,
URL mutlak dengan skema HTTPS seperti yang ditunjukkan di sini.
<?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>
Parameter
Parameter berikut diteruskan ke aktivitas sebagai tambahan Intent:
methodNames
methodData
topLevelOrigin
topLevelCertificateChain
paymentRequestOrigin
total
modifiers
paymentRequestId
val extras: Bundle? = intent?.extras
methodNames
Nama metode yang digunakan. Elemen-elemen tersebut
adalah kunci dalam
Kamus methodData
. Ini adalah metode yang didukung aplikasi pembayaran.
val methodNames: List<String>? = extras.getStringArrayList("methodNames")
methodData
Pemetaan dari setiap methodNames
ke
methodData
.
val methodData: Bundle? = extras.getBundle("methodData")
merchantName
Konten tag HTML <title>
di halaman checkout penjual (
konteks penjelajahan tingkat atas browser).
val merchantName: String? = extras.getString("merchantName")
topLevelOrigin
Asal penjual tanpa skema (Asal tanpa skema dari
konteks penjelajahan tingkat atas). Misalnya, https://mystore.com/checkout
adalah
diteruskan sebagai mystore.com
.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
Rantai sertifikat penjual (Rantai sertifikat tingkat teratas
konteks penjelajahan). Null untuk localhost dan file pada disk, yang keduanya aman
tanpa adanya sertifikat SSL. Setiap Parcelable
adalah Paket dengan
Kunci certificate
dan nilai array byte.
val topLevelCertificateChain: Array<Parcelable>? =
extras.getParcelableArray("topLevelCertificateChain")
val list: List<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p as Bundle).getByteArray("certificate")
}
paymentRequestOrigin
Origin tanpa skema dari konteks penjelajahan iframe yang memanggil konstruktor new
PaymentRequest(methodData, details, options)
di JavaScript. Jika
fungsi konstruktor dipanggil dari konteks tingkat atas, lalu nilai dari
sama dengan nilai parameter topLevelOrigin
.
val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")
total
String JSON yang mewakili jumlah total transaksi.
val total: String? = extras.getString("total")
Berikut adalah contoh konten string:
{"currency":"USD","value":"25.00"}
modifiers
Output JSON.stringify(details.modifiers)
, dengan details.modifiers
hanya berisi supportedMethods
dan total
.
paymentRequestId
Kolom PaymentRequest.id
yang "push-payment" aplikasi harus terkait dengan
status transaksi. Situs penjual akan menggunakan kolom ini untuk mengkueri
"push-payment" aplikasi untuk status transaksi out of band.
val paymentRequestId: String? = extras.getString("paymentRequestId")
Respons
Aktivitas dapat mengirimkan responsnya kembali melalui setResult
dengan RESULT_OK
.
setResult(Activity.RESULT_OK, Intent().apply {
putExtra("methodName", "https://bobbucks.dev/pay")
putExtra("details", "{\"token\": \"put-some-data-here\"}")
})
finish()
Anda harus menentukan dua parameter sebagai tambahan Intent:
methodName
: Nama metode yang digunakan.details
: String JSON yang berisi informasi yang diperlukan penjual untuk menyelesaikan transaksi. Jika berhasiltrue
, makadetails
harus dibangun sedemikian rupa sehinggaJSON.parse(details)
akan berhasil.
Anda dapat meneruskan RESULT_CANCELED
jika transaksi belum diselesaikan di
aplikasi pembayaran, misalnya, jika pengguna gagal
mengetikkan kode PIN yang benar untuk
akun mereka di aplikasi pembayaran. Browser dapat membiarkan pengguna memilih
aplikasi pembayaran lain.
setResult(RESULT_CANCELED)
finish()
Jika hasil aktivitas dari respons pembayaran diterima dari pembayaran yang dipanggil
aplikasi disetel ke RESULT_OK
, maka Chrome akan memeriksa methodName
yang tidak kosong dan
details
dalam tambahannya. Jika validasi gagal, Chrome akan menampilkan penolakan
promise dari request.show()
dengan salah satu developer berikut mengalami error
pesan:
'Payment app returned invalid response. Missing field "details".'
'Payment app returned invalid response. Missing field "methodName".'
Izin
Aktivitas ini dapat memeriksa pemanggil dengan metode getCallingPackage()
.
val caller: String? = callingPackage
Langkah terakhir adalah memverifikasi sertifikat penandatanganan pemanggil untuk mengonfirmasi bahwa memiliki tanda tangan yang benar.
Langkah 4: Verifikasi sertifikat penandatanganan penelepon
Anda dapat memeriksa nama paket pemanggil dengan Binder.getCallingUid()
di
IS_READY_TO_PAY
, dan dengan Activity.getCallingPackage()
dalam PAY
. Untuk
benar-benar memverifikasi bahwa pemanggil adalah {i>browser<i} yang Anda inginkan, Anda harus
periksa sertifikat penandatanganannya dan
pastikan sertifikatnya cocok dengan
dengan sejumlah nilai.
Jika Anda menargetkan API level 28 dan yang lebih tinggi, serta berintegrasi dengan browser
yang memiliki satu sertifikat
penandatanganan, Anda dapat menggunakan
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()
lebih disarankan untuk satu sertifikat
browser Anda, karena metode ini
menangani rotasi sertifikat dengan benar. (Chrome memiliki
sertifikat penandatanganan tunggal.) Aplikasi yang memiliki beberapa sertifikat penandatanganan tidak dapat
memutarnya.
Jika Anda perlu mendukung level API 27 dan yang lebih lama, atau jika Anda perlu menangani
{i>browser<i} yang memiliki beberapa sertifikat
penandatanganan, Anda dapat menggunakan
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) } }