瞭解如何調整 Android 付款應用程式,使其與網路付款功能搭配使用,為客戶提供更優質的使用者體驗。
Payment Request API 是將 提供網站內建瀏覽器介面,可讓使用者輸入必要付款 資訊。API 也可以叫用特定平台的付款 應用程式。
與僅使用 Android Intents 相比,網頁版付款功能可提供更優異的整合體驗 包括瀏覽器、安全性和使用者體驗:
- 付款應用程式是在商家網站上以互動模式啟動。
- 實作方式為現有的付款應用程式補充內容, 善用使用者族群
- 系統會檢查付款應用程式的簽章,以防止 側載。
- 付款應用程式支援多種付款方式。
- 任何付款方式,例如加密貨幣、銀行轉帳等等。 整合。Android 裝置上的付款應用程式甚至可以整合 必須取得裝置上的硬體晶片存取權。
在 Android 付款應用程式中實作 Web Payments 需要四個步驟:
- 讓商家找到你的付款應用程式。
- 告知商家客戶是否已註冊付款方式 (例如抵免額) 卡片),
- 讓客戶付款。
- 驗證呼叫端的簽署憑證。
如要查看網路付款實際運作情形,請參閱 android-web-payment 示範。
步驟 1:讓商家找到您的付款應用程式
商家必須透過付款 要求 API 和 使用付款方式,指明您支援的付款方式 ID)。
如果您擁有付款應用程式專用的付款方式 ID, 您可以設定自己的付款方式 資訊清單,讓瀏覽器 並發掘您的應用程式
步驟 2:通知商家客戶是否已購買可付款的付款方式
商家可以呼叫 hasEnrolledInstrument()
來查詢客戶是否
才能付款。你可以
實作 IS_READY_TO_PAY
做為 Android 服務,即可回答這項查詢。
AndroidManifest.xml
使用動作,使用意圖篩選器宣告服務
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>
IS_READY_TO_PAY
服務為選用項目。如果函式中沒有這種意圖處理常式
網路瀏覽器會假設應用程式一律可以
付款。
AIDL
IS_READY_TO_PAY
服務的 API 是在 AIDL 中定義。建立兩個 AIDL
包含下列內容的檔案:
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);
}
實作 IsReadyToPayService
以下為最簡單的 IsReadyToPayService
實作方式。
範例:
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
}
}
回應
服務可透過 handleIsReadyToPay(Boolean)
方法傳送回應。
callback?.handleIsReadyToPay(true)
權限
你可以使用 Binder.getCallingUid()
查看來電者的身分。請注意,
您必須在 isReadyToPay
方法 (而非 onBind
方法) 中執行此操作。
override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
try {
val callingPackage = packageManager.getNameForUid(Binder.getCallingUid())
// …
如要瞭解如何驗證呼叫端的簽署憑證,請參閱驗證呼叫端的簽署憑證。 ,驗證呼叫套件是否含正確的簽章。
步驟 3:讓客戶付款
商家呼叫 show()
來啟動付款功能
應用程式
以便客戶付款付款應用程式是透過 Android 叫用
意圖參數中包含交易資訊的意圖 PAY
。
付款應用程式會回應 methodName
和 details
,也就是付款應用程式
且無法明確指示瀏覽器瀏覽器會將 details
將字串傳入商家的 JavaScript 物件中
不會強制執行超出此限制的任何效力。瀏覽器不會修改
details
;就會將參數值直接傳遞給商家。
AndroidManifest.xml
具有 PAY
意圖篩選器的活動應有 <meta-data>
標記,
識別的
應用程式。
如要支援多種付款方式,請新增 <meta-data>
標記,並加入
<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
必須是字串清單,且每個字串都必須是有效字串。
含有 HTTPS 配置的絕對網址,如下所示。
<?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>
參數
系統會以意圖額外項目的形式將下列參數傳送至活動:
methodNames
methodData
topLevelOrigin
topLevelCertificateChain
paymentRequestOrigin
total
modifiers
paymentRequestId
val extras: Bundle? = intent?.extras
methodNames
使用方法的名稱。這些元素是
methodData
字典。以下是付款應用程式支援的付款方式。
val methodNames: List<String>? = extras.getStringArrayList("methodNames")
methodData
從每個 methodNames
對應至
methodData
。
val methodData: Bundle? = extras.getBundle("methodData")
merchantName
商家結帳頁面中的 <title>
HTML 標記內容 (
瀏覽器頂層瀏覽情境)。
val merchantName: String? = extras.getString("merchantName")
topLevelOrigin
沒有配置 (無配置) 來源的商家來源
頂層瀏覽情境)。例如,https://mystore.com/checkout
是
以 mystore.com
的形式傳遞。
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
商家的憑證鏈結 (頂層的憑證鏈結)
瀏覽內容)。localhost 和磁碟上的檔案為空值,這兩個項目都是安全的
存取沒有 SSL 憑證的內容每個 Parcelable
都是與
certificate
鍵和位元組陣列值。
val topLevelCertificateChain: Array<Parcelable>? =
extras.getParcelableArray("topLevelCertificateChain")
val list: List<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p as Bundle).getByteArray("certificate")
}
paymentRequestOrigin
在 JavaScript 中叫用 new
PaymentRequest(methodData, details, options)
建構函式的 iframe 瀏覽結構定義無配置來源。如果
呼叫的函式是從頂層內容叫用,之後的
參數等於 topLevelOrigin
參數的值。
val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")
total
代表交易總金額的 JSON 字串。
val total: String? = extras.getString("total")
以下為字串內容範例:
{"currency":"USD","value":"25.00"}
modifiers
JSON.stringify(details.modifiers)
的輸出內容,其中 details.modifiers
只包含 supportedMethods
和 total
。
paymentRequestId
「push-payment」的 PaymentRequest.id
欄位應用程式應根據
交易狀態商家網站會使用這個欄位
"push-payment"確保交易狀態中斷的應用程式。
val paymentRequestId: String? = extras.getString("paymentRequestId")
回應
活動可以透過 RESULT_OK
透過 setResult
傳回回應。
setResult(Activity.RESULT_OK, Intent().apply {
putExtra("methodName", "https://bobbucks.dev/pay")
putExtra("details", "{\"token\": \"put-some-data-here\"}")
})
finish()
您必須將兩個參數指定為意圖額外項目:
methodName
:使用方法的名稱。details
:JSON 字串,包含商家需要的資訊 完成交易。如果成功是true
,則details
必須是 以讓JSON.parse(details)
成功的方式建構。
如果交易並未在RESULT_CANCELED
付款應用程式,例如使用者輸入的 PIN 碼有誤
登入帳戶瀏覽器可能會讓使用者選擇
不同的付款應用程式
setResult(RESULT_CANCELED)
finish()
如果所叫用付款收到的付款回應活動結果
應用程式已設為 RESULT_OK
,Chrome 就會檢查是否有 methodName
以外的內容,並且
內含額外項目 details
。如果驗證失敗,Chrome 會傳回遭拒
request.show()
的承諾,但發生下列其中一項開發人員發生錯誤
訊息:
'Payment app returned invalid response. Missing field "details".'
'Payment app returned invalid response. Missing field "methodName".'
權限
活動可以透過 getCallingPackage()
方法檢查呼叫端。
val caller: String? = callingPackage
最後一個步驟是驗證呼叫端的簽署憑證,以確認 呼叫套件含有正確的簽章
步驟 4:驗證呼叫端的簽署憑證
您可以在以下位置使用 Binder.getCallingUid()
查看呼叫端的套件名稱:
IS_READY_TO_PAY
,以及在 PAY
中的 Activity.getCallingPackage()
。為了
請先確定呼叫端是您要的瀏覽器
檢查憑證的簽署憑證是否正確
值。
如果您指定 API 級別 28 以上版本,並且要與瀏覽器整合
擁有單一簽署憑證
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()
因為可正確處理憑證輪替。(Chrome 的
單次簽署憑證)。含有多個簽署憑證的應用程式不得
旋轉圖片。
如果您需要支援 27 以下版本的舊版 API,或需要處理
擁有多個簽署憑證的瀏覽器
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) } }