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 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
- minSdkVersion: 19 hoặc cao hơn
- compileSdkVersion: 28 hoặc cao hơn
- Install the Google Play services version 19.1.0 or greater.
- Install the Google Pay app and add a payment method.
- Add a payment method to Google.
- Adhere to our Google Pay API Acceptable Use Policy and the Google Play developer policy.
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
- Define your Google Pay API version
private val baseRequest = JSONObject().apply {
put("apiVersion", 2)
put("apiVersionMinor", 0)
}
- 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
- 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.
- 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 setEnvironment
là WalletConstants.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=SZorG5Hqjzc https://developers.google.com/pay/api/android/overview https://github.com/google-pay/android-quickstart
All rights reserved