מתן פרטים ליצירת קשר ופרטי משלוח מאפליקציית תשלומים ל-Android

איך מעדכנים את אפליקציית התשלומים ב-Android כדי לספק כתובת למשלוח ואת הפרטים ליצירת קשר של המשלם באמצעות ממשקי Web Payments API.

Sahel Sharify
Sahel Sharify

הזנת כתובת למשלוח ופרטים ליצירת קשר באמצעות טופס אינטרנט יכולה להיות חוויה מסורבלת ללקוחות. הוא עלול לגרום לשגיאות ולירידה במספר ההמרות מחיר.

לכן ב-Payment Request API יש תמיכה בתכונה שמאפשרת לבקש משלוח כתובת ופרטים ליצירת קשר. יש לכך כמה יתרונות:

  • המשתמשים יכולים לבחור את הכתובת הנכונה בכמה הקשות פשוטות.
  • הכתובת תמיד מוחזרת בשדה הסטנדרטי .
  • יש סיכוי נמוך יותר לשלוח כתובת שגויה.

דפדפנים יכולים לדחות את האיסוף של כתובות למשלוח ופרטים ליצירת קשר למשך אפליקציית תשלומים כדי לספק חוויית תשלום אחידה. הפונקציונליות הזו שנקראת הענקת גישה.

כשהדבר מתאפשר, Chrome מאציל את איסוף פרטי המשלוח של הלקוח הכתובת והפרטים ליצירת קשר אל אפליקציית התשלומים ב-Android שהופעלה. הענקת גישה מפחיתה את הקושי במהלך התשלום בקופה.

אתר המוכר יכול לעדכן באופן דינמי את אפשרויות המשלוח ואת המחיר הכולל בהתאם לבחירת הלקוח בכתובת למשלוח ובמשלוח כאפשרות.

אפשרות המשלוח והכתובת למשלוח נכנסו לתוקף. אפשר לראות איך השינוי משפיע על אפשרויות המשלוח ועל המחיר הכולל באופן דינמי.

כדי להוסיף תמיכה בהענקת גישה לאפליקציית תשלומים קיימת ב-Android: לבצע את השלבים הבאים:

  1. להצהיר על הענקת גישה נתמכת
  2. ניתוח של PAY תוספות כוונה לתשלום עבור התשלום הנדרש .
  3. איך מספקים את המידע הנדרש לתשלום תשובה.
  4. [אופציונלי] תמיכה בתהליך דינמי:
    1. הודעה למוכר על שינויים באמצעי התשלום שנבחר על ידי המשתמש, כתובת למשלוח .
    2. לקבל מהמוכר פרטי תשלום מעודכנים (לדוגמה, הסכום הכולל המותאם לפי אפשרות המשלוח שנבחרה עלות).

הצהרה על הקצאות נתמכות

הדפדפן צריך לדעת מהי רשימת הפרטים הנוספים שהתשלום שלך אפליקציה יכולה לספק כדי להאציל את איסוף המידע הזה אפליקציה. יש להצהיר על הענקת הגישה שנתמכת בתור <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" ]

בדוגמה הבאה אפשר לציין רק כתובת למשלוח ואת כתובת האימייל של המשלם address.

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string-array name="supported_delegations">
    <item>payerEmail</item>
    <item>shippingAddress</item>
  </string-array>
</resources>

ניתוח של PAY תוספות Intent עבור אפשרויות התשלום הנדרשות

המוכר יכול לציין מידע נדרש נוסף באמצעות 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 עם מחרוזת.
  • 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 הוא True.
  • shippingAddress - הכתובת למשלוח שסופקה על ידי המשתמש. הוא צריך להיות חבילה שאינה ריקה אם הערך של paymentOptions.requestShipping הוא True. החבילה הם צריכים להכיל את המפתחות הבאים שמייצגים חלקים שונים ב address.
    • city
    • countryCode
    • dependentLocality
    • organization
    • phone
    • postalCode
    • recipient
    • region
    • sortingCode
    • addressLine לכל המפתחות מלבד 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".'

דוגמת הקוד הבאה היא דוגמה לתגובה תקינה:

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")
    }
}

אופציונלי: זרימה דינמית של תמיכה

לפעמים העלות הכוללת של עסקה עולה, למשל כשהמשתמש בוחר באפשרות של משלוח אקספרס, או כשיש רשימה של אפשרויות המשלוח הזמינות או המחירים שלהן משתנים כשהמשתמש בוחר משלוח בינלאומי address. אם האפליקציה מספקת את הכתובת או האפשרות למשלוח שנבחרו על ידי המשתמש, צריכה להיות אפשרות להודיע למוכר על כל כתובת או אפשרות למשלוח משתנה ולהראות למשתמש את פרטי התשלום המעודכנים (מסופקים על ידי סוחר).

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

מודיע למוכר על שינויים בכתובת למשלוח שסופקה על ידי המשתמש. כרום המערכת תבדוק אם יש חבילת 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 - המערך המפרק של shipping אפשרויות
  • error - מחרוזת שמכילה הודעת שגיאה כללית (למשל, המאפיין changeShippingOption לא מספק מזהה חוקי של אפשרות משלוח)
  • stringifiedPaymentMethodErrors – מחרוזת JSON שמייצגת אימות שגיאות באמצעי התשלום
  • addressErrors – חבילה עם מפתחות אופציונליים שזהים למשלוח כתובת ומחרוזת ערכים. כל מפתח מייצג שגיאת אימות שקשורה לערך חלק מהכתובת למשלוח.

אם מפתח חסר, המשמעות היא שהערך שלו לא השתנה.