Google Pay를 사용하여 Android에서 빠른 결제 환경 빌드(Kotlin)

1. 소개

Google Pay API는 사용자가 어디서나 Google 계정에 저장된 결제 정보를 사용하여 결제할 수 있도록 지원합니다. 이 Codelab에서는 Google Pay의 Android용 클라이언트 라이브러리를 사용하여, 더 많은 전환을 유도하고 고객 만족도를 높이는 빠르고 편리하고 안전한 환경을 만들어 보며 간단한 샘플 모바일 애플리케이션의 결제 환경을 개선합니다.

Auto T-Shirt Shop은 인공 지능의 최신 기능을 활용하고 스타일, 선호도, 날씨, 계절, 패션 트렌드와 같은 정보를 사용하여 구매하기에 가장 적합한 상품을 추천하는 혁신적인 스토어입니다.

참여에 관한 측정항목이 이례적으로 높은 수치를 기록하고 있습니다. 이와 동시에 결제 프로세스 도중 유기 수치도 마찬가지로 높습니다. 이 문제를 해결할 방법을 모색하던 프로젝트 담당자 중 하나가 다른 비슷한 사이트에서 Google Pay를 통해 고무적인 결과를 도출했음을 보여주는 동영상을 시청한 기억을 떠올렸고, 이에 따라 Google Pay를 사용해 보기로 결정하고는 여러분에게 Google Pay 통합 작업을 맡겼습니다.

빌드할 항목

이 Codelab에서는 기존 애플리케이션에 Google Pay를 통합하는 방법을 알아봅니다. 이 과정에서 사용자가 Google Pay에 의해 지원되는 결제 수단을 사용하여 결제할 수 있는지 여부를 확인하는 방법, 결제 버튼의 배치와 디자인, 거래를 실행하는 방법을 알아봅니다.

f5f96f8afc94448c.png

학습할 내용

  • 기존 애플리케이션에 Google Pay를 통합하는 방법
  • 선호하는 여러 결제 수단 중에서 선택하는 방법
  • 사용자가 Google Pay를 사용하여 결제할 준비가 되었는지 확인하는 방법

필요한 항목

  • 인터넷에 연결된 컴퓨터
  • Android 애플리케이션을 빌드할 준비가 된 개발 환경(Android 스튜디오 권장)
  • 최신 버전의 Google Play 서비스가 설치된 Android 기기

2. 시작하기

GitHub에서 저장소 클론:

다음 명령어를 사용하여 컴퓨터에 있는 폴더에 저장소를 클론합니다.

$ git clone https://github.com/google-pay/android-quickstart

ZIP 파일을 선호하는 경우:

샘플 애플리케이션 살펴보기

저장소는 간단한 파일 구조로 되어 있습니다. 이 Codelab의 주된 목표는 어떤 프로그래밍 언어, 라이브러리 또는 도구를 사용하든 기존 애플리케이션과 추후 개발할 애플리케이션에 맞게 이 통합을 조정할 수 있는 역량을 제공하는 것입니다.

3. 샘플 애플리케이션 또는 자체 애플리케이션에 Google Pay 통합

애플리케이션 살펴보기

애플리케이션을 빌드한 후에는 에뮬레이터 또는 실제 기기를 사용하여 미리 볼 수 있습니다. Google 계정 및 계정에 연결된 결제 수단이 적용되어 있는 기기를 사용할 것을 권장합니다.

이 Codelab에서 사용하는 애플리케이션은 최첨단 머신러닝 모델을 사용하여 여러 개의 복잡한 특징을 기반으로 방문자에게 하나의 티셔츠를 추천하는 데모 모바일 스토어입니다. 혹시 사이트를 새로고칠 때마다 다른 티셔츠가 추천되는 이유가 궁금하다면... 솔직히 말씀드릴게요. 이 최첨단 머신러닝 모델은 사실 랜덤 생성기랍니다(미안해요).

이 데모 스토어는 기존 애플리케이션 또는 추후 개발할 애플리케이션에 구매 수단을 추가하기 전과 비슷하게 보이도록 빌드되었습니다. 이 Codelab에서는 이 데모 애플리케이션을 사용할 것을 권장하지만, 원하는 경우 이 Codelab을 사용하여 기존 애플리케이션에 Google Pay를 통합하는 것도 좋습니다.

아직 데모 애플리케이션을 실행하지 않았다면, 지금 데모 애플리케이션을 있는 그대로의 상태로 실행하세요.

175215c8c8696ede.png

데모 애플리케이션을 살펴보셨다면, 그다지 특별한 점은 없었지요? 제품 세부정보 뷰와 가격, 설명, 그리고 일반적인 결제 양식으로 이동하는 데 사용되는 버튼이 있는 것을 보셨을 겁니다.

이 Codelab의 목표는 이 따분한 흐름을 Google Pay로 구동되는 두 번 탭 경험으로 바꾸는 것입니다.

계획

이 통합에 대해 더 효과적으로 이해하는 데 도움이 되도록, 프로세스를 다음과 같은 기본적인 단계로 구분했습니다.

  1. 필요한 종속 항목 추가
  2. Google Pay를 사용하여 결제할 수 있는지 여부 확인
  3. Google Pay를 통해 결제 버튼 표시
  4. 결제 요청 만들기 및 보내기
  5. 결과 수집

4. 필요한 종속 항목 추가

build.gradle에 Google Play 서비스 추가

Google Pay API를 사용하기 위해 가장 먼저 해야 할 일은 필요한 패키지가 포함된 Google Play 서비스 내부에 종속 항목을 추가하는 것입니다. 그러려면 애플리케이션 모듈 안에 있는 build.gradle 파일의 dependencies 목록에 다음과 같은 implementation 항목을 추가합니다. 이 Codelab에서 이 모듈의 이름은 app입니다.

implementation "com.google.android.gms:play-services-wallet:18.0.0"

매니페스트 파일에서 API 사용 설정

마지막으로, 매니페스트 파일의 application 노드 안에 meta-data 요소를 추가합니다. 이렇게 하면 이 API를 사용하고자 한다는 사실을 시스템이 알게 되고 이에 대한 액세스를 사용하도록 설정합니다.

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

5. 사용자 인터페이스 및 Google Pay 버튼을 표시할 위치 계획

뷰의 레이아웃과 전반적인 경험은 사용자가 성공적인 결제 거래를 완료할 가능성에 영향을 주는 중요한 측면입니다. Google Pay를 사용하여 몇 번의 탭만으로 결제 수단을 선택할 수 있도록 지원하면 애플리케이션에서 사용자에게 결제 방법을 제공할 위치와 시점에 관한 더 다양한 옵션을 사용할 수 있게 됩니다. 예를 들어, 프로세스의 초반부(예: 상품의 세부정보 보기)에서 사용자가 마음에 드는 상품에 대해 빠르게 결제할 수 있도록 간단한 결제 옵션을 추가할 수 있습니다.

UI와 탐색을 어떤 식으로 구성할지 정했으면 다음 단계는 Google Pay를 사용한 결제 거래를 트리거하는 버튼을 배치하는 것입니다. 이 버튼의 스타일은 사용자가 애플리케이션에서 버튼을 볼 때 일관성과 친숙함을 느낄 수 있도록 마련된 일련의 가이드라인을 준수해야 합니다.

a18b3311d84d9dcc.png

이 태스크를 쉽게 진행할 수 있도록, 여러 해상도와 언어에 맞게 구성된 모든 사용 가능한 옵션을 하나의 번들로 구성하여 다운로드할 수 있도록 준비해 두었습니다. 이 번들에는 프로젝트에 곧바로 붙여넣을 수 있는 layout, drawable, values 리소스가 포함되어 있습니다.

layout 디렉터리에서 뷰 정의 activity_checkout.xml에 추가해야 하는 리소스를 확인할 수 있습니다. 리소스를 뷰에 추가하려면 include 요소를 사용하여 원하는 위치에 배치하면 됩니다.

<include
    android:id="@+id/googlePayButton"
    layout="@layout/buy_with_googlepay_button"
    android:layout_width=<your_width_dimension>
    android:layout_height="@dimen/buy_button_height"
    android:visibility="gone"/>

6. Google Pay API 초기화 및 구성

API 클라이언트 초기화

API를 사용하려면 Google Pay API를 호출하는 데 사용되는 클라이언트 객체를 인스턴스화해야 합니다. 인스턴스화는 활동이 만들어진 즉시 할 수 있습니다.

private lateinit var paymentsClient: PaymentsClient

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_checkout)

    paymentsClient = createPaymentsClient(this)
}

fun createPaymentsClient(activity: Activity): PaymentsClient {
    val walletOptions = Wallet.WalletOptions.Builder()
            .setEnvironment(WalletConstants.ENVIRONMENT_TEST).build()
    return Wallet.getPaymentsClient(activity, walletOptions)
}

결제 클라이언트는 WalletOptions 객체를 사용하여 초기화됩니다. 환경을 ENVIRONMENT_TEST로 설정하면 통합 전반에서 더미 결제 정보를 사용하여 테스트할 수 있습니다. 실제 거래를 지원하는 작업을 만들 준비가 되었으면 ENVIRONMENT_PRODUCTION으로 환경 속성을 업데이트합니다.

구조

매번 Google Pay API와 커뮤니케이션할 때 요청에 포함해야 하는 몇 가지 구성 파라미터가 있습니다(예: 타겟팅하는 API의 버전). 이 Codelab에서는 이 객체에 애플리케이션에서 지원하는 결제 수단에 관한 정보도 포함되어 있습니다. 최종 구조는 다음과 같습니다.

{
    apiVersion: number,
    apiVersionMinor: number,
    allowedPaymentMethods: Array
}

속성 allowedPaymentMethods는 결제 수단 목록을 받습니다. 모든 결제 수단에 대해 다음과 같은 속성을 포함해야 합니다.

{
    type: 'CARD',
    parameters: {
        allowedCardNetworks: Array.<string>,
        allowedAuthMethods: Array.<string>
    }
}

typeparameters 외에도 뒷부분에서 tokenizationSpecification 속성을 추가하게 됩니다. 이 속성은 사용자가 Google Pay를 사용하여 결제할 수 있는지 여부를 확인하는 데 사용되지는 않으나, PaymentDataRequest 호출이 선택된 결제 수단과 관련된 데이터를 처리할 방법을 정의하는 데 사용됩니다. 지금부터 한 단계씩 살펴보겠습니다.

결제 수단 구성

이 예시에서는 Mastercard 및 Visa 카드 결제만 토큰화된 형태와 기본 계정 번호(PAN) 형태로 지원할 것입니다. 결제 수단의 코드는 다음과 같습니다.

private val baseCardPaymentMethod = JSONObject().apply {
    put("type", "CARD")
    put("parameters", JSONObject().apply {
        put("allowedCardNetworks", JSONArray(listOf("VISA", "MASTERCARD")))
        put("allowedAuthMethods", JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")))
    })
}

종합적으로 살펴보기

지금까지 살펴본 내용을 정리하겠습니다.

여러분은 애플리케이션에서 지원할 하나의 결제 수단을 정의했고, API 버전 2.0을 사용할 계획입니다. 다음은 그 결과로 도출된 구성의 코드입니다.

private val baseCardPaymentMethod = JSONObject().apply {
    put("type", "CARD")
    put("parameters", JSONObject().apply {
        put("allowedCardNetworks", JSONArray(listOf("VISA", "MASTERCARD")))
        put("allowedAuthMethods", JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")))
    })
}

private val googlePayBaseConfiguration = JSONObject().apply {
    put("apiVersion", 2)
    put("apiVersionMinor", 0)
    put("allowedPaymentMethods",  JSONArray().put(baseCardPaymentMethod))
}

기본 구성이 준비되었으니 이제 재미있는 부분으로 넘어가겠습니다.

7. Google Pay를 사용하여 결제할 수 있는지 여부 확인

Google Pay의 주요 목표 중 하나는 사용자에게 빠르고 편리한 결제 경험을 제공하는 것입니다. 이는 사용자가 Google Pay를 사용하여 결제할 수 있는 상황뿐 아니라 Google Pay를 사용하여 결제할 수 없는 상황에도 적용됩니다. 개발자는 isReadyToPay 요청을 사용하여 사용자가 Google Pay로 결제할 수 있는지 여부를 확인하고, 확인 결과에 따라 사이트 경험을 수정할 수 있습니다.

사용자가 Google Pay를 통해 결제할 수 있나요?

가장 먼저 해야 할 일은 애플리케이션에서 결제를 진행하려는 사용자가 Google Pay를 통해 결제할 수 있는지 여부를 확인하는 것입니다. 이 요청에는 Google Pay API의 버전과 사이트에서 허용되는 결제 수단을 지정해야 합니다. 이전 단계에서 정의한 기본 구성 객체는 다음 코드를 포함합니다.

val readyToPayRequest =
        IsReadyToPayRequest.fromJson(googlePayBaseConfiguration.toString())

val readyToPayTask = paymentsClient.isReadyToPay(readyToPayRequest)
task.addOnCompleteListener { task ->
    try {
        task.getResult(ApiException::class.java)?.let(::setGooglePayAvailable)
    } catch (exception: ApiException) {
        // Error determining readiness to use Google Pay.
        // Inspect the logs for more details.
    }
}

보시다시피 호출이 성공적이지 않은 응답을 반환하면 Google Pay 컨텍스트에서는 추가로 취해야 할 조치가 없습니다. 이 경우 가장 적절한 다음 단계는 다른 결제 수단을 지원하는 추가적인 UI를 표시하는 것일 것입니다.

반면에 응답이 성공적이면 사용자가 Google Pay를 사용하도록 지원할 수 있으며, 사용자가 동작을 취하면(예: 버튼을 클릭하면) 결제 프로세스를 시작하는 Google Pay 버튼을 표시할 수 있습니다.

Google Pay를 통해 결제 버튼 표시

이 시점에서 Google Pay 버튼을 표시할 수 있습니다.

private fun setGooglePayAvailable(available: Boolean) {
    if (available) {
        googlePayButton.visibility = View.VISIBLE
        googlePayButton.setOnClickListener { requestPayment() }
    } else {
       // Unable to pay using Google Pay. Update your UI accordingly.
    }
}

private fun requestPayment() {
  // TODO: Perform transaction
}

버튼 클릭 이벤트를 처리하는 함수도 정의해야 합니다. 다음 섹션에서는 이 함수를 사용하여 결제 수단을 요청할 것입니다.

8. 실제 결제

결제 요청 준비

여러분은 지금까지 Google Pay API를 로드하고 애플리케이션 사용자가 Google Pay를 통해 결제를 진행할 수 있는지 여부를 확인했습니다. 그 결과 UI에 Google Pay 결제 버튼을 표시했고, 이제 사용자가 거래를 시작할 준비가 되었습니다. 이제 로그인한 서로 다른 사용자가 사용할 수 있는 결제 수단을 포함하는 결제 시트를 로드할 차례입니다.

앞에서 isReadyToPay 요청을 정의할 때와 같이, 이 호출에도 앞에서 정의한 기본 구성 객체에 포함된 속성(apiVersion, apiVersionMinor, allowedPaymentMethods) 외에도 몇 가지 새로운 속성이 필요합니다. 이번에는 tokenizationSpecification이라는 새로운 속성과 이 요청의 목적에만 관련 있는 결제 수단의 추가 parameters가 있습니다. 이에 더해 transactionInfomerchantInfo도 추가해야 합니다.

결제 수단에 필요한 추가 정보 포함

먼저 앞에서 사용한 기본 카드 결제 수단의 복사본을 만듭니다. 이제 이 카드 결제 수단에는 선택된 결제 수단과 관련된 데이터를 처리할 방법을 정의하는 tokenizationSpecification 속성과 실제 거래에 요구되는 추가 데이터 요구사항이 필요합니다. 이 예시에서는 전체 청구서 수신 주소와 전화번호가 필요합니다.

tokenizationSpecification 속성

토큰화 사양은 사용자가 선택한 결제 수단이 처리되어 거래를 완료하는 데 사용되는 방식을 정합니다.

지원되는 처리 전략에는 두 가지가 있습니다. PCI DSS 규격 서버에서 결제 거래를 처리하는 경우에는 DIRECT 사양 유형을 사용하세요. 이 예시에서는 결제 게이트웨이를 사용하여 결제를 처리하므로 PAYMENT_GATEWAY 사양 유형을 설정합니다. 토큰화 사양의 코드는 다음과 같습니다.

private val tokenizationSpecification = JSONObject().apply {
    put("type", "PAYMENT_GATEWAY")
    put("parameters", JSONObject(mapOf(
            "gateway" to "example",
            "gatewayMerchantId" to "exampleGatewayMerchantId")))
}

parameters 섹션에서 Google Pay API가 지원하는 결제 대행업체 목록에 포함된 게이트웨이와 각 게이트웨이에 필요한 추가 구성을 지정할 수 있습니다. 이 Codelab에서는 실행된 거래에 대해 테스트 결과를 생성하는 example 게이트웨이만으로도 충분합니다.

추가 파라미터

마찬가지로, 이제 거래를 성공적으로 진행하기 위해 요청해야 하는 정보에 관한 추가 세부정보를 제공할 수 있습니다. 이 예시에서는 이 거래에서는 완전한 형식으로 된 사용자의 청구서 수신 주소를 지정해야 한다는 사실을 나타내기 위해 billingAddressRequired 속성과 billingAddressParameters 속성을 추가하고 전화번호를 포함해야 하는 것을 볼 수 있습니다.

private val cardPaymentMethod = JSONObject().apply {
    put("type", "CARD")
    put("tokenizationSpecification", tokenizationSpecification)
    put("parameters", JSONObject().apply {
        put("allowedCardNetworks", JSONArray(listOf("VISA", "MASTERCARD")))
        put("allowedAuthMethods", JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")))
        put("billingAddressRequired", true)
        put("billingAddressParameters", JSONObject(mapOf("format" to "FULL")))
    })
}

거래에 관한 정보 추가

transactionInfo 속성은 거래에 관한 금융 세부정보, 즉 가격ISO 4217 알파 형식으로 된 통화 코드가격의 상태(거래의 성격에 따라 final 또는 estimated일 수 있음 - 후자의 경우 예: 지정된 배송지 주소에 따라 가격이 달라질 수 있음)를 갖는 객체를 포함합니다.

private val transactionInfo = JSONObject().apply {
    put("totalPrice", "123.45")
    put("totalPriceStatus", "FINAL")
    put("currencyCode", "USD")
}

판매자에 관한 정보 추가

결제 요청은 merchantInfo 속성 아래에서 요청을 실행하는 판매자에 관한 정보를 받습니다. 이 Codelab에서는 이 중에서 두 가지 항목을 중점적으로 살펴봅니다.

  • merchantId에는 애플리케이션이 Google에 의해 프로덕션 환경에서 사용되도록 승인되면 계정에 연결될 식별자를 지정해야 합니다. 판매자 ID는 Google Pay 비즈니스 콘솔의 계정 아래에서 확인할 수 있습니다. TEST 환경을 사용할 때는 이 값이 사용되지 않습니다.
  • merchantName은 사용자에게 표시되는 애플리케이션 또는 조직 이름입니다. 이 값은 사용자에게 작업을 요청하는 주체에 관한 추가 정보를 제공하기 위해 Google Pay 결제 시트 내부에서 표시될 수 있습니다.

준비가 되면 paymentDataRequest 객체에 판매자에 관한 정보를 추가합니다.

private val merchantInfo = JSONObject().apply {
    put("merchantName", "Example Merchant")
    put("merchantId", "01234567890123456789")
}

결제 정보 요청 및 결과 처리

이제 앞에서 정의한 구성을 최종 객체에 병합하고 loadPaymentData 요청으로 전달합니다.

private val paymentDataRequestJson = JSONObject(googlePayBaseConfiguration.toString()).apply {
    put("allowedPaymentMethods", JSONArray().put(cardPaymentMethod))
    put("transactionInfo", transactionInfo)
    put("merchantInfo", merchantInfo)
}

이 시점에서는 Google Pay API에 유효한 결제 수단을 요청하는 데 필요한 모든 정보가 확보되었습니다. Google Pay API에 유효한 결제 수단을 요청하려면 PaymentsClient 객체의 loadPaymentData 메서드를 사용하여 방금 정의한 구성을 전달합니다.

val paymentDataRequest =
        PaymentDataRequest.fromJson(paymentDataRequestJson.toString())

AutoResolveHelper.resolveTask(
        paymentsClient.loadPaymentData(paymentDataRequest),
        this, LOAD_PAYMENT_DATA_REQUEST_CODE)

이 메서드를 호출하면 Google Pay 결제 시트의 표시가 트리거됩니다. 구성 오류가 없다면 현재 로그인한 계정에 연결된 유효한 결제 수단 목록을 볼 수 있습니다.

결제 수단을 선택하면 시트가 닫히고 결과가 활동으로 전송되어 onActivityResult 메서드를 통해 캡처됩니다.

public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    when (requestCode) {
        LOAD_PAYMENT_DATA_REQUEST_CODE -> {
            when (resultCode) {
                Activity.RESULT_OK ->
                    PaymentData.getFromIntent(data)?.let(::handlePaymentSuccess)

                Activity.RESULT_CANCELED -> {
                    // The user cancelled without selecting a payment method.
                }

                AutoResolveHelper.RESULT_ERROR -> {
                    AutoResolveHelper.getStatusFromIntent(data)?.let {
                        handleError(it.statusCode)
                    }
                }
            }
        }
    }
}

선택이 성공적이면 선택된 결제 수단에 관한 관련 정보를 포함하는 PaymentData 객체를 사용하여 결과가 처리됩니다.

{
  "apiVersionMinor": 0,
  "apiVersion": 2,
  "paymentMethodData": {
    "description": "Visa •••• 1234",
    "tokenizationData": {
      "type": "PAYMENT_GATEWAY",
      "token": "examplePaymentMethodToken"
    },
    "type": "CARD",
    "info": {
      "cardNetwork": "VISA",
      "cardDetails": "1234",
      "billingAddress": {
        "phoneNumber": ...,
        ...
      }
    }
  }
}

이제 이 결제 수단 정보를 사용하여 실제 거래를 실행할 수 있습니다.

private fun handlePaymentSuccess(paymentData: PaymentData) {
    val paymentMethodToken = paymentData
            .getJSONObject("tokenizationData")
            .getString("token")

    // Sample TODO: Use this token to perform a payment through your payment gateway
}

9. 축하합니다

자체 애플리케이션에 성공적으로 Google Pay API를 통합했습니다.

애플리케이션을 프로덕션으로 이동하기 전에 통합 체크리스트를 검토하세요. 체크리스트를 검토하고 완료하면 클라이언트 구성에 추가할 판매자 ID(merchantId)를 받게 됩니다. 마찬가지로, 서드 파티 결제 대행업체 또는 게이트웨이를 사용할 계획이거나 이미 사용하고 있다면, Google Pay의 지원되는 대행업체 목록을 참고하여 그에 맞게 구성하세요. Google Pay를 직접 통합하는 경우에는 이 주제에 관한 설명서 섹션을 참고하세요.

학습한 내용

  • 애플리케이션으로 Google API를 가져오고 구성하기
  • API 지원 여부를 확인하고 그에 맞게 대응하기
  • 사용자가 Google Pay를 통해 결제할 수 있도록 버튼 추가하기
  • 이전에 저장된 사용자 결제 정보를 로드하고 처리하기

다음 단계

  • 아직 테스트하지 않았다면 실제 애플리케이션에서 Google Pay를 테스트합니다.
  • Google Pay 비즈니스 콘솔에서 판매자 ID를 획득합니다.
  • 통합 체크리스트를 검토합니다.
  • 직접 통합 유형과 결제 게이트웨이 또는 대행업체를 사용하는 유형을 살펴보고, 둘 중 어느 방법이 더 적합한지 정합니다.

자세히 알아보기

이 정보가 유익했나요?

매우 유용함 적당히 유용함 별로 유용하지 않음

다른 유형의 통합(직접 통합, 카드 및 패스 API, 패스 API)에 관한 Codelab을 살펴보시겠어요?

예, 살펴보겠습니다 아니요, 살펴보지 않겠습니다