教程

按照本教程中的步骤将 Web 应用与 Google Pay API 集成,并将其配置为接受支付卡。

第 1 步:指定您的 Google Pay API 版本

声明您的网站使用的 Google Pay API 版本。该主要及次要版本会影响每个传递对象中预期的字段,并且会包含在响应中。

    const baseRequest = {
      apiVersion: 2,
      apiVersionMinor: 0
    };

第 2 步:选择支付的令牌化方法

Google 会通过商家的网关或直接在商家的安全服务器上加密有关买家所选卡的信息,以便进行安全处理。下面提供了这两种选项的示例。

网关

    const tokenizationSpecification = {
      type: 'PAYMENT_GATEWAY',
      parameters: {
        'gateway': 'example',
        'gatewayMerchantId': 'exampleGatewayMerchantId'
      }
    };

请将“example”替换为 Google 所支持网关的网关标识符,并将“exampleGatewayMerchantId”替换为您的网关所提供的商家唯一标识符。

如需详细了解所支持的网关和唯一标识符,请参阅网关令牌化文档PAYMENT_GATEWAY 令牌化类型是 Google Pay API 中为商家实现卡付款方式的最常见类型。

直接

    const tokenizationSpecification = {
      "type": "DIRECT",
      "parameters": {
        "protocolVersion": "ECv2",
        "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y="
      }
    }

请将缩写的 publicKey 值替换为您在 Google Pay 开发者资料中提供的公钥,以接收使用该密钥加密的付款信息。如需了解如何配置适合 Google Pay API 的加密密钥,请参阅我们的付款数据加密文档

第 3 步:指定支持的支付卡网络

指定您的网站接受的支付卡网络。

    const allowedCardNetworks = ["AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"];
    

Google Pay API 可以返回在 Google.com 上留存的支付卡 (PAN_ONLY) 和/或在 Android 设备上以 3D 安全密文进行身份验证的设备令牌 (CRYPTOGRAM_3DS)。

    const allowedCardAuthMethods = ["PAN_ONLY", "CRYPTOGRAM_3DS"];
    

如需了解详情,请参阅我们的对象参考中的 CardParameters。请与您的网关或处理方联系,了解所支持的支付卡网络以及对 Android 设备令牌的支持情况。

第 4 步:说明您允许的付款方式

将您支持的身份验证方法和支持的支付卡网络结合使用,以说明您的网站对 CARD 付款方式的支持情况。

    const baseCardPaymentMethod = {
      type: 'CARD',
      parameters: {
        allowedAuthMethods: allowedCardAuthMethods,
        allowedCardNetworks: allowedCardNetworks
      }
    };

扩展基本的卡付款方式对象,以说明预期返回给您的应用的信息,包括令牌化的付款数据。

    const cardPaymentMethod = Object.assign(
      {tokenizationSpecification: tokenizationSpecification},
      baseCardPaymentMethod
    );

如需详细了解所支持的 parameters,请参阅 CardParameters

除了 CARD 之外,Google Pay 还支持 PAYPAL 付款方式。如需详细了解如何将 PAYPAL 添加为 Google Pay 的付款方式,请参阅 PayPal 开发者文档

第 5 步:加载 Google Pay API JavaScript 库

在您的网页上添加 Google 托管的 JavaScript。

    <script
      async
      src="https://pay.google.com/gp/p/js/pay.js"
      onload="console.log('TODO: add onload function')">
    </script>

加载完 Google Pay API JavaScript 库后,请初始化 PaymentsClient 对象。初始开发使用 TEST 环境,该环境会返回适合引用付款响应结构的虚拟付款方式。在此环境中,选定的付款方式无法进行交易。如需详细了解会返回可扣款付款方式的 PRODUCTION 环境的要求,请参阅集成核对清单

const paymentsClient =
        new google.payments.api.PaymentsClient({environment: 'TEST'});

第 6 步:确定是否能使用 Google Pay API 进行付款

将您允许的付款方式添加到基本请求对象中。

    const isReadyToPayRequest = Object.assign({}, baseRequest);
    isReadyToPayRequest.allowedPaymentMethods = [baseCardPaymentMethod];

对于您的指定付款方式,请调用 isReadyToPay(),以确定当前设备和/或浏览器是否支持 Google Pay API。

    paymentsClient.isReadyToPay(isReadyToPayRequest)
        .then(function(response) {
          if (response.result) {
            // add a Google Pay payment button
          }
        })
        .catch(function(err) {
          // show error in developer console for debugging
          console.error(err);
        });

第 7 步:添加 Google Pay 付款按钮

向您的网页添加 Google Pay 付款按钮,以鼓励买家使用 Google Pay API 和您的网站支持的付款方式结算。如需详细了解可用的按钮类型、颜色和显示要求,请参阅品牌推广指南

    const button =
        paymentsClient.createButton({onClick: () => console.log('TODO: click handler')});
    document.getElementById('container').appendChild(button);

第 8 步:创建 PaymentDataRequest 对象

构建一个 JavaScript 对象来说明您的网站对 Google Pay API 的支持情况。如需查看受支持属性的完整列表,请参阅 PaymentDataRequest

const paymentDataRequest = Object.assign({}, baseRequest);

添加您的应用所支持的付款方式,例如响应中预期的任何其他数据的配置。

    paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod];
    

以下示例显示了如何指定买家授权的总价和币种。

paymentDataRequest.transactionInfo = {
      totalPriceStatus: 'FINAL',
      totalPrice: '123.45',
      currencyCode: 'USD',
      countryCode: 'US'
    };

提供用户可见的商家名称,并在 TEST 中使用我们的 merchantId 值。如需了解详情以及了解何时替换 TESTmerchantId 值,请参阅 MerchantInfo

    paymentDataRequest.merchantInfo = {
      merchantName: 'Example Merchant'
      merchantId: '0123456789'
    };
    

第 9 步:为用户手势注册事件处理程序

为购买按钮注册 Click 事件处理程序,以便在其与 Google Pay 付款按钮互动后立即调用 loadPaymentData()

在 Google 用户授权您的网站接收与用户所选付款方式和可选联系数据相关的信息后,处理来自 Google Pay API 的响应。

请从 paymentData 响应中提取付款令牌。如果您实现了网关集成,请将此令牌传递给您的网关,无需进行任何修改。

    paymentsClient.loadPaymentData(paymentDataRequest).then(function(paymentData){
      // if using gateway tokenization, pass this token without modification
      paymentToken = paymentData.paymentMethodData.tokenizationData.token;
    }).catch(function(err){
      // show error in developer console for debugging
      console.error(err);
    });

第 10 步(可选):在用户互动之前预取付款数据

在调用 loadPaymentData() 之前从 Google Pay API 预取您网站的付款配置,以缩短执行时间。

    paymentsClient.prefetchPaymentData(paymentDataRequest);
    

第 11 步(可选):设置授权付款

“授权付款”用于启动付款流程并确认付款的授权状态。要设置授权付款,请执按以下步骤操作:

  1. PaymentOptions 中注册 onPaymentAuthorized() 回调。
  2. 使用 PAYMENT_AUTHORIZATION 回调 Intent 时,可调用 loadPaymentData() 函数。
  3. 实现 onPaymentAuthorized()

注册 onPaymentAuthorized 回调

以下代码示例演示了如何注册 onPaymentAuthorized 回调:

    {
      environment: "TEST",
      merchantInfo: {
        merchantName: "Example Merchant",
        merchantId: "0123456789"
      },
      paymentDataCallbacks: {
        onPaymentAuthorized: onPaymentAuthorized
      }
    }
    

使用回调 intent 加载付款数据

以下代码示例演示了如何使用“授权付款”来初始化付款表格:

    const paymentDataRequest = Object.assign({}, baseRequest);
    paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod];
    paymentDataRequest.transactionInfo = getGoogleTransactionInfo();
    paymentDataRequest.merchantInfo = {
      merchantName: 'Example Merchant'
      merchantId: '0123456789',
    };

    paymentDataRequest.callbackIntents = ["PAYMENT_AUTHORIZATION"];
    

处理 onPaymentAuthorized 回调

付款人通过用户手势批准付款(例如,点击付款)后,Google 会使用 PaymentData 对象调用 onPaymentAuthorized() 回调。

该回调返回一个 Promise<PaymentAuthorizationResult> 值。PaymentAuthorizationResult 对象具有 SUCCESSERROR 交易状态。成功后,付款表格将成功关闭。如果您遇到错误,则付款表格会显示系统处理完付款后返回的错误详情。用户可以更改付款表格的付款数据并再次授权付款。

    function onPaymentAuthorized(paymentData) {
      return new Promise(function(resolve, reject){
        // handle the response
        processPayment(paymentData)
        .then(function() {
          resolve({transactionState: 'SUCCESS'});
        })
        .catch(function() {
          resolve({
            transactionState: 'ERROR',
            error: {
              intent: 'PAYMENT_AUTHORIZATION',
              message: 'Insufficient funds',
              reason: 'PAYMENT_DATA_INVALID'
            }
          });
        });
      });
    }
    

第 12 步(启用运费时为可选):设置动态价格更新

动态价格更新允许商家根据所选的送货地址动态地更新运费选项和交易信息。此外,您还可以根据所选的运费选项动态地更新交易信息。

要设置动态价格更新,请按以下步骤操作:

  1. PaymentOptions 中注册 onPaymentAuthorizedonPaymentDataChanged 回调。
  2. 使用回调 Intent 调用 loadPaymentData() 函数。如需了解详情,请参阅相应的示例
  3. 实现 onPaymentAuthorizedonPaymentDataChanged

注册 PaymentData 回调

以下代码示例显示动态价格更新需要在 PaymentsClient paymentOptions 对象中注册回调函数。

    {
      environment: "TEST",
      merchantInfo: {
        merchantName: "Example Merchant",
        merchantId: "0123456789"
      },
      paymentDataCallbacks: {
        onPaymentAuthorized: onPaymentAuthorized,
        onPaymentDataChanged: onPaymentDataChanged
      }
    }
    

使用回调 intent 加载付款数据

以下代码示例演示了如何使用所需的送货地址和运费选项配置来初始化付款表格:

    const paymentDataRequest = Object.assign({}, baseRequest);
    paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod];
    paymentDataRequest.transactionInfo = getGoogleTransactionInfo();
    paymentDataRequest.merchantInfo = {
      merchantId: '0123456789',
      merchantName: 'Example Merchant'
    };

    paymentDataRequest.callbackIntents = ["SHIPPING_ADDRESS",  "SHIPPING_OPTION", "PAYMENT_AUTHORIZATION"];

    paymentDataRequest.shippingAddressRequired = true;
    paymentDataRequest.shippingAddressParameters = getGoogleShippingAddressParameters();
    paymentDataRequest.shippingOptionRequired = true;
    

处理 onPaymentDataChanged 回调

onPaymentDataChanged 回调是使用 IntermediatePaymentData 对象调用的,该对象中包含在付款表格中选择的送货地址和运费选项。

该回调返回一个 Promise<PaymentDataRequestUpdate> 值。PaymentDataRequestUpdate 对象包含新的交易信息、运费选项和付款数据错误,这些数据用于更新付款表格。

直接在付款表格中处理所有例外情况,例如无用的送货地址或无效的运费选项。设置 PaymentDataError 对象以突出显示错误原因,并向用户显示错误消息。请务必在消息中添加相关 intent。如需详细了解如何设置对象和消息,请参阅以下代码示例:

    function onPaymentDataChanged(intermediatePaymentData) {
      return new Promise(function(resolve, reject) {

        let shippingAddress = intermediatePaymentData.shippingAddress;
        let shippingOptionData = intermediatePaymentData.shippingOptionData;
        let paymentDataRequestUpdate = {};

        if (intermediatePaymentData.callbackTrigger == "INITIALIZE" || intermediatePaymentData.callbackTrigger == "SHIPPING_ADDRESS") {
          if(shippingAddress.administrativeArea == "NJ")  {
            paymentDataRequestUpdate.error = getGoogleUnserviceableAddressError();
          }
          else {
            paymentDataRequestUpdate.newShippingOptionParameters = getGoogleDefaultShippingOptions();
            let selectedShippingOptionId = paymentDataRequestUpdate.newShippingOptionParameters.defaultSelectedOptionId;
            paymentDataRequestUpdate.newTransactionInfo = calculateNewTransactionInfo(selectedShippingOptionId);
          }
        }
        else if (intermediatePaymentData.callbackTrigger == "SHIPPING_OPTION") {
          paymentDataRequestUpdate.newTransactionInfo = calculateNewTransactionInfo(shippingOptionData.id);
        }

        resolve(paymentDataRequestUpdate);
      });
    }
    

总结

以下示例演示了 Google Pay API JavaScript 库和动态价格更新的完整端到端示例:

教程

    <div id="container"></div>
    
    <script>
    /**
     * Define the version of the Google Pay API referenced when creating your
     * configuration
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|apiVersion in PaymentDataRequest}
     */
    const baseRequest = {
      apiVersion: 2,
      apiVersionMinor: 0
    };

    /**
     * Card networks supported by your site and your gateway
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     * @todo confirm card networks supported by your site and gateway
     */
    const allowedCardNetworks = ["AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"];

    /**
     * Card authentication methods supported by your site and your gateway
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     * @todo confirm your processor supports Android device tokens for your
     * supported card networks
     */
    const allowedCardAuthMethods = ["PAN_ONLY", "CRYPTOGRAM_3DS"];

    /**
     * Identify your gateway and your site'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
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#gateway|PaymentMethodTokenizationSpecification}
     */
    const tokenizationSpecification = {
      type: 'PAYMENT_GATEWAY',
      parameters: {
        'gateway': 'example',
        'gatewayMerchantId': 'exampleGatewayMerchantId'
      }
    };

    /**
     * Describe your site's support for the CARD payment method and its required
     * fields
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     */
    const baseCardPaymentMethod = {
      type: 'CARD',
      parameters: {
        allowedAuthMethods: allowedCardAuthMethods,
        allowedCardNetworks: allowedCardNetworks
      }
    };

    /**
     * Describe your site's support for the CARD payment method including optional
     * fields
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     */
    const cardPaymentMethod = Object.assign(
      {},
      baseCardPaymentMethod,
      {
        tokenizationSpecification: tokenizationSpecification
      }
    );

    /**
     * An initialized google.payments.api.PaymentsClient object or null if not yet set
     *
     * @see {@link getGooglePaymentsClient}
     */
    let paymentsClient = null;

    /**
     * Configure your site's support for payment methods supported by the Google Pay
     * API.
     *
     * Each member of allowedPaymentMethods should contain only the required fields,
     * allowing reuse of this base request when determining a viewer's ability
     * to pay and later requesting a supported payment method
     *
     * @returns {object} Google Pay API version, payment methods supported by the site
     */
    function getGoogleIsReadyToPayRequest() {
      return Object.assign(
          {},
          baseRequest,
          {
            allowedPaymentMethods: [baseCardPaymentMethod]
          }
      );
    }

    /**
     * Configure support for the Google Pay API
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|PaymentDataRequest}
     * @returns {object} PaymentDataRequest fields
     */
    function getGooglePaymentDataRequest() {
      const paymentDataRequest = Object.assign({}, baseRequest);
      paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod];
      paymentDataRequest.transactionInfo = getGoogleTransactionInfo();
      paymentDataRequest.merchantInfo = {
        // @todo a merchant ID is available for a production environment after approval by Google
        // See {@link https://developers.google.com/pay/api/web/guides/test-and-deploy/integration-checklist|Integration checklist}
        // merchantId: '0123456789',
        merchantName: 'Example Merchant'
      };
      return paymentDataRequest;
    }

    /**
     * Return an active PaymentsClient or initialize
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/client#PaymentsClient|PaymentsClient constructor}
     * @returns {google.payments.api.PaymentsClient} Google Pay API client
     */
    function getGooglePaymentsClient() {
      if ( paymentsClient === null ) {
        paymentsClient = new google.payments.api.PaymentsClient({environment: 'TEST'});
      }
      return paymentsClient;
    }

    /**
     * Initialize Google PaymentsClient after Google-hosted JavaScript has loaded
     *
     * Display a Google Pay payment button after confirmation of the viewer's
     * ability to pay.
     */
    function onGooglePayLoaded() {
      const paymentsClient = getGooglePaymentsClient();
      paymentsClient.isReadyToPay(getGoogleIsReadyToPayRequest())
          .then(function(response) {
            if (response.result) {
              addGooglePayButton();
              // @todo prefetch payment data to improve performance after confirming site functionality
              // prefetchGooglePaymentData();
            }
          })
          .catch(function(err) {
            // show error in developer console for debugging
            console.error(err);
          });
    }

    /**
     * Add a Google Pay purchase button alongside an existing checkout button
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#ButtonOptions|Button options}
     * @see {@link https://developers.google.com/pay/api/web/guides/brand-guidelines|Google Pay brand guidelines}
     */
    function addGooglePayButton() {
      const paymentsClient = getGooglePaymentsClient();
      const button =
          paymentsClient.createButton({onClick: onGooglePaymentButtonClicked});
      document.getElementById('container').appendChild(button);
    }

    /**
     * Provide Google Pay API with a payment amount, currency, and amount status
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo|TransactionInfo}
     * @returns {object} transaction info, suitable for use as transactionInfo property of PaymentDataRequest
     */
    function getGoogleTransactionInfo() {
      return {
        countryCode: 'US',
        currencyCode: 'USD',
        totalPriceStatus: 'FINAL',
        // set to cart total
        totalPrice: '1.00'
      };
    }

    /**
     * Prefetch payment data to improve performance
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/client#prefetchPaymentData|prefetchPaymentData()}
     */
    function prefetchGooglePaymentData() {
      const paymentDataRequest = getGooglePaymentDataRequest();
      // transactionInfo must be set but does not affect cache
      paymentDataRequest.transactionInfo = {
        totalPriceStatus: 'NOT_CURRENTLY_KNOWN',
        currencyCode: 'USD'
      };
      const paymentsClient = getGooglePaymentsClient();
      paymentsClient.prefetchPaymentData(paymentDataRequest);
    }

    /**
     * Show Google Pay payment sheet when Google Pay payment button is clicked
     */
    function onGooglePaymentButtonClicked() {
      const paymentDataRequest = getGooglePaymentDataRequest();
      paymentDataRequest.transactionInfo = getGoogleTransactionInfo();

      const paymentsClient = getGooglePaymentsClient();
      paymentsClient.loadPaymentData(paymentDataRequest)
          .then(function(paymentData) {
            // handle the response
            processPayment(paymentData);
          })
          .catch(function(err) {
            // show error in developer console for debugging
            console.error(err);
          });
    }

    /**
     * Process payment data returned by the Google Pay API
     *
     * @param {object} paymentData response from Google Pay API after user approves payment
     * @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentData|PaymentData object reference}
     */
    function processPayment(paymentData) {
      // show returned data in developer console for debugging
        console.log(paymentData);
      // @todo pass payment token to your gateway to process payment
      paymentToken = paymentData.paymentMethodData.tokenizationData.token;
    }</script>
    <script async
      src="https://pay.google.com/gp/p/js/pay.js"
      onload="onGooglePayLoaded()"></script>
    

授权付款

    <div id="container"></div>
    
    <script>
    /**
     * Define the version of the Google Pay API referenced when creating your
     * configuration
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|apiVersion in PaymentDataRequest}
     */
    const baseRequest = {
      apiVersion: 2,
      apiVersionMinor: 0
    };

    /**
     * Card networks supported by your site and your gateway
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     * @todo confirm card networks supported by your site and gateway
     */
    const allowedCardNetworks = ["AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"];

    /**
     * Card authentication methods supported by your site and your gateway
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     * @todo confirm your processor supports Android device tokens for your
     * supported card networks
     */
    const allowedCardAuthMethods = ["PAN_ONLY", "CRYPTOGRAM_3DS"];

    /**
     * Identify your gateway and your site'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
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#gateway|PaymentMethodTokenizationSpecification}
     */
    const tokenizationSpecification = {
      type: 'PAYMENT_GATEWAY',
      parameters: {
        'gateway': 'example',
        'gatewayMerchantId': 'exampleGatewayMerchantId'
      }
    };

    /**
     * Describe your site's support for the CARD payment method and its required
     * fields
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     */
    const baseCardPaymentMethod = {
      type: 'CARD',
      parameters: {
        allowedAuthMethods: allowedCardAuthMethods,
        allowedCardNetworks: allowedCardNetworks
      }
    };

    /**
     * Describe your site's support for the CARD payment method including optional
     * fields
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     */
    const cardPaymentMethod = Object.assign(
      {},
      baseCardPaymentMethod,
      {
        tokenizationSpecification: tokenizationSpecification
      }
    );

    /**
     * An initialized google.payments.api.PaymentsClient object or null if not yet set
     *
     * @see {@link getGooglePaymentsClient}
     */
    let paymentsClient = null;

    /**
     * Configure your site's support for payment methods supported by the Google Pay
     * API.
     *
     * Each member of allowedPaymentMethods should contain only the required fields,
     * allowing reuse of this base request when determining a viewer's ability
     * to pay and later requesting a supported payment method
     *
     * @returns {object} Google Pay API version, payment methods supported by the site
     */
    function getGoogleIsReadyToPayRequest() {
      return Object.assign(
          {},
          baseRequest,
          {
            allowedPaymentMethods: [baseCardPaymentMethod]
          }
      );
    }

    /**
     * Configure support for the Google Pay API
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|PaymentDataRequest}
     * @returns {object} PaymentDataRequest fields
     */
    function getGooglePaymentDataRequest() {
      const paymentDataRequest = Object.assign({}, baseRequest);
      paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod];
      paymentDataRequest.transactionInfo = getGoogleTransactionInfo();
      paymentDataRequest.merchantInfo = {
        // @todo a merchant ID is available for a production environment after approval by Google
        // See {@link https://developers.google.com/pay/api/web/guides/test-and-deploy/integration-checklist|Integration checklist}
        // merchantId: '01234567890123456789',
        merchantName: 'Example Merchant'
      };

      paymentDataRequest.callbackIntents = ["PAYMENT_AUTHORIZATION"];

      return paymentDataRequest;
    }

    /**
     * Return an active PaymentsClient or initialize
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/client#PaymentsClient|PaymentsClient constructor}
     * @returns {google.payments.api.PaymentsClient} Google Pay API client
     */
    function getGooglePaymentsClient() {
      if ( paymentsClient === null ) {
        paymentsClient = new google.payments.api.PaymentsClient({
        	environment: 'TEST',
          paymentDataCallbacks: {
          	onPaymentAuthorized: onPaymentAuthorized
          }
        });
      }
      return paymentsClient;
    }

    /**
     * Handles authorize payments callback intents.
     *
     * @param {object} paymentData response from Google Pay API after a payer approves payment through user gesture.
     * @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentData object reference}
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentAuthorizationResult}
     * @returns Promise<{object}> Promise of PaymentAuthorizationResult object to acknowledge the payment authorization status.
     */
    function onPaymentAuthorized(paymentData) {
    	return new Promise(function(resolve, reject){
        // handle the response
        processPayment(paymentData)
        .then(function() {
          resolve({transactionState: 'SUCCESS'});
        })
        .catch(function() {
          resolve({
            transactionState: 'ERROR',
            error: {
              intent: 'PAYMENT_AUTHORIZATION',
              message: 'Insufficient funds',
              reason: 'PAYMENT_DATA_INVALID'
            }
          });
      	});
      });
    }

    /**
     * Initialize Google PaymentsClient after Google-hosted JavaScript has loaded
     *
     * Display a Google Pay payment button after confirmation of the viewer's
     * ability to pay.
     */
    function onGooglePayLoaded() {
      const paymentsClient = getGooglePaymentsClient();
      paymentsClient.isReadyToPay(getGoogleIsReadyToPayRequest())
          .then(function(response) {
            if (response.result) {
              addGooglePayButton();
            }
          })
          .catch(function(err) {
            // show error in developer console for debugging
            console.error(err);
          });
    }

    /**
     * Add a Google Pay purchase button alongside an existing checkout button
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#ButtonOptions|Button options}
     * @see {@link https://developers.google.com/pay/api/web/guides/brand-guidelines|Google Pay brand guidelines}
     */
    function addGooglePayButton() {
      const paymentsClient = getGooglePaymentsClient();
      const button =
          paymentsClient.createButton({onClick: onGooglePaymentButtonClicked});
      document.getElementById('container').appendChild(button);
    }

    /**
     * Provide Google Pay API with a payment amount, currency, and amount status
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo|TransactionInfo}
     * @returns {object} transaction info, suitable for use as transactionInfo property of PaymentDataRequest
     */
    function getGoogleTransactionInfo() {
      return {
            displayItems: [
            {
              label: "Subtotal",
              type: "SUBTOTAL",
              price: "11.00",
            },
          {
              label: "Tax",
              type: "TAX",
              price: "1.00",
            }
        ],
        countryCode: 'US',
        currencyCode: "USD",
        totalPriceStatus: "FINAL",
        totalPrice: "12.00",
        totalPriceLabel: "Total"
      };
    }

    /**
     * Show Google Pay payment sheet when Google Pay payment button is clicked
     */
    function onGooglePaymentButtonClicked() {
      const paymentDataRequest = getGooglePaymentDataRequest();
      paymentDataRequest.transactionInfo = getGoogleTransactionInfo();

      const paymentsClient = getGooglePaymentsClient();
      paymentsClient.loadPaymentData(paymentDataRequest);
    }

    /**
     * Process payment data returned by the Google Pay API
     *
     * @param {object} paymentData response from Google Pay API after user approves payment
     * @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentData|PaymentData object reference}
     */
    function processPayment(paymentData) {
    	return new Promise(function(resolve, reject) {
      	setTimeout(function() {
      		// @todo pass payment token to your gateway to process payment
      		paymentToken = paymentData.paymentMethodData.tokenizationData.token;

        	resolve({});
        }, 3000);
      });
    }</script>
    <script async
      src="https://pay.google.com/gp/p/js/pay.js"
      onload="onGooglePayLoaded()"></script>
    

动态价格更新

    <div id="container"></div>
    
    <script>
    /**
     * Define the version of the Google Pay API referenced when creating your
     * configuration
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|apiVersion in PaymentDataRequest}
     */
    const baseRequest = {
      apiVersion: 2,
      apiVersionMinor: 0
    };

    /**
     * Card networks supported by your site and your gateway
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     * @todo confirm card networks supported by your site and gateway
     */
    const allowedCardNetworks = ["AMEX", "DISCOVER", "JCB", "MASTERCARD", "VISA"];

    /**
     * Card authentication methods supported by your site and your gateway
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     * @todo confirm your processor supports Android device tokens for your
     * supported card networks
     */
    const allowedCardAuthMethods = ["PAN_ONLY", "CRYPTOGRAM_3DS"];

    /**
     * Identify your gateway and your site'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
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#gateway|PaymentMethodTokenizationSpecification}
     */
    const tokenizationSpecification = {
      type: 'PAYMENT_GATEWAY',
      parameters: {
        'gateway': 'example',
        'gatewayMerchantId': 'exampleGatewayMerchantId'
      }
    };

    /**
     * Describe your site's support for the CARD payment method and its required
     * fields
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     */
    const baseCardPaymentMethod = {
      type: 'CARD',
      parameters: {
        allowedAuthMethods: allowedCardAuthMethods,
        allowedCardNetworks: allowedCardNetworks
      }
    };

    /**
     * Describe your site's support for the CARD payment method including optional
     * fields
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     */
    const cardPaymentMethod = Object.assign(
      {},
      baseCardPaymentMethod,
      {
        tokenizationSpecification: tokenizationSpecification
      }
    );

    /**
     * An initialized google.payments.api.PaymentsClient object or null if not yet set
     *
     * @see {@link getGooglePaymentsClient}
     */
    let paymentsClient = null;

    /**
     * Configure your site's support for payment methods supported by the Google Pay
     * API.
     *
     * Each member of allowedPaymentMethods should contain only the required fields,
     * allowing reuse of this base request when determining a viewer's ability
     * to pay and later requesting a supported payment method
     *
     * @returns {object} Google Pay API version, payment methods supported by the site
     */
    function getGoogleIsReadyToPayRequest() {
      return Object.assign(
          {},
          baseRequest,
          {
            allowedPaymentMethods: [baseCardPaymentMethod]
          }
      );
    }

    /**
     * Configure support for the Google Pay API
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|PaymentDataRequest}
     * @returns {object} PaymentDataRequest fields
     */
    function getGooglePaymentDataRequest() {
      const paymentDataRequest = Object.assign({}, baseRequest);
      paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod];
      paymentDataRequest.transactionInfo = getGoogleTransactionInfo();
      paymentDataRequest.merchantInfo = {
        // @todo a merchant ID is available for a production environment after approval by Google
        // See {@link https://developers.google.com/pay/api/web/guides/test-and-deploy/integration-checklist|Integration checklist}
        // merchantId: '01234567890123456789',
        merchantName: 'Example Merchant'
      };

      paymentDataRequest.callbackIntents = ["SHIPPING_ADDRESS",  "SHIPPING_OPTION", "PAYMENT_AUTHORIZATION"];
      paymentDataRequest.shippingAddressRequired = true;
      paymentDataRequest.shippingAddressParameters = getGoogleShippingAddressParameters();
      paymentDataRequest.shippingOptionRequired = true;

      return paymentDataRequest;
    }

    /**
     * Return an active PaymentsClient or initialize
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/client#PaymentsClient|PaymentsClient constructor}
     * @returns {google.payments.api.PaymentsClient} Google Pay API client
     */
    function getGooglePaymentsClient() {
      if ( paymentsClient === null ) {
        paymentsClient = new google.payments.api.PaymentsClient({
          environment: "TEST",
          merchantInfo: {
            merchantName: "Example Merchant",
            merchantId: "01234567890123456789"
          },
          paymentDataCallbacks: {
          	onPaymentAuthorized: onPaymentAuthorized,
            onPaymentDataChanged: onPaymentDataChanged
          }
        });
      }
      return paymentsClient;
    }

    function onPaymentAuthorized(paymentData) {
    	return new Promise(function(resolve, reject){

      // handle the response
      processPayment(paymentData)
        .then(function() {
          resolve({transactionState: 'SUCCESS'});
        })
        .catch(function() {
        	resolve({
            transactionState: 'ERROR',
            error: {
              intent: 'PAYMENT_AUTHORIZATION',
              message: 'Insufficient funds',
              reason: 'PAYMENT_DATA_INVALID'
            }
          });
        });

      });
    }

    /**
     * Handles dynamic buy flow shipping address and shipping options callback intents.
     *
     * @param {object} itermediatePaymentData response from Google Pay API a shipping address or shipping option is selected in the payment sheet.
     * @see {@link https://developers.google.com/pay/api/web/reference/response-objects#IntermediatePaymentData|IntermediatePaymentData object reference}
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentDataRequestUpdate|PaymentDataRequestUpdate}
     * @returns Promise<{object}> Promise of PaymentDataRequestUpdate object to update the payment sheet.
     */
    function onPaymentDataChanged(intermediatePaymentData) {
      return new Promise(function(resolve, reject) {

       	let shippingAddress = intermediatePaymentData.shippingAddress;
        let shippingOptionData = intermediatePaymentData.shippingOptionData;
        let paymentDataRequestUpdate = {};

        if (intermediatePaymentData.callbackTrigger == "INITIALIZE" || intermediatePaymentData.callbackTrigger == "SHIPPING_ADDRESS") {
          if(shippingAddress.administrativeArea == "NJ")  {
            paymentDataRequestUpdate.error = getGoogleUnserviceableAddressError();
          }
          else {
            paymentDataRequestUpdate.newShippingOptionParameters = getGoogleDefaultShippingOptions();
            let selectedShippingOptionId = paymentDataRequestUpdate.newShippingOptionParameters.defaultSelectedOptionId;
            paymentDataRequestUpdate.newTransactionInfo = calculateNewTransactionInfo(selectedShippingOptionId);
          }
        }
        else if (intermediatePaymentData.callbackTrigger == "SHIPPING_OPTION") {
          paymentDataRequestUpdate.newTransactionInfo = calculateNewTransactionInfo(shippingOptionData.id);
        }

        resolve(paymentDataRequestUpdate);
      });
    }

    /**
     * Helper function to create a new TransactionInfo object.

     * @param string shippingOptionId respresenting the selected shipping option in the payment sheet.
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo|TransactionInfo}
     * @returns {object} transaction info, suitable for use as transactionInfo property of PaymentDataRequest
     */
    function calculateNewTransactionInfo(shippingOptionId) {
            let newTransactionInfo = getGoogleTransactionInfo();

      let shippingCost = getShippingCosts()[shippingOptionId];
      newTransactionInfo.displayItems.push({
        type: "LINE_ITEM",
        label: "Shipping cost",
        price: shippingCost,
        status: "FINAL"
      });

      let totalPrice = 0.00;
      newTransactionInfo.displayItems.forEach(displayItem => totalPrice += parseFloat(displayItem.price));
      newTransactionInfo.totalPrice = totalPrice.toString();

      return newTransactionInfo;
    }

    /**
     * Initialize Google PaymentsClient after Google-hosted JavaScript has loaded
     *
     * Display a Google Pay payment button after confirmation of the viewer's
     * ability to pay.
     */
    function onGooglePayLoaded() {
      const paymentsClient = getGooglePaymentsClient();
      paymentsClient.isReadyToPay(getGoogleIsReadyToPayRequest())
          .then(function(response) {
            if (response.result) {
              addGooglePayButton();
              // @todo prefetch payment data to improve performance after confirming site functionality
              // prefetchGooglePaymentData();
            }
          })
          .catch(function(err) {
            // show error in developer console for debugging
            console.error(err);
          });
    }

    /**
     * Add a Google Pay purchase button alongside an existing checkout button
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#ButtonOptions|Button options}
     * @see {@link https://developers.google.com/pay/api/web/guides/brand-guidelines|Google Pay brand guidelines}
     */
    function addGooglePayButton() {
      const paymentsClient = getGooglePaymentsClient();
      const button =
          paymentsClient.createButton({onClick: onGooglePaymentButtonClicked});
      document.getElementById('container').appendChild(button);
    }

    /**
     * Provide Google Pay API with a payment amount, currency, and amount status
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo|TransactionInfo}
     * @returns {object} transaction info, suitable for use as transactionInfo property of PaymentDataRequest
     */
    function getGoogleTransactionInfo() {
      return {
            displayItems: [
            {
              label: "Subtotal",
              type: "SUBTOTAL",
              price: "11.00",
            },
          {
              label: "Tax",
              type: "TAX",
              price: "1.00",
            }
        ],
        countryCode: 'US',
        currencyCode: "USD",
        totalPriceStatus: "FINAL",
        totalPrice: "12.00",
        totalPriceLabel: "Total"
      };
    }

    /**
     * Provide a key value store for shippping options.
     */
    function getShippingCosts() {
            return {
        "shipping-001": "0.00",
        "shipping-002": "1.99",
        "shipping-003": "10.00"
      }
    }

    /**
     * Provide Google Pay API with shipping address parameters when using dynamic buy flow.
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#ShippingAddressParameters|ShippingAddressParameters}
     * @returns {object} shipping address details, suitable for use as shippingAddressParameters property of PaymentDataRequest
     */
    function getGoogleShippingAddressParameters() {
            return  {
            allowedCountryCodes: ['US'],
        phoneNumberRequired: true
      };
    }

    /**
     * Provide Google Pay API with shipping options and a default selected shipping option.
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#ShippingOptionParameters|ShippingOptionParameters}
     * @returns {object} shipping option parameters, suitable for use as shippingOptionParameters property of PaymentDataRequest
     */
    function getGoogleDefaultShippingOptions() {
            return {
          defaultSelectedOptionId: "shipping-001",
          shippingOptions: [
            {
              "id": "shipping-001",
              "label": "Free: Standard shipping",
              "description": "Free Shipping delivered in 5 business days."
            },
            {
              "id": "shipping-002",
              "label": "$1.99: Standard shipping",
              "description": "Standard shipping delivered in 3 business days."
            },
            {
              "id": "shipping-003",
              "label": "$10: Express shipping",
              "description": "Express shipping delivered in 1 business day."
            },
          ]
      };
    }

    /**
     * Provide Google Pay API with a payment data error.
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentDataError|PaymentDataError}
     * @returns {object} payment data error, suitable for use as error property of PaymentDataRequestUpdate
     */
    function getGoogleUnserviceableAddressError() {
            return {
        reason: "SHIPPING_ADDRESS_UNSERVICEABLE",
        message: "Cannot ship to the selected address",
        intent: "SHIPPING_ADDRESS"
            };
    }

    /**
     * Prefetch payment data to improve performance
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/client#prefetchPaymentData|prefetchPaymentData()}
     */
    function prefetchGooglePaymentData() {
      const paymentDataRequest = getGooglePaymentDataRequest();
      // transactionInfo must be set but does not affect cache
      paymentDataRequest.transactionInfo = {
        totalPriceStatus: 'NOT_CURRENTLY_KNOWN',
        currencyCode: 'USD'
      };
      const paymentsClient = getGooglePaymentsClient();
      paymentsClient.prefetchPaymentData(paymentDataRequest);
    }

    /**
     * Show Google Pay payment sheet when Google Pay payment button is clicked
     */
    function onGooglePaymentButtonClicked() {
      const paymentDataRequest = getGooglePaymentDataRequest();
      paymentDataRequest.transactionInfo = getGoogleTransactionInfo();

      const paymentsClient = getGooglePaymentsClient();
      paymentsClient.loadPaymentData(paymentDataRequest);
    }

    /**
     * Process payment data returned by the Google Pay API
     *
     * @param {object} paymentData response from Google Pay API after user approves payment
     * @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentData|PaymentData object reference}
     */
    function processPayment(paymentData) {
    	return new Promise(function(resolve, reject) {
      	setTimeout(function() {
        	// show returned data in developer console for debugging
       	 console.log(paymentData);
      		// @todo pass payment token to your gateway to process payment
      		paymentToken = paymentData.paymentMethodData.tokenizationData.token;

        	resolve({});
        }, 3000);
      });
    }</script>
    <script async
      src="https://pay.google.com/gp/p/js/pay.js"
      onload="onGooglePayLoaded()"></script>