רוכשים חוויית תשלום מהירה ב-Android באמצעות Google Pay

1. מבוא

שירות Google Pay API מאפשר למשתמשים לשלם בכל מקום באמצעות פרטי התשלום ששמורים בחשבונות Google שלהם. בשיעור ה-Lab הזה תשתמשו בספריית הלקוח של Google Pay ל-Android כדי לשפר את חוויית התשלום מתוך אפליקציה פשוטה לדוגמה לנייד. כך ניתן ליצור חוויה מהירה, נוחה ובטוחה יותר, שתוביל ליותר המרות וללקוחות מרוצים יותר.

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

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

סקירה כללית

ה-Codelab הזה ידריך אותך איך לשלב את Google Pay באפליקציה קיימת, כולל קביעה אם משתמש יכול לשלם באמצעי תשלום שנתמך ב-Google Pay, המיקום והעיצוב של לחצן התשלום וביצוע העסקה.

אפליקציה לדוגמה

ב-Codelab הזה תלמדו איך:

  1. שילוב של Google Pay באפליקציה קיימת ל-Android
  2. איך לקבוע אם המשתמש מוכן לשלם באמצעות Google Pay
  3. איך מוסיפים את לחצן Google Pay לממשק המשתמש
  4. השלמת פעולת תשלום באמצעות Google Pay

דרישות מוקדמות

  • Git
  • Android Studio או סביבת פיתוח חלופית של אפליקציות ל-Android
  • אמולטור או מכשיר Android שבהם מותקנת הגרסה האחרונה של Google Play Services

תמיכה

אם נתקעת, במאגר google-pay/android-quickstart של GitHub יש פתרון מלא.

2. שנתחיל?

שכפול המאגר מ-GitHub

כדי לשכפל את המאגר לתיקייה במחשב, משתמשים בפקודה הבאה:

git clone https://github.com/google-pay/android-quickstart

או אם אתם מעדיפים להשתמש בארכיון ZIP:

רפרף באפליקציה לדוגמה

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

3. פתיחת הפרויקט ב-Android Studio

מאגר GitHub ששכפולם מכיל פרויקט Android עם פעילות בסיסית. בשלב הזה, תהיה אפשרות לערוך בפעילות הזו כדי לאמת את המוכנות ל-Google Pay ולהציג לחצן Google Pay.

  1. פתיחת Android Studio
  2. בוחרים קובץ ולאחר מכן פתיחה.
  3. בוחרים את הספרייה kotlin במאגר
  4. לוחצים על פתיחה.

הוספת ספריית Google Pay כתלות בקובץ build.gradle

  1. פתיחת קובץ ה-build של Gradle ברמת המודול (kotlin/app/build.gradle.kts)
  2. הוספת ספריית Google Pay לקטע dependencies
implementation "com.google.android.gms:play-services-wallet:19.3.0"
  1. שומרים את הקובץ.
  2. בוחרים באפשרות File (קובץ) ואז את Sync Project with Gradle Files (סנכרון הפרויקט עם קובצי Gradle)

הפעלת Google Pay API בקובץ המניפסט ב-Android

לסיום, מוסיפים רכיב meta-data בתוך הצומת application של קובץ המניפסט:

<meta-data
    android:name="com.google.android.gms.wallet.api.enabled"
    android:value="true" />

4. מחליטים איפה למקם את לחצן Google Pay בממשק

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

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

דוגמה ללחצנים דינמיים ב-Google Pay

ספריית Google Pay כוללת תצוגה שעוזרת להוסיף את הלחצן לממשק המשתמש בקלות. אם יוצרים פריסות באמצעות XML, צריך לעשות את זה:

<com.google.android.gms.wallet.button.PayButton
    android:id="@+id/googlePayButton"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

בהמשך תוכלו לאתחל את הלחצן באופן פרוגרמטי (ראו דוגמה).
אם משתמשים ב-Jetpack פיתוח נייטיב, צריך לכלול בקובץ build.gradle או build.gradle.kts את הגרסה האחרונה של התלות הבאה:

implementation "com.google.pay.button:compose-pay-button:1.0.0"

ומוסיפים את לחצן Google Pay לפריסת הכתיבה באופן הבא:

PayButton(
    modifier = Modifier
        .testTag("payButton")
        .fillMaxWidth(),
    onClick = onGooglePayButtonClick,
    allowedPaymentMethods = PaymentsUtil.allowedPaymentMethods.toString()
)

לצד הדוגמה ב-GitHub, מוסבר איך להרכיב את רשימת אמצעי התשלום שניתן להשתמש בהם.

5. הפעלה והגדרה של Google Pay API

יצירת מופע של לקוח ה-API

כדי להתחיל להשתמש ב-API, צריך ליצור אובייקט לקוח ולהשתמש בו כדי לבצע קריאות ל-Google Pay API. תוכלו לעשות זאת מיד לאחר יצירת הפעילות או נאמן המידע:

private val paymentsClient: PaymentsClient = createPaymentsClient(context)

fun createPaymentsClient(context: Context): PaymentsClient {
    val walletOptions = Wallet.WalletOptions.Builder()
            .setEnvironment(WalletConstants.ENVIRONMENT_TEST).build()
    return Wallet.getPaymentsClient(context, walletOptions)
}

לקוח התשלום מופעל עם אובייקט WalletOptions. אם מגדירים את הסביבה כ-ENVIRONMENT_TEST, אפשר לערוך ניסויים עם פרטי תשלום לבדיקה. כשתהיו מוכנים להנפיק עסקאות תשלום בפועל, תוכלו לעדכן את מאפיין הסביבה ל-ENVIRONMENT_PRODUCTION.

המבנה של בקשה מ-Google Pay

כששולחים בקשות נגד Google Pay API, יש כמה פרמטרים של הגדרה שצריך לכלול בבקשות, למשל גרסת ה-API המטורגטת. למטרת ה-Codelab הזה, האובייקט הזה מכיל גם מידע על אמצעי התשלום המקובלים באפליקציה שלך. המבנה הסופי נראה כך:

{
    apiVersion: number,
    apiVersionMinor: number,
    allowedPaymentMethods: Array
}

בנכס allowedPaymentMethods מופיעה רשימה של אמצעי תשלום. עבור כל אמצעי תשלום, צריך לכלול את המאפיינים הבאים:

{
    type: 'CARD',
    parameters: {
        allowedCardNetworks: Array.<string>,
        allowedAuthMethods: Array.<string>
    }
}

הגדרת אמצעי התשלום

לצורך הדוגמה הזו, אתם עומדים לקבל רק תשלומים בכרטיס מאסטרקארד ו-Visa, גם בטופס שהומר לאסימון וגם בטופס של מספר החשבון הראשי (PAN). כך נראה אמצעי התשלום:

private val baseCardPaymentMethod =
    JSONObject()
        .put("type", "CARD")
        .put("parameters", JSONObject()
            .put("allowedCardNetworks", JSONArray(listOf("VISA", "MASTERCARD")))
            .put("allowedAuthMethods", JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")))
        )

סיכום של כל המידע

אז מה אמרנו עד עכשיו. הגדרת אמצעי תשלום אחד שיהיה קביל באפליקציה שלך, ועליך לעבוד עם גרסה 2.0 של ה-API. כך נראית ההגדרה שמתקבלת:

private val baseCardPaymentMethod = JSONObject()
    .put("type", "CARD")
    .put("parameters", JSONObject()
        .put("allowedCardNetworks", JSONArray(listOf("VISA", "MASTERCARD")))
        .put("allowedAuthMethods", JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")))
    )

private val googlePayBaseConfiguration = JSONObject()
    .put("apiVersion", 2)
    .put("apiVersionMinor", 0)
    .put("allowedPaymentMethods",  JSONArray(listOf(baseCardPaymentMethod)))
}

6. בדיקת המוכנות לשלם באמצעות Google Pay

שימוש בבקשה isReadyToPay מאפשר לך לקבוע אם משתמש באפליקציה שלך יכול לשלם באמצעות Google Pay. אפשר להשתמש במידע הזה כדי לשנות את חוויית המשתמש באפליקציה בהתאם.

בבקשה הזו עליך לציין את גרסת Google Pay API ואת רשימת אמצעי התשלום המותרים באפליקציה שלך. זה בדיוק מה שאובייקט ההגדרה הבסיסית שהוגדר בשלב הקודם מכיל:

private suspend fun fetchCanUseGooglePay(): Boolean {
    val request = IsReadyToPayRequest.fromJson(googlePayBaseConfiguration.toString())
    return paymentsClient.isReadyToPay(request).await()
}

מלבד זאת, Google Pay API משתמש במעבדי Task כדי לטפל בשיחות מרחוק. בעזרת Tasks API אפשר לפתור פעולות של Task באמצעות קורוטינים. מידע נוסף

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

הצגת לחצן Google Pay

עכשיו צריך לחזור לממשק המשתמש ולעדכן את המיקום של לחצן Google Pay על סמך התוצאה של השיחה הקודמת. בדוגמה הזו נעשה שימוש במחלקה כדי להחזיק את מצב התצוגה, שמתעדכן על סמך קלט כמו הקריאה ל-isReadyToPay.

if (payUiState !is PaymentUiState.NotStarted) {
    PayButton(
        modifier = Modifier
            .testTag("payButton")
            .fillMaxWidth(),
        onClick = onGooglePayButtonClick,
        allowedPaymentMethods = PaymentsUtil.allowedPaymentMethods.toString()
    )
}

7. הגיע הזמן לשלם!

### מכינים את בקשת התשלום

בשלב הזה טענתם את Google Pay API וזיהיתם שהמשתמש באפליקציה שלכם יכול לבצע תשלום באמצעות Google Pay. כתוצאה מכך, הצגתם בממשק המשתמש את לחצן התשלום של Google Pay ועכשיו המשתמש שלכם מוכן לביצוע העסקה. עכשיו הגיע הזמן לטעון את גיליון התשלומים עם אמצעי התשלום שזמינים למשתמש שלך.

בדומה לקריאה ל-isReadyToPay, הבקשה הזו מחייבת את המאפיינים באובייקט ההגדרה הבסיסית שהוגדר קודם לכן (apiVersion, apiVersionMinor ו-allowedPaymentMethods). הפעם נדרש גם נכס חדש בשם tokenizationSpecification ופרמטרים נוספים לתיאור העסקה ולבקשת מידע, כמו כתובות לחיוב ולמשלוח או כתובות משתמשים. כתובת אימייל או מספר טלפון.

#### הנכס tokenizationSpecification

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

קיימים שני סוגים שונים של מנגנוני טיפול נתמכים. אם אתם מעבדים את עסקת התשלום מתוך שרתים שתואמים ל-PCI DSS, יש להשתמש בסוג המפרט DIRECT. בדוגמה זו משתמשים בשער תשלומים כדי לעבד את התשלום, לכן מגדירים את סוג המפרט PAYMENT_GATEWAY:

private val tokenizationSpecification = JSONObject()
    .put("type", "PAYMENT_GATEWAY")
    .put("parameters", JSONObject(mapOf(
            "gateway" to "example",
            "gatewayMerchantId" to "exampleGatewayMerchantId")))
}

בקטע parameters אפשר לציין שער מתוך רשימת הספקים הנתמכים על ידי Google Pay API, וכן הגדרות נוספות שנדרשות לכל שער. למטרת שיעור ה-Lab הזה, משתמשים בשער example, שמחזיר תוצאות בדיקה לעסקאות שבוצעו.

פרמטרים נוספים

בדומה לכך, תוכל לבקש פרטים נוספים מהמשתמש כדי לבצע את ההזמנה בהצלחה. בדוגמה הזו אפשר לראות איך מוסיפים את הנכסים billingAddressRequired ו-billingAddressParameters, כדי לציין שלעסקה הזו הכתובת לחיוב של המשתמש נדרשת בפורמט מלא וכוללת מספר טלפון:

private val cardPaymentMethod = JSONObject()
    .put("type", "CARD")
    .put("tokenizationSpecification", tokenizationSpecification)
    .put("parameters", JSONObject()
        .put("allowedCardNetworks", JSONArray(listOf("VISA", "MASTERCARD")))
        .put("allowedAuthMethods", JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")))
        .put("billingAddressRequired", true)
        .put("billingAddressParameters", JSONObject(mapOf("format" to "FULL")))
    )

#### לכלול מידע נוסף על העסקה

הנכס transactionInfo מכיל אובייקט עם פרטים פיננסיים של העסקה, כלומר המחיר וקוד המטבע (פורמט אלפא של ISO 4217) וגם סטטוס המחיר, שיכול להיות סופי או משוער בהתאם לאופי העסקה (למשל, המחיר עשוי להשתנות בהתאם לכתובת למשלוח שצוינה):

private val transactionInfo = JSONObject()
    .put("totalPrice", "123.45")
    .put("totalPriceStatus", "FINAL")
    .put("currencyCode", "USD")
}

#### הוספת פרטי העסק

בקשת התשלום כוללת מידע על המוֹכר שמבצע את הבקשה בנכס merchantInfo. ב-Codelab הזה נתמקד בשני מאפיינים:

  • הדומיין merchantId מכיל את המזהה שמשויך לחשבון שלך. אפשר למצוא את מספר חשבון Merchant שלך בGoogle Pay & מסוף ארנק. שימו לב שהשדה הזה לא מוערך כשמשתמשים בסביבת TEST.
  • merchantName הוא השם הגלוי למשתמש של האפליקציה או הארגון. יכול להיות שהפרטים האלה יוצגו בגיליון התשלומים של Google Pay, כדי לספק למשתמשים הקשר נוסף לגבי זהות שולח הבקשה.

בסיום התהליך, פשוט מוסיפים את פרטי המוֹכר לאובייקט paymentDataRequest:

private val merchantInfo = JSONObject()
    .put("merchantName", "Example Merchant")
    .put("merchantId", "01234567890123456789")

שליחת הבקשה ועיבוד התוצאה

עכשיו, מאחדים את אובייקט ההגדרה ומעבירים אותו לבקשה loadPaymentData:

private val paymentDataRequestJson = JSONObject(googlePayBaseConfiguration.toString())
    .put("allowedPaymentMethods", JSONArray().put(cardPaymentMethod))
    .put("transactionInfo", transactionInfo)
    .put("merchantInfo", merchantInfo)

בשלב הזה, כל מה שצריך כדי לבקש מ-Google Pay API להזין אמצעי תשלום תקף. כדי לעשות זאת, משתמשים בשיטה loadPaymentData באובייקט PaymentsClient ומעבירים את ההגדרות האישיות שהגדרתם:

fun getLoadPaymentDataTask(): Task<PaymentData> {
    val request = PaymentDataRequest.fromJson(paymentDataRequestJson.toString())
    return paymentsClient.loadPaymentData(request)
}

private fun requestPayment() {
    val task = getLoadPaymentDataTask()
    task.addOnCompleteListener(paymentDataLauncher::launch)
}

כדי להחזיר תוצאות, ב-Google Pay API נעשה שימוש ב-Activity result API. ה-API כולל חוזים שנועדו לפשט את עיבוד התוצאה ולהגיב עליה. בדוגמה הזו נעשה שימוש בחוזה GetPaymentDataResult, שכולל מידע על הפעולה בנוסף לתוצאת התשלום:

private val paymentDataLauncher = registerForActivityResult(GetPaymentDataResult()) { taskResult ->
    when (taskResult.status.statusCode) {
        CommonStatusCodes.SUCCESS -> {
            taskResult.result!!.let {
                Log.i("Google Pay result:", it.toJson())
                // TODO something with the result
            }
        }
        CommonStatusCodes.CANCELED -> // The user canceled
        AutoResolveHelper.RESULT_ERROR -> // The API returned an error (it.status: Status)
        CommonStatusCodes.INTERNAL_ERROR -> // Handle other unexpected errors
    }
}

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

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

אם הבחירה תתבצע בהצלחה, התוצאה תוחזר עם אובייקט PaymentData שמכיל מידע רלוונטי על אמצעי התשלום שנבחר:

{
  "apiVersionMinor": 0,
  "apiVersion": 2,
  "paymentMethodData": {
    "description": "Visa •••• 1234",
    "tokenizationData": {
      "type": "PAYMENT_GATEWAY",
      "token": "examplePaymentMethodToken"
    },
    "type": "CARD",
    "info": {
      "cardNetwork": "VISA",
      "cardDetails": "1234",
      "billingAddress": {
        "phoneNumber": ...,
        ...
      }
    }
  }
}

עכשיו תוכלו להשתמש באמצעי התשלום הזה כדי להשלים את העסקה באמצעות שער התשלומים:

private fun handlePaymentSuccess(paymentData: PaymentData) {
    val paymentMethodToken = paymentData
            .getJSONObject("tokenizationData")
            .getString("token")

    // Sample TODO: Use this token to perform a payment through your payment gateway
}

8. מעולה!

הוספת בהצלחה את שירות Google Pay לאפליקציה ל-Android.

### השלבים הבאים

### מידע נוסף

האם המידע הועיל לך?

מועיל מאוד בדיוק מה שציפיתי לראות לא ממש