‫Google Pay API ל-Jetpack פיתוח נייטיב ב-Android

1. מבוא

מה תפַתחו

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

מה תלמדו

  • איך מתקינים ומגדירים את ספריית Google Pay Jetpack Compose
  • איך להציג את לחצן Google Pay ולטפל בקליקים
  • איך מבקשים טוקן תשלום מ-Google Pay

הדרישות

  • ‫Android Studio (הגרסה היציבה האחרונה) מותקן.
  • ‫Android SDK ואמולטור או מכשיר שהוגדרו ב-Android Studio.
  • לסביבת הייצור, תצטרכו merchantId. ההרשמה במסוף של Google Pay ו-Wallet נמשכת רק דקה, אז כדאי לעשות את זה עכשיו.

2. יצירת פרויקט Jetpack Compose

יצירת קבצים בפרויקט

  1. יוצרים פרויקט חדש של Jetpack Compose ב-Android Studio בשם gpay-compose:
    • פותחים את Android Studio ובוחרים באפשרות New Project.
    • בוחרים את התבנית Empty Activity (Jetpack Compose).
    • שם: gpay-compose, שם החבילה: com.example.gpay.
    • שפה: Kotlin, גרסת SDK מינימלית: API 21 ומעלה.
    • לוחצים על סיום כדי ליצור את הפרויקט.
  2. מוסיפים את התלות של לחצן Google Pay Compose.בקובץ app/build.gradle(.kts), מוסיפים:
    dependencies {
        implementation("com.google.pay.button:compose-pay-button:1.0.0")
    }
    
    Groovy DSL:
    dependencies {
        implementation 'com.google.pay.button:compose-pay-button:1.0.0'
    }
    
  3. מוסיפים תלות ב-Google Play Services Wallet (כדי לפתוח את דף Google Pay):בקובץ app/build.gradle(.kts), מוסיפים את התלות הבאה:
    dependencies {
        implementation("com.google.android.gms:play-services-wallet:19.3.0")
    }
    
    אם בפרויקט שלכם נעשה שימוש ב-Groovy DSL, משתמשים בפקודה:
    dependencies {
        implementation 'com.google.android.gms:play-services-wallet:19.3.0'
    }
    
  4. פותחים את MainActivity.kt ב-Android Studio ומחליפים את התוכן שלו ב-scaffold הבא של אפליקציית Compose מינימלית (בהמשך נחבר את הלחצן):
    package com.example.gpay
    
    import android.os.Bundle
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.compose.foundation.layout.Box
    import androidx.compose.foundation.layout.fillMaxSize
    import androidx.compose.material3.MaterialTheme
    import androidx.compose.material3.Surface
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    
    class MainActivity : ComponentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContent {
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                        // Google Pay button added in the next section
                    }
                }
            }
        }
    }
    

3. הגדרת Google Pay

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

מוסיפים את קבועי ההגדרה של Google Pay אל MainActivity.kt (תשתמשו בהם שוב בשלב הבא):

private const val merchantId = "12345678901234567890"

// This is the base configuration for all Google Pay payment data requests.
private val baseGooglePayRequest = """
    {
      "apiVersion": 2,
      "apiVersionMinor": 0,
      "allowedPaymentMethods": [
        {
          "type": "CARD",
          "parameters": {
            "allowedAuthMethods": [
              "PAN_ONLY", "CRYPTOGRAM_3DS"
            ],
            "allowedCardNetworks": [
              "AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"
            ]
          },
          "tokenizationSpecification": {
            "type": "PAYMENT_GATEWAY",
            "parameters": {
              "gateway": "example",
              "gatewayMerchantId": "exampleGatewayMerchantId"
            }
          }
        }
      ],
      "merchantInfo": {
        "merchantId": "$merchantId"
      }
    }
""".trimIndent()

משאבים

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

4. הוספת לחצן Google Pay

משתמשים ב-Compose Pay Button כדי לעבד לחצן מקורי של Google Pay, וב-Wallet API כדי לפתוח את גיליון Google Pay.

מחליפים את כל התוכן של MainActivity.kt בדוגמה המלאה הבאה (כולל הגדרה + לחצן + תהליך תשלום):

package com.example.gpay

import android.os.Bundle
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.tasks.Task
import com.google.android.gms.wallet.contract.TaskResultContracts.GetPaymentDataResult
import com.google.android.gms.wallet.*
import com.google.pay.button.ButtonTheme
import com.google.pay.button.ButtonType
import com.google.pay.button.PayButton
import org.json.JSONObject

private const val merchantId = "12345678901234567890"

// Base Google Pay request used for both the button and the Wallet request
private val baseGooglePayRequest = """
    {
      "apiVersion": 2,
      "apiVersionMinor": 0,
      "allowedPaymentMethods": [
        {
          "type": "CARD",
          "parameters": {
            "allowedAuthMethods": [
              "PAN_ONLY", "CRYPTOGRAM_3DS"
            ],
            "allowedCardNetworks": [
              "AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"
            ]
          },
          "tokenizationSpecification": {
            "type": "PAYMENT_GATEWAY",
            "parameters": {
              "gateway": "example",
              "gatewayMerchantId": "exampleGatewayMerchantId"
            }
          }
        }
      ],
      "merchantInfo": {
        "merchantId": "$merchantId"
      }
    }
""".trimIndent()

class MainActivity : ComponentActivity() {
  private val paymentDataLauncher = registerForActivityResult(GetPaymentDataResult()) { taskResult ->
    when (taskResult.status.statusCode) {
      CommonStatusCodes.SUCCESS -> {
        handlePaymentData(taskResult.result!!)
      }
      //CommonStatusCodes.CANCELED -> The user canceled
      //CommonStatusCodes.DEVELOPER_ERROR -> The API returned an error (it.status: Status)
      //else -> Handle internal and other unexpected errors
    }
  }
  private lateinit var paymentsClient: PaymentsClient

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // Create the PaymentsClient
    paymentsClient = Wallet.getPaymentsClient(
      this,
      Wallet.WalletOptions.Builder()
        .setEnvironment(WalletConstants.ENVIRONMENT_TEST) // Switch to PRODUCTION when ready
        .build()
    )

    // Derive allowedPaymentMethods for the button from baseGooglePayRequest
    val allowedPaymentMethods = JSONObject(baseGooglePayRequest)
      .getJSONArray("allowedPaymentMethods")
      .toString()

    setContent {
      Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        PayButton(
          onClick = { requestPayment(paymentDataLauncher) },
          allowedPaymentMethods = allowedPaymentMethods,
          type = ButtonType.Pay,
          theme = ButtonTheme.Light
        )
      }
    }
  }

  private fun requestPayment(launcher: ActivityResultLauncher<Task<PaymentData>>) {
    // Build a PaymentDataRequest from the base request by adding transaction info
    val requestJson = JSONObject(baseGooglePayRequest).apply {
      put("transactionInfo", JSONObject().apply {
        put("totalPrice", "14.95")
        put("totalPriceStatus", "FINAL")
        put("countryCode", "US")
        put("currencyCode", "USD")
      })
    }
    val request = PaymentDataRequest.fromJson(requestJson.toString())
    val task = paymentsClient.loadPaymentData(request)
    task.addOnCompleteListener(paymentDataLauncher::launch)
  }

  private fun handlePaymentData(paymentData: PaymentData?) {
    val json = paymentData?.toJson() ?: return
    val paymentMethodData = JSONObject(json).getJSONObject("paymentMethodData")
    val tokenizationData = paymentMethodData.getJSONObject("tokenizationData")
    val token = tokenizationData.getString("token")
    // Send 'token' to your payment service provider (PSP)
    println("Payment token: $token")
  }
}

5. שליחת בקשת תשלום

כשלוחצים על הלחצן של Google Pay,‏ requestPayment(...) יוצר PaymentDataRequest על ידי הוספת transactionInfo אל baseGooglePayRequest, פותח את גיליון Google Pay ומחזיר אסימון תשלום.

נקודות עיקריות

  • כפתור: PayButton רכיב שמעבד את כפתור Google Pay המקורי.
  • לקוח: PaymentsClient מוגדר עם TEST או PRODUCTION.
  • הפעלה: משתמשים ב-loadPaymentData ומבצעים פתרון בעזרת IntentSender כשנדרש.
  • אסימון: מנתחים את PaymentData.toJson() כדי לחלץ את paymentMethodData.tokenizationData.token ושולחים אותו לספק שירותי התשלום.

6. סיכום

כל הכבוד, סיימתם את ה-Codelab הזה! למדתם איך לשלב את Google Pay API באפליקציית Jetpack Compose ל-Android.

הרצת הפרויקט

מריצים את הפרויקט מ-Android Studio ‏ (Run > Run 'app') כדי להפעיל את האפליקציה.

לאן כדאי ללכת מכאן

מקורות מידע נוספים