كيفية تحديث تطبيق Android للدفع لتوفير عنوان الشحن ومعلومات الاتصال الخاصة بجهة الدفع مع Web Payments API.
قد يكون إدخال عنوان الشحن ومعلومات الاتصال من خلال نموذج ويب تجربة مرهقة للعملاء. يمكن أن يؤدي إلى حدوث أخطاء وانخفاض معدّل الإحالات الناجحة
وهذا هو سبب دعم واجهة برمجة التطبيقات Payment Request API لطلب عنوان الشحن ومعلومات الاتصال. يوفر هذا مزايا متعددة:
- ويمكن للمستخدمين اختيار العنوان الصحيح ببضع نقرات فقط.
- يتم عرض العنوان دائمًا بالتنسيق الموحّد.
- ومن غير المرجّح إرسال عنوان غير صحيح.
يمكن للمتصفّحات تأجيل عملية جمع عنوان الشحن ومعلومات الاتصال إلى تطبيق دفع لتوفير تجربة دفع موحّدة. تُسمّى هذه الوظيفة التفويض.
يفوّض Chrome، كلما أمكن، عملية جمع عنوان الشحن ومعلومات الاتصال الخاصة بالعميل إلى تطبيق الدفع على Android الذي تم استدعاؤه. يؤدي هذا التفويض إلى تقليل الصعوبات التي قد تواجهها أثناء عملية الدفع.
يمكن للموقع الإلكتروني للتاجر تعديل خيارات الشحن والسعر الإجمالي ديناميكيًا استنادًا إلى اختيار العميل لعنوان الشحن وخيار الشحن.
لإضافة دعم التفويض إلى تطبيق دفع حالي على Android، عليك تنفيذ الخطوات التالية:
- الإفصاح عن التفويضات المتوافقة:
- تحليل بيانات
PAY
الإضافية على الهدف لمعرفة خيارات الدفع المطلوبة - قدِّم المعلومات المطلوبة في استجابة الدفعة.
- [اختياري] إتاحة التدفق الديناميكي:
الإفصاح عن التفويضات المتوافقة
يحتاج المتصفّح إلى معرفة قائمة بالمعلومات الإضافية التي يمكن أن يوفّرها تطبيق
الدفع الخاص بك حتى يتمكّن من تفويض جمع هذه المعلومات إلى تطبيقك. يجب تحديد التفويضات المتوافقة باعتبارها <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. - تعرض السمة
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
true.payerPhone
- رقم هاتف الجهة المسدِّدة يجب أن تكون هذه السلسلة غير فارغة عندما تكون قيمةpaymentOptions.requestPayerPhone
true.payerEmail
- عنوان البريد الإلكتروني الخاص بجهة الدفع يجب أن تكون هذه سلسلة غير فارغة عندما تكون قيمةpaymentOptions.requestPayerEmail
true.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
- حزمة تحتوي على مفاتيح اختيارية مماثلة لقيم عنوان الشحن وقيم السلسلة. يمثل كل مفتاح خطأ في التحقق يتعلق بالجزء المقابل له من عنوان الشحن.
يعني عدم وجود المفتاح أن قيمته لم تتغير.