Menyediakan informasi pengiriman dan kontak dari aplikasi pembayaran Android

Cara mengupdate aplikasi pembayaran Android untuk memberikan alamat pengiriman dan informasi kontak pembayar dengan Web Payments API.

Sharify Sahel
Sahel Sharify

Memasukkan alamat pengiriman dan informasi kontak melalui formulir web dapat menjadi pengalaman yang rumit bagi pelanggan. Hal ini dapat menyebabkan error dan menurunkan rasio konversi.

Itulah sebabnya Payment Request API mendukung fitur untuk meminta alamat pengiriman dan informasi kontak. Hal ini memberikan beberapa manfaat:

  • Pengguna dapat memilih alamat yang tepat hanya dengan beberapa ketukan.
  • Alamat selalu ditampilkan dalam format standar.
  • Mengirimkan alamat yang salah kecil kemungkinannya.

Browser dapat mengalihkan pengumpulan alamat pengiriman dan informasi kontak ke aplikasi pembayaran untuk memberikan pengalaman pembayaran terpadu. Fungsi ini disebut delegasi.

Jika memungkinkan, Chrome akan mendelegasikan pengumpulan alamat pengiriman dan informasi kontak pelanggan ke aplikasi pembayaran Android yang dipanggil. Delegasi ini mengurangi hambatan selama checkout.

Situs penjual dapat memperbarui opsi pengiriman dan harga total secara dinamis, bergantung pada pilihan alamat pengiriman dan opsi pengiriman pelanggan.

Penerapan opsi pengiriman dan alamat pengiriman. Lihat pengaruhnya terhadap opsi pengiriman dan harga total secara dinamis.

Untuk menambahkan dukungan delegasi ke aplikasi pembayaran Android yang sudah ada, terapkan langkah-langkah berikut:

  1. Deklarasikan delegasi yang didukung.
  2. Mengurai tambahan intent PAY untuk opsi pembayaran yang diperlukan.
  3. Berikan informasi yang diperlukan dalam respons pembayaran.
  4. [Opsional] Mendukung alur dinamis:
    1. Beri tahu penjual tentang perubahan pada opsi metode pembayaran, alamat pengiriman, atau pengiriman yang dipilih pengguna.
    2. Menerima detail pembayaran yang diperbarui dari penjual (misalnya, total jumlah yang disesuaikan berdasarkan biaya opsi pengiriman yang dipilih).

Mendeklarasikan delegasi yang didukung

Browser perlu mengetahui daftar informasi tambahan yang dapat diberikan oleh aplikasi pembayaran agar dapat mendelegasikan pengumpulan informasi tersebut ke aplikasi Anda. Deklarasikan delegasi yang didukung sebagai <meta-data> di AndroidManifest.xml aplikasi Anda.

<activity
  android:name=".PaymentActivity"
  …
  <meta-data
    android:name="org.chromium.payment_supported_delegations"
    android:resource="@array/supported_delegations" />
</activity>

<resource> harus berupa daftar string yang dipilih dari nilai valid berikut:

[ "payerName", "payerEmail", "payerPhone", "shippingAddress" ]

Contoh berikut hanya dapat memberikan alamat pengiriman dan alamat email pembayar.

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string-array name="supported_delegations">
    <item>payerEmail</item>
    <item>shippingAddress</item>
  </string-array>
</resources>

Mengurai tambahan intent PAY untuk opsi pembayaran yang diperlukan

Penjual dapat menentukan informasi tambahan yang diperlukan menggunakan kamus paymentOptions. Chrome akan menyediakan daftar opsi yang diperlukan yang dapat disediakan aplikasi Anda dengan meneruskan parameter berikut ke aktivitas PAY sebagai Tambahan intent.

paymentOptions

paymentOptions adalah subset opsi pembayaran yang ditentukan penjual yang dukungan delegasinya telah dideklarasikan oleh aplikasi Anda.

val paymentOptions: Bundle? = extras.getBundle("paymentOptions")
val requestPayerName: Boolean? = paymentOptions?.getBoolean("requestPayerName")
val requestPayerPhone: Boolean? = paymentOptions?.getBoolean("requestPayerPhone")
val requestPayerEmail: Boolean? = paymentOptions?.getBoolean("requestPayerEmail")
val requestShipping: Boolean? = paymentOptions?.getBoolean("requestShipping")
val shippingType: String? = paymentOptions?.getString("shippingType")

Properti tersebut dapat mencakup parameter berikut:

  • requestPayerName - Boolean yang menunjukkan apakah nama pembayar diperlukan atau tidak.
  • requestPayerPhone - Boolean yang menunjukkan apakah ponsel pembayar diperlukan atau tidak.
  • requestPayerEmail - Boolean yang menunjukkan apakah email pembayar diperlukan atau tidak.
  • requestShipping - Boolean yang menunjukkan apakah informasi pengiriman diperlukan atau tidak.
  • shippingType - String yang menampilkan jenis pengiriman. Jenis pengiriman dapat berupa "shipping", "delivery", atau "pickup". Aplikasi Anda dapat menggunakan petunjuk ini di UI saat meminta alamat pengguna atau pilihan opsi pengiriman.

shippingOptions

shippingOptions adalah array yang dapat dibagikan dari opsi pengiriman yang ditentukan penjual. Parameter ini hanya akan ada jika paymentOptions.requestShipping == true.

val shippingOptions: List<ShippingOption>? =
    extras.getParcelableArray("shippingOptions")?.mapNotNull {
        p -> from(p as Bundle)
    }

Setiap opsi pengiriman adalah Bundle dengan kunci berikut.

  • id - ID opsi pengiriman.
  • label - Label opsi pengiriman yang ditampilkan kepada pengguna.
  • amount - Paket biaya pengiriman yang berisi kunci currency dan value dengan nilai string.
    • currency menampilkan mata uang biaya pengiriman, sebagai kode alfabet 3 huruf ISO4217 yang tersusun dengan baik
    • value menunjukkan nilai biaya pengiriman, sebagai nilai uang desimal yang valid
  • selected - Apakah opsi pengiriman harus dipilih atau tidak saat aplikasi pembayaran menampilkan opsi pengiriman.

Semua kunci selain selected memiliki nilai string. selected memiliki nilai boolean.

val id: String = bundle.getString("id")
val label: String = bundle.getString("label")
val amount: Bundle = bundle.getBundle("amount")
val selected: Boolean = bundle.getBoolean("selected", false)

Memberikan informasi yang diperlukan dalam respons pembayaran

Aplikasi Anda harus menyertakan informasi tambahan yang diperlukan dalam responsnya terhadap aktivitas PAY.

Untuk melakukannya, parameter berikut harus ditetapkan sebagai ekstra Intent:

  • payerName - Nama lengkap pembayar. String ini harus berupa string yang tidak kosong jika paymentOptions.requestPayerName bernilai benar (true).
  • payerPhone - Nomor telepon pembayar. String ini harus berupa string yang tidak kosong jika paymentOptions.requestPayerPhone bernilai benar (true).
  • payerEmail - Alamat email pembayar. String ini harus berupa string yang tidak kosong jika paymentOptions.requestPayerEmail bernilai benar (true).
  • shippingAddress - Alamat pengiriman yang diberikan pengguna. Paket ini harus berupa paket yang tidak kosong jika paymentOptions.requestShipping bernilai benar (true). Paket tersebut harus memiliki kunci berikut yang mewakili berbagai bagian dalam alamat fisik.
    • city
    • countryCode
    • dependentLocality
    • organization
    • phone
    • postalCode
    • recipient
    • region
    • sortingCode
    • addressLine Semua kunci selain addressLine memiliki nilai string. addressLine adalah array string.
  • shippingOptionId - ID opsi pengiriman yang dipilih pengguna. Nilai ini harus berupa string yang tidak kosong jika paymentOptions.requestShipping bernilai benar (true).

Memvalidasi respons pembayaran

Jika hasil aktivitas respons pembayaran yang diterima dari aplikasi pembayaran yang dipanggil disetel ke RESULT_OK, Chrome akan memeriksa informasi tambahan yang diperlukan dalam tambahannya. Jika validasi gagal, Chrome akan menampilkan promise yang ditolak dari request.show() dengan salah satu pesan error yang ditampilkan kepada developer berikut:

'Payment app returned invalid response. Missing field "payerEmail".'
'Payment app returned invalid response. Missing field "payerName".'
'Payment app returned invalid response. Missing field "payerPhone".'
'Payment app returned invalid shipping address in response.'
'... is not a valid CLDR country code, should be 2 upper case letters [A-Z]'
'Payment app returned invalid response. Missing field "shipping option".'

Contoh kode berikut adalah contoh respons yang valid:

fun Intent.populateRequestedPaymentOptions() {
    if (requestPayerName) {
        putExtra("payerName", "John Smith")
    }
    if (requestPayerPhone) {
        putExtra("payerPhone", "4169158200")
    }
    if (requestPayerEmail) {
        putExtra("payerEmail", "john.smith@gmail.com")
    }
    if(requestShipping) {
        val address: Bundle = Bundle()
        address.putString("countryCode", "CA")
        val addressLines: Array<String> =
                arrayOf<String>("111 Richmond st. West")
        address.putStringArray("addressLines", addressLines)
        address.putString("region", "Ontario")
        address.putString("city", "Toronto")
        address.putString("postalCode", "M5H2G4")
        address.putString("recipient", "John Smith")
        address.putString("phone", "4169158200")
        putExtra("shippingAddress", address)
        putExtra("shippingOptionId", "standard")
    }
}

Opsional: Mendukung alur dinamis

Terkadang, total biaya transaksi meningkat, seperti saat pengguna memilih opsi pengiriman ekspres, atau saat daftar opsi pengiriman yang tersedia atau harganya berubah saat pengguna memilih alamat pengiriman internasional. Saat memberikan alamat atau opsi pengiriman yang dipilih pengguna, aplikasi Anda harus dapat memberi tahu penjual tentang perubahan alamat atau opsi pengiriman, dan menampilkan detail pembayaran yang diperbarui kepada pengguna (disediakan oleh penjual).

AIDL

Untuk memberi tahu penjual tentang perubahan baru, gunakan layanan PaymentDetailsUpdateService yang dideklarasikan di AndroidManifest.xml di Chrome. Untuk menggunakan layanan ini, buat dua file AIDL dengan konten berikut:

app/src/main/aidl/org/chromium/components/payments/IPaymentDetailsUpdateService

package org.chromium.components.payments;
import android.os.Bundle;

interface IPaymentDetailsUpdateServiceCallback {
    oneway void updateWith(in Bundle updatedPaymentDetails);

    oneway void paymentDetailsNotUpdated();
}

app/src/main/aidl/org/chromium/components/payments/IPaymentDetailsUpdateServiceCallback

package org.chromium.components.payments;
import android.os.Bundle;
import org.chromium.components.payments.IPaymentDetailsUpdateServiceCallback;

interface IPaymentDetailsUpdateService {
    oneway void changePaymentMethod(in Bundle paymentHandlerMethodData,
            IPaymentDetailsUpdateServiceCallback callback);

    oneway void changeShippingOption(in String shippingOptionId,
            IPaymentDetailsUpdateServiceCallback callback);

    oneway void changeShippingAddress(in Bundle shippingAddress,
            IPaymentDetailsUpdateServiceCallback callback);
}

Beri tahu penjual tentang perubahan pada metode pembayaran, alamat pengiriman, atau opsi pengiriman yang dipilih pengguna

private fun bind() {
    // The action is introduced in Chrome version 92, which supports the service in Chrome
    // and other browsers (e.g., WebLayer).
    val newIntent = Intent("org.chromium.intent.action.UPDATE_PAYMENT_DETAILS")
        .setPackage(callingBrowserPackage)
    if (packageManager.resolveService(newIntent, PackageManager.GET_RESOLVED_FILTER) == null) {
        // Fallback to Chrome-only approach.
        newIntent.setClassName(
            callingBrowserPackage,
            "org.chromium.components.payments.PaymentDetailsUpdateService")
        newIntent.action = IPaymentDetailsUpdateService::class.java.name
    }
    isBound = bindService(newIntent, connection, Context.BIND_AUTO_CREATE)
}

private val connection = object : ServiceConnection {
    override fun onServiceConnected(className: ComponentName, service: IBinder) {
        val service = IPaymentDetailsUpdateService.Stub.asInterface(service)
        try {
            if (isOptionChange) {
                service?.changeShippingOption(selectedOptionId, callback)
            } else (isAddressChange) {
                service?.changeShippingAddress(selectedAddress, callback)
            } else {
                service?.changePaymentMethod(methodData, callback)
            }
        } catch (e: RemoteException) {
            // Handle the remote exception
        }
    }
}

callingPackageName yang digunakan untuk intent awal layanan dapat memiliki salah satu nilai berikut, bergantung pada browser yang telah memulai permintaan pembayaran.

Saluran Chrome Nama Paket
Stabil "com.android.chrome"
Beta "com.chrome.beta"
Pengembang "com.chrome.dev"
Canary "com.chrome.canary"
Chromium "org.chromium.chrome"
Kotak Penelusuran Cepat Google (penyemat WebLayer) "com.google.android.googlequicksearchbox"

changePaymentMethod

Memberi tahu penjual tentang perubahan pada metode pembayaran yang dipilih pengguna. Paket paymentHandlerMethodData berisi kunci methodName dan details opsional dengan nilai string. Chrome akan memeriksa paket yang tidak kosong dengan methodName yang tidak kosong dan mengirimkan updatePaymentDetails dengan salah satu pesan error berikut melalui callback.updateWith jika validasi gagal.

'Method data required.'
'Method name required.'

changeShippingOption

Memberi tahu penjual tentang perubahan pada opsi pengiriman yang dipilih pengguna. shippingOptionId harus berupa ID salah satu opsi pengiriman yang ditentukan penjual. Chrome akan memeriksa shippingOptionId yang tidak kosong dan mengirim updatePaymentDetails dengan pesan error berikut melalui callback.updateWith jika validasi gagal.

'Shipping option identifier required.'

changeShippingAddress

Memberi tahu penjual tentang perubahan pada alamat pengiriman yang diberikan pengguna. Chrome akan memeriksa paket shippingAddress yang tidak kosong dengan countryCode yang valid dan mengirim updatePaymentDetails dengan pesan error berikut melalui callback.updateWith jika validasi gagal.

'Payment app returned invalid shipping address in response.'

Pesan error status tidak valid

Jika Chrome mengalami status yang tidak valid setelah menerima permintaan perubahan, Chrome akan memanggil callback.updateWith dengan paket updatePaymentDetails yang disamarkan. Paket hanya akan berisi kunci error dengan "Invalid state". Contoh status yang tidak valid adalah:

  • Saat Chrome masih menunggu respons penjual terhadap perubahan sebelumnya (seperti peristiwa perubahan yang sedang berlangsung).
  • ID opsi pengiriman yang disediakan aplikasi pembayaran bukan milik opsi pengiriman apa pun yang ditentukan penjual.

Terima detail pembayaran yang diperbarui dari penjual

private fun unbind() {
    if (isBound) {
        unbindService(connection)
        isBound = false
    }
}

private val callback: IPaymentDetailsUpdateServiceCallback =
    object : IPaymentDetailsUpdateServiceCallback.Stub() {
        override fun paymentDetailsNotUpdated() {
            // Payment request details have not changed.
            unbind()
        }

        override fun updateWith(updatedPaymentDetails: Bundle) {
            newPaymentDetails = updatedPaymentDetails
            unbind()
        }
    }

updatePaymentDetails adalah paket yang setara dengan kamus PaymentRequestDetailsUpdate WebIDL (setelah menyamarkan kolom modifiers) dan berisi kunci opsional berikut:

  • total - Paket yang berisi kunci currency dan value, kedua kunci memiliki nilai string
  • shippingOptions - Array shipping options
  • error - String yang berisi pesan error umum (misalnya, saat changeShippingOption tidak memberikan ID opsi pengiriman yang valid)
  • stringifiedPaymentMethodErrors - String JSON yang mewakili error validasi untuk metode pembayaran
  • addressErrors - Paket dengan kunci opsional yang identik dengan alamat pengiriman dan nilai string. Setiap kunci mewakili error validasi terkait bagian alamat pengiriman yang sesuai.

Kunci yang tidak ada berarti nilainya tidak berubah.