Google Pay API cho Jetpack Compose trên Android

1. Giới thiệu

Sản phẩm bạn sẽ tạo ra

Khi hoàn tất lớp học lập trình này, bạn sẽ có một ứng dụng Jetpack Compose khả thi tối thiểu với tính năng tích hợp Google Pay đang hoạt động cho Android. Dự án này truy xuất một mã thông báo thanh toán có thể được gửi đến một nhà cung cấp dịch vụ thanh toán để xử lý.

Kiến thức bạn sẽ học được

  • Cách cài đặt và định cấu hình thư viện Jetpack Compose của Google Pay
  • Cách hiển thị nút Google Pay và xử lý các lượt nhấp
  • Cách yêu cầu mã thông báo thanh toán từ Google Pay

Bạn cần có

  • Android Studio (phiên bản ổn định mới nhất) đã cài đặt.
  • Android SDK và một trình mô phỏng hoặc thiết bị được thiết lập trong Android Studio.
  • Đối với môi trường sản xuất, bạn sẽ cần có Google Pay merchantId. Bạn chỉ mất một phút để đăng ký trên Bảng điều khiển Google Pay và Ví, vì vậy, bạn nên thực hiện ngay.

2. Tạo dự án Jetpack Compose

Tạo tệp dự án

  1. Tạo một dự án Jetpack Compose mới trong Android Studio có tên là gpay-compose:
    • Mở Android Studio rồi chọn New Project.
    • Chọn mẫu Empty Activity (Jetpack Compose)
    • Tên: gpay-compose, Tên gói: com.example.gpay.
    • Ngôn ngữ: Kotlin, SDK tối thiểu: API 21 trở lên.
    • Nhấp vào Finish (Hoàn tất) để tạo dự án.
  2. Thêm phần phụ thuộc Nút Compose của Google Pay.Trong tệp app/build.gradle(.kts), hãy thêm:
    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. Thêm phần phụ thuộc Wallet của Dịch vụ Google Play (để mở trang Google Pay):Trong tệp app/build.gradle(.kts), hãy thêm phần phụ thuộc sau:
    dependencies {
        implementation("com.google.android.gms:play-services-wallet:19.3.0")
    }
    
    Nếu dự án của bạn sử dụng Groovy DSL, hãy dùng:
    dependencies {
        implementation 'com.google.android.gms:play-services-wallet:19.3.0'
    }
    
  4. Mở MainActivity.kt trong Android Studio rồi thay thế nội dung bằng khung ứng dụng Compose tối thiểu sau (chúng ta sẽ kết nối nút này sau):
    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. Định cấu hình Google Pay

Yêu cầu thanh toán bằng Google Pay cần có một đối tượng yêu cầu. Đối tượng được xác định ở đây là baseGooglePayRequest chứa các chế độ cài đặt chung tối thiểu cho tất cả các yêu cầu. Các chế độ cài đặt bổ sung sẽ được thêm tuỳ thuộc vào yêu cầu được đưa ra mà chúng ta sẽ xem xét trong lớp học lập trình này.

Thêm các hằng số cấu hình Google Pay vào MainActivity.kt (bạn sẽ sử dụng lại các hằng số này trong bước tiếp theo):

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()

Tài nguyên

  • Tài liệu tham khảo về API: Tài liệu về các đối tượng yêu cầu API Google Pay
  • Tài liệu tham khảo về API: Tham khảo PaymentMethod để biết thêm thông tin về các phương thức uỷ quyền được phép, các mạng thẻ được phép và thông số kỹ thuật về mã hoá, bao gồm cả giá trị cổng thích hợp.

4. Thêm nút Google Pay

Sử dụng Nút thanh toán Compose để hiển thị nút Google Pay gốc và API Wallet để mở trang Google Pay.

Thay thế toàn bộ nội dung của MainActivity.kt bằng ví dụ hoàn chỉnh sau (bao gồm cấu hình + nút + quy trình thanh toán):

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. Tạo yêu cầu thanh toán

Khi bạn nhấn nút Google Pay, requestPayment(...) sẽ tạo PaymentDataRequest bằng cách thêm transactionInfo vào baseGooglePayRequest, mở trang Google Pay và trả về mã thông báo thanh toán.

Điểm chính

  • Nút: PayButton hiển thị nút Google Pay gốc.
  • Ứng dụng: PaymentsClient được định cấu hình bằng TEST hoặc PRODUCTION.
  • Khởi chạy: Sử dụng loadPaymentData và phân giải bằng IntentSender khi cần.
  • Mã thông báo: Phân tích cú pháp PaymentData.toJson() để trích xuất paymentMethodData.tokenizationData.token và gửi đến PSP của bạn.

6. Kết luận

Chúc mừng bạn đã hoàn thành lớp học lập trình này! Bạn đã học được cách tích hợp API Google Pay vào một ứng dụng Jetpack Compose cho Android.

Chạy dự án

Chạy dự án từ Android Studio (Run > Run 'app') để bắt đầu ứng dụng.

Nội dung tiếp theo nên tìm hiểu

Tài nguyên khác