가이드

Android 애플리케이션이 Google Play 스토어를 통해 배포되는 경우 Google Pay API와 통합할 수 있습니다. 결제 카드를 허용하도록 구성할 수도 있습니다. 애플리케이션을 통합하고 결제 카드를 허용하도록 구성하려면 이 가이드의 단계를 따르세요.

1단계: Google Pay API 버전 정의

애플리케이션에서 사용하는 Google Pay API 버전을 선언합니다. 주 버전과 부 버전은 전달된 각 객체에서 예상되는 필드에 영향을 미치며 응답에 포함됩니다.

다른 모든 요청 객체에 있는 속성을 포함하는 기본 요청 객체를 만듭니다.

Kotlin

    private val baseRequest = JSONObject().apply {
        put("apiVersion", 2)
        put("apiVersionMinor", 0)
    }

자바

  private static JSONObject getBaseRequest() throws JSONException {
    return new JSONObject().put("apiVersion", 2).put("apiVersionMinor", 0);
  }

2단계: 결제 시스템 공급자의 결제 토큰 요청

Google은 결제 시스템 공급자의 보안 처리를 위해 결제자가 선택한 카드 정보를 암호화합니다.

Kotlin

private fun gatewayTokenizationSpecification(): JSONObject {
    return JSONObject().apply {
        put("type", "PAYMENT_GATEWAY")
        put("parameters", JSONObject(mapOf(
                "gateway" to "example",
                "gatewayMerchantId" to "exampleGatewayMerchantId")))
    }
}

자바

  private static JSONObject getGatewayTokenizationSpecification() throws JSONException {
    return new JSONObject() {{
      put("type", "PAYMENT_GATEWAY");
      put("parameters", new JSONObject() {{
        put("gateway", "example");
        put("gatewayMerchantId", "exampleGatewayMerchantId");
      }});
    }};
  }

exampleexampleGatewayMerchantId를 결제 시스템 공급자에 해당하는 값으로 바꿉니다. 다음 표를 참조하여 결제 시스템 공급자의 gatewaygatewayMerchantId 값을 찾습니다.

게이트웨이 매개변수 및 문서
ACI
  "gateway": "aciworldwide"
  "gatewayMerchantId": "YOUR_ENTITY_ID"

개발자 문서

Adyen
  "gateway": "adyen"
  "gatewayMerchantId": "YOUR_MERCHANT_ACCOUNT_NAME"

개발자 문서

Alfa-Bank
  "gateway": "alfabank"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

AllPayments
  "gateway": "allpayments"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

APPEX
  "gateway": "epos"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Assist
  "gateway": "assist"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Billing Systems
  "gateway": "billingsystems"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Bizzon
  "gateway": "bizzon"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Blue Media
  "gateway": "bluemedia"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

BlueSnap
  "gateway": "bluesnap"
  "gatewayMerchantId": "YOUR_shopToken"

개발자 문서

Braintree
  "gateway": "braintree"
  "braintree:apiVersion": "v1"
  "braintree:sdkVersion": "braintree.client.VERSION"
  "braintree:merchantId": "YOUR_BRAINTREE_MERCHANT_ID"
  "braintree:clientKey": "YOUR_BRAINTREE_TOKENIZATION_KEY"

개발자 문서

Braspag
  "gateway": "cielo"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

CardConnect
  "gateway": "cardconnect"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Cathay United Bank
  "gateway": "cathaybk"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Chase Merchant Services
  "gateway": "chase"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Checkout.com
  "gateway": "checkoutltd"
  "gatewayMerchantId": "YOUR_PUBLIC_KEY"

개발자 문서

CloudPayments
  "gateway": "cloudpayments"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Computop
  "gateway": "computop"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

ConcordPay
  "gateway": "concordpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Credorax
  "gateway": "credorax"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Cybersource
  "gateway": "cybersource"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Datatrans
  "gateway": "datatrans"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

dLocal
  "gateway": "dlocal"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Dotpay
  "gateway": "dotpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

e-SiTef - Software Express
  "gateway": "softwareexpress"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

EasyPay
  "gateway": "easypay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

EBANX
  "gateway": "ebanx"
  "gatewayMerchantId": "YOUR_PUBLIC_INTEGRATION_KEY"

개발자 문서

EBANX Pay
  "gateway": "ebanxpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

eCard
  "gateway": "ecard"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

ECPay
  "gateway": "ecpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서가 없음

eGHL
  "gateway": "eghl"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

eSafe
  "gateway": "esafe"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서가 없음

Evopay
  "gateway": "evopay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Evo Payment Gateway
  "gateway": "evopaymentgateway"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서가 없음

Fat Zebra
  "gateway": "fatzebra"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

First Data(Payeezy)
  "gateway": "firstdata"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

FreedomPay
  "gateway": "freedompay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Gestpay
  "gateway": "gestpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Global One Pay
  "gateway": "globalonepay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서가 없음

Global Payments
  "gateway": "globalpayments"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

GMO Payment Gateway
  "gateway": "gmopg"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

GoPay
  "gateway": "gopay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

GP Webpay
  "gateway": "gpwebpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서가 없음

HiTrust
  "gateway": "hitrustpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

InPlat
  "gateway": "inplat"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

IntellectMoney
  "gateway": "intellectmoney"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

iPay88
  "gateway": "ipay88"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

iQmetrix
  "gateway": "iqmetrixpaymentservicesgateway"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

IXOPAY
  "gateway": "ixopay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

JudoPay
  "gateway": "judopay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서가 없음

Kassa
  "gateway": "kassacom"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Kineox
  "gateway": "kineox"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

LiqPay
  "gateway": "liqpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

LogPay
  "gateway": "logpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Lyra
  "gateway": "lyra"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Mastercard Payment Gateway Services
  "gateway": "mpgs"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

MOBI.Money
  "gateway": "mobimoney"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Molpay
  "gateway": "molpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서가 없음

Moneris
  "gateway": "moneris"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Moneta
  "gateway": "moneta"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서가 없음

Monext
  "gateway": "monext"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Money.Mail.Ru
  "gateway": "moneymailru"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Multicarta
  "gateway": "mulitcarta"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Mundipagg
  "gateway": "mundipagg"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

MyCheck
  "gateway": "mycheck"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

MyPay
  "gateway": "mypay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서가 없음

Newebpay(이전 STPath, Pay2Go)
  "gateway": "newebpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Nexi
  "gateway": "nexi"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

NMI
  "gateway": "gatewayservices"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Nuvei
  "gateway": "nuvei"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Onelya
  "gateway": "onelya"
  "gatewayMerchantId": "YOUR_GATWAY_MERCHANT_ID"

Developer docs

Payfacto
  "gateway": "payfacto"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

paygent
  "gateway": "paygent"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

PayLane
  "gateway": "paylane"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Payler
  "gateway": "payler"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

페이로드
  "gateway": "payload"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Paymark
  "gateway": "paymark"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Paymentwall
  "gateway": "paymentwall"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Paymo
  "gateway": "paymo"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

PayOnline
  "gateway": "payonline"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Paysafe
  "gateway": "paysafe"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Payture
  "gateway": "payture"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

PayU
  "gateway": "payu"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Payway
  "gateway": "payway"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Platon
  "gateway": "platon"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Portmone
  "gateway": "portmonecom"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Primer
  "gateway": "primer"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Przelewy24
  "gateway": "przelewy24"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Radial
  "gateway": "radial"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

RBK.money
  "gateway": "rbkmoney"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Rebilly
  "gateway": "Rebilly"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Redsys
  "gateway": "redsys"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Sberbank
  "gateway": "sberbank"
  "gatewayMerchantId": "YOUR_ORGANIZATION_NAME"

개발자 문서

Sipay
  "gateway": "sipay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Softbank Payment Service
  "gateway": "sbps"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Solid
  "gateway": "solid"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Sony Payment Services
  "gateway": "sonypaymentservices"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Spreedly
  "gateway": "spreedly"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Square
  "gateway": "square"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Stripe
  "gateway": "stripe"
  "stripe:version": "2018-10-31"
  "stripe:publishableKey": "YOUR_PUBLIC_STRIPE_KEY"

개발자 문서

TapPay(Cherri Tech)
  "gateway": "tappay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

TAS 링크
  "gateway": "taslink"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Tatra banka(CardPay)
  "gateway": "tatrabanka"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서가 없음

Tinkoff
  "gateway": "tinkoff"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

theMAP
  "gateway": "themap"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

TPay.com
  "gateway": "tpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Tranzzo
  "gateway": "tranzzo"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

UAPAY
  "gateway": "uapay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Uniteller
  "gateway": "uniteller"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

UPC
  "gateway": "upc"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Vantiv
  "gateway": "vantiv"
  "vantiv:merchantPayPageId": "YOUR_PAY_PAGE_ID"
  "vantiv:merchantOrderId": "YOUR_ORDER_ID"
  "vantiv:merchantTransactionId": "YOUR_TRANSACTION_ID"
  "vantiv:merchantReportGroup": "*web"

개발자 문서

Veritrans
  "gateway": "veritrans"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Vindicia
  "gateway": "vindicia"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

WayForPay
  "gateway": "wayforpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Windcave
  "gateway": "windcave"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Wirecard
  "gateway": "wirecard"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Worldnet
  "gateway": "worldnet"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

개발자 문서

Worldpay
  "gateway": "worldpay"
  "gatewayMerchantId": "YOUR_WORLDPAY_MERCHANT_ID"

개발자 문서

Yandex.Checkout
  "gateway": "yandexcheckout"
  "gatewayMerchantId": "YOUR_SHOP_ID"

개발자 문서

PAYMENT_GATEWAY 토큰화 유형은 Google Pay API로 카드 결제 수단의 판매자를 구현하는 가장 일반적인 방식입니다. 결제 시스템 공급자가 지원되지 않는 경우에는 DIRECT 통합을 통해 Google Pay를 허용하면 됩니다. 자세한 내용은 직접 토큰화 문서를 참조하세요.

3단계: 지원되는 결제 카드 네트워크 정의

애플리케이션에서 사용할 수 있는 카드 네트워크를 정의합니다.

Kotlin

private val allowedCardNetworks = JSONArray(listOf(
        "AMEX",
        "DISCOVER",
        "INTERAC",
        "JCB",
        "MASTERCARD",
        "VISA"))

자바

private static JSONArray getAllowedCardNetworks() {
  return new JSONArray()
      .put("AMEX")
      .put("DISCOVER")
      .put("INTERAC")
      .put("JCB")
      .put("MASTERCARD")
      .put("VISA");
}

Google Pay API는 Google.com에 등록된 카드(PAN_ONLY) 또는 3D Secure 암호(CRYPTOGRAM_3DS)로 인증된 Android 기기의 기기 토큰을 반환할 수 있습니다.

Kotlin

private val allowedCardAuthMethods = JSONArray(listOf(
        "PAN_ONLY",
        "CRYPTOGRAM_3DS"))

자바

private static JSONArray getAllowedCardAuthMethods() {
  return new JSONArray()
      .put("PAN_ONLY")
      .put("CRYPTOGRAM_3DS");
}

자세한 내용은 JSON 객체 참조의 CardParameters를 확인하세요. 지원되는 카드 네트워크 및 Android 기기 토큰 지원은 게이트웨이 또는 대행업체를 통해 확인하세요.

4단계: 사용할 수 있는 결제 수단 설명

애플리케이션에서 CARD 결제 수단을 지원하는 방법을 설명하려면 지원되는 인증 방법과 지원되는 카드 네트워크를 결합합니다.

Kotlin

    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 static JSONObject getBaseCardPaymentMethod() throws JSONException {
    JSONObject cardPaymentMethod = new JSONObject();
    cardPaymentMethod.put("type", "CARD");

    JSONObject parameters = new JSONObject();
    parameters.put("allowedAuthMethods", getAllowedCardAuthMethods());
    parameters.put("allowedCardNetworks", getAllowedCardNetworks());
    // Optionally, you can add billing address/phone number associated with a CARD payment method.
    parameters.put("billingAddressRequired", true);

    JSONObject billingAddressParameters = new JSONObject();
    billingAddressParameters.put("format", "FULL");

    parameters.put("billingAddressParameters", billingAddressParameters);

    cardPaymentMethod.put("parameters", parameters);

    return cardPaymentMethod;
  }

기본 카드 결제 수단 객체를 확장하여 토큰화된 결제 데이터 등 애플리케이션에 반환될 정보를 설명합니다.

Kotlin

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

        return cardPaymentMethod
    }

자바

  private static JSONObject getCardPaymentMethod() throws JSONException {
    JSONObject cardPaymentMethod = getBaseCardPaymentMethod();
    cardPaymentMethod.put("tokenizationSpecification", getGatewayTokenizationSpecification());

    return cardPaymentMethod;
  }

지원되는 parameters에 대한 자세한 내용은 JSON 객체 참조의 CardParameters를 확인하세요.

Google Pay는 CARD 외에 PAYPAL 결제 수단도 지원합니다. Google Pay에 PayPal을 결제 수단으로 추가하는 방법에 대한 자세한 내용은 PayPal 개발자 문서를 참조하세요.

5단계: PaymentsClient 인스턴스 만들기

ActivityonCreate 메서드에 PaymentsClient 인스턴스를 만듭니다. PaymentsClient는 Google Pay API와의 상호작용에 사용됩니다.

Kotlin

    fun createPaymentsClient(activity: Activity): PaymentsClient {
        val walletOptions = Wallet.WalletOptions.Builder()
                .setEnvironment(Constants.PAYMENTS_ENVIRONMENT)
                .build()

        return Wallet.getPaymentsClient(activity, walletOptions)
    }

자바

  public static PaymentsClient createPaymentsClient(Activity activity) {
    Wallet.WalletOptions walletOptions =
        new Wallet.WalletOptions.Builder().setEnvironment(Constants.PAYMENTS_ENVIRONMENT).build();
    return Wallet.getPaymentsClient(activity, walletOptions);
  }

6단계: Google Pay API로 결제할 준비가 되었는지 확인

다음 코드 스니펫을 사용하여 사용할 수 있는 결제 수단을 기본 요청 객체에 추가합니다.

Kotlin

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

        } catch (e: JSONException) {
            null
        }
    }

자바

  public static Optional<JSONObject> getIsReadyToPayRequest() {
    try {
      JSONObject isReadyToPayRequest = getBaseRequest();
      isReadyToPayRequest.put(
          "allowedPaymentMethods", new JSONArray().put(getBaseCardPaymentMethod()));

      return Optional.of(isReadyToPayRequest);

    } catch (JSONException e) {
      return Optional.empty();
    }
  }

Google Pay 버튼을 표시하기 전에 isReadyToPay API를 호출하여 사용자가 Google Pay API로 결제할 수 있는지 확인합니다. 구성 속성의 전체 목록은 IsReadyToPayRequest JSON 객체 문서를 참조하세요.

Kotlin

    private fun possiblyShowGooglePayButton() {

        val isReadyToPayJson = PaymentsUtil.isReadyToPayRequest() ?: return
        val request = IsReadyToPayRequest.fromJson(isReadyToPayJson.toString()) ?: return

        // The call to isReadyToPay is asynchronous and returns a Task. We need to provide an
        // OnCompleteListener to be triggered when the result of the call is known.
        val task = paymentsClient.isReadyToPay(request)
        task.addOnCompleteListener { completedTask ->
            try {
                completedTask.getResult(ApiException::class.java)?.let(::setGooglePayAvailable)
            } catch (exception: ApiException) {
                // Process error
                Log.w("isReadyToPay failed", exception)
            }
        }
    }

자바

  private void possiblyShowGooglePayButton() {

    final Optional<JSONObject> isReadyToPayJson = PaymentsUtil.getIsReadyToPayRequest();
    if (!isReadyToPayJson.isPresent()) {
      return;
    }

    // The call to isReadyToPay is asynchronous and returns a Task. We need to provide an
    // OnCompleteListener to be triggered when the result of the call is known.
    IsReadyToPayRequest request = IsReadyToPayRequest.fromJson(isReadyToPayJson.get().toString());
    Task<Boolean> task = paymentsClient.isReadyToPay(request);
    task.addOnCompleteListener(this,
        new OnCompleteListener<Boolean>() {
          @Override
          public void onComplete(@NonNull Task<Boolean> task) {
            if (task.isSuccessful()) {
              setGooglePayAvailable(task.getResult());
            } else {
              Log.w("isReadyToPay failed", task.getException());
            }
          }
        });
  }

이 예시에 나온 것처럼 isReadyToPay 함수에서 성공적인 결과를 받은 후에만 Google Pay를 결제 옵션으로 표시하는 것이 가장 좋습니다. 결제 옵션을 구현할 때는 include 레이아웃을 통해 Google Pay 결제 버튼을 표시하는 것이 가장 일반적입니다. 애플리케이션에서 사용할 수 있는 Google Pay 결제 버튼, 로고, 마크에 대한 자세한 내용은 브랜드 가이드라인을 참조하세요.

7단계: PaymentDataRequest 객체 만들기

PaymentDataRequest JSON 객체는 Google Pay 결제 명세서에서 결제자에게 요청하는 정보를 설명합니다.

거래 가격 및 제공된 가격의 상태에 대한 정보를 제공하세요. 자세한 내용은 TransactionInfo JSON 객체 문서를 참조하세요.

다음 예시에서는 가격, 가격 상태, 통화 거래 정보를 얻는 방법을 보여줍니다.

Kotlin

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

자바

  private static JSONObject getTransactionInfo(String price) throws JSONException {
    JSONObject transactionInfo = new JSONObject();
    transactionInfo.put("totalPrice", price);
    transactionInfo.put("totalPriceStatus", "FINAL");
    transactionInfo.put("countryCode", Constants.COUNTRY_CODE);
    transactionInfo.put("currencyCode", Constants.CURRENCY_CODE);
    transactionInfo.put("checkoutOption", "COMPLETE_IMMEDIATE_PURCHASE");

    return transactionInfo;
  }

사용자에게 표시되는 판매자 이름을 제공합니다. 자세한 내용은 MerchantInfo JSON 객체 문서를 참조하세요.

다음 예시는 판매자 이름을 얻는 방법을 보여줍니다.

Kotlin

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

자바

  private static JSONObject getMerchantInfo() throws JSONException {
    return new JSONObject().put("merchantName", "Example Merchant");
  }

기본 요청 객체를 새 PaymentDataRequest JSON 객체에 할당합니다. 그런 다음 응답에서 예상되는 추가 데이터 구성과 같이 애플리케이션이 지원하는 결제 수단을 추가합니다. 마지막으로 거래 및 요청한 판매자에 대한 정보를 추가합니다.

다음 예시는 결제 데이터를 요청하는 방법을 보여줍니다.

Kotlin

fun getPaymentDataRequest(price: String): JSONObject? {
    try {
        return baseRequest.apply {
            put("allowedPaymentMethods", JSONArray().put(cardPaymentMethod()))
            put("transactionInfo", getTransactionInfo(price))
            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)
        }
    } catch (e: JSONException) {
        return null
    }
}

자바


  public static Optional<JSONObject> getPaymentDataRequest(long priceCents) {

    final String price = PaymentsUtil.centsToString(priceCents);

    try {
      JSONObject paymentDataRequest = PaymentsUtil.getBaseRequest();
      paymentDataRequest.put(
          "allowedPaymentMethods", new JSONArray().put(PaymentsUtil.getCardPaymentMethod()));
      paymentDataRequest.put("transactionInfo", PaymentsUtil.getTransactionInfo(price));
      paymentDataRequest.put("merchantInfo", PaymentsUtil.getMerchantInfo());

      /* An optional shipping address requirement is a top-level property of the PaymentDataRequest
      JSON object. */
      paymentDataRequest.put("shippingAddressRequired", true);

      JSONObject shippingAddressParameters = new JSONObject();
      shippingAddressParameters.put("phoneNumberRequired", false);

      JSONArray allowedCountryCodes = new JSONArray(Constants.SHIPPING_SUPPORTED_COUNTRIES);

      shippingAddressParameters.put("allowedCountryCodes", allowedCountryCodes);
      paymentDataRequest.put("shippingAddressParameters", shippingAddressParameters);
      return Optional.of(paymentDataRequest);

    } catch (JSONException e) {
      return Optional.empty();
    }
  }

자세한 내용은 PaymentDataRequest JSON 객체 문서를 참조하세요.

8단계: 사용자 동작용 이벤트 핸들러 등록

사용자가 Google Pay 결제 버튼을 활성화한 후 Google Pay 결제 명세서를 표시하도록 요청하려면 OnClickListener를 정의합니다.

Kotlin

        googlePayButton.setOnClickListener { requestPayment() }

자바

    googlePayButton.setOnClickListener(
        new View.OnClickListener() {
          @Override
          public void onClick(View view) {
            requestPayment(view);
          }
        });

PaymentDataRequest 객체는 결제 데이터 요청을 나타내는 Parcelable입니다. PaymentDataRequest는 결제를 지원하는 데 필요한 정보를 제공합니다. AutoResolveHelper 클래스를 사용하여 Task를 자동으로 해결한 다음 Activity 클래스의 onActivityResult 메서드에서 결과를 처리합니다.

9단계: 응답 객체 처리

결제자가 Google Pay 결제 명세서에서 요청된 정보를 올바르게 제공하면 onActivityResultPaymentData 객체가 반환됩니다.

결제 정보를 대행업체에 전달하고 사용자에게 구매 확인서를 제공하려면 성공적인 응답을 JSON으로 변환하세요. PaymentDataRequest에 전달된 transactionInfo.totalPriceStatusESTIMATED일 경우 반환된 결제 수단에 청구하기 전에 최종 가격을 표시해야 합니다.

paymentData 응답에서 결제 토큰을 추출합니다. 게이트웨이 통합을 구현하는 경우 이 토큰을 수정하지 않고 게이트웨이에 전달합니다.

Kotlin

    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when (requestCode) {
            // Value passed in AutoResolveHelper
            LOAD_PAYMENT_DATA_REQUEST_CODE -> {
                when (resultCode) {
                    RESULT_OK ->
                        data?.let { intent ->
                            PaymentData.getFromIntent(intent)?.let(::handlePaymentSuccess)
                        }

                    RESULT_CANCELED -> {
                        // The user cancelled the payment attempt
                    }

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

                // Re-enables the Google Pay payment button.
                googlePayButton.isClickable = true
            }
        }
    }

자바

  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
      // value passed in AutoResolveHelper
      case LOAD_PAYMENT_DATA_REQUEST_CODE:
        switch (resultCode) {

          case Activity.RESULT_OK:
            PaymentData paymentData = PaymentData.getFromIntent(data);
            handlePaymentSuccess(paymentData);
            break;

          case Activity.RESULT_CANCELED:
            // The user cancelled the payment attempt
            break;

          case AutoResolveHelper.RESULT_ERROR:
            Status status = AutoResolveHelper.getStatusFromIntent(data);
            handleError(status.getStatusCode());
            break;
        }

        // Re-enables the Google Pay payment button.
        googlePayButton.setClickable(true);
    }
  }

응답의 내용과 구조에 대한 자세한 내용은 PaymentData JSON 객체 참조를 확인하세요.

완성된 모양

다음 스니펫은 제대로 구성된 프로젝트의 전체 예시를 보여줍니다. 프로젝트 수준의 설정 단계는 프로젝트 구성을 참조하세요.

CheckoutActivity.kt(Kotlin)

Activity 예시에서는 레이아웃에 googlepay_button라는 id 속성이 있는 Google Pay 결제 버튼이 있다고 가정합니다.

/*
 * Copyright 2018 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.gms.samples.wallet.activity

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.text.Html
import android.util.Log
import android.view.View
import android.widget.Toast
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.samples.wallet.util.PaymentsUtil
import com.google.android.gms.samples.wallet.R
import com.google.android.gms.samples.wallet.util.Json
import com.google.android.gms.wallet.*
import kotlinx.android.synthetic.main.activity_checkout.*
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject

/**
 * Checkout implementation for the app
 */
class CheckoutActivity : Activity() {

    private val SHIPPING_COST_CENTS = 9 * PaymentsUtil.CENTS.toLong()

    /**
     * A client for interacting with the Google Pay API.
     *
     * @see [PaymentsClient](https://developers.google.com/android/reference/com/google/android/gms/wallet/PaymentsClient)
     */
    private lateinit var paymentsClient: PaymentsClient

    private lateinit var garmentList: JSONArray
    private lateinit var selectedGarment: JSONObject

    /**
     * Arbitrarily-picked constant integer you define to track a request for payment data activity.
     *
     * @value #LOAD_PAYMENT_DATA_REQUEST_CODE
     */
    private val LOAD_PAYMENT_DATA_REQUEST_CODE = 991

    /**
     * Initialize the Google Pay API on creation of the activity
     *
     * @see Activity.onCreate
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_checkout)

        // Set up the mock information for our item in the UI.
        selectedGarment = fetchRandomGarment()
        displayGarment(selectedGarment)

        // Initialize a Google Pay API client for an environment suitable for testing.
        // It's recommended to create the PaymentsClient object inside of the onCreate method.
        paymentsClient = PaymentsUtil.createPaymentsClient(this)
        possiblyShowGooglePayButton()

        googlePayButton.setOnClickListener { requestPayment() }
    }

    /**
     * Determine the viewer's ability to pay with a payment method supported by your app and display a
     * Google Pay payment button.
     *
     * @see [](https://developers.google.com/android/reference/com/google/android/gms/wallet/PaymentsClient.html.isReadyToPay
    ) */
    private fun possiblyShowGooglePayButton() {

        val isReadyToPayJson = PaymentsUtil.isReadyToPayRequest() ?: return
        val request = IsReadyToPayRequest.fromJson(isReadyToPayJson.toString()) ?: return

        // The call to isReadyToPay is asynchronous and returns a Task. We need to provide an
        // OnCompleteListener to be triggered when the result of the call is known.
        val task = paymentsClient.isReadyToPay(request)
        task.addOnCompleteListener { completedTask ->
            try {
                completedTask.getResult(ApiException::class.java)?.let(::setGooglePayAvailable)
            } catch (exception: ApiException) {
                // Process error
                Log.w("isReadyToPay failed", exception)
            }
        }
    }

    /**
     * If isReadyToPay returned `true`, show the button and hide the "checking" text. Otherwise,
     * notify the user that Google Pay is not available. Please adjust to fit in with your current
     * user flow. You are not required to explicitly let the user know if isReadyToPay returns `false`.
     *
     * @param available isReadyToPay API response.
     */
    private fun setGooglePayAvailable(available: Boolean) {
        if (available) {
            googlePayButton.visibility = View.VISIBLE
        } else {
            Toast.makeText(
                    this,
                    "Unfortunately, Google Pay is not available on this device",
                    Toast.LENGTH_LONG).show();
        }
    }

    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 garmentPrice = selectedGarment.getDouble("price")
        val priceCents = Math.round(garmentPrice * PaymentsUtil.CENTS.toLong()) + SHIPPING_COST_CENTS

        val paymentDataRequestJson = PaymentsUtil.getPaymentDataRequest(priceCents)
        if (paymentDataRequestJson == null) {
            Log.e("RequestPayment", "Can't fetch payment data request")
            return
        }
        val request = PaymentDataRequest.fromJson(paymentDataRequestJson.toString())

        // Since loadPaymentData may show the UI asking the user to select a payment method, we use
        // AutoResolveHelper to wait for the user interacting with it. Once completed,
        // onActivityResult will be called with the result.
        if (request != null) {
            AutoResolveHelper.resolveTask(
                    paymentsClient.loadPaymentData(request), this, LOAD_PAYMENT_DATA_REQUEST_CODE)
        }
    }

    /**
     * Handle a resolved activity from the Google Pay payment sheet.
     *
     * @param requestCode Request code originally supplied to AutoResolveHelper in requestPayment().
     * @param resultCode Result code returned by the Google Pay API.
     * @param data Intent from the Google Pay API containing payment or error data.
     * @see [Getting a result
     * from an Activity](https://developer.android.com/training/basics/intents/result)
     */
    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when (requestCode) {
            // Value passed in AutoResolveHelper
            LOAD_PAYMENT_DATA_REQUEST_CODE -> {
                when (resultCode) {
                    RESULT_OK ->
                        data?.let { intent ->
                            PaymentData.getFromIntent(intent)?.let(::handlePaymentSuccess)
                        }

                    RESULT_CANCELED -> {
                        // The user cancelled the payment attempt
                    }

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

                // Re-enables the Google Pay payment button.
                googlePayButton.isClickable = true
            }
        }
    }

    /**
     * PaymentData response object contains the payment information, as well as any additional
     * requested information, such as billing and shipping address.
     *
     * @param paymentData A response object returned by Google after a payer approves payment.
     * @see [Payment
     * Data](https://developers.google.com/pay/api/android/reference/object.PaymentData)
     */
    private fun handlePaymentSuccess(paymentData: PaymentData) {
        val paymentInformation = paymentData.toJson() ?: return

        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("GooglePaymentToken", paymentMethodData
                    .getJSONObject("tokenizationData")
                    .getString("token"))

        } catch (e: JSONException) {
            Log.e("handlePaymentSuccess", "Error: " + e.toString())
        }

    }

    /**
     * At this stage, the user has already seen a popup informing them an error occurred. Normally,
     * only logging is required.
     *
     * @param statusCode will hold the value of any constant from CommonStatusCode or one of the
     * WalletConstants.ERROR_CODE_* constants.
     * @see [
     * Wallet Constants Library](https://developers.google.com/android/reference/com/google/android/gms/wallet/WalletConstants.constant-summary)
     */
    private fun handleError(statusCode: Int) {
        Log.w("loadPaymentData failed", String.format("Error code: %d", statusCode))
    }

    private fun fetchRandomGarment() : JSONObject {
        if (!::garmentList.isInitialized) {
            garmentList = Json.readFromResources(this, R.raw.tshirts)
        }

        val randomIndex:Int = Math.round(Math.random() * (garmentList.length() - 1)).toInt()
        return garmentList.getJSONObject(randomIndex)
    }

    private fun displayGarment(garment:JSONObject) {
        detailTitle.setText(garment.getString("title"))
        detailPrice.setText("\$${garment.getString("price")}")

        val escapedHtmlText:String = Html.fromHtml(garment.getString("description")).toString()
        detailDescription.setText(Html.fromHtml(escapedHtmlText))

        val imageUri = "@drawable/${garment.getString("image")}"
        val imageResource = resources.getIdentifier(imageUri, null, packageName)
        detailImage.setImageResource(imageResource)
    }
}

    

PaymentsUtil.kt(Kotlin)

이 예시 파일에서는 IsReadyToPayRequest 또는 PaymentDataRequest를 빌드하기에 적합한 JSON 객체를 빌드합니다.

/*
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.gms.samples.wallet.util

import android.app.Activity
import com.google.android.gms.samples.wallet.Constants
import com.google.android.gms.wallet.PaymentsClient
import com.google.android.gms.wallet.Wallet
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.math.BigDecimal
import java.math.RoundingMode

/**
 * Contains helper static methods for dealing with the Payments API.
 *
 * Many of the parameters used in the code are optional and are set here merely to call out their
 * existence. Please consult the documentation to learn more and feel free to remove ones not
 * relevant to your implementation.
 */
object PaymentsUtil {
    val CENTS = BigDecimal(100)

    /**
     * Create a Google Pay API base request object with properties used in all requests.
     *
     * @return Google Pay API base request object.
     * @throws JSONException
     */
    private val baseRequest = JSONObject().apply {
        put("apiVersion", 2)
        put("apiVersionMinor", 0)
    }

    /**
     * Gateway Integration: Identify your gateway and your app's gateway merchant identifier.
     *
     *
     * The Google Pay API response will return an encrypted payment method capable of being charged
     * by a supported gateway after payer authorization.
     *
     *
     * TODO: Check with your gateway on the parameters to pass and modify them in Constants.java.
     *
     * @return Payment data tokenization for the CARD payment method.
     * @throws JSONException
     * @see [PaymentMethodTokenizationSpecification](https://developers.google.com/pay/api/android/reference/object.PaymentMethodTokenizationSpecification)
     */
    private fun gatewayTokenizationSpecification(): JSONObject {
        return JSONObject().apply {
            put("type", "PAYMENT_GATEWAY")
            put("parameters", JSONObject(Constants.PAYMENT_GATEWAY_TOKENIZATION_PARAMETERS))
        }
    }

    /**
     * `DIRECT` Integration: Decrypt a response directly on your servers. This configuration has
     * additional data security requirements from Google and additional PCI DSS compliance complexity.
     *
     *
     * Please refer to the documentation for more information about `DIRECT` integration. The
     * type of integration you use depends on your payment processor.
     *
     * @return Payment data tokenization for the CARD payment method.
     * @throws JSONException
     * @see [PaymentMethodTokenizationSpecification](https://developers.google.com/pay/api/android/reference/object.PaymentMethodTokenizationSpecification)
     */
    private fun directTokenizationSpecification(): JSONObject {
        if (Constants.DIRECT_TOKENIZATION_PUBLIC_KEY == "REPLACE_ME" ||
                (Constants.DIRECT_TOKENIZATION_PARAMETERS.isEmpty() ||
                 Constants.DIRECT_TOKENIZATION_PUBLIC_KEY.isEmpty())) {

            throw RuntimeException(
                    "Please edit the Constants.java file to add protocol version & public key.")
        }

        return JSONObject().apply {
            put("type", "DIRECT")
            put("parameters", JSONObject(Constants.DIRECT_TOKENIZATION_PARAMETERS))
        }
    }

    /**
     * Card networks supported by your app and your gateway.
     *
     *
     * TODO: Confirm card networks supported by your app and gateway & update in Constants.java.
     *
     * @return Allowed card networks
     * @see [CardParameters](https://developers.google.com/pay/api/android/reference/object.CardParameters)
     */
    private val allowedCardNetworks = JSONArray(Constants.SUPPORTED_NETWORKS)

    /**
     * Card authentication methods supported by your app and your gateway.
     *
     *
     * TODO: Confirm your processor supports Android device tokens on your supported card networks
     * and make updates in Constants.java.
     *
     * @return Allowed card authentication methods.
     * @see [CardParameters](https://developers.google.com/pay/api/android/reference/object.CardParameters)
     */
    private val allowedCardAuthMethods = JSONArray(Constants.SUPPORTED_METHODS)

    /**
     * Describe your app's support for the CARD payment method.
     *
     *
     * The provided properties are applicable to both an IsReadyToPayRequest and a
     * PaymentDataRequest.
     *
     * @return A CARD PaymentMethod object describing accepted cards.
     * @throws JSONException
     * @see [PaymentMethod](https://developers.google.com/pay/api/android/reference/object.PaymentMethod)
     */
    // Optionally, you can add billing address/phone number associated with a CARD payment method.
    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)
        }
    }

    /**
     * Describe the expected returned payment data for the CARD payment method
     *
     * @return A CARD PaymentMethod describing accepted cards and optional fields.
     * @throws JSONException
     * @see [PaymentMethod](https://developers.google.com/pay/api/android/reference/object.PaymentMethod)
     */
    private fun cardPaymentMethod(): JSONObject {
        val cardPaymentMethod = baseCardPaymentMethod()
        cardPaymentMethod.put("tokenizationSpecification", gatewayTokenizationSpecification())

        return cardPaymentMethod
    }

    /**
     * An object describing accepted forms of payment by your app, used to determine a viewer's
     * readiness to pay.
     *
     * @return API version and payment methods supported by the app.
     * @see [IsReadyToPayRequest](https://developers.google.com/pay/api/android/reference/object.IsReadyToPayRequest)
     */
    fun isReadyToPayRequest(): JSONObject? {
        return try {
            baseRequest.apply {
                put("allowedPaymentMethods", JSONArray().put(baseCardPaymentMethod()))
            }

        } catch (e: JSONException) {
            null
        }
    }

    /**
     * Information about the merchant requesting payment information
     *
     * @return Information about the merchant.
     * @throws JSONException
     * @see [MerchantInfo](https://developers.google.com/pay/api/android/reference/object.MerchantInfo)
     */
    private val merchantInfo: JSONObject =
            JSONObject().put("merchantName", "Example Merchant")

    /**
     * Creates an instance of [PaymentsClient] for use in an [Activity] using the
     * environment and theme set in [Constants].
     *
     * @param activity is the caller's activity.
     */
    fun createPaymentsClient(activity: Activity): PaymentsClient {
        val walletOptions = Wallet.WalletOptions.Builder()
                .setEnvironment(Constants.PAYMENTS_ENVIRONMENT)
                .build()

        return Wallet.getPaymentsClient(activity, walletOptions)
    }

    /**
     * Provide Google Pay API with a payment amount, currency, and amount status.
     *
     * @return information about the requested payment.
     * @throws JSONException
     * @see [TransactionInfo](https://developers.google.com/pay/api/android/reference/object.TransactionInfo)
     */
    @Throws(JSONException::class)
    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)
        }
    }

    /**
     * An object describing information requested in a Google Pay payment sheet
     *
     * @return Payment data expected by your app.
     * @see [PaymentDataRequest](https://developers.google.com/pay/api/android/reference/object.PaymentDataRequest)
     */
    fun getPaymentDataRequest(priceCemts: Long): JSONObject? {
        return try {
            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)
            }
        } catch (e: JSONException) {
            null
        }
    }
}

/**
 * Converts cents to a string format accepted by [PaymentsUtil.getPaymentDataRequest].
 *
 * @param cents value of the price.
 */
fun Long.centsToString() = BigDecimal(this)
        .divide(PaymentsUtil.CENTS)
        .setScale(2, RoundingMode.HALF_EVEN)
        .toString()

    

CheckoutActivity.java(자바)

Activity 예시에서는 레이아웃에 googlepay_button라는 id 속성이 있는 Google Pay 결제 버튼이 있다고 가정합니다.

/*
 * Copyright 2020 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.gms.samples.wallet.activity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.google.android.gms.common.api.Status;
import com.google.android.gms.samples.wallet.databinding.ActivityCheckoutBinding;
import com.google.android.gms.samples.wallet.util.PaymentsUtil;
import com.google.android.gms.samples.wallet.R;
import com.google.android.gms.samples.wallet.util.Json;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.wallet.AutoResolveHelper;
import com.google.android.gms.wallet.IsReadyToPayRequest;
import com.google.android.gms.wallet.PaymentData;
import com.google.android.gms.wallet.PaymentDataRequest;
import com.google.android.gms.wallet.PaymentsClient;

import java.util.Locale;
import java.util.Optional;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

/**
 * Checkout implementation for the app
 */
public class CheckoutActivity extends AppCompatActivity {

  // Arbitrarily-picked constant integer you define to track a request for payment data activity.
  private static final int LOAD_PAYMENT_DATA_REQUEST_CODE = 991;

  private static final long SHIPPING_COST_CENTS = 90 * PaymentsUtil.CENTS_IN_A_UNIT.longValue();

  // A client for interacting with the Google Pay API.
  private PaymentsClient paymentsClient;

  private ActivityCheckoutBinding layoutBinding;
  private View googlePayButton;

  private JSONArray garmentList;
  private JSONObject selectedGarment;

  /**
   * Initialize the Google Pay API on creation of the activity
   *
   * @see Activity#onCreate(android.os.Bundle)
   */
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    initializeUi();

    // Set up the mock information for our item in the UI.
    try {
      selectedGarment = fetchRandomGarment();
      displayGarment(selectedGarment);
    } catch (JSONException e) {
      throw new RuntimeException("The list of garments cannot be loaded");
    }

    // Initialize a Google Pay API client for an environment suitable for testing.
    // It's recommended to create the PaymentsClient object inside of the onCreate method.
    paymentsClient = PaymentsUtil.createPaymentsClient(this);
    possiblyShowGooglePayButton();
  }

  /**
   * Handle a resolved activity from the Google Pay payment sheet.
   *
   * @param requestCode Request code originally supplied to AutoResolveHelper in requestPayment().
   * @param resultCode  Result code returned by the Google Pay API.
   * @param data        Intent from the Google Pay API containing payment or error data.
   * @see <a href="https://developer.android.com/training/basics/intents/result">Getting a result
   * from an Activity</a>
   */
  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
      // value passed in AutoResolveHelper
      case LOAD_PAYMENT_DATA_REQUEST_CODE:
        switch (resultCode) {

          case Activity.RESULT_OK:
            PaymentData paymentData = PaymentData.getFromIntent(data);
            handlePaymentSuccess(paymentData);
            break;

          case Activity.RESULT_CANCELED:
            // The user cancelled the payment attempt
            break;

          case AutoResolveHelper.RESULT_ERROR:
            Status status = AutoResolveHelper.getStatusFromIntent(data);
            handleError(status.getStatusCode());
            break;
        }

        // Re-enables the Google Pay payment button.
        googlePayButton.setClickable(true);
    }
  }

  private void initializeUi() {

    // Use view binding to access the UI elements
    layoutBinding = ActivityCheckoutBinding.inflate(getLayoutInflater());
    setContentView(layoutBinding.getRoot());

    // The Google Pay button is a layout file – take the root view
    googlePayButton = layoutBinding.googlePayButton.getRoot();
    googlePayButton.setOnClickListener(
        new View.OnClickListener() {
          @Override
          public void onClick(View view) {
            requestPayment(view);
          }
        });
  }

  private void displayGarment(JSONObject garment) throws JSONException {
    layoutBinding.detailTitle.setText(garment.getString("title"));
    layoutBinding.detailPrice.setText(
        String.format(Locale.getDefault(), "$%.2f", garment.getDouble("price")));

    final String escapedHtmlText = Html.fromHtml(
        garment.getString("description"), Html.FROM_HTML_MODE_COMPACT).toString();
    layoutBinding.detailDescription.setText(Html.fromHtml(
        escapedHtmlText, Html.FROM_HTML_MODE_COMPACT));

    final String imageUri = String.format("@drawable/%s", garment.getString("image"));
    final int imageResource = getResources().getIdentifier(imageUri, null, getPackageName());
    layoutBinding.detailImage.setImageResource(imageResource);
  }

  /**
   * Determine the viewer's ability to pay with a payment method supported by your app and display a
   * Google Pay payment button.
   *
   * @see <a href="https://developers.google.com/android/reference/com/google/android/gms/wallet/
   * PaymentsClient.html#isReadyToPay(com.google.android.gms.wallet.
   * IsReadyToPayRequest)">PaymentsClient#IsReadyToPay</a>
   */
  private void possiblyShowGooglePayButton() {

    final Optional<JSONObject> isReadyToPayJson = PaymentsUtil.getIsReadyToPayRequest();
    if (!isReadyToPayJson.isPresent()) {
      return;
    }

    // The call to isReadyToPay is asynchronous and returns a Task. We need to provide an
    // OnCompleteListener to be triggered when the result of the call is known.
    IsReadyToPayRequest request = IsReadyToPayRequest.fromJson(isReadyToPayJson.get().toString());
    Task<Boolean> task = paymentsClient.isReadyToPay(request);
    task.addOnCompleteListener(this,
        new OnCompleteListener<Boolean>() {
          @Override
          public void onComplete(@NonNull Task<Boolean> task) {
            if (task.isSuccessful()) {
              setGooglePayAvailable(task.getResult());
            } else {
              Log.w("isReadyToPay failed", task.getException());
            }
          }
        });
  }

  /**
   * If isReadyToPay returned {@code true}, show the button and hide the "checking" text. Otherwise,
   * notify the user that Google Pay is not available. Please adjust to fit in with your current
   * user flow. You are not required to explicitly let the user know if isReadyToPay returns {@code
   * false}.
   *
   * @param available isReadyToPay API response.
   */
  private void setGooglePayAvailable(boolean available) {
    if (available) {
      googlePayButton.setVisibility(View.VISIBLE);
    } else {
      Toast.makeText(this, R.string.googlepay_status_unavailable, Toast.LENGTH_LONG).show();
    }
  }

  /**
   * PaymentData response object contains the payment information, as well as any additional
   * requested information, such as billing and shipping address.
   *
   * @param paymentData A response object returned by Google after a payer approves payment.
   * @see <a href="https://developers.google.com/pay/api/android/reference/
   * object#PaymentData">PaymentData</a>
   */
  private void handlePaymentSuccess(PaymentData paymentData) {

    // Token will be null if PaymentDataRequest was not constructed using fromJson(String).
    final String paymentInfo = paymentData.toJson();
    if (paymentInfo == null) {
      return;
    }

    try {
      JSONObject paymentMethodData = new JSONObject(paymentInfo).getJSONObject("paymentMethodData");
      // If the gateway is set to "example", no payment information is returned - instead, the
      // token will only consist of "examplePaymentMethodToken".

      final JSONObject tokenizationData = paymentMethodData.getJSONObject("tokenizationData");
      final String token = tokenizationData.getString("token");
      final JSONObject info = paymentMethodData.getJSONObject("info");
      final String billingName = info.getJSONObject("billingAddress").getString("name");
      Toast.makeText(
          this, getString(R.string.payments_show_name, billingName),
          Toast.LENGTH_LONG).show();

      // Logging token string.
      Log.d("Google Pay token: ", token);

    } catch (JSONException e) {
      throw new RuntimeException("The selected garment cannot be parsed from the list of elements");
    }
  }

  /**
   * At this stage, the user has already seen a popup informing them an error occurred. Normally,
   * only logging is required.
   *
   * @param statusCode will hold the value of any constant from CommonStatusCode or one of the
   *                   WalletConstants.ERROR_CODE_* constants.
   * @see <a href="https://developers.google.com/android/reference/com/google/android/gms/wallet/
   * WalletConstants#constant-summary">Wallet Constants Library</a>
   */
  private void handleError(int statusCode) {
    Log.e("loadPaymentData failed", String.format("Error code: %d", statusCode));
  }

  public void requestPayment(View view) {

    // Disables the button to prevent multiple clicks.
    googlePayButton.setClickable(false);

    // The price provided to the API should include taxes and shipping.
    // This price is not displayed to the user.
    try {
      double garmentPrice = selectedGarment.getDouble("price");
      long garmentPriceCents = Math.round(garmentPrice * PaymentsUtil.CENTS_IN_A_UNIT.longValue());
      long priceCents = garmentPriceCents + SHIPPING_COST_CENTS;

      Optional<JSONObject> paymentDataRequestJson = PaymentsUtil.getPaymentDataRequest(priceCents);
      if (!paymentDataRequestJson.isPresent()) {
        return;
      }

      PaymentDataRequest request =
          PaymentDataRequest.fromJson(paymentDataRequestJson.get().toString());

      // Since loadPaymentData may show the UI asking the user to select a payment method, we use
      // AutoResolveHelper to wait for the user interacting with it. Once completed,
      // onActivityResult will be called with the result.
      if (request != null) {
        AutoResolveHelper.resolveTask(
            paymentsClient.loadPaymentData(request),
            this, LOAD_PAYMENT_DATA_REQUEST_CODE);
      }

    } catch (JSONException e) {
      throw new RuntimeException("The price cannot be deserialized from the JSON object.");
    }
  }

  private JSONObject fetchRandomGarment() {

    // Only load the list of items if it has not been loaded before
    if (garmentList == null) {
      garmentList = Json.readFromResources(this, R.raw.tshirts);
    }

    // Take a random element from the list
    int randomIndex = Math.toIntExact(Math.round(Math.random() * (garmentList.length() - 1)));
    try {
      return garmentList.getJSONObject(randomIndex);
    } catch (JSONException e) {
      throw new RuntimeException("The index specified is out of bounds.");
    }
  }
}

PaymentsUtil.java(자바)

이 예시 파일에서는 IsReadyToPayRequest 또는 PaymentDataRequest를 빌드하기에 적합한 JSON 객체를 빌드합니다.

/*
 * Copyright 2020 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.gms.samples.wallet.util;

import android.app.Activity;

import com.google.android.gms.samples.wallet.Constants;
import com.google.android.gms.wallet.PaymentsClient;
import com.google.android.gms.wallet.Wallet;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Optional;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Contains helper static methods for dealing with the Payments API.
 *
 * <p>Many of the parameters used in the code are optional and are set here merely to call out their
 * existence. Please consult the documentation to learn more and feel free to remove ones not
 * relevant to your implementation.
 */
public class PaymentsUtil {

  public static final BigDecimal CENTS_IN_A_UNIT = new BigDecimal(100d);

  /**
   * Create a Google Pay API base request object with properties used in all requests.
   *
   * @return Google Pay API base request object.
   * @throws JSONException
   */
  private static JSONObject getBaseRequest() throws JSONException {
    return new JSONObject().put("apiVersion", 2).put("apiVersionMinor", 0);
  }

  /**
   * Creates an instance of {@link PaymentsClient} for use in an {@link Activity} using the
   * environment and theme set in {@link Constants}.
   *
   * @param activity is the caller's activity.
   */
  public static PaymentsClient createPaymentsClient(Activity activity) {
    Wallet.WalletOptions walletOptions =
        new Wallet.WalletOptions.Builder().setEnvironment(Constants.PAYMENTS_ENVIRONMENT).build();
    return Wallet.getPaymentsClient(activity, walletOptions);
  }

  /**
   * Gateway Integration: Identify your gateway and your app's gateway merchant identifier.
   *
   * <p>The Google Pay API response will return an encrypted payment method capable of being charged
   * by a supported gateway after payer authorization.
   *
   * <p>TODO: Check with your gateway on the parameters to pass and modify them in Constants.java.
   *
   * @return Payment data tokenization for the CARD payment method.
   * @throws JSONException
   * @see <a href=
   * "https://developers.google.com/pay/api/android/reference/object#PaymentMethodTokenizationSpecification">PaymentMethodTokenizationSpecification</a>
   */
  private static JSONObject getGatewayTokenizationSpecification() throws JSONException {
    return new JSONObject() {{
      put("type", "PAYMENT_GATEWAY");
      put("parameters", new JSONObject() {{
        put("gateway", "example");
        put("gatewayMerchantId", "exampleGatewayMerchantId");
      }});
    }};
  }

  /**
   * {@code DIRECT} Integration: Decrypt a response directly on your servers. This configuration has
   * additional data security requirements from Google and additional PCI DSS compliance complexity.
   *
   * <p>Please refer to the documentation for more information about {@code DIRECT} integration. The
   * type of integration you use depends on your payment processor.
   *
   * @return Payment data tokenization for the CARD payment method.
   * @throws JSONException
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#PaymentMethodTokenizationSpecification">PaymentMethodTokenizationSpecification</a>
   */
  private static JSONObject getDirectTokenizationSpecification()
      throws JSONException, RuntimeException {
    if (Constants.DIRECT_TOKENIZATION_PARAMETERS.isEmpty()
        || Constants.DIRECT_TOKENIZATION_PUBLIC_KEY.isEmpty()
        || Constants.DIRECT_TOKENIZATION_PUBLIC_KEY == null
        || Constants.DIRECT_TOKENIZATION_PUBLIC_KEY == "REPLACE_ME") {
      throw new RuntimeException(
          "Please edit the Constants.java file to add protocol version & public key.");
    }
    JSONObject tokenizationSpecification = new JSONObject();

    tokenizationSpecification.put("type", "DIRECT");
    JSONObject parameters = new JSONObject(Constants.DIRECT_TOKENIZATION_PARAMETERS);
    tokenizationSpecification.put("parameters", parameters);

    return tokenizationSpecification;
  }

  /**
   * Card networks supported by your app and your gateway.
   *
   * <p>TODO: Confirm card networks supported by your app and gateway & update in Constants.java.
   *
   * @return Allowed card networks
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#CardParameters">CardParameters</a>
   */
  private static JSONArray getAllowedCardNetworks() {
    return new JSONArray(Constants.SUPPORTED_NETWORKS);
  }

  /**
   * Card authentication methods supported by your app and your gateway.
   *
   * <p>TODO: Confirm your processor supports Android device tokens on your supported card networks
   * and make updates in Constants.java.
   *
   * @return Allowed card authentication methods.
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#CardParameters">CardParameters</a>
   */
  private static JSONArray getAllowedCardAuthMethods() {
    return new JSONArray(Constants.SUPPORTED_METHODS);
  }

  /**
   * Describe your app's support for the CARD payment method.
   *
   * <p>The provided properties are applicable to both an IsReadyToPayRequest and a
   * PaymentDataRequest.
   *
   * @return A CARD PaymentMethod object describing accepted cards.
   * @throws JSONException
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#PaymentMethod">PaymentMethod</a>
   */
  private static JSONObject getBaseCardPaymentMethod() throws JSONException {
    JSONObject cardPaymentMethod = new JSONObject();
    cardPaymentMethod.put("type", "CARD");

    JSONObject parameters = new JSONObject();
    parameters.put("allowedAuthMethods", getAllowedCardAuthMethods());
    parameters.put("allowedCardNetworks", getAllowedCardNetworks());
    // Optionally, you can add billing address/phone number associated with a CARD payment method.
    parameters.put("billingAddressRequired", true);

    JSONObject billingAddressParameters = new JSONObject();
    billingAddressParameters.put("format", "FULL");

    parameters.put("billingAddressParameters", billingAddressParameters);

    cardPaymentMethod.put("parameters", parameters);

    return cardPaymentMethod;
  }

  /**
   * Describe the expected returned payment data for the CARD payment method
   *
   * @return A CARD PaymentMethod describing accepted cards and optional fields.
   * @throws JSONException
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#PaymentMethod">PaymentMethod</a>
   */
  private static JSONObject getCardPaymentMethod() throws JSONException {
    JSONObject cardPaymentMethod = getBaseCardPaymentMethod();
    cardPaymentMethod.put("tokenizationSpecification", getGatewayTokenizationSpecification());

    return cardPaymentMethod;
  }

  /**
   * An object describing accepted forms of payment by your app, used to determine a viewer's
   * readiness to pay.
   *
   * @return API version and payment methods supported by the app.
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#IsReadyToPayRequest">IsReadyToPayRequest</a>
   */
  public static Optional<JSONObject> getIsReadyToPayRequest() {
    try {
      JSONObject isReadyToPayRequest = getBaseRequest();
      isReadyToPayRequest.put(
          "allowedPaymentMethods", new JSONArray().put(getBaseCardPaymentMethod()));

      return Optional.of(isReadyToPayRequest);

    } catch (JSONException e) {
      return Optional.empty();
    }
  }

  /**
   * Provide Google Pay API with a payment amount, currency, and amount status.
   *
   * @return information about the requested payment.
   * @throws JSONException
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#TransactionInfo">TransactionInfo</a>
   */
  private static JSONObject getTransactionInfo(String price) throws JSONException {
    JSONObject transactionInfo = new JSONObject();
    transactionInfo.put("totalPrice", price);
    transactionInfo.put("totalPriceStatus", "FINAL");
    transactionInfo.put("countryCode", Constants.COUNTRY_CODE);
    transactionInfo.put("currencyCode", Constants.CURRENCY_CODE);
    transactionInfo.put("checkoutOption", "COMPLETE_IMMEDIATE_PURCHASE");

    return transactionInfo;
  }

  /**
   * Information about the merchant requesting payment information
   *
   * @return Information about the merchant.
   * @throws JSONException
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#MerchantInfo">MerchantInfo</a>
   */
  private static JSONObject getMerchantInfo() throws JSONException {
    return new JSONObject().put("merchantName", "Example Merchant");
  }

  /**
   * An object describing information requested in a Google Pay payment sheet
   *
   * @return Payment data expected by your app.
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#PaymentDataRequest">PaymentDataRequest</a>
   */
  public static Optional<JSONObject> getPaymentDataRequest(long priceCents) {

    final String price = PaymentsUtil.centsToString(priceCents);

    try {
      JSONObject paymentDataRequest = PaymentsUtil.getBaseRequest();
      paymentDataRequest.put(
          "allowedPaymentMethods", new JSONArray().put(PaymentsUtil.getCardPaymentMethod()));
      paymentDataRequest.put("transactionInfo", PaymentsUtil.getTransactionInfo(price));
      paymentDataRequest.put("merchantInfo", PaymentsUtil.getMerchantInfo());

      /* An optional shipping address requirement is a top-level property of the PaymentDataRequest
      JSON object. */
      paymentDataRequest.put("shippingAddressRequired", true);

      JSONObject shippingAddressParameters = new JSONObject();
      shippingAddressParameters.put("phoneNumberRequired", false);

      JSONArray allowedCountryCodes = new JSONArray(Constants.SHIPPING_SUPPORTED_COUNTRIES);

      shippingAddressParameters.put("allowedCountryCodes", allowedCountryCodes);
      paymentDataRequest.put("shippingAddressParameters", shippingAddressParameters);
      return Optional.of(paymentDataRequest);

    } catch (JSONException e) {
      return Optional.empty();
    }
  }

  /**
   * Converts cents to a string format accepted by {@link PaymentsUtil#getPaymentDataRequest}.
   *
   * @param cents value of the price in cents.
   */
  public static String centsToString(long cents) {
    return new BigDecimal(cents)
        .divide(CENTS_IN_A_UNIT, RoundingMode.HALF_EVEN)
        .setScale(2, RoundingMode.HALF_EVEN)
        .toString();
  }
}