Cung cấp thông tin vận chuyển và thông tin liên hệ qua ứng dụng thanh toán của Android

Cách cập nhật ứng dụng thanh toán trên Android để cung cấp địa chỉ giao hàng và thông tin liên hệ của người thanh toán bằng API thanh toán trên web.

Sahel Sharify
Sahel Sharify

Việc nhập địa chỉ giao hàng và thông tin liên hệ qua biểu mẫu trên web có thể là một trải nghiệm rườm rà đối với khách hàng. Việc này có thể gây ra lỗi và làm giảm tỷ lệ chuyển đổi.

Đó là lý do tại sao Payment Request API hỗ trợ tính năng yêu cầu địa chỉ giao hàng và thông tin liên hệ. Điều này mang lại nhiều lợi ích:

  • Người dùng có thể chọn đúng địa chỉ chỉ bằng vài lần nhấn.
  • Địa chỉ luôn được trả về ở định dạng chuẩn.
  • Khả năng bạn gửi địa chỉ không chính xác sẽ ít hơn.

Các trình duyệt có thể trì hoãn việc thu thập địa chỉ giao hàng và thông tin liên hệ cho một ứng dụng thanh toán để cung cấp trải nghiệm thanh toán hợp nhất. Chức năng này được gọi là uỷ quyền.

Bất cứ khi nào có thể, Chrome sẽ uỷ quyền thu thập địa chỉ giao hàng và thông tin liên hệ của khách hàng cho ứng dụng thanh toán Android đã gọi. Việc uỷ quyền này sẽ giúp giảm bớt phiền hà trong quá trình thanh toán.

Trang web của người bán có thể tự động cập nhật các lựa chọn giao hàng và tổng giá tuỳ thuộc vào lựa chọn của khách hàng về địa chỉ giao hàng và phương thức vận chuyển.

Thay đổi về cách giao hàng và địa chỉ giao hàng trong thực tế. Xem ảnh hưởng của thuộc tính này đến các lựa chọn vận chuyển và tổng giá một cách linh động.

Để thêm tính năng hỗ trợ uỷ quyền vào một ứng dụng thanh toán hiện có trên Android, hãy triển khai các bước sau:

  1. Khai báo các hoạt động uỷ quyền được hỗ trợ.
  2. Phân tích cú pháp phần bổ sung ý định PAY cho các tuỳ chọn thanh toán bắt buộc.
  3. Cung cấp thông tin bắt buộc khi phản hồi thông tin thanh toán.
  4. [Không bắt buộc] Hỗ trợ luồng động:
    1. Thông báo cho người bán về những thay đổi trong phương thức thanh toán, địa chỉ giao hàng hoặc lựa chọn giao hàng mà người dùng đã chọn.
    2. Nhận thông tin thanh toán đã cập nhật từ người bán (ví dụ: tổng số tiền điều chỉnh dựa trên chi phí của tuỳ chọn vận chuyển đã chọn).

Khai báo các lần uỷ quyền được hỗ trợ

Trình duyệt cần biết danh sách thông tin bổ sung mà ứng dụng thanh toán của bạn có thể cung cấp để có thể uỷ quyền thu thập thông tin đó cho ứng dụng. Hãy khai báo các hoạt động uỷ quyền được hỗ trợ dưới dạng <meta-data> trong AndroidManifest.xml của ứng dụng.

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

<resource> phải là một danh sách chuỗi được chọn từ các giá trị hợp lệ sau:

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

Ví dụ sau chỉ có thể cung cấp địa chỉ giao hàng và địa chỉ email của người thanh toán.

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

Phân tích cú pháp dữ liệu bổ sung ý định PAY cho những lựa chọn thanh toán bắt buộc

Người bán có thể chỉ định thông tin bắt buộc bổ sung bằng từ điển paymentOptions. Chrome sẽ cung cấp danh sách các tuỳ chọn bắt buộc mà ứng dụng của bạn có thể cung cấp bằng cách truyền các tham số sau đây tới hoạt động PAY dưới dạng Ý định bổ sung.

paymentOptions

paymentOptions là tập hợp con các tuỳ chọn thanh toán do người bán chỉ định mà ứng dụng của bạn đã khai báo hỗ trợ uỷ quyền.

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")

Tham số này có thể bao gồm các tham số sau:

  • requestPayerName – Boolean cho biết liệu tên của người thanh toán có bắt buộc hay không.
  • requestPayerPhone – Boolean cho biết liệu có bắt buộc phải có điện thoại của người thanh toán hay không.
  • requestPayerEmail – Boolean cho biết email của người thanh toán có bắt buộc hay không.
  • requestShipping – Boolean cho biết liệu có bắt buộc phải có thông tin vận chuyển hay không.
  • shippingType – Chuỗi cho thấy loại hình vận chuyển. Loại hình vận chuyển có thể là "shipping", "delivery" hoặc "pickup". Ứng dụng có thể dùng gợi ý này trong giao diện người dùng khi yêu cầu người dùng cung cấp địa chỉ hoặc phương thức giao hàng.

shippingOptions

shippingOptions là các mảng theo gói gồm các lựa chọn vận chuyển do người bán chỉ định. Tham số này sẽ chỉ tồn tại khi paymentOptions.requestShipping == true.

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

Mỗi cách tính phí vận chuyển là một Bundle với các khoá sau.

  • id – Mã nhận dạng cách tính phí vận chuyển.
  • label – Nhãn tuỳ chọn vận chuyển hiển thị cho người dùng.
  • amount – Gói phí vận chuyển chứa các khoá currencyvalue có giá trị chuỗi.
  • selected – Liệu có nên chọn cách tính phí vận chuyển hay không khi ứng dụng thanh toán hiển thị các lựa chọn vận chuyển.

Tất cả khoá không phải selected đều có giá trị chuỗi. selected có giá trị 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)

Cung cấp thông tin bắt buộc trong phản hồi thanh toán

Ứng dụng của bạn phải bao gồm thông tin bổ sung bắt buộc khi phản hồi hoạt động PAY.

Để làm vậy, bạn phải chỉ định các tham số sau làm ý định bổ sung:

  • payerName – Tên đầy đủ của người thanh toán. Đây phải là một chuỗi không trống khi paymentOptions.requestPayerName là true.
  • payerPhone – Số điện thoại của người thanh toán. Đây phải là một chuỗi không trống khi paymentOptions.requestPayerPhone là true.
  • payerEmail – Địa chỉ email của người thanh toán. Đây phải là một chuỗi không trống khi paymentOptions.requestPayerEmail là đúng.
  • shippingAddress – Địa chỉ giao hàng do người dùng cung cấp. Đây phải là một gói không trống khi paymentOptions.requestShipping là đúng. Gói phải có các khoá sau đại diện cho các phần khác nhau ở một địa chỉ thực tế.
    • city
    • countryCode
    • dependentLocality
    • organization
    • phone
    • postalCode
    • recipient
    • region
    • sortingCode
    • addressLine Tất cả các khoá khác ngoài addressLine đều có giá trị chuỗi. addressLine là một mảng chuỗi.
  • shippingOptionId – Giá trị nhận dạng của cách tính phí vận chuyển do người dùng chọn. Đây phải là một chuỗi không trống khi paymentOptions.requestShipping là true.

Xác thực phản hồi về khoản thanh toán

Nếu kết quả hoạt động của một phản hồi thanh toán nhận được từ ứng dụng thanh toán đã gọi được đặt thành RESULT_OK, thì Chrome sẽ kiểm tra để tìm thông tin bổ sung bắt buộc trong các ứng dụng bổ sung. Nếu xác thực không thành công, Chrome sẽ trả về một lời hứa bị từ chối từ request.show() kèm theo một trong các thông báo lỗi sau đây dành cho nhà phát triển:

'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".'

Mã mẫu sau đây là ví dụ về phản hồi hợp lệ:

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")
    }
}

Không bắt buộc: Hỗ trợ luồng động

Đôi khi, tổng chi phí của một giao dịch tăng lên, chẳng hạn như khi người dùng chọn tuỳ chọn vận chuyển nhanh, hoặc khi danh sách các tuỳ chọn vận chuyển có sẵn hoặc giá của các tuỳ chọn thay đổi khi người dùng chọn một địa chỉ giao hàng quốc tế. Khi cung cấp địa chỉ hoặc tuỳ chọn giao hàng do người dùng chọn, ứng dụng của bạn có thể thông báo cho người bán về mọi thay đổi đối với địa chỉ giao hàng hoặc tuỳ chọn và cho người dùng thấy thông tin thanh toán mới (do người bán cung cấp).

AIDL

Để thông báo cho người bán về các thay đổi mới, hãy sử dụng dịch vụ PaymentDetailsUpdateService được khai báo trong AndroidManifest.xml của Chrome. Để sử dụng dịch vụ này, hãy tạo 2 tệp AIDL với nội dung sau:

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);
}

Thông báo cho người bán về những thay đổi đối với phương thức thanh toán, địa chỉ giao hàng hoặc lựa chọn giao hàng mà người dùng đã chọn

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 dùng cho ý định bắt đầu của dịch vụ có thể có một trong các giá trị sau, tuỳ thuộc vào trình duyệt đã bắt đầu yêu cầu thanh toán.

Kênh Chrome Tên gói
Ổn định "com.android.chrome"
Beta "com.chrome.beta"
Nhà phát triển "com.chrome.dev"
Canary "com.chrome.canary"
Chromium "org.chromium.chrome"
Hộp Tìm kiếm Nhanh của Google (trình nhúng WebLayer) "com.google.android.googlequicksearchbox"

changePaymentMethod

Thông báo cho người bán về những thay đổi đối với phương thức thanh toán do người dùng chọn. Gói paymentHandlerMethodData chứa các khoá methodNamedetails không bắt buộc, cả hai đều có giá trị chuỗi. Chrome sẽ kiểm tra một gói không trống có methodName không trống và gửi updatePaymentDetails kèm theo một trong các thông báo lỗi sau qua callback.updateWith nếu quá trình xác thực không thành công.

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

changeShippingOption

Thông báo cho người bán về những thay đổi đối với hình thức vận chuyển do người dùng chọn. shippingOptionId phải là giá trị nhận dạng của một trong các lựa chọn vận chuyển do người bán chỉ định. Chrome sẽ kiểm tra một shippingOptionId không trống và gửi một updatePaymentDetails kèm theo thông báo lỗi sau đây qua callback.updateWith nếu quá trình xác thực không thành công.

'Shipping option identifier required.'

changeShippingAddress

Thông báo cho người bán về những thay đổi đối với địa chỉ giao hàng do người dùng cung cấp. Chrome sẽ kiểm tra gói shippingAddress không trống có countryCode hợp lệ và gửi updatePaymentDetails kèm theo thông báo lỗi sau đây qua callback.updateWith nếu quá trình xác thực không thành công.

'Payment app returned invalid shipping address in response.'

Thông báo lỗi về trạng thái không hợp lệ

Nếu gặp trạng thái không hợp lệ khi nhận được bất kỳ yêu cầu thay đổi nào, Chrome sẽ gọi callback.updateWith với một gói updatePaymentDetails đã bị loại bỏ. Gói này sẽ chỉ chứa khoá error"Invalid state". Ví dụ về trạng thái không hợp lệ:

  • Khi Chrome vẫn đang chờ người bán phản hồi một thay đổi trước đó (chẳng hạn như một sự kiện thay đổi đang diễn ra).
  • Mã nhận dạng tuỳ chọn vận chuyển do ứng dụng thanh toán cung cấp không thuộc bất kỳ lựa chọn vận chuyển nào do người bán chỉ định.

Nhận thông tin thanh toán mới từ người bán

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 là gói tương đương với từ điển PaymentRequestDetailsUpdate WebIDL (sau khi loại bỏ trường modifiers) và chứa các khoá không bắt buộc sau:

  • total – Một gói chứa các khoá currencyvalue, cả hai khoá đều có giá trị chuỗi
  • shippingOptions – Mảng các lựa chọn vận chuyển theo gói
  • error – Một chuỗi chứa thông báo lỗi chung (ví dụ: khi changeShippingOption không cung cấp giá trị nhận dạng tuỳ chọn vận chuyển hợp lệ)
  • stringifiedPaymentMethodErrors – Chuỗi JSON thể hiện lỗi xác thực của phương thức thanh toán
  • addressErrors – Một gói có các khoá tuỳ chọn giống hệt với địa chỉ vận chuyển và giá trị chuỗi. Mỗi khoá đại diện cho một lỗi xác thực liên quan đến phần tương ứng của địa chỉ giao hàng.

Khoá vắng mặt có nghĩa là giá trị của khoá không thay đổi.