بیاموزید که چگونه برنامه پرداخت Android خود را با Web Payments سازگار کنید و تجربه کاربری بهتری برای مشتریان فراهم کنید.
Payment Request API یک رابط داخلی مبتنی بر مرورگر را به وب میآورد که به کاربران اجازه میدهد اطلاعات پرداخت مورد نیاز را راحتتر از همیشه وارد کنند. API همچنین می تواند برنامه های پرداخت ویژه پلتفرم را فراخوانی کند.
در مقایسه با استفاده از Intent Android، Web Payments امکان ادغام بهتر با مرورگر، امنیت و تجربه کاربر را فراهم می کند:
- برنامه پرداخت به صورت مودال در زمینه وب سایت تاجر راه اندازی شده است.
- پیاده سازی مکمل برنامه پرداخت موجود شما است و به شما امکان می دهد از پایگاه کاربری خود استفاده کنید.
- برای جلوگیری از بارگذاری جانبی ، امضای برنامه پرداخت بررسی میشود.
- برنامه های پرداخت می توانند چندین روش پرداخت را پشتیبانی کنند.
- هر روش پرداخت مانند ارز دیجیتال، انتقال بانکی و غیره را می توان یکپارچه کرد. برنامههای پرداخت در دستگاههای اندرویدی حتی میتوانند روشهایی را که نیاز به دسترسی به تراشه سختافزاری روی دستگاه دارند، ادغام کنند.
برای پیاده سازی Web Payments در یک برنامه پرداخت Android، چهار مرحله لازم است:
- به تاجران اجازه دهید برنامه پرداخت شما را کشف کنند.
- اگر مشتری ابزار ثبت نامی (مانند کارت اعتباری) آماده پرداخت را دارد، به تاجر اطلاع دهید.
- اجازه دهید مشتری پرداخت کند.
- گواهی امضای تماس گیرنده را تأیید کنید.
برای مشاهده پرداخت های وب در عمل، نسخه ی نمایشی پرداخت وب اندروید را بررسی کنید.
مرحله 1: به تاجران اجازه دهید برنامه پرداخت شما را کشف کنند
برای اینکه یک تاجر بتواند از برنامه پرداخت شما استفاده کند، باید از API درخواست پرداخت استفاده کند و روش پرداختی را که پشتیبانی میکنید با استفاده از شناسه روش پرداخت مشخص کند.
اگر یک شناسه روش پرداخت دارید که مختص برنامه پرداخت شما است، می توانید مانیفست روش پرداخت خود را تنظیم کنید تا مرورگرها بتوانند برنامه شما را پیدا کنند.
مرحله 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
اختیاری است. اگر چنین کنترلکنندهای در برنامه پرداخت وجود نداشته باشد، مرورگر وب فرض میکند که برنامه همیشه میتواند پرداختها را انجام دهد.
ایدل
API برای سرویس IS_READY_TO_PAY
در 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 intent PAY
با اطلاعات تراکنش در پارامترهای intent فراخوانی می شود.
برنامه پرداخت با methodName
و details
پاسخ میدهد که مختص برنامه پرداخت هستند و برای مرورگر غیرشفاف هستند. مرورگر رشته details
را از طریق JSON deserialization به یک شی جاوا اسکریپت برای تاجر تبدیل می کند، اما هیچ اعتباری فراتر از آن اعمال نمی کند. مرورگر 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
باید فهرستی از رشته ها باشد که هر کدام باید یک URL معتبر و مطلق با طرح 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>
پارامترها
پارامترهای زیر به عنوان Intent اضافی به فعالیت ارسال می شوند:
-
methodNames
-
methodData
-
topLevelOrigin
-
topLevelCertificateChain
-
paymentRequestOrigin
-
total
-
modifiers
-
paymentRequestId
val extras: Bundle? = intent?.extras
روش نام ها
نام روش های مورد استفاده عناصر کلیدهای فرهنگ لغت methodData
هستند. اینها روش هایی هستند که برنامه پرداخت پشتیبانی می کند.
val methodNames: List<String>? = extras.getStringArrayList("methodNames")
methodData
نگاشت هر یک از methodNames
به methodData
.
val methodData: Bundle? = extras.getBundle("methodData")
نام تجاری
محتویات تگ HTML <title>
صفحه تسویه حساب تاجر (زمینه مرور سطح بالای مرورگر).
val merchantName: String? = extras.getString("merchantName")
topLevelOrigin
مبدا تاجر بدون طرح (The scheme-less origin of the level browsing context). به عنوان مثال، https://mystore.com/checkout
به عنوان mystore.com
منتقل می شود.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
زنجیره گواهی تاجر (زنجیره گواهی زمینه مرور سطح بالا). تهی برای لوکال هاست و فایل روی دیسک، که هر دو زمینه امن و بدون گواهینامه SSL هستند. هر Parcelable
یک Bundle با یک کلید certificate
و یک مقدار آرایه بایت است.
val topLevelCertificateChain: Array<Parcelable>? =
extras.getParcelableArray("topLevelCertificateChain")
val list: List<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p as Bundle).getByteArray("certificate")
}
paymentRequestOrigin
مبدأ بدون طرح زمینه مرور iframe که سازنده new PaymentRequest(methodData, details, options)
در جاوا اسکریپت فراخوانی میکند. اگر سازنده از زمینه سطح بالا فراخوانی شده باشد، آنگاه مقدار این پارامتر با مقدار پارامتر 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
فیلد PaymentRequest.id
که برنامههای «Push-payment» باید با وضعیت تراکنش مرتبط باشند. وبسایتهای بازرگان از این فیلد برای پرس و جو از برنامههای «پرداخت فشاری» برای وضعیت تراکنش خارج از باند استفاده میکنند.
val paymentRequestId: String? = extras.getString("paymentRequestId")
پاسخ
فعالیت می تواند پاسخ خود را از طریق setResult
با RESULT_OK
ارسال کند.
setResult(Activity.RESULT_OK, Intent().apply {
putExtra("methodName", "https://bobbucks.dev/pay")
putExtra("details", "{\"token\": \"put-some-data-here\"}")
})
finish()
شما باید دو پارامتر را به عنوان Intent اضافه کنید:
-
methodName
: نام روش مورد استفاده. -
details
: رشته JSON حاوی اطلاعات لازم برای تاجر برای تکمیل تراکنش است. اگر موفقیتtrue
باشد،details
باید به گونه ای ساخته شوند کهJSON.parse(details)
موفق شود.
اگر تراکنش در برنامه پرداخت انجام نشده باشد، میتوانید RESULT_CANCELED
پاس کنید، برای مثال، اگر کاربر موفق به تایپ کد پین صحیح حساب خود در برنامه پرداخت نشده باشد. ممکن است مرورگر به کاربر اجازه دهد برنامه پرداخت دیگری را انتخاب کند.
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
و با Activity.getCallingPackage()
در PAY
بررسی کنید. برای اینکه واقعاً تأیید کنید که تماسگیرنده مرورگری است که در ذهن دارید، باید گواهی امضای آن را بررسی کنید و مطمئن شوید که با مقدار صحیح مطابقت دارد.
اگر سطح 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 یک گواهی امضا دارد.) برنامه هایی که چندین گواهی امضا دارند نمی توانند آنها را بچرخانند.
اگر نیاز به پشتیبانی از سطوح API قدیمیتر 27 و پایینتر دارید، یا اگر نیاز به مدیریت مرورگرهایی با چندین گواهی امضا دارید، میتوانید از 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) } }