1. 소개
빌드할 항목
이 Codelab을 완료하면 Android용 Google Pay 통합이 작동하는 최소 실행 가능한 Jetpack Compose 앱이 만들어집니다. 이 프로젝트는 처리할 결제 서비스 제공업체에 전송될 수 있는 결제 토큰을 가져옵니다.
학습할 내용
- Google Pay Jetpack Compose 라이브러리를 설치하고 구성하는 방법
- Google Pay 버튼을 표시하고 클릭을 처리하는 방법
- Google Pay에서 결제 토큰을 요청하는 방법
필요한 항목
- Android 스튜디오 (최신 안정화 버전)가 설치되어 있어야 합니다.
- Android SDK 및 Android 스튜디오에 설정된 에뮬레이터 또는 기기
- 프로덕션의 경우 Google Pay
merchantId가 필요합니다. Google Pay 및 월렛 콘솔에 등록하는 데는 1분밖에 걸리지 않으므로 지금 등록하는 것이 좋습니다.
2. Jetpack Compose 프로젝트 만들기
프로젝트 파일 만들기
- Android 스튜디오에서
gpay-compose라는 새 Jetpack Compose 프로젝트를 만듭니다.- Android 스튜디오를 열고
New Project를 선택합니다. Empty Activity (Jetpack Compose)템플릿을 선택합니다.- 이름:
gpay-compose, 패키지 이름:com.example.gpay - 언어: Kotlin, 최소 SDK: API 21 이상
- 완료하여 프로젝트를 생성합니다.
- Android 스튜디오를 열고
- Google Pay Compose 버튼 종속 항목을 추가합니다.
app/build.gradle(.kts)파일에 다음을 추가합니다. Groovy DSL:dependencies { implementation("com.google.pay.button:compose-pay-button:1.0.0") }dependencies { implementation 'com.google.pay.button:compose-pay-button:1.0.0' } - Google Play 서비스 월렛 종속 항목 추가 (Google Pay 시트 열기):
app/build.gradle(.kts)파일에 다음 종속 항목을 추가합니다. 프로젝트에서 Groovy DSL을 사용하는 경우 다음을 사용합니다.dependencies { implementation("com.google.android.gms:play-services-wallet:19.3.0") }dependencies { implementation 'com.google.android.gms:play-services-wallet:19.3.0' } - Android 스튜디오에서
MainActivity.kt를 열고 콘텐츠를 다음 최소 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에서 검토합니다.
MainActivity.kt에 Google Pay 구성 상수를 추가합니다. 다음 단계에서 이를 재사용합니다.
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 참조: 허용된 승인 방법, 허용된 카드 네트워크, 적절한 게이트웨이 값을 비롯한 토큰화 사양에 대한 자세한 내용은
PaymentMethod을 참고하세요.
4. Google Pay 버튼 추가
Compose 결제 버튼을 사용하여 기본 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(...)가 baseGooglePayRequest에 transactionInfo를 추가하여 PaymentDataRequest를 빌드하고 Google Pay 시트를 열고 결제 토큰을 반환합니다.
핵심 사항
- 버튼:
PayButton는 네이티브 Google Pay 버튼을 렌더링합니다. - 클라이언트:
PaymentsClient이 TEST 또는 PRODUCTION으로 구성됩니다. - 실행:
loadPaymentData을 사용하고 필요한 경우IntentSender로 해결합니다. - 토큰:
PaymentData.toJson()를 파싱하여paymentMethodData.tokenizationData.token를 추출하고 PSP로 전송합니다.
6. 결론
이 Codelab을 완료했습니다. Android용 Jetpack Compose 앱에 Google Pay API를 통합하는 방법을 알아봤습니다.
프로젝트 실행
Android 스튜디오 (Run > Run 'app')에서 프로젝트를 실행하여 앱을 시작합니다.
다음 단계
추가 리소스
- Discord의 #payments 채널에서 대화에 참여하세요
- X에서 @GooglePayDevs 팔로우하기
- YouTube에서 Google Pay 관련 동영상 시청하기