كيفية تعديل تطبيق الدفع على Android لتوفير عنوان الشحن ومعلومات الاتصال الخاصة الدافع باستخدام واجهات برمجة تطبيقات Web Payments
تاريخ النشر: 17 يوليو 2020، تاريخ آخر تعديل: 27 مايو 2025
قد يجد العملاء صعوبة في إدخال عنوان الشحن ومعلومات الاتصال من خلال نموذج على الويب. قد يتسبب ذلك في حدوث أخطاء وانخفاض معدّل الإحالات الناجحة.
لهذا السبب، تتيح واجهة 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/chromium_payment_supported_delegations" />
</activity>
يجب أن يشير android:resource إلى <string-array> يحتوي على كل القيم التالية أو مجموعة فرعية منها:
payerNamepayerEmailpayerPhoneshippingAddress
يمكن للمثال التالي تقديم عنوان شحن وعنوان البريد الإلكتروني الخاص بالدافع فقط.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="chromium_payment_supported_delegations">
<item>payerEmail</item>
<item>shippingAddress</item>
</string-array>
</resources>
تحليل إضافات الغرض PAY للحصول على خيارات الدفع المطلوبة
يمكن للتاجر تحديد معلومات إضافية مطلوبة باستخدام قاموس
paymentOptions. سيوفّر Chrome قائمة بالخيارات المطلوبة التي يمكن أن يوفّرها تطبيقك من خلال تمرير paymentOptions
إضافات Intent
إلى النشاط PAY.
paymentOptions
paymentOptions هي مجموعة فرعية من خيارات الدفع التي يحدّدها التاجر والتي أعلن تطبيقك عن إتاحة تفويضها.
Kotlin
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")
جافا
Bundle paymentOptions = extras.getBundle("paymentOptions");
if (paymentOptions != null) {
Boolean requestPayerName = paymentOptions.getBoolean("requestPayerName");
Boolean requestPayerPhone = paymentOptions.getBoolean("requestPayerPhone");
Boolean requestPayerEmail = paymentOptions.getBoolean("requestPayerEmail");
Boolean requestShipping = paymentOptions.getBoolean("requestShipping");
String shippingType = paymentOptions.getString("shippingType");
}
يمكن أن تتضمّن المَعلمات التالية:
-
requestPayerName: القيمة المنطقية التي تشير إلى ما إذا كان اسم الدافع مطلوبًا أم لا. -
requestPayerPhone: القيمة المنطقية التي تشير إلى ما إذا كان رقم هاتف الدافع مطلوبًا أم لا. -
requestPayerEmail: القيمة المنطقية التي تشير إلى ما إذا كان عنوان البريد الإلكتروني الخاص بالدافع مطلوبًا أم لا. -
requestShipping: القيمة المنطقية التي تشير إلى ما إذا كان يجب تقديم معلومات الشحن أم لا. shippingType: السلسلة التي تعرض نوع الشحن يمكن أن يكون نوع الشحن"shipping"أو"delivery"أو"pickup". يمكن لتطبيقك استخدام هذه الإشارة في واجهة المستخدم عند طلب عنوان المستخدم أو خيارات الشحن التي يختارها.
shippingOptions
shippingOptions هي مصفوفة قابلة للتجزئة تتضمّن خيارات الشحن التي يحدّدها التاجر. لن تتوفّر هذه المَعلمة إلا عندما تكون قيمة paymentOptions.requestShipping ==
true.
Kotlin
val shippingOptions: List<ShippingOption>? =
extras.getParcelableArray("shippingOptions")?.mapNotNull {
p -> from(p as Bundle)
}
جافا
Parcelable[] shippingOptions = extras.getParcelableArray("shippingOptions");
for (Parcelable it : shippingOptions) {
if (it != null && it instanceof Bundle) {
Bundle shippingOption = (Bundle) it;
}
}
كل خيار شحن هو Bundle مع المفاتيح التالية.
- استبدِل
idبمعرّف خيار الشحن. label: تمثّل هذه السمة تصنيف خيار الشحن المعروض للمستخدم.amount: حزمة تكلفة الشحن التي تحتوي على المفتاحَينcurrencyوvalueمع قيم السلسلة- تعرض
currencyعملة تكلفة الشحن كرمز أبجدي مكوّن من 3 أحرف ISO4217 بتنسيق سليم. - تعرض
valueقيمة تكلفة الشحن، على شكل قيمة نقدية عشرية صالحة
- تعرض
selected: تحدّد ما إذا كان يجب تحديد خيار الشحن عندما يعرض تطبيق الدفع خيارات الشحن.
تحتوي جميع المفاتيح باستثناء selected على قيم سلسلة. يحتوي selected على قيمة منطقية.
Kotlin
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)
جافا
String id = bundle.getString("id");
String label = bundle.getString("label");
Bundle amount = bundle.getBundle("amount");
Boolean selected = bundle.getBoolean("selected", false);
تقديم المعلومات المطلوبة في ردّ الدفع
يجب أن يتضمّن تطبيقك المعلومات الإضافية المطلوبة في رده على النشاط PAY.
لإجراء ذلك، يجب تحديد المَعلمات التالية كإضافات Intent:
-
payerName: الاسم الكامل للدافع يجب أن تكون هذه السلسلة غير فارغة عندما تكون قيمةpaymentOptions.requestPayerNameصحيحة. -
payerPhone: رقم هاتف الدافع يجب أن تكون هذه السلسلة غير فارغة عندما تكون قيمةpaymentOptions.requestPayerPhoneصحيحة. - استبدِل
payerEmailبعنوان البريد الإلكتروني الخاص بالدافع. يجب أن تكون هذه السلسلة غير فارغة عندما تكون قيمةpaymentOptions.requestPayerEmailصحيحة. shippingAddress: عنوان الشحن الذي يقدّمه المستخدم. يجب أن تكون هذه الحزمة غير فارغة عندما تكون قيمةpaymentOptions.requestShippingصحيحة. يجب أن تحتوي الحزمة على المفاتيح التالية التي تمثّل أجزاء مختلفة من عنوان فعلي.countryCodepostalCodesortingCoderegioncitydependentLocalityaddressLineorganizationrecipientphoneتحتوي جميع المفاتيح باستثناءaddressLineعلى قيم نصية.addressLineهي مصفوفة من السلاسل.
shippingOptionId: معرّف خيار الشحن الذي اختاره المستخدم. يجب أن تكون هذه السلسلة غير فارغة عندما تكون قيمةpaymentOptions.requestShippingصحيحة.
التحقّق من صحة ردّ الدفع
إذا تم ضبط نتيجة النشاط لردّ الدفع الذي تم تلقّيه من تطبيق الدفع الذي تم استدعاؤه على 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".'
في ما يلي عينة تعليمات برمجية لاستجابة صالحة:
Kotlin
fun Intent.populateRequestedPaymentOptions() {
if (requestPayerName) {
putExtra("payerName", "John Smith")
}
if (requestPayerPhone) {
putExtra("payerPhone", "5555555555")
}
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", "5555555555")
putExtra("shippingAddress", address)
putExtra("shippingOptionId", "standard")
}
}
جافا
private Intent populateRequestedPaymentOptions() {
Intent result = new Intent();
if (requestPayerName) {
result.putExtra("payerName", "John Smith");
}
if (requestPayerPhone) {
presult.utExtra("payerPhone", "5555555555");
}
if (requestPayerEmail) {
result.putExtra("payerEmail", "john.smith@gmail.com");
}
if (requestShipping) {
Bundle address = new Bundle();
address.putExtra("countryCode", "CA");
address.putExtra("postalCode", "M5H2G4");
address.putExtra("region", "Ontario");
address.putExtra("city", "Toronto");
String[] addressLines = new String[] {"111 Richmond st. West"};
address.putExtra("addressLines", addressLines);
address.putExtra("recipient", "John Smith");
address.putExtra("phone", "5555555555");
result.putExtra("shippingAddress", address);
result.putExtra("shippingOptionId", "standard");
}
return result;
}
اختياري: إتاحة المسار الديناميكي
في بعض الأحيان، تزداد التكلفة الإجمالية للمعاملة، مثلاً عندما يختار المستخدم خيار الشحن السريع، أو عندما تتغيّر قائمة خيارات الشحن المتاحة أو أسعارها عندما يختار المستخدم عنوان شحن دولي. عندما يوفّر تطبيقك عنوان الشحن أو خيار الشحن الذي اختاره المستخدم، يجب أن يكون قادرًا على إبلاغ التاجر بأي تغييرات في عنوان الشحن أو خيار الشحن، وعرض تفاصيل الدفع المعدَّلة (التي يقدّمها التاجر) للمستخدم.
لإعلام التاجر بالتغييرات الجديدة، عليك تنفيذ واجهة
IPaymentDetailsUpdateServiceCallback وتعريفها في
AndroidManifest.xml باستخدام intent filter UPDATE_PAYMENT_DETAILS.
بعد استدعاء الغرض PAY مباشرةً، سيربط Chrome بالخدمة UPDATE_PAYMENT_DETAILS (إذا كانت متوفرة) في الحزمة نفسها التي يتضمّنها الغرض PAY، وسيستدعي setPaymentDetailsUpdateService(service) لتزويد تطبيق الدفع بنقطة النهاية IPaymentDetailsUpdateService لإرسال إشعارات بشأن التغييرات في طريقة الدفع أو خيار الشحن أو عنوان الشحن الخاص بالمستخدم.
استخدِم packageManager.getPackagesForUid(Binder.getCallingUid()) عند تلقّي
اتصال بين العمليات (IPC) للتحقّق من أنّ التطبيق الذي استدعى الغرض PAY يحمل اسم الحزمة نفسه الذي يحمله التطبيق الذي استدعى الطرق IPaymentDetailsUpdateServiceCallback.
لغة تعريف واجهة نظام Android (AIDL)
أنشئ ملفَي AIDL يتضمّنان المحتوى التالي:
org/chromium/components/payments/IPaymentDetailsUpdateServiceCallback.aidl
package org.chromium.components.payments;
import android.os.Bundle;
import org.chromium.components.payments.IPaymentDetailsUpdateService;
interface IPaymentDetailsUpdateServiceCallback {
oneway void updateWith(in Bundle updatedPaymentDetails);
oneway void paymentDetailsNotUpdated();
oneway void setPaymentDetailsUpdateService(IPaymentDetailsUpdateService service);
}
org/chromium/components/payments/IPaymentDetailsUpdateService.aidl
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);
}
الخدمة
نفِّذ خدمة IPaymentDetailsUpdateServiceCallback.
Kotlin
class SampleUpdatePaymentDetailsCallbackService : Service() {
private val binder = object : IPaymentDetailsUpdateServiceCallback.Stub() {
override fun updateWith(updatedPaymentDetails: Bundle) {}
override fun paymentDetailsNotUpdated() {}
override fun setPaymentDetailsUpdateService(service: IPaymentDetailsUpdateService) {}
}
override fun onBind(intent: Intent?): IBinder? {
return binder
}
}
جافا
import org.chromium.components.paymsnts.IPaymentDetailsUpdateServiceCallback;
public class SampleUpdatePaymentDetailsCallbackService extends Service {
private final IPaymentDetailsUpdateServiceCallback.Stub mBinder =
new IPaymentDetailsUpdateServiceCallback.Stub() {
@Override
public void updateWith(Bundle updatedPaymentDetails) {}
@Override
public void paymentDetailsNotUpdated() {}
@Override
public void setPaymentDetailsUpdateService(IPaymentDetailsUpdateService service) {}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
AndroidManifest.xml
عرض الخدمة لـ IPaymentDetailsUpdateServiceCallback في AndroidManifest.xml
<service
android:name=".SampleUpdatePaymentDetailsCallbackService"
android:exported="true">
<intent-filter>
<action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS" />
</intent-filter>
</service>
إبلاغ التاجر بالتغييرات في طريقة الدفع أو عنوان الشحن أو خيار الشحن الذي اختاره المستخدم
Kotlin
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
}
جافا
if (service == null) {
return;
}
try {
if (isOptionChange) {
service.changeShippingOption(selectedOptionId, callback);
} else (isAddressChange) {
service.changeShippingAddress(selectedAddress, callback);
} else {
service.changePaymentMethod(methodData, callback);
}
} catch (RemoteException e) {
// Handle the remote exception
}
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 بانتظار ردّ التاجر على تغيير سابق (مثل حدث تغيير جارٍ).
- لا ينتمي معرّف خيار الشحن الذي يوفّره تطبيق الدفع إلى أي من خيارات الشحن التي يحدّدها التاجر.
تلقّي تفاصيل الدفع المعدَّلة من التاجر
Kotlin
override fun updateWith(updatedPaymentDetails: Bundle) {}
override fun paymentDetailsNotUpdated() {}
جافا
@Override
public void updateWith(Bundle updatedPaymentDetails) {}
@Override
public void paymentDetailsNotUpdated() {}
updatedPaymentDetails هي حزمة مكافئة لقائمة PaymentRequestDetailsUpdate
WebIDL وتحتوي على المفاتيح الاختيارية التالية:
total: حزمة تحتوي على المفتاحينcurrencyوvalue، وكلاهما يتضمّن قيمًا من النوع سلسلةshippingOptions- مصفوفة قابلة للتسلسل من خيارات الشحنerror: سلسلة تحتوي على رسالة خطأ عامة (مثلاً، عندما لا يوفّرchangeShippingOptionمعرّفًا صالحًا لخيار الشحن)-
stringifiedPaymentMethodErrors: سلسلة JSON تمثّل أخطاء التحقّق من صحة طريقة الدفع -
addressErrors- حزمة تتضمّن مفاتيح اختيارية مطابقة لعنوان الشحن وقيم سلسلة. يمثّل كل مفتاح خطأ في التحقّق من الصحة مرتبطًا بالجزء المقابل من عنوان الشحن. -
modifiers: مصفوفة قابلة للتسلسل من الحِزم، تحتوي كل منها على الحقلينtotalوmethodData، وهما أيضًا حِزم.
يعني المفتاح غير المتوفّر أنّ قيمته لم تتغيّر.