Tìm hiểu cách điều chỉnh ứng dụng thanh toán Android của bạn để hoạt động với Thanh toán trên web và cung cấp trải nghiệm người dùng tốt hơn cho khách hàng.
API yêu cầu thanh toán mang đến web, một giao diện tích hợp dựa trên trình duyệt cho phép người dùng nhập thông tin thanh toán bắt buộc dễ dàng hơn bao giờ hết. API cũng có thể gọi khoản thanh toán theo nền tảng cụ thể của chúng tôi.
So với việc chỉ sử dụng Android Intent, Thanh toán web cho phép tích hợp tốt hơn với trình duyệt, tính bảo mật và trải nghiệm người dùng:
- Ứng dụng thanh toán được chạy dưới dạng phương thức, trong bối cảnh trang web của người bán.
- Triển khai bổ sung cho ứng dụng thanh toán hiện có của bạn, cho phép bạn tận dụng cơ sở người dùng của bạn.
- Chữ ký của ứng dụng thanh toán được kiểm tra để ngăn cài đặt không qua cửa hàng ứng dụng.
- Ứng dụng thanh toán có thể hỗ trợ nhiều phương thức thanh toán.
- Bất kỳ phương thức thanh toán nào, chẳng hạn như tiền mã hoá, chuyển khoản ngân hàng, v.v. đều có thể được được tích hợp. Ứng dụng thanh toán trên thiết bị Android thậm chí có thể tích hợp các phương thức yêu cầu quyền truy cập vào chip phần cứng trên thiết bị.
Cần bốn bước để triển khai Thanh toán trên web trong ứng dụng thanh toán Android:
- Cho phép người bán khám phá ứng dụng thanh toán của bạn.
- Cho người bán biết khách hàng đã đăng ký phương thức thanh toán nào (chẳng hạn như tín dụng) để sẵn sàng thanh toán.
- Cho phép khách hàng thanh toán.
- Xác minh chứng chỉ ký của người gọi.
Để xem hoạt động của tính năng Thanh toán trên web, hãy xem android-web-payment bản minh hoạ.
Bước 1: Cho phép người bán khám phá ứng dụng thanh toán của bạn
Để người bán sử dụng ứng dụng thanh toán của bạn, họ cần sử dụng nút Thanh toán Yêu cầu API và chỉ định phương thức thanh toán mà bạn hỗ trợ thông qua phương thức thanh toán mã nhận dạng.
Nếu có giá trị nhận dạng phương thức thanh toán riêng cho ứng dụng thanh toán của mình, bạn có thể thiết lập phương thức thanh toán của riêng mình tệp kê khai để trình duyệt có thể khám phá ứng dụng của bạn.
Bước 2: Cho người bán biết liệu khách hàng đã đăng ký một phương thức đã đăng ký và đã sẵn sàng để thanh toán hay chưa
Người bán có thể gọi cho hasEnrolledInstrument()
để tìm hiểu xem khách hàng có thể
đều có thể thanh toán. Bạn có thể
hãy triển khai IS_READY_TO_PAY
dưới dạng dịch vụ Android để trả lời truy vấn này.
AndroidManifest.xml
Khai báo dịch vụ bằng bộ lọc ý định kèm theo thao tác
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>
Dịch vụ IS_READY_TO_PAY
là không bắt buộc. Nếu không có trình xử lý ý định như vậy trong
ứng dụng thanh toán, thì trình duyệt web sẽ giả định rằng ứng dụng luôn có thể thực hiện
thanh toán.
AIDL
API của dịch vụ IS_READY_TO_PAY
được xác định trong AIDL. Tạo hai AIDL
có nội dung sau:
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);
}
Triển khai IsReadyToPayService
Cách triển khai đơn giản nhất của IsReadyToPayService
được thể hiện như sau
ví dụ:
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
}
}
Phản hồi
Dịch vụ có thể gửi phản hồi thông qua phương thức handleIsReadyToPay(Boolean)
.
callback?.handleIsReadyToPay(true)
Quyền
Bạn có thể sử dụng Binder.getCallingUid()
để kiểm tra xem người gọi là ai. Xin lưu ý rằng bạn
phải thực hiện việc này trong phương thức isReadyToPay
, chứ không phải trong phương thức onBind
.
override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
try {
val callingPackage = packageManager.getNameForUid(Binder.getCallingUid())
// …
Xem Xác minh chứng chỉ ký của người gọi để biết cách thực hiện để xác minh rằng gói gọi có chữ ký chính xác.
Bước 3: Cho phép khách hàng thanh toán
Người bán gọi show()
để bắt đầu thanh toán
ứng dụng
để khách hàng có thể thanh toán. Ứng dụng thanh toán được gọi thông qua Android
ý định PAY
với thông tin giao dịch trong các tham số ý định.
Ứng dụng thanh toán này sẽ phản hồi bằng methodName
và details
, là các ứng dụng thanh toán
cụ thể và không rõ ràng đối với trình duyệt. Trình duyệt chuyển đổi details
vào đối tượng JavaScript cho người bán thông qua quá trình huỷ chuyển đổi tuần tự JSON, nhưng
không thực thi bất kỳ hiệu lực nào ngoài phạm vi đó. Trình duyệt không sửa đổi
details
; giá trị của thông số đó được chuyển trực tiếp đến người bán.
AndroidManifest.xml
Hoạt động có bộ lọc ý định PAY
phải có thẻ <meta-data>
sẽ xác định giá trị nhận dạng phương thức thanh toán mặc định
.
Để hỗ trợ nhiều phương thức thanh toán, hãy thêm thẻ <meta-data>
kèm theo
Tài nguyên <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
phải là một danh sách chuỗi, mỗi chuỗi phải là một chuỗi hợp lệ,
URL tuyệt đối với giao thức HTTPS như minh họa dưới đây.
<?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>
Tham số
Các tham số sau được truyền đến hoạt động dưới dạng phần bổ sung Ý định:
methodNames
methodData
topLevelOrigin
topLevelCertificateChain
paymentRequestOrigin
total
modifiers
paymentRequestId
val extras: Bundle? = intent?.extras
methodNames
Tên của các phương thức đang được sử dụng. Các phần tử này là chìa khoá trong
Từ điển methodData
. Đây là những phương thức mà ứng dụng thanh toán này hỗ trợ.
val methodNames: List<String>? = extras.getStringArrayList("methodNames")
methodData
Ánh xạ từ mỗi methodNames
đến
methodData
.
val methodData: Bundle? = extras.getBundle("methodData")
merchantName
Nội dung của thẻ HTML <title>
trên trang thanh toán của người bán (thẻ
ngữ cảnh duyệt web cấp cao nhất của trình duyệt).
val merchantName: String? = extras.getString("merchantName")
topLevelOrigin
Nguồn gốc của người bán không có lược đồ (Nguồn gốc không có lược đồ
ngữ cảnh duyệt web cấp cao nhất). Ví dụ: https://mystore.com/checkout
là
được truyền dưới dạng mystore.com
.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
Chuỗi chứng chỉ của người bán (Chuỗi chứng chỉ của cấp cao nhất
ngữ cảnh duyệt web). Giá trị rỗng cho localhost và tệp trên ổ đĩa, cả hai đều an toàn
mà không có chứng chỉ SSL. Mỗi Parcelable
là một Gói có
khoá certificate
và một giá trị mảng byte.
val topLevelCertificateChain: Array<Parcelable>? =
extras.getParcelableArray("topLevelCertificateChain")
val list: List<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p as Bundle).getByteArray("certificate")
}
paymentRequestOrigin
Nguồn gốc không có lược đồ của ngữ cảnh duyệt web iframe đã gọi hàm khởi tạo new
PaymentRequest(methodData, details, options)
trong JavaScript. Nếu
hàm khởi tạo được gọi từ ngữ cảnh cấp cao nhất, sau đó là giá trị của hàm này
bằng với giá trị của tham số topLevelOrigin
.
val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")
total
Chuỗi JSON biểu thị tổng số tiền của giao dịch.
val total: String? = extras.getString("total")
Dưới đây là nội dung mẫu của chuỗi:
{"currency":"USD","value":"25.00"}
modifiers
Kết quả của JSON.stringify(details.modifiers)
, trong đó details.modifiers
chỉ chứa supportedMethods
và total
.
paymentRequestId
Trường PaymentRequest.id
"thanh toán đẩy" nên liên kết với
trạng thái giao dịch. Trang web của người bán sẽ sử dụng trường này để truy vấn
"push-payment" ứng dụng cho trạng thái giao dịch ngoài dải.
val paymentRequestId: String? = extras.getString("paymentRequestId")
Phản hồi
Hoạt động có thể gửi lại phản hồi thông qua setResult
bằng RESULT_OK
.
setResult(Activity.RESULT_OK, Intent().apply {
putExtra("methodName", "https://bobbucks.dev/pay")
putExtra("details", "{\"token\": \"put-some-data-here\"}")
})
finish()
Bạn phải chỉ định 2 tham số làm phần bổ sung Ý định:
methodName
: Tên của phương thức đang được sử dụng.details
: Chuỗi JSON chứa thông tin cần thiết để người bán hoàn tất giao dịch. Nếu thành công làtrue
, thìdetails
phải là được tạo theo cáchJSON.parse(details)
sẽ thành công.
Bạn có thể chuyển RESULT_CANCELED
nếu giao dịch chưa hoàn tất trong
ứng dụng thanh toán, ví dụ: nếu người dùng không nhập được mã PIN chính xác cho
tài khoản của họ trong ứng dụng thanh toán. Trình duyệt có thể cho phép người dùng chọn một
ứng dụng thanh toán khác.
setResult(RESULT_CANCELED)
finish()
Nếu kết quả hoạt động của một phản hồi thanh toán nhận được từ khoản thanh toán đã gọi
ứng dụng được đặt thành RESULT_OK
, sau đó Chrome sẽ kiểm tra methodName
không trống và
details
trong các ứng dụng khác. Nếu xác thực không thành công, Chrome sẽ trả về một thông báo bị từ chối
lời hứa từ request.show()
với một trong các lỗi sau đây do nhà phát triển gây ra
thư:
'Payment app returned invalid response. Missing field "details".'
'Payment app returned invalid response. Missing field "methodName".'
Quyền
Hoạt động có thể kiểm tra phương thức gọi bằng phương thức getCallingPackage()
.
val caller: String? = callingPackage
Bước cuối cùng là xác minh chứng chỉ ký của người gọi để xác nhận rằng gói gọi có chữ ký phù hợp.
Bước 4: Xác minh chứng chỉ ký của người gọi
Bạn có thể kiểm tra tên gói của người gọi bằng Binder.getCallingUid()
trong
IS_READY_TO_PAY
và với Activity.getCallingPackage()
trong PAY
. Để
xác minh rằng phương thức gọi là trình duyệt bạn muốn, bạn nên
hãy kiểm tra chứng chỉ ký của chứng chỉ đó và đảm bảo rằng chứng chỉ đó khớp với đúng
giá trị.
Nếu bạn đang nhắm đến API cấp 28 trở lên và đang tích hợp với một trình duyệt
có một chứng chỉ ký duy nhất, bạn có thể sử dụng
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
)
Ưu tiên PackageManager.hasSigningCertificate()
cho một chứng chỉ đơn lẻ
cho trình duyệt vì nó xử lý chính xác việc xoay vòng chứng chỉ. (Chrome có
chứng chỉ ký một lần). Ứng dụng có nhiều chứng chỉ ký không thể
xoay chúng.
Nếu bạn cần hỗ trợ API cũ từ 27 trở xuống, hoặc nếu bạn cần xử lý
các trình duyệt có nhiều chứng chỉ ký, bạn có thể sử dụng
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) } }