Pelajari cara menyesuaikan aplikasi pembayaran Android Anda agar berfungsi dengan Pembayaran Web dan memberikan pengalaman pengguna yang lebih baik bagi pelanggan.
Payment Request API menghadirkan antarmuka bawaan berbasis browser ke web yang memungkinkan pengguna memasukkan informasi pembayaran yang diperlukan dengan lebih mudah daripada sebelumnya. API juga dapat memanggil aplikasi pembayaran khusus platform.
Dibandingkan hanya dengan 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 merupakan tambahan untuk aplikasi pembayaran yang sudah ada, sehingga Anda dapat memanfaatkan basis pengguna Anda.
- Tanda tangan aplikasi pembayaran diperiksa untuk mencegah sideload.
- Aplikasi pembayaran dapat mendukung beberapa metode pembayaran.
- Metode pembayaran apa pun, seperti mata uang kripto, transfer bank, dan lainnya, dapat diintegrasikan. Aplikasi pembayaran di perangkat Android bahkan dapat mengintegrasikan metode yang memerlukan akses ke chip hardware di 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 terdaftar (seperti kartu kredit) yang siap membayar.
- Mengizinkan pelanggan melakukan pembayaran.
- Verifikasi sertifikat penandatanganan penelepon.
Untuk melihat cara kerja Pembayaran Web, lihat demo android-web-payment.
Langkah 1: Biarkan penjual menemukan aplikasi pembayaran Anda
Agar dapat menggunakan aplikasi pembayaran Anda, penjual harus menggunakan Payment Request API dan menentukan metode pembayaran yang Anda dukung menggunakan ID metode pembayaran.
Jika memiliki ID metode pembayaran yang unik untuk aplikasi pembayaran, Anda dapat menyiapkan manifes metode pembayaran sendiri agar 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 mengkueri apakah pelanggan dapat melakukan pembayaran atau tidak. Anda dapat
mengimplementasikan IS_READY_TO_PAY
sebagai layanan Android untuk menjawab kueri ini.
AndroidManifest.xml
Deklarasikan layanan Anda menggunakan 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, browser web akan berasumsi bahwa aplikasi tersebut selalu dapat
melakukan pembayaran.
AIDL
API untuk layanan IS_READY_TO_PAY
ditentukan dalam AIDL. Buat dua file AIDL
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 IsReadyToPayService
yang paling sederhana ditunjukkan dalam contoh
berikut:
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)
Permission
Anda dapat menggunakan Binder.getCallingUid()
untuk memeriksa siapa pemanggil. Perhatikan bahwa Anda
harus melakukannya dalam metode isReadyToPay
, bukan dalam metode onBind
.
override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
try {
val callingPackage = packageManager.getNameForUid(Binder.getCallingUid())
// …
Lihat Memverifikasi sertifikat penandatanganan penelepon tentang cara memverifikasi bahwa paket panggilan memiliki tanda tangan yang tepat.
Langkah 3: Izinkan pelanggan melakukan pembayaran
Penjual memanggil show()
untuk meluncurkan aplikasi
pembayaran
agar pelanggan dapat melakukan pembayaran. Aplikasi pembayaran dipanggil melalui PAY
intent Android
dengan informasi transaksi dalam parameter intent.
Aplikasi pembayaran merespons dengan methodName
dan details
, yang merupakan aplikasi pembayaran
khusus dan buram untuk browser. Browser mengonversi string details
menjadi objek JavaScript untuk penjual melalui deserialisasi JSON, tetapi
tidak menerapkan 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
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 berupa URL absolut
yang valid 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 ini 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
diteruskan sebagai mystore.com
.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
Rantai sertifikat penjual (Rantai sertifikat konteks penjelajahan
tingkat teratas). Null untuk localhost dan file di disk, yang keduanya merupakan konteks
aman tanpa 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 konstruktor dipanggil dari konteks tingkat atas, nilai parameter ini 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 harus dikaitkan oleh aplikasi "push-payment" dengan
status transaksi. Situs penjual akan menggunakan kolom ini untuk membuat kueri
aplikasi "push-payment" untuk status transaksi keluar 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 keberhasilan adalahtrue
,details
harus dibuat sedemikian rupa agarJSON.parse(details)
akan berhasil.
Anda dapat meneruskan RESULT_CANCELED
jika transaksi belum diselesaikan di
aplikasi pembayaran, misalnya, jika pengguna gagal memasukkan kode PIN yang benar untuk
akunnya di aplikasi pembayaran. Browser dapat mengizinkan pengguna memilih
aplikasi pembayaran lain.
setResult(RESULT_CANCELED)
finish()
Jika hasil aktivitas respons pembayaran yang diterima dari aplikasi pembayaran yang dipanggil
ditetapkan ke RESULT_OK
, Chrome akan memeriksa methodName
dan
details
yang tidak kosong. Jika validasi gagal, Chrome akan menampilkan promise yang ditolak dari request.show()
dengan salah satu pesan error yang dihadapi developer berikut:
'Payment app returned invalid response. Missing field "details".'
'Payment app returned invalid response. Missing field "methodName".'
Permission
Aktivitas ini dapat memeriksa pemanggil dengan metode getCallingPackage()
.
val caller: String? = callingPackage
Langkah terakhir adalah memverifikasi sertifikat penandatanganan pemanggil untuk mengonfirmasi bahwa paket panggilan memiliki tanda tangan yang tepat.
Langkah 4: Verifikasi sertifikat penandatanganan penelepon
Anda dapat memeriksa nama paket pemanggil dengan Binder.getCallingUid()
di
IS_READY_TO_PAY
, dan dengan Activity.getCallingPackage()
di PAY
. Untuk
benar-benar memverifikasi bahwa pemanggil adalah browser yang Anda inginkan, Anda harus
memeriksa sertifikat penandatanganannya dan memastikan bahwa sertifikat tersebut cocok dengan nilai
yang benar.
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 browser sertifikat
tunggal karena menangani rotasi sertifikat dengan benar. (Chrome memiliki
satu sertifikat penandatanganan.) Aplikasi yang memiliki beberapa sertifikat penandatanganan tidak dapat
memutarnya.
Jika Anda perlu mendukung level API 27 dan yang lebih lama, atau jika perlu menangani
browser dengan 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) } }