كيفية تحديث تطبيق الدفع على Android لتوفير عنوان الشحن ومعلومات الاتصال الخاصة بالجهة المسدِّدة باستخدام واجهات برمجة التطبيقات Web Payments API
قد يكون إدخال عنوان الشحن ومعلومات الاتصال من خلال نموذج ويب تجربة مرهقة للعملاء. قد تتسبب في حدوث أخطاء وانخفاض معدل الإحالات الناجحة.
ولهذا السبب توفر واجهة برمجة تطبيقات طلب الدفع ميزة لطلب عنوان الشحن ومعلومات الاتصال. يوفر ذلك مزايا متعددة:
- ويمكن للمستخدمين اختيار العنوان الصحيح ببضع نقرات فقط.
- ويتم عرض العنوان دائمًا بالتنسيق الموحّد.
- ومن غير المرجح أن يتم إرسال عنوان غير صحيح.
ويمكن للمتصفّحات تأجيل جمع عنوان الشحن ومعلومات الاتصال إلى أحد تطبيقات الدفع لتوفير تجربة دفع موحّدة. تُسمى هذه الوظيفة التفويض.
يفوض Chrome، كلما أمكن، جمع عنوان الشحن ومعلومات الاتصال الخاصة بالعميل إلى تطبيق الدفع على Android الذي تم استدعاؤه. ويساهم هذا التفويض في الحد من المعوقات المرتبطة بعملية الدفع.
يمكن أن يعدّل موقع التاجر الإلكتروني خيارات الشحن والسعر الإجمالي ديناميكيًا استنادًا إلى اختيار العميل لعنوان الشحن وخيار الشحن.
لإضافة دعم التفويض إلى تطبيق دفع حالي في Android، يمكنك تنفيذ الخطوات التالية:
- توضيح التفويضات المتوافقة
- تحليل
PAY
intent الإضافية لخيارات الدفع المطلوبة. - تقديم المعلومات المطلوبة في الردّ على عمليات الدفع:
- [اختياري] إتاحة المسار الديناميكي:
تعريف التفويضات المتوافقة
يحتاج المتصفّح إلى معرفة قائمة المعلومات الإضافية التي يمكن أن يوفّرها تطبيق الدفع كي يتمكّن من تفويض جمع هذه المعلومات إلى تطبيقك. يُرجى توضيح التفويضات المتوافقة باعتبارها <meta-data>
في ملف AndroidManifest.xml الخاص بتطبيقك.
<activity
android:name=".PaymentActivity"
…
<meta-data
android:name="org.chromium.payment_supported_delegations"
android:resource="@array/supported_delegations" />
</activity>
يجب أن يكون <resource>
قائمة بسلاسل يتم اختيارها من القيم الصالحة التالية:
[ "payerName", "payerEmail", "payerPhone", "shippingAddress" ]
يمكن في المثال التالي تقديم عنوان الشحن وعنوان البريد الإلكتروني للجهة المسدِّدة فقط.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="supported_delegations">
<item>payerEmail</item>
<item>shippingAddress</item>
</string-array>
</resources>
تحليل عناصر "PAY
" الإضافية حسب النية بالشراء لخيارات الدفع المطلوبة
ويمكن للتاجر تحديد المعلومات الإضافية المطلوبة باستخدام قاموس
paymentOptions
. سيقدّم Chrome قائمة بالخيارات المطلوبة التي يمكن أن يوفّرها تطبيقك من خلال تمرير المَعلمات التالية إلى نشاط PAY
باعتبارها عناصر إضافية intent.
paymentOptions
paymentOptions
هي مجموعة فرعية من خيارات الدفع التي يحدّدها التاجر والتي أعلن تطبيقك عن إتاحة التفويض لها.
val paymentOptions: Bundle? = extras.getBundle("paymentOptions")
val requestPayerName: Boolean? = paymentOptions?.getBoolean("requestPayerName")
val requestPayerPhone: Boolean? = paymentOptions?.getBoolean("requestPayerPhone")
val requestPayerEmail: Boolean? = paymentOptions?.getBoolean("requestPayerEmail")
val requestShipping: Boolean? = paymentOptions?.getBoolean("requestShipping")
val shippingType: String? = paymentOptions?.getString("shippingType")
يمكن أن يتضمّن المَعلمات التالية:
requestPayerName
- القيمة المنطقية التي تشير إلى ما إذا كان اسم الجهة المسدِّدة مطلوبًا أم لاrequestPayerPhone
- القيمة المنطقية التي تشير إلى ما إذا كان رقم هاتف الجهة المسدِّدة مطلوبًا أم لاrequestPayerEmail
- القيمة المنطقية التي تشير إلى ما إذا كان يجب إدخال عنوان البريد الإلكتروني للمسؤول عن الدفعrequestShipping
- القيمة المنطقية التي تشير إلى ما إذا كانت معلومات الشحن مطلوبة أم لا.shippingType
- السلسلة التي توضح نوع الشحن. يمكن أن يكون نوع الشحن"shipping"
أو"delivery"
أو"pickup"
. يمكن لتطبيقك استخدام هذا التلميح في واجهة المستخدم عند طلب عنوان المستخدم أو اختيار خيارات الشحن.
shippingOptions
تمثّل السمة shippingOptions
مصفوفة من خيارات الشحن التي يحدّدها التاجر. ولن تتوفّر هذه المَعلمة إلا عند استخدام paymentOptions.requestShipping ==
true
.
val shippingOptions: List<ShippingOption>? =
extras.getParcelableArray("shippingOptions")?.mapNotNull {
p -> from(p as Bundle)
}
كل خيار شحن هو Bundle
مع المفاتيح التالية.
id
- معرّف خيار الشحنlabel
: تشير هذه السمة إلى تصنيف خيار الشحن المعروض للمستخدم.amount
: حزمة تكلفة الشحن التي تحتوي على مفتاحَيcurrency
وvalue
مع قيم سلسلة- تعرض السمة
currency
عملة تكلفة الشحن، وهي عبارة عن رمز أبجدي ISO4217 مكتوب بشكل صحيح مكوَّن من 3 أحرف. - تعرض
value
قيمة تكلفة الشحن في شكل قيمة نقدية صالحة لعدد عشري.
- تعرض السمة
selected
- لتحديد ما إذا كان يجب تحديد خيار الشحن عند عرض تطبيق الدفع خيارات الشحن أم لا.
تشتمل جميع المفاتيح غير selected
على قيم سلسلة. تتضمن selected
قيمة منطقية.
val id: String = bundle.getString("id")
val label: String = bundle.getString("label")
val amount: Bundle = bundle.getBundle("amount")
val selected: Boolean = bundle.getBoolean("selected", false)
تقديم المعلومات المطلوبة في الردّ الخاص بالدفع
يجب أن يُدرج تطبيقك المعلومات الإضافية المطلوبة في ردّه على
نشاط PAY
.
لإجراء ذلك، يجب تحديد المَعلمات التالية كعناصر إضافية في Intent:
payerName
- الاسم الكامل للمسؤول عن الدفع يجب أن تكون هذه السلسلة غير فارغة عندما تكون قيمةpaymentOptions.requestPayerName
صحيحة.payerPhone
- رقم هاتف الجهة المسدِّدة يجب أن تكون هذه السلسلة غير فارغة عندما تكون قيمةpaymentOptions.requestPayerPhone
صحيحة.payerEmail
: عنوان البريد الإلكتروني للمسؤول عن الدفع يجب أن تكون هذه السلسلة غير فارغة عندما تكون قيمةpaymentOptions.requestPayerEmail
صحيحة.shippingAddress
: عنوان الشحن الذي قدّمه المستخدم يجب أن تكون هذه حزمة غير فارغة عندما تكون القيمةpaymentOptions.requestShipping
true. يجب أن تتضمّن الحزمة المفاتيح التالية التي تمثّل أجزاءً مختلفة في العنوان الجغرافي.city
countryCode
dependentLocality
organization
phone
postalCode
recipient
region
sortingCode
addressLine
تحتوي جميع المفاتيح غيرaddressLine
على قيم سلسلة. السمةaddressLine
هي مصفوفة من السلاسل.
shippingOptionId
: معرّف خيار الشحن الذي اختاره المستخدم ويجب أن تكون هذه السلسلة غير فارغة عندما تكون القيمةpaymentOptions.requestShipping
true.
التحقّق من صحة الرد على طلب الدفع
إذا تم ضبط نتيجة النشاط الخاصة بنتيجة رد على دفع تم استلامها من تطبيق الدفع الذي تم استدعاؤه على RESULT_OK
، سيتحقق Chrome من المعلومات الإضافية المطلوبة في الميزات الإضافية. إذا تعذّر إجراء عملية التحقّق، سيعرض Chrome وعدًا مرفوضًا من request.show()
يتضمّن إحدى رسائل الخطأ التالية التي يواجهها المطوّرون:
'Payment app returned invalid response. Missing field "payerEmail".'
'Payment app returned invalid response. Missing field "payerName".'
'Payment app returned invalid response. Missing field "payerPhone".'
'Payment app returned invalid shipping address in response.'
'... is not a valid CLDR country code, should be 2 upper case letters [A-Z]'
'Payment app returned invalid response. Missing field "shipping option".'
نموذج الرمز البرمجي التالي هو مثال على استجابة صالحة:
fun Intent.populateRequestedPaymentOptions() {
if (requestPayerName) {
putExtra("payerName", "John Smith")
}
if (requestPayerPhone) {
putExtra("payerPhone", "4169158200")
}
if (requestPayerEmail) {
putExtra("payerEmail", "john.smith@gmail.com")
}
if(requestShipping) {
val address: Bundle = Bundle()
address.putString("countryCode", "CA")
val addressLines: Array<String> =
arrayOf<String>("111 Richmond st. West")
address.putStringArray("addressLines", addressLines)
address.putString("region", "Ontario")
address.putString("city", "Toronto")
address.putString("postalCode", "M5H2G4")
address.putString("recipient", "John Smith")
address.putString("phone", "4169158200")
putExtra("shippingAddress", address)
putExtra("shippingOptionId", "standard")
}
}
اختياري: إتاحة التدفق الديناميكي
في بعض الأحيان، تزداد التكلفة الإجمالية للمعاملة، مثلاً عندما يختار المستخدم خيار الشحن السريع، أو عندما تتغير قائمة خيارات الشحن المتاحة أو أسعارها عندما يختار المستخدم عنوان شحن دولي. عندما يقدّم تطبيقك عنوان الشحن أو خيار الشحن الذي اختاره المستخدم، يجب أن يتمكّن من إعلام التاجر بأي تغييرات في عنوان الشحن أو في خياراته، وأن يعرض على المستخدم تفاصيل الدفع المعدّلة (التي يقدّمها التاجر).
لغة تعريف واجهة نظام Android (AIDL)
لإبلاغ التاجر بالتغييرات الجديدة، استخدِم خدمة PaymentDetailsUpdateService
المعلَنة في ملف AndroidManifest.xml من Chrome. لاستخدام هذه الخدمة، أنشئ ملفين
AIDL بالمحتوى التالي:
app/src/main/aidl/org/chromium/components/payments/IPaymentDetailsUpdateService
package org.chromium.components.payments;
import android.os.Bundle;
interface IPaymentDetailsUpdateServiceCallback {
oneway void updateWith(in Bundle updatedPaymentDetails);
oneway void paymentDetailsNotUpdated();
}
app/src/main/aidl/org/chromium/components/payments/IPaymentDetailsUpdateServiceCallback
package org.chromium.components.payments;
import android.os.Bundle;
import org.chromium.components.payments.IPaymentDetailsUpdateServiceCallback;
interface IPaymentDetailsUpdateService {
oneway void changePaymentMethod(in Bundle paymentHandlerMethodData,
IPaymentDetailsUpdateServiceCallback callback);
oneway void changeShippingOption(in String shippingOptionId,
IPaymentDetailsUpdateServiceCallback callback);
oneway void changeShippingAddress(in Bundle shippingAddress,
IPaymentDetailsUpdateServiceCallback callback);
}
إبلاغ التاجر بالتغييرات التي أجراها المستخدم على طريقة الدفع أو عنوان الشحن أو خيار الشحن التي اختارها
private fun bind() {
// The action is introduced in Chrome version 92, which supports the service in Chrome
// and other browsers (e.g., WebLayer).
val newIntent = Intent("org.chromium.intent.action.UPDATE_PAYMENT_DETAILS")
.setPackage(callingBrowserPackage)
if (packageManager.resolveService(newIntent, PackageManager.GET_RESOLVED_FILTER) == null) {
// Fallback to Chrome-only approach.
newIntent.setClassName(
callingBrowserPackage,
"org.chromium.components.payments.PaymentDetailsUpdateService")
newIntent.action = IPaymentDetailsUpdateService::class.java.name
}
isBound = bindService(newIntent, connection, Context.BIND_AUTO_CREATE)
}
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val service = IPaymentDetailsUpdateService.Stub.asInterface(service)
try {
if (isOptionChange) {
service?.changeShippingOption(selectedOptionId, callback)
} else (isAddressChange) {
service?.changeShippingAddress(selectedAddress, callback)
} else {
service?.changePaymentMethod(methodData, callback)
}
} catch (e: RemoteException) {
// Handle the remote exception
}
}
}
يمكن أن تشتمل السمة callingPackageName
المستخدمة لغرض بدء الخدمة على إحدى القيم التالية اعتمادًا على المتصفح الذي أرسل طلب الدفع.
قناة Chrome | اسم الحزمة |
---|---|
إسطبل |
"com.android.chrome"
|
إصدار تجريبي |
"com.chrome.beta"
|
في وضع التطوير |
"com.chrome.dev"
|
الكاناري |
"com.chrome.canary"
|
Chromium |
"org.chromium.chrome"
|
مربّع البحث السريع من Google (أداة تضمين WebLayer) |
"com.google.android.googlequicksearchbox"
|
changePaymentMethod
يتم إشعار التاجر بالتغييرات في طريقة الدفع التي اختارها المستخدم. تحتوي
الحزمة paymentHandlerMethodData
على methodName
ومفتاحَي details
اختياريَين
مع قيم سلسلة. سيتحقق Chrome من وجود حزمة غير فارغة مع methodName
غير فارغ وسيرسل updatePaymentDetails
مع إحدى رسائل الخطأ التالية عبر callback.updateWith
في حال تعذُّر التحقق من الصحة.
'Method data required.'
'Method name required.'
changeShippingOption
يتم إشعار التاجر بالتغييرات في خيار الشحن الذي اختاره المستخدم.
يجب أن تكون السمة shippingOptionId
معرّفًا لأحد خيارات الشحن التي يحدّدها التاجر. سيتحقق Chrome من وجود shippingOptionId
غير فارغ وسيرسل updatePaymentDetails
مع رسالة الخطأ التالية عبر callback.updateWith
في حال تعذُّر إتمام عملية التحقّق.
'Shipping option identifier required.'
changeShippingAddress
يتم إشعار التاجر بالتغييرات في عنوان الشحن الذي قدّمه المستخدم. سيبحث Chrome
عن حزمة shippingAddress
غير فارغة تتضمّن قيمة countryCode
صالحة
وسيرسل updatePaymentDetails
مع رسالة الخطأ التالية عبر
callback.updateWith
في حال تعذُّر عملية التحقّق.
'Payment app returned invalid shipping address in response.'
رسالة خطأ حالة غير صالحة
إذا واجه Chrome حالة غير صالحة عند تلقّي أي من طلبات التغيير،
سيتم طلب callback.updateWith
مع حزمة updatePaymentDetails
تم إخفاؤها. ستحتوي الحزمة على مفتاح error
مع "Invalid state"
فقط.
تشمل الأمثلة على الحالة غير الصالحة ما يلي:
- عندما يظل Chrome في انتظار رد التاجر على تغيير سابق (مثلاً، حدث تغيير جارٍ)
- لا ينتمي معرّف خيار الشحن المقدَّم من تطبيق الدفع إلى أي من خيارات الشحن التي حدّدها التاجر.
تلقّي تفاصيل الدفع المعدّلة من التاجر
private fun unbind() {
if (isBound) {
unbindService(connection)
isBound = false
}
}
private val callback: IPaymentDetailsUpdateServiceCallback =
object : IPaymentDetailsUpdateServiceCallback.Stub() {
override fun paymentDetailsNotUpdated() {
// Payment request details have not changed.
unbind()
}
override fun updateWith(updatedPaymentDetails: Bundle) {
newPaymentDetails = updatedPaymentDetails
unbind()
}
}
تتشابه السمة updatePaymentDetails
مع قاموس
PaymentRequestDetailsUpdate
WebIDL (بعد إخفاء الحقل
modifiers
) وتتضمّن المفاتيح الاختيارية التالية:
total
- حزمة تحتوي على المفتاحينcurrency
وvalue
، ويحتوي كلا المفتاحين على قيم سلسلةshippingOptions
- مصفوفة أجزاء خيارات الشحنerror
- سلسلة تحتوي على رسالة خطأ عامة (على سبيل المثال، عندما لا توفّر "changeShippingOption
" معرّفًا صالحًا لخيار الشحن)stringifiedPaymentMethodErrors
- سلسلة JSON تمثل أخطاء التحقق من طريقة الدفعaddressErrors
- حزمة تحتوي على مفاتيح اختيارية تتطابق مع عنوان الشحن وقيم السلسلة. يمثل كل مفتاح خطأ في التحقق من الصحة يتعلق بالجزء المقابل له من عنوان الشحن.
ويعني عدم توفّر المفتاح أنّ قيمته لم تتغيّر.