Cung cấp thông tin vận chuyển và thông tin liên hệ từ một ứng dụng thanh toán trên Android

Cách cập nhật ứng dụng thanh toá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 Web Payments API.

Sahel Sharify
Sahel Sharify

Xuất bản: ngày 17 tháng 7 năm 2020, Cập nhật lần gần đây nhất: ngày 27 tháng 5 năm 2025

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

Đó là lý do Payment Request API hỗ trợ một 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 hoá.
  • Ít có khả năng bạn gửi địa chỉ không chính xác.

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 để mang đến 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 cho ứng dụng thanh toán Android được gọi để thu thập địa chỉ giao hàng và thông tin liên hệ của khách hàng. Việc uỷ quyền này giúp giảm bớt khó khăn trong quá trình thanh toán.

Trang web của người bán có thể cập nhật linh hoạt các lựa chọn vận chuyển 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à lựa chọn vận chuyển.

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

Để thêm tính năng hỗ trợ uỷ quyền vào một ứng dụng thanh toán Android hiện có, 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 các phần bổ sung về ý định PAY cho các lựa chọn thanh toán bắt buộc.
  3. Cung cấp thông tin bắt buộc trong phản hồi thanh toán.
  4. Không bắt buộc: Hỗ trợ luồng linh động:
    1. 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.
    2. Nhận thông tin thanh toán mới 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 lựa chọn vận chuyển đã chọn).

Khai báo các hoạt động 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 để trình duyệt có thể uỷ quyền thu thập thông tin đó cho ứng dụng của bạn. Khai báo các uỷ quyền được hỗ trợ dưới dạng <meta-data> trong tệp AndroidManifest.xml của ứng dụng.

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

android:resource phải trỏ đến một <string-array> chứa tất cả hoặc một số giá trị sau:

  • payerName
  • payerEmail
  • payerPhone
  • shippingAddress

Ví dụ sau đây 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="chromium_payment_supported_delegations">
    <item>payerEmail</item>
    <item>shippingAddress</item>
  </string-array>
</resources>

Phân tích cú pháp các mã bổ sung ý định PAY cho các lựa chọn thanh toán bắt buộc

Người bán có thể chỉ định thêm thông tin bắt buộc bằng cách sử dụng từ điển paymentOptions. Chrome sẽ cung cấp danh sách các lựa 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 paymentOptions các phần bổ sung của Intent đến hoạt động PAY.

paymentOptions

paymentOptions là một nhóm nhỏ các lựa 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.

Kotlin

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

Java

Bundle paymentOptions = extras.getBundle("paymentOptions");
if (paymentOptions != null) {
    Boolean requestPayerName = paymentOptions.getBoolean("requestPayerName");
    Boolean requestPayerPhone = paymentOptions.getBoolean("requestPayerPhone");
    Boolean requestPayerEmail = paymentOptions.getBoolean("requestPayerEmail");
    Boolean requestShipping = paymentOptions.getBoolean("requestShipping");
    String shippingType = paymentOptions.getString("shippingType");
}

Bạn có thể thêm các tham số sau:

  • requestPayerName – Giá trị boolean cho biết tên của người thanh toán có bắt buộc hay không.
  • requestPayerPhone – Giá trị boolean cho biết người thanh toán có bắt buộc phải có số điện thoại hay không.
  • requestPayerEmail – Giá trị boolean cho biết người thanh toán có bắt buộc phải cung cấp email hay không.
  • requestShipping – Giá trị boolean cho biết thông tin vận chuyển có bắt buộc hay không.
  • shippingType – Chuỗi cho biết 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ủa bạn có thể sử 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 lựa chọn về cách vận chuyển.

shippingOptions

shippingOptions là mảng có thể phân chia 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.

Kotlin

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

Java

Parcelable[] shippingOptions = extras.getParcelableArray("shippingOptions");
for (Parcelable it : shippingOptions) {
  if (it != null && it instanceof Bundle) {
    Bundle shippingOption = (Bundle) it;
  }
}

Mỗi lựa chọn vận chuyển là một Bundle có các khoá sau.

  • id – Giá trị nhận dạng của lựa chọn vận chuyển.
  • label – Nhãn lựa chọn vận chuyển mà người dùng nhìn thấy.
  • amount – Gói chi phí vận chuyển chứa các khoá currencyvalue có giá trị chuỗi.
  • selected – Liệu bạn có nên chọn lựa chọn vận chuyển khi ứng dụng thanh toán hiển thị các lựa chọn vận chuyển hay không.

Tất cả các khoá khác ngoài selected đều có giá trị chuỗi. selected có giá trị boolean.

Kotlin

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)

Java

String id = bundle.getString("id");
String label = bundle.getString("label");
Bundle amount = bundle.getBundle("amount");
Boolean selected = 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 có thêm thông tin bắt buộc trong phản hồi cho hoạt động PAY.

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

  • payerName – Họ và tên 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à true.
  • 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à true. Gói này phải có các khoá sau đây, đại diện cho các phần khác nhau trong địa chỉ thực.
    • countryCode
    • postalCode
    • sortingCode
    • region
    • city
    • dependentLocality
    • addressLine
    • organization
    • recipient
    • phone 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 lựa chọn 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 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 được gọi được đặt thành RESULT_OK, thì Chrome sẽ kiểm tra thông tin bổ sung bắt buộc trong các phần bổ sung của ứng dụng đó. Nếu quá trình 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() cùng với 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".'

Đoạn mã sau đây là ví dụ về một phản hồi hợp lệ:

Kotlin

fun Intent.populateRequestedPaymentOptions() {
    if (requestPayerName) {
        putExtra("payerName", "John Smith")
    }
    if (requestPayerPhone) {
        putExtra("payerPhone", "5555555555")
    }
    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", "5555555555")
        putExtra("shippingAddress", address)
        putExtra("shippingOptionId", "standard")
    }
}

Java

private Intent populateRequestedPaymentOptions() {
    Intent result = new Intent();
    if (requestPayerName) {
        result.putExtra("payerName", "John Smith");
    }
    if (requestPayerPhone) {
        presult.utExtra("payerPhone", "5555555555");
    }
    if (requestPayerEmail) {
        result.putExtra("payerEmail", "john.smith@gmail.com");
    }
    if (requestShipping) {
        Bundle address = new Bundle();
        address.putExtra("countryCode", "CA");
        address.putExtra("postalCode", "M5H2G4");
        address.putExtra("region", "Ontario");
        address.putExtra("city", "Toronto");
        String[] addressLines = new String[] {"111 Richmond st. West"};
        address.putExtra("addressLines", addressLines);
        address.putExtra("recipient", "John Smith");
        address.putExtra("phone", "5555555555");
        result.putExtra("shippingAddress", address);
        result.putExtra("shippingOptionId", "standard");
    }
    return result;
}

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

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

Để thông báo cho người bán về những thay đổi mới, hãy triển khai giao diện IPaymentDetailsUpdateServiceCallback và khai báo giao diện đó trong AndroidManifest.xml bằng bộ lọc ý định UPDATE_PAYMENT_DETAILS.

Ngay sau khi gọi ý định PAY, Chrome sẽ kết nối với dịch vụ UPDATE_PAYMENT_DETAILS (nếu có) trong cùng gói với ý định PAY và sẽ gọi setPaymentDetailsUpdateService(service) để cung cấp cho ứng dụng thanh toán của bạn điểm cuối IPaymentDetailsUpdateService nhằm thông báo về những thay đổi đối với phương thức thanh toán, lựa chọn vận chuyển hoặc địa chỉ giao hàng của người dùng.

Sử dụng packageManager.getPackagesForUid(Binder.getCallingUid()) khi nhận được Thông tin liên lạc giữa các quy trình (IPC) để xác thực rằng ứng dụng đã gọi ý định PAY có cùng tên gói với ứng dụng đã gọi các phương thức IPaymentDetailsUpdateServiceCallback.

AIDL

Tạo 2 tệp AIDL có nội dung sau:

org/chromium/components/payments/IPaymentDetailsUpdateServiceCallback.aidl

package org.chromium.components.payments;

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

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

    oneway void paymentDetailsNotUpdated();

    oneway void setPaymentDetailsUpdateService(IPaymentDetailsUpdateService service);
}

org/chromium/components/payments/IPaymentDetailsUpdateService.aidl

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

Dịch vụ

Triển khai dịch vụ IPaymentDetailsUpdateServiceCallback.

Kotlin

class SampleUpdatePaymentDetailsCallbackService : Service() {
    private val binder = object : IPaymentDetailsUpdateServiceCallback.Stub() {
        override fun updateWith(updatedPaymentDetails: Bundle) {}

        override fun paymentDetailsNotUpdated() {}

        override fun setPaymentDetailsUpdateService(service: IPaymentDetailsUpdateService) {}
    }

    override fun onBind(intent: Intent?): IBinder? {
        return binder
    }
}

Java

import org.chromium.components.paymsnts.IPaymentDetailsUpdateServiceCallback;

public class SampleUpdatePaymentDetailsCallbackService extends Service {
    private final IPaymentDetailsUpdateServiceCallback.Stub mBinder =
        new IPaymentDetailsUpdateServiceCallback.Stub() {
            @Override
            public void updateWith(Bundle updatedPaymentDetails) {}

            @Override
            public void paymentDetailsNotUpdated() {}

            @Override
            public void setPaymentDetailsUpdateService(IPaymentDetailsUpdateService service) {}
        };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

AndroidManifest.xml

Hiển thị dịch vụ cho IPaymentDetailsUpdateServiceCallback trong AndroidManifest.xml.

<service
    android:name=".SampleUpdatePaymentDetailsCallbackService"
    android:exported="true">
    <intent-filter>
        <action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS" />
    </intent-filter>
</service>

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

Kotlin

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
}

Java

if (service == null) {
  return;
}

try {
    if (isOptionChange) {
        service.changeShippingOption(selectedOptionId, callback);
    } else (isAddressChange) {
        service.changeShippingAddress(selectedAddress, callback);
    } else {
        service.changePaymentMethod(methodData, callback);
    }
} catch (RemoteException e) {
    // Handle the remote exception
}

changePaymentMethod

Thông báo cho người bán về những thay đổi trong phương thức thanh toán do người dùng chọn. Gói paymentHandlerMethodData chứa methodName và các khoá details 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 bằng cách sử dụng 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 lựa chọn 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 shippingOptionId không trống và gửi updatePaymentDetails kèm theo thông báo lỗi sau bằng cách sử dụng 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 trong đị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 bằng cách sử dụng 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 trạng thái không hợp lệ

Nếu gặp phải 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 bằng một gói updatePaymentDetails đã được chỉnh sửa. 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ờ phản hồi của người bán đối với một thay đổi trước đó (chẳng hạn như một sự kiện thay đổi đang diễn ra).
  • Giá trị nhận dạng lựa chọn vận chuyển do ứng dụng thanh toán cung cấp không thuộc về 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 nhất từ người bán

Kotlin

override fun updateWith(updatedPaymentDetails: Bundle) {}

override fun paymentDetailsNotUpdated() {}

Java

@Override
public void updateWith(Bundle updatedPaymentDetails) {}

@Override
public void paymentDetailsNotUpdated() {}

updatedPaymentDetails là gói tương đương với từ điển PaymentRequestDetailsUpdate WebIDL 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ó thể phân chia của shipping options
  • 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 lựa chọn vận chuyển hợp lệ)
  • stringifiedPaymentMethodErrors – Chuỗi JSON đại diện cho các lỗi xác thực đối với phương thức thanh toán
  • addressErrors – Một gói có các khoá không bắt buộc giống với shipping address và các 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.
  • modifiers – Một mảng có thể truyền qua của các Gói, mỗi gói có một trường totalmethodData, cũng là các Gói.

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