راهنمای توسعه دهندگان اپلیکیشن پرداخت اندروید

بیاموزید که چگونه برنامه پرداخت Android خود را با Web Payments سازگار کنید و تجربه کاربری بهتری برای مشتریان فراهم کنید.

Payment Request API یک رابط داخلی مبتنی بر مرورگر را به وب می‌آورد که به کاربران اجازه می‌دهد اطلاعات پرداخت مورد نیاز را راحت‌تر از همیشه وارد کنند. API همچنین می تواند برنامه های پرداخت ویژه پلتفرم را فراخوانی کند.

پشتیبانی مرورگر

  • کروم: 60.
  • لبه: 15.
  • فایرفاکس: پشت پرچم
  • سافاری: 11.1.

منبع

جریان پرداخت با برنامه Google Pay مخصوص پلتفرم که از پرداخت‌های وب استفاده می‌کند.

در مقایسه با استفاده از Intent Android، Web Payments امکان ادغام بهتر با مرورگر، امنیت و تجربه کاربر را فراهم می کند:

  • برنامه پرداخت به صورت مودال در زمینه وب سایت تاجر راه اندازی شده است.
  • پیاده سازی مکمل برنامه پرداخت موجود شما است و به شما امکان می دهد از پایگاه کاربری خود استفاده کنید.
  • برای جلوگیری از بارگذاری جانبی ، امضای برنامه پرداخت بررسی می‌شود.
  • برنامه های پرداخت می توانند چندین روش پرداخت را پشتیبانی کنند.
  • هر روش پرداخت مانند ارز دیجیتال، انتقال بانکی و غیره را می توان یکپارچه کرد. برنامه‌های پرداخت در دستگاه‌های اندرویدی حتی می‌توانند روش‌هایی را که نیاز به دسترسی به تراشه سخت‌افزاری روی دستگاه دارند، ادغام کنند.

برای پیاده سازی Web Payments در یک برنامه پرداخت Android، چهار مرحله لازم است:

  1. به تاجران اجازه دهید برنامه پرداخت شما را کشف کنند.
  2. اگر مشتری ابزار ثبت نامی (مانند کارت اعتباری) آماده پرداخت را دارد، به تاجر اطلاع دهید.
  3. اجازه دهید مشتری پرداخت کند.
  4. گواهی امضای تماس گیرنده را تأیید کنید.

برای مشاهده پرداخت های وب در عمل، نسخه ی نمایشی پرداخت وب اندروید را بررسی کنید.

مرحله 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) } }