教學課程

請按照本教學課程中的步驟,將透過 Google Play 商店發布的 Android 應用程式與 Google Pay API 整合,並設定為接受付款卡。

步驟 1:定義 Google Pay API 版本

宣告應用程式使用的 Google Pay API 版本。主要版本和次要版本會影響每個傳送物件中預期的欄位,並且會包含在回應中。

建立基本要求物件,並在其中包含所有其他要求物件中存在的屬性。


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

步驟 2:為付款服務供應商申請付款代碼

Google 會將付款人所選卡片的相關資訊加密,以便付款服務供應商進行安全處理。


      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"

開發人員說明文件

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"

開發人員說明文件

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"

開發人員說明文件

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"

開發人員說明文件

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"

沒有可用的開發人員說明文件

Evo Payment Gateway

      "gateway": "evopaymentgateway"
      "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

沒有可用的開發人員說明文件

Fat Zebra

      "gateway": "fatzebra"
      "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

First Data

      "gateway": "firstdata"
      "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

開發人員說明文件

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

Developer docs

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"

開發人員說明文件

iPay88

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

開發人員說明文件

iQmetrix

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

開發人員說明文件

JudoPay

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

沒有可用的開發人員說明文件

Kineox

      "gateway": "kineox"
      "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

      "gateway": "newebpay"
      "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

開發人員說明文件

Nexi

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

開發人員說明文件

NMI

      "gateway": "creditcall"
      "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

開發人員說明文件

Nuvei

      "gateway": "nuvei"
      "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"

開發人員說明文件

Paymark

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

開發人員說明文件

Paymentwall

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

開發人員說明文件

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

Developer docs

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"

開發人員說明文件

Portmone

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

開發人員說明文件

Przelewy24

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

開發人員說明文件

RBK.money

      "gateway": "rbkmoney"
      "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

      "gateway": "tappay"
      "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"

開發人員說明文件

Uniteller

      "gateway": "uniteller"
      "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 中實作的卡片付款方式。如果系統不支援你的付款服務供應商,你或許可以透過直接整合來採用 Google Pay。詳情請參閱直接代碼化說明文件

步驟 3:定義支援的付款發卡機構

定義您的應用程式接受的發卡機構。

    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),或傳回 Android 裝置上已透過 3-D 安全密文驗證的裝置代碼 (CRYPTOGRAM_3DS)。

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

詳情請參閱 CardParameters JSON 物件參考資料。如要瞭解支援的發卡機構及 Android 裝置代碼的支援情形,請與閘道或處理方聯絡。

步驟 4:說明可用的付款方式

結合支援的驗證方式和發卡機構來說明您的應用程式對 CARD 付款方式的支援情形。


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

擴充基本卡片付款方式物件,以說明預期傳回至應用程式的資訊 (其中必須包含代碼化付款資料)。


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

        return cardPaymentMethod;
      }
    

如要進一步瞭解支援的 parameters,請參閱 JSON 物件參考資料中的 CardParameters 相關說明。

除了支援 CARD 以外,Google Pay 也支援 PAYPAL 付款方式。如要進一步瞭解如何將 PAYPAL 新增為 Google Pay 付款方式,請參閱 PayPal 的開發人員說明文件

步驟 5:建立 PaymentsClient 執行個體

ActivityonCreate 方法中建立 PaymentsClient 的執行個體。PaymentsClient 的用途是與 Google Pay API 進行互動。


      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 付款

使用下列程式碼片段,為您的基本要求物件新增允許的付款方式:


      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 物件說明文件


      private void possiblyShowGooglePayButton() {
        final Optional<JSONObject> isReadyToPayJson = PaymentsUtil.getIsReadyToPayRequest();
        if (!isReadyToPayJson.isPresent()) {
          return;
        }
        IsReadyToPayRequest request = IsReadyToPayRequest.fromJson(isReadyToPayJson.get().toString());
        if (request == null) {
          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.
        Task<Boolean> task = mPaymentsClient.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 物件說明文件

以下範例顯示如何取得交易資訊,特別是價格、價格狀態和幣別這幾種交易資訊。


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

        return transactionInfo;
      }
    

提供可讓使用者查看的商家名稱。詳情請參閱 MerchantInfo JSON 物件說明文件

以下範例顯示如何取得商家名稱:


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

將基本要求物件指派給新的 PaymentDataRequest JSON 物件。接下來,請新增應用程式支援的付款方式,包括回應中預期的任何其他資料設定。最後,請新增交易和提出要求之商家的相關資訊。

以下範例顯示如何要求付款資料:


      public static Optional<JSONObject> getPaymentDataRequest(String price) {
        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:針對使用者手勢註冊事件處理常式

定義 OnClickListener,要求系統在使用者啟用 Google Pay 付款按鈕後顯示 Google Pay 付款畫面。


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

PaymentDataRequest 物件是代表付款資料要求的 Parcelable,可提供支援付款所需的資訊。請使用 AutoResolveHelper 類別自動解析 Task,然後再處理 Activity 類別中 onActivityResult 方法 的結果。

步驟 9:處理回應物件

當付款人成功在 Google Pay 付款畫面中提供要求的資訊之後,系統就會將 PaymentData 物件傳回給 onActivityResult

請將成功的回應轉換為 JSON,以便將付款資訊傳送至處理方,並向使用者顯示購買確認訊息。如果傳送至 PaymentDataRequesttransactionInfo.totalPriceStatusESTIMATED,您必須先顯示最終價格,才能以傳回的付款方式扣款。

擷取 paymentData 回應中的付款代碼。如果您實作了閘道整合,請將這個代碼傳送至閘道,不要進行任何修改。


      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:
                // Nothing to here normally - the user simply cancelled without selecting a
                // payment method.
                break;
              case AutoResolveHelper.RESULT_ERROR:
                Status status = AutoResolveHelper.getStatusFromIntent(data);
                handleError(status.getStatusCode());
                break;
              default:
                // Do nothing.
            }

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

如要進一步瞭解回應的內容和結構,請參閱 PaymentData JSON 物件參考資料

完整的實作範例

以下是設定恰當的完整專案範例。如需專案層級的設定步驟,請參閱設定 Google Pay API 的〈設定您的專案〉一節。

CheckoutActivity.java

此範例 Activity 假設您的版面配置中有 ID 屬性為 googlepay_button 的 Google Pay 付款按鈕。

    /*
     * 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;

    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.annotation.NonNull;
    import android.util.Log;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.TextView;
    import android.widget.Toast;
    import com.google.android.gms.common.api.ApiException;
    import com.google.android.gms.common.api.Status;
    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.Optional;
    import org.json.JSONException;
    import org.json.JSONObject;

    /**
     * Checkout implementation for the app
     */
    public class CheckoutActivity extends Activity {
      /**
       * A client for interacting with the Google Pay API.
       *
       * @see <a
       *     href="https://developers.google.com/android/reference/com/google/android/gms/wallet/PaymentsClient">PaymentsClient</a>
       */
      private PaymentsClient mPaymentsClient;

      /**
       * A Google Pay payment button presented to the viewer for interaction.
       *
       * @see <a href="https://developers.google.com/pay/api/android/guides/brand-guidelines">Google Pay
       *     payment button brand guidelines</a>
       */
      private View mGooglePayButton;

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

      private TextView mGooglePayStatusText;

      private ItemInfo mBikeItem = new ItemInfo("Simple Bike", 300 * 1000000, R.drawable.bike);
      private long mShippingCost = 90 * 1000000;
      /**
       * 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);
        setContentView(R.layout.activity_checkout);

        // Set up the mock information for our item in the UI.
        initItemUI();

        mGooglePayButton = findViewById(R.id.googlepay_button);
        mGooglePayStatusText = findViewById(R.id.googlepay_status);

        // 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.
        mPaymentsClient = PaymentsUtil.createPaymentsClient(this);
        possiblyShowGooglePayButton();

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

      /**
       * 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;
        }
        IsReadyToPayRequest request = IsReadyToPayRequest.fromJson(isReadyToPayJson.get().toString());
        if (request == null) {
          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.
        Task<Boolean> task = mPaymentsClient.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) {
          mGooglePayStatusText.setVisibility(View.GONE);
          mGooglePayButton.setVisibility(View.VISIBLE);
        } else {
          mGooglePayStatusText.setText(R.string.googlepay_status_unavailable);
        }
      }

      /**
       * 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:
                // Nothing to here normally - the user simply cancelled without selecting a
                // payment method.
                break;
              case AutoResolveHelper.RESULT_ERROR:
                Status status = AutoResolveHelper.getStatusFromIntent(data);
                handleError(status.getStatusCode());
                break;
              default:
                // Do nothing.
            }

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

      /**
       * 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">Payment
       *     Data</a>
       */
      private void handlePaymentSuccess(PaymentData paymentData) {
        String paymentInformation = paymentData.toJson();

        // Token will be null if PaymentDataRequest was not constructed using fromJson(String).
        if (paymentInformation == null) {
          return;
        }
        JSONObject paymentMethodData;

        try {
          paymentMethodData = new JSONObject(paymentInformation).getJSONObject("paymentMethodData");
          // If the gateway is set to "example", no payment information is returned - instead, the
          // token will only consist of "examplePaymentMethodToken".
          if (paymentMethodData
                  .getJSONObject("tokenizationData")
                  .getString("type")
                  .equals("PAYMENT_GATEWAY")
              && paymentMethodData
                  .getJSONObject("tokenizationData")
                  .getString("token")
                  .equals("examplePaymentMethodToken")) {
            AlertDialog alertDialog =
                new AlertDialog.Builder(this)
                    .setTitle("Warning")
                    .setMessage(
                        "Gateway name set to \"example\" - please modify "
                            + "Constants.java and replace it with your own gateway.")
                    .setPositiveButton("OK", null)
                    .create();
            alertDialog.show();
          }

          String 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 (JSONException e) {
          Log.e("handlePaymentSuccess", "Error: " + e.toString());
          return;
        }
      }

      /**
       * 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.w("loadPaymentData failed", String.format("Error code: %d", statusCode));
      }

      // This method is called when the Pay with Google button is clicked.
      public void requestPayment(View view) {
        // Disables the button to prevent multiple clicks.
        mGooglePayButton.setClickable(false);

        // The price provided to the API should include taxes and shipping.
        // This price is not displayed to the user.
        String price = PaymentsUtil.microsToString(mBikeItem.getPriceMicros() + mShippingCost);

        // TransactionInfo transaction = PaymentsUtil.createTransaction(price);
        Optional<JSONObject> paymentDataRequestJson = PaymentsUtil.getPaymentDataRequest(price);
        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(
              mPaymentsClient.loadPaymentData(request), this, LOAD_PAYMENT_DATA_REQUEST_CODE);
        }
      }

      private void initItemUI() {
        TextView itemName = findViewById(R.id.text_item_name);
        ImageView itemImage = findViewById(R.id.image_item_image);
        TextView itemPrice = findViewById(R.id.text_item_price);

        itemName.setText(mBikeItem.getName());
        itemImage.setImageResource(mBikeItem.getImageResourceId());
        itemPrice.setText(PaymentsUtil.microsToString(mBikeItem.getPriceMicros()));
      }
    }
    
    

PaymentsUtil.java

此範例檔案會建構適合建立 IsReadyToPayRequestPaymentDataRequest 的 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;

    import android.app.Activity;
    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.HashMap;
    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 {
      private static final BigDecimal MICROS = new BigDecimal(1000000d);

      private PaymentsUtil() {}

      /**
       * 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);

        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(String price) {
        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 micros to a string format accepted by {@link PaymentsUtil#getPaymentDataRequest}.
       *
       * @param micros value of the price.
       */
      public static String microsToString(long micros) {
        return new BigDecimal(micros).divide(MICROS).setScale(2, RoundingMode.HALF_EVEN).toString();
      }
    }