دليل مطوّري تطبيقات الدفع المتوافقة مع Android

تعرَّف على كيفية تعديل تطبيق الدفع على Android بما يتوافق مع Web Payments وتقديم تجربة مستخدم أفضل للعملاء.

تتيح Payment Request API توفير ويب، واجهة مدمجة تستند إلى المتصفح تسمح للمستخدمين بإدخال الدفعة المطلوبة المعلومات أسهل من أي وقت مضى. يمكن أن تطلب واجهة برمجة التطبيقات أيضًا عمليات دفع خاصة بالنظام الأساسي التطبيقات.

دعم المتصفح

  • Chrome: 60.
  • الحافة: 15.
  • متصفّح Firefox: خلف علم
  • Safari: الإصدار 11.1.

المصدر

مسار الدفع من خلال تطبيق Google Pay الخاص بالمنصّة والذي يستخدم Web Payments

مقارنةً باستخدام أهداف Android Intent فقط، تتيح خدمة Web Payments عملية تكامل أفضل مع المتصفّح والأمان وتجربة المستخدم:

  • تم إطلاق تطبيق الدفع كوسيلة في سياق الموقع الإلكتروني للتاجر.
  • تُعدّ عملية التنفيذ عنصرًا تكميليًا لتطبيق الدفع الحالي، ما يتيح لك الاستفادة من قاعدة مستخدميك.
  • يتم وضع علامة في مربّع توقيع تطبيق الدفع لمنع التثبيت من مصدر غير معروف.
  • يمكن أن تتيح تطبيقات الدفع استخدام طرق دفع متعددة.
  • يمكن استخدام أي طريقة دفع، مثل العملات المشفّرة والحوالات المصرفية وغيرها التكامل. يمكن لتطبيقات الدفع على أجهزة Android أيضًا دمج طرق تتطلّب الوصول إلى شريحة الجهاز على الجهاز.

هناك أربع خطوات لاستخدام Web Payments في تطبيق الدفع على Android:

  1. يمكنك السماح للتجّار باكتشاف تطبيق الدفع الخاص بك.
  2. يمكنك السماح للتاجر بمعرفة ما إذا كان العميل لديه وسيلة تسجيل مُسجّلة (مثل الرصيد). جاهزة للدفع).
  3. دع العميل يدفع.
  4. تحقَّق من شهادة توقيع المتصل.

للاطلاع على دفعات الويب عمليًا، راجع android-web-payment تجريبي.

الخطوة 1: السماح للتجّار باكتشاف تطبيق الدفع الخاص بك

ليتمكّن التاجر من استخدام تطبيق الدفع، عليه استخدام طريقة الدفع. طلب واجهة برمجة التطبيقات و حدِّد طريقة الدفع المتوافقة باستخدام طريقة الدفع المعرّف.

إذا كان لديك معرّف طريقة دفع فريد لتطبيق الدفع الذي تستخدمه، إعداد طريقة الدفع الخاصة بك حتى تتمكن المتصفحات من اكتشاف تطبيقك.

الخطوة 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 اختيارية. إذا لم يكن هناك معالج لمثل هذا الهدف في تطبيق الدفع، فسيفترض متصفح الويب أن التطبيق يمكنه دائمًا إجراء والمدفوعات.

لغة تعريف واجهة نظام Android ‏(AIDL)

يتم تحديد واجهة برمجة التطبيقات لخدمة 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 الغرض من PAY مع تضمين معلومات المعاملة في مَعلمات intent.

يستجيب تطبيق الدفع باستخدام methodName وdetails، وهما تطبيقان للدفع. محددة وغير واضحة بالنسبة إلى المتصفح. يحوِّل المتصفّح details إلى كائن JavaScript للتاجر من خلال إلغاء تسلسل JSON، لا تفرض أي صلاحية تتجاوز ذلك. لا يُعدِّل المتصفح 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

methodNames

تمثّل هذه السمة أسماء الطرق المُستخدَمة. العناصر هي المفاتيح في قاموس methodData. في ما يلي الطرق المتوافقة مع تطبيق الدفع.

val methodNames: List<String>? = extras.getStringArrayList("methodNames")

methodData

يشير هذا المصطلح إلى عملية ربط من methodNames إلى methodData

val methodData: Bundle? = extras.getBundle("methodData")

merchantName

محتوى علامة HTML <title> في صفحة الدفع الخاصة بالتاجر ( سياق التصفح الأعلى مستوى للمتصفح).

val merchantName: String? = extras.getString("merchantName")

topLevelOrigin

أصل التاجر بدون المخطط (أصل سياق التصفح على المستوى الأعلى). على سبيل المثال، https://mystore.com/checkout هو تم اجتياز الاختبار باسم mystore.com.

val topLevelOrigin: String? = extras.getString("topLevelOrigin")

topLevelCertificateChain

سلسلة شهادات التاجر (سلسلة الشهادات من المستوى الأعلى سياق التصفح). قيمة خالية للمضيف المحلي والملف على القرص، وكلاهما آمن والسياقات بدون شهادات طبقة المقابس الآمنة. كل Parcelable عبارة عن حزمة تحتوي على المفتاح 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) في JavaScript. إذا كانت تم استدعاء الدالة الإنشائية من سياق المستوى الأعلى، ثم قيمة هذه تساوي قيمة معلَمة 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 الذي "الدفع السريع" يجب ربطها حالة المعاملة. ستستخدم المواقع الإلكترونية للتجّار هذا الحقل للاستعلام عن &quot;push-payment&quot; تطبيقات لحالة المعاملات خارج النطاق.

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. من أجل التأكد من أن المتصل هو المتصفح الذي في ذهنك، فيجب عليك التحقق من شهادة التوقيع والتأكد من تطابقها مع

إذا كنت تستهدف المستوى 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 من واجهة برمجة التطبيقات القديمة والمستويات الأدنى، أو إذا كنت بحاجة إلى معالجة المتصفحات التي تحتوي على شهادات توقيع متعددة، يمكنك استخدام 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) } }