كيفية تعديل تطبيق الدفع على Android لتقديم عنوان الشحن ومعلومات الاتصال الخاصة بالمدفوع عنه باستخدام Web Payments APIs
تاريخ النشر: 17 تموز (يوليو) 2020، تاريخ التعديل الأخير: 27 أيار (مايو) 2025
قد تكون إدخال عنوان الشحن ومعلومات الاتصال من خلال نموذج ويب تجربة مزعجة للعملاء. ويمكن أن يؤدي ذلك إلى حدوث أخطاء وانخفاض معدل الإحالات الناجحة.
لهذا السبب، تتيح واجهة برمجة التطبيقات Payment Request API ميزة لطلب عنوان الشحن ومعلومات الاتصال. ويعود ذلك بالفائدة على النحو التالي:
- يمكن للمستخدمين اختيار العنوان الصحيح ببضع نقرات فقط.
- ويتم عرض العنوان دائمًا بالتنسيق المعيار.
- من غير المرجّح إرسال عنوان غير صحيح.
يمكن للمتصفّحات تأجيل جمع عنوان الشحن ومعلومات الاتصال إلى تطبيق دفع لتقديم تجربة دفع موحّدة. يُطلق على هذه الوظيفة اسم تفويض.
كلما أمكن، يفوّض Chrome جمع عنوان الشحن ومعلومات الاتصال الخاصة بالعميل إلى تطبيق الدفع المتوافق مع Android الذي تمّ استدعاؤه. ويقلل التفوّض من الصعوبات التي تواجهك أثناء الدفع.
يمكن للموقع الإلكتروني للتاجر تعديل خيارات الشحن والسعر الإجمالي ديناميكيًا، وذلك استنادًا إلى اختيار العميل لعنوان الشحن وخيار الشحن.
لإضافة ميزة التفويض إلى تطبيق دفع حالي على Android، اتّبِع الخطوات التالية:
- الإفصاح عن عمليات التفويض المتوافقة
- استخدم
PAY
extras لتحليل خيارات الدفع المطلوبة. - تقديم المعلومات المطلوبة في ردّ الدفع
- [اختياري] إتاحة المسار الديناميكي:
الإفصاح عن التفويضات المتوافقة
يجب أن يعرف المتصفّح قائمة المعلومات الإضافية التي يمكن أن يوفّرها
تطبيق الدفع ليتمكّن من تفويض جمع هذه المعلومات إلى
تطبيقك. يجب الإفصاح عن التفويضات المتوافقة على أنّها <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>
يحتوي على جميع القيم التالية أو مجموعة فرعية منها:
payerName
payerEmail
payerPhone
shippingAddress
لا يمكن أن يقدّم المثال التالي سوى عنوان الشحن وعنوان البريد الإلكتروني للمدفوع عنه.
<?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")
Java
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)
}
Java
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
تعرِض عملة تكلفة الشحن بالتنسيق التالي: ISO4217ISO4217.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)
Java
String id = bundle.getString("id");
String label = bundle.getString("label");
Bundle amount = bundle.getBundle("amount");
Boolean selected = bundle.getBoolean("selected", false);
تقديم المعلومات المطلوبة في ردّ الدفع
يجب أن يتضمّن تطبيقك المعلومات الإضافية المطلوبة في ردّه على
نشاط PAY
.
ولإجراء ذلك، يجب تحديد المَعلمات التالية كإضافات للهدف:
payerName
- الاسم الكامل للدافع يجب أن تكون هذه السلسلة غير فارغة عندما تكون قيمةpaymentOptions.requestPayerName
صحيحة.payerPhone
: رقم هاتف المسؤول عن الدفع يجب أن تكون هذه السلسلة غير فارغة عندما تكون قيمةpaymentOptions.requestPayerPhone
صحيحة.payerEmail
: عنوان البريد الإلكتروني للمدفِع يجب أن تكون هذه القيمة سلسلة غير فارغة عندما تكونpaymentOptions.requestPayerEmail
صحيحة.shippingAddress
- عنوان الشحن الذي قدّمه المستخدم. يجب أن تكون هذه الحزمة غير فارغة عندما تكون قيمةpaymentOptions.requestShipping
صحيحة. يجب أن تحتوي الحِزمة على المفاتيح التالية التي تمثّل أجزاء مختلفة في عنوان جغرافي.countryCode
postalCode
sortingCode
region
city
dependentLocality
addressLine
organization
recipient
phone
تملك جميع المفاتيح باستثناء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", "[email protected]")
}
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")
}
}
Java
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", "[email protected]");
}
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
باستخدام فلتر الغرض 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
}
}
Java
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
}
Java
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() {}
Java
@Override
public void updateWith(Bundle updatedPaymentDetails) {}
@Override
public void paymentDetailsNotUpdated() {}
updatedPaymentDetails
هي الحزمة المكافئة لقواميس
PaymentRequestDetailsUpdate
WebIDL، وتحتوي على
المفاتيح الاختيارية التالية:
total
: حزمة تحتوي على مفتاحَيcurrency
وvalue
، وكلا المفتاحَين لهما قيم سلاسلshippingOptions
- الصفيف القابل للتقسيم من shipping optionserror
: سلسلة تحتوي على رسالة خطأ عامة (مثلاً عندما لا يوفّرchangeShippingOption
معرّفًا صالحًا لخيار الشحن)stringifiedPaymentMethodErrors
- سلسلة JSON تمثّل أخطاء التحقّق لطريقة الدفعaddressErrors
- حِزمة تحتوي على مفاتيح اختيارية مطابقة لقيم shipping address وسلسلة يمثّل كل مفتاح خطأ في عملية التحقّق من صحة القيمة المتعلّقة بالجزء المعني من عنوان الشحن.-
modifiers
: مصفوفة قابلة للتقسيم إلى حزم، يتضمّن كلّ منها حقلَيtotal
وmethodData
، وهما أيضًا حِزم.
يعني المفتاح غير المتوفّر أنّ قيمته لم تتغيّر.