Android – Tích hợp Google Pay

Như mọi người đã biết, Từ 15/11/2022 Google Pay chính thức hoạt động ở Việt Nam. Google Pay (hay còn được gọi là Google Wallet) là một giải pháp thanh toán di động được cung cấp bởi Google cho phép người dùng thanh toán trực tuyến hoặc trong các cửa hàng với sử dụng thiết

Như mọi người đã biết, Từ 15/11/2022 Google Pay chính thức hoạt động ở Việt Nam. Google Pay (hay còn được gọi là Google Wallet) là một giải pháp thanh toán di động được cung cấp bởi Google cho phép người dùng thanh toán trực tuyến hoặc trong các cửa hàng với sử dụng thiết bị di động của họ. Nó cung cấp một cách đơn giản và tiện lợi cho người dùng thanh toán bằng cách sử dụng thông tin thanh toán đã lưu trữ trong tài khoản Google của họ.

Thông tin các ngân hàng ở Việt hỗ trợ tích hợp và thanh toán bằng Google Pay mọi người có thể tham khảo tại : https://support.google.com/wallet/answer/12315406?hl=en

Ở bài viết này, mình xin chia sẻ các bước để bạn có thể tích hợp thanh toán Google Pay trong ứng dụng Android

I. Cấu hình project

1. Yêu cầu ban đầu

2. Cấu hình Gradle, AndroidManifest

Bước đầu để tích hợp Google Pay, chúng ta cần implement thư viện Google Pay Api

build.gradle

buildscript {
  repositories {
    google()
    mavenCentral()
  }
}

allprojects {
  repositories {
    google()
    mavenCentral()
  }
}

app/build.gradle

implementation 'com.google.android.gms:play-services-wallet:**.*.*'
implementation 'com.google.android.gms:play-services-identity:**.*.*'

Thêm quyền truy cập vào file AndroidManifest.xml

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

II. Triển khai Code

1. Cấu hình Google Pay Api

  1. Define your Google Pay API version
private val baseRequest = JSONObject().apply {
        put("apiVersion", 2)
        put("apiVersionMinor", 0)
    }
  1. Request Payment Token
private fun gatewayTokenizationSpecification(): JSONObject {
    return JSONObject().apply {
        put("type", "PAYMENT_GATEWAY")
        put("parameters", JSONObject(mapOf(
                "gateway" to "example",
                "gatewayMerchantId" to "exampleGatewayMerchantId")))
    }
}

Thay thế “example” và “exampleGatewayMerchantId” bằng các giá trị thích hợp cho nhà cung cấp dịch vụ thanh toán của bạn

  • gateway : Xác định cho 1 cổng mà được Google support
  • gatewayMerchantId: ID duy nhất liên kêt người bán với cổng đã cho
  1. Define supported payment card networks:

Định nghĩa 1 số thẻ được chấp nhận thanh toán

 private val allowedCardNetworks = JSONArray(Constants.SUPPORTED_NETWORKS)

hiện 1 số thẻ được hỗ trợ là: val SUPPORTED_NETWORKS = listOf( "AMEX", "DISCOVER", "JCB", "MASTERCARD", "VISA")
Tiếp theo cần định nghĩa phương thức xác thực được chấp nhận trong giao dịch

private val allowedCardAuthMethods = JSONArray(Constants.SUPPORTED_METHODS)

hiện các phương thức được hỗ trợ là val SUPPORTED_METHODS = listOf( "PAN_ONLY", "CRYPTOGRAM_3DS")

  • PAN_ONLY là một loại PaymentMethod mà chỉ chấp nhận thông tin thẻ tín dụng hoặc thẻ ghi nợ (PAN, hoặc Primary Account Number) nhưng không chấp nhận thông tin bảo mật khác như mã CVV hoặc ngày hết hạn.
  • CRYPTOGRAM_3DS là một loại PaymentMethod mà chứa một mã mã hoá (cryptogram) được tạo ra bởi ngân hàng phát hành thẻ tín dụng hoặc thẻ ghi nợ, và được sử dụng trong quá trình xác minh giao dịch. Mã mã hoá này được sử dụng để xác minh rằng giao dịch được thực hiện bởi người sở hữu thật của thẻ, và không phải là một giao dịch giả mạo hoặc trái phép.

Cả hai loại PaymentMethod này đều được sử dụng trong Google Pay API để xác định cách thanh toán sẽ được thực hiện trong một giao dịch. Tuy nhiên, hãy lưu ý rằng các điều khoản và điều lệ về việc sử dụng các loại PaymentMethod này có thể khác nhau tùy theo quốc gia hoặc khu vực.

  1. Describe your allowed payment methods
    Cấu hình phương thức thanh toán qua các định nghĩa bên trên
 private fun baseCardPaymentMethod(): JSONObject {
        return JSONObject().apply {

            val parameters = JSONObject().apply {
                put("allowedAuthMethods", allowedCardAuthMethods)
                put("allowedCardNetworks", allowedCardNetworks)
                put("billingAddressRequired", true)
                put("billingAddressParameters", JSONObject().apply {
                    put("format", "FULL")
                })
            }

            put("type", "CARD")
            put("parameters", parameters)
        }
    }
 private fun cardPaymentMethod(): JSONObject {
        val cardPaymentMethod = baseCardPaymentMethod()
        cardPaymentMethod.put("tokenizationSpecification", gatewayTokenizationSpecification())

        return cardPaymentMethod
    }

2. Khởi tạo PaymentClient

Để khởi tạo Google Pay Api, chúng ta cần khởi tạo 1 PaymentClient

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

        return Wallet.getPaymentsClient(context, walletOptions)
    }

Chúng ta có thể cấu hình môi trường test hoặc Product bằng cách setEnvironmentWalletConstants.ENVIRONMENT_TEST hoặc WalletConstants.ENVIRONMENT_PRODUCTION

  • ENVIRONMENT_PRODUCTION: Đây là môi trường sản xuất thực tế, tức là người dùng sẽ thực sự thanh toán tiền bằng cách sử dụng Google Pay trong ứng dụng của bạn.
  • ENVIRONMENT_TEST: Đây là môi trường thử nghiệm, tức là người dùng sẽ không thực sự thanh toán tiền bằng cách sử dụng Google Pay trong ứng dụng của bạn. Môi trường này được sử dụng để kiểm tra xem ứng dụng của bạn có tích hợp đúng với Google Pay không.
    Bạn chỉ có thể sử dụng các thẻ thanh toán có sẵn trong bảng “Card Test” của Google Pay Developer documentation. (Tìm hiểu thêm thông tin về Card Test tại: https://developers.google.com/pay/api/android/guides/resources/test-card-suite?hl=vi)

Bạn nên sử dụng môi trường ENVIRONMENT_TEST trong quá trình phát triển và kiểm tra, rồi chuyển sang môi trường ENVIRONMENT_PRODUCTION khi ứng dụng của bạn đã sẵn sàng được phát hành.

Để có thể sử dụng môi trường product, bạn cần đăng ký quyền truy cập tại: https://g.co/pay/sign-up

3. Xác định GooglePay có sẵn sàng để sử dụng hay chưa?

Sau khi đã cấu hình xong Google Pay Api, bước tiếp theo chúng ta cần kiểm tra xem thiết bị có sẵn sàng để thanh toán bằng Google Pay hay không

fun isReadyToPayRequest(): JSONObject? {
        return try {
            baseRequest.apply {
                put("allowedPaymentMethods", JSONArray().put(baseCardPaymentMethod()))
            }

        } catch (e: JSONException) {
            null
        }
    }
private fun fetchCanUseGooglePay() {
        val isReadyToPayJson = PaymentsUtil.isReadyToPayRequest()
        if (isReadyToPayJson == null) _canUseGooglePay.value = false

        val request = IsReadyToPayRequest.fromJson(isReadyToPayJson.toString())
        val task = paymentsClient.isReadyToPay(request)
        task.addOnCompleteListener { completedTask ->
            try {
                _canUseGooglePay.value = completedTask.getResult(ApiException::class.java)
            } catch (exception: ApiException) {
                Log.w("isReadyToPay failed", exception)
                _canUseGooglePay.value = false
            }
        }
    }

Nếu _canUseGooglePay return false nghĩa là thiết bị/ người dùng đang không thể sử dụng Google Pay để thanh toán. Theo khuyến nghị của Google, trong trường hợph đó bạn ko nên hiển thị button Google Pay

4. Xác định thông tin giao dịch bằng PaymentDataRequest

PaymentDataRequest là một đối tượng trong Google Pay API, mà được sử dụng để xác định các thông tin cần thiết cho một giao dịch thanh toán bằng Google Pay. Đối tượng này bao gồm các thông tin như loại PaymentMethod mà người dùng muốn sử dụng, tổng số tiền cần thanh toán, tiền tệ sử dụng, và các thông tin khác như tên người mua và địa chỉ giao hàng.

Khởi tạo thông tin giao dịch: const val COUNTRY_CODE = "US" , const val CURRENCY_CODE = "USD" ( Trong trường hợp ở Việt Nam là “VN” – “VND”, của Nhật bản là “JP” – “JPY”)

private fun getTransactionInfo(price: String): JSONObject {
        return JSONObject().apply {
            put("totalPrice", price)
            put("totalPriceStatus", "FINAL")
            put("countryCode", Constants.COUNTRY_CODE)
            put("currencyCode", Constants.CURRENCY_CODE)
        }
    }

(Tham khảo thêm về các thông tin của TransactionInfo tại: https://developers.google.com/pay/api/android/reference/request-objects#TransactionInfo)

Khởi tạo thông tin của merchant

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

Khởi taọ PaymentDataRequest

fun getPaymentDataRequest(priceCemts: Long): JSONObject {
        return baseRequest.apply {
                put("allowedPaymentMethods", JSONArray().put(cardPaymentMethod()))
                put("transactionInfo", getTransactionInfo(priceCemts.centsToString()))
                put("merchantInfo", merchantInfo)

                // An optional shipping address requirement is a top-level property of the
                // PaymentDataRequest JSON object.
                val shippingAddressParameters = JSONObject().apply {
                    put("phoneNumberRequired", false)
                    put("allowedCountryCodes", JSONArray(listOf("US", "GB")))
                }
                put("shippingAddressParameters", shippingAddressParameters)
                put("shippingAddressRequired", true)
            }
    }

(Tham khảo về các thông tin của PaymentDataRequets tại: https://developers.google.com/pay/api/android/reference/request-objects#PaymentDataRequest)

5. Thanh toán

Thực hiện giao dịch thanh toán khi Click vào Button Google Pay

 googlePayButton.setOnClickListener { requestPayment() }
private fun requestPayment() {

       // Disables the button to prevent multiple clicks.
       googlePayButton.isClickable = false

       // The price provided to the API should include taxes and shipping.
       // This price is not displayed to the user.
       val dummyPriceCents = 100L
       val shippingCostCents = 900L
       val task = model.getLoadPaymentDataTask(dummyPriceCents + shippingCostCents)

       task.addOnCompleteListener { completedTask ->
           if (completedTask.isSuccessful) {
               completedTask.result.let(::handlePaymentSuccess)
           } else {
               when (val exception = completedTask.exception) {
                   is ResolvableApiException -> {
                       resolvePaymentForResult.launch(
                           IntentSenderRequest.Builder(exception.resolution).build()
                       )
                   }
                   is ApiException -> {
                       handleError(exception.statusCode, exception.message)
                   }
                   else -> {
                       handleError(
                           CommonStatusCodes.INTERNAL_ERROR, "Unexpected non API" +
                                   " exception when trying to deliver the task result to an activity!"
                       )
                   }
               }
           }

           // Re-enables the Google Pay payment button.
           googlePayButton.isClickable = true
       }
   }
 private fun handlePaymentSuccess(paymentData: PaymentData) {
       val paymentInformation = paymentData.toJson()

       try {
           // Token will be null if PaymentDataRequest was not constructed using fromJson(String).
           val paymentMethodData = JSONObject(paymentInformation).getJSONObject("paymentMethodData")
           val billingName = paymentMethodData.getJSONObject("info")
                   .getJSONObject("billingAddress").getString("name")
           Log.d("BillingName", billingName)

           Toast.makeText(this, getString(R.string.payments_show_name, billingName), Toast.LENGTH_LONG).show()

           // Logging token string.
           Log.d("Google Pay token", paymentMethodData
                   .getJSONObject("tokenizationData")
                   .getString("token"))

       } catch (error: JSONException) {
           Log.e("handlePaymentSuccess", "Error: $error")
       }
   }

ViewModel:

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

6. Xử lý kết quả thanh toán

Sau khi hoàn thành thanh toán, kết quả sẽ được trả về tại onActivityResult kèm với thông tin PaymentData

// Handle potential conflict from calling loadPaymentData
    private val resolvePaymentForResult = registerForActivityResult(StartIntentSenderForResult()) {
            result: ActivityResult ->
        when (result.resultCode) {
            RESULT_OK ->
                result.data?.let { intent ->
                    PaymentData.getFromIntent(intent)?.let(::handlePaymentSuccess)
                }

            RESULT_CANCELED -> {
                // The user cancelled the payment attempt
            }
        }
    }
private fun handlePaymentSuccess(paymentData: PaymentData) {
        val paymentInformation = paymentData.toJson()

        try {
            // Token will be null if PaymentDataRequest was not constructed using fromJson(String).
            val paymentMethodData = JSONObject(paymentInformation).getJSONObject("paymentMethodData")
            val billingName = paymentMethodData.getJSONObject("info")
                    .getJSONObject("billingAddress").getString("name")
            Log.d("BillingName", billingName)

            Toast.makeText(this, getString(R.string.payments_show_name, billingName), Toast.LENGTH_LONG).show()

            // Logging token string.
            Log.d("Google Pay token", paymentMethodData
                    .getJSONObject("tokenizationData")
                    .getString("token"))

        } catch (error: JSONException) {
            Log.e("handlePaymentSuccess", "Error: $error")
        }
    }

III. Quy định về Brand

Để sử dụng và hiển thị button Google Play, bạn cần tuân thủ về chính sách và quy định về brand của Google

  • Chỉ sử dụng các nút Google Pay do Google cung cấp.
  • Sử dụng các nút Google Pay để bắt đầu quy trình thanh toán.
  • Sử dụng cùng một kiểu nút trên toàn bộ trang web của bạn.
  • Đảm bảo rằng kích thước của các nút Google Pay bằng hoặc lớn hơn các nút khác.

Không được

  • Tạo các nút Google Pay của riêng bạn hoặc thay đổi phông chữ, màu sắc, phần đệm trong nút theo bất kỳ cách nào.
  • Sử dụng các nút thanh toán của Google Pay để bắt đầu bất kỳ hành động nào ngoài quy trình thanh toán.
  • Làm cho nút Google Pay nhỏ hơn các nút khác.

1 số quy định khác:

  • Viết hoa các chữ cái “G” và “P”
  • Không được viết tắt Google Pay
  • Phù hợp với style UI trong ứng dụng
  • Không dịch “Google Pay”
  • Sử dụng trademark symbol (™) trong hoạt động truyền thông tiếp thị

Tham khảo thêm về Brand Guidelines: https://developers.google.com/pay/api/android/guides/brand-guidelines

IV. Tổng kết

Với xu thế thanh toán không dùng tiền mặt ở Việt Nam và thế giới ngày càng tăng cao, với sự xuất hiện của các ví điện tử kèm các kênh thanh toán mới, Google Pay hứa hẹn là 1 giải pháp thanh toán thông minh và an toàn trên các thiết bị smart phone


Tham khảo:
https://www.youtube.com/watch?v=SZorG5Hqjzchttps://developers.google.com/pay/api/android/overviewhttps://github.com/google-pay/android-quickstart

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ