Google Pay Integration (Flutter)

This section explains how to integrate Google Pay using the Novalnet Flutter SDK.

Prerequisites

Before integrating Google Pay, ensure the following:

  • Flutter SDK 3.0 or later
  • Dart SDK 3.0 or later
  • Google Pay & Wallet Console account created
  • Merchant profile approved
  • Android app registered (Package Name + SHA-256)
  • Google Pay API enabled for production
  • Supported card added to the device

Step 1: Configure Google Pay on Merchant Side (Google Pay Console)

The merchant must configure the following in their Google Pay Console:

  • Merchant ID – Required for production (from Google Pay Console)
  • Merchant Name – Displayed in payment sheet
  • Package Name – Your Android app ID
  • SHA-256 – Your app signing certificate

Step 2: Install Dependency

Add the stable version of PAY plugin under dependencies in pubspec.yaml file.

pubspec.yaml
dependencies:        # level 0					
  pay: ^3.2.0        # +2 spaces

Run flutter pub get to fetch and install the dependencies.

Note: Whenever you make changes or updates to the dependencies, run the flutter pub get command to apply and fetch the updated packages

Indentation Notes :
dependencies: must be at the root level (no spaces before it)
pay: must be indented with 2 spaces under dependencies
YAML is space-sensitive, so incorrect indentation will cause errors

Step 3:Import Packages

After adding the dependency, import the Packages in your Google Pay integration file.

import_package
import 'package:pay/pay.dart';
import 'package:flutter/material.dart';   //Provides Material Design components

Step 4: Google Pay Configuration

This configuration defines how Google Pay should process the payment request in your Flutter app. It includes supported payment methods, gateway details, transaction amount, currency, and merchant information shown to the user. The JSON is passed to the pay plugin to initialize and display the Google Pay sheet. Make sure to replace all placeholder values with your actual merchant and transaction details before use.

GooglePay Configuration
static String _googlePayConfig() {
    return '''
{
  "provider": "google_pay",
  "data": {
    "environment": "ENVIRONMENT",
    "apiVersion": 2,
    "apiVersionMinor": 0,

    "allowedPaymentMethods": [
      {
        "type": "CARD",
        "parameters": {
          "allowedAuthMethods": ["PAN_ONLY", "CRYPTOGRAM_3DS"],
          "allowedCardNetworks": ["VISA", "MASTERCARD"]
        },
        "tokenizationSpecification": {
          "type": "PAYMENT_GATEWAY",
          "parameters": {
            "gateway": "GATEWAY",
            "gatewayMerchantId": "	"
          }
        }
      }
    ],

    "transactionInfo": {
      "totalPriceStatus": "FINAL",
      "totalPrice": "TOTAL AMOUNT",
      "currencyCode": "CURRENCY"
    },

    "merchantInfo": {
      "merchantName": "MERCHANT_NAME"
    }
  }
''';
  }
  }
Field Placeholder / Value Description
environment ENVIRONMENT Defines the mode (TEST or PRODUCTION)
type CARD (fixed) Payment method type (predefined, do not change)
allowedAuthMethods ["PAN_ONLY","CRYPTOGRAM_3DS"] (fixed) Supported authentication methods (predefined)
allowedCardNetworks ["VISA","MASTERCARD"] (configurable) Supported card networks
tokenizationSpecification.type PAYMENT_GATEWAY (fixed) Defines how payment data is processed (via gateway)
gateway GATEWAY Payment gateway provider name
gatewayMerchantId YOUR_GATEWAY_MERCHANT_ID Merchant ID provided by the gateway
totalPriceStatus FINAL (fixed) Indicates final transaction amount (do not change)
totalPrice TOTAL_AMOUNT Payment amount (2 decimals, use dot)
currencyCode CURRENCY ISO currency code (e.g., EUR, USD, INR)
merchantName MERCHANT_NAME Name shown in Google Pay payment sheet

Step 5: Environment Setup

Environment Usage
Test No real money, used during development
Production Real payments
  • Use test during development
  • Switch to production only after: Merchant Configuration done in Google Pay Console.

Step 6 : Check Google Pay Availability

This step checks whether the device is fully ready to make a Google Pay payment before starting the payment flow. It initializes the payment configuration and uses the userCanPay method to verify support. If canPay returns false, it means Google Pay cannot be used on the device. This typically happens if Google Pay not set up, no supported card is added, or the device/region does not support Google Pay. It can also occur if there is a configuration issue (e.g., incorrect environment or merchant setup).

GooglePay Availability
final config = PaymentConfiguration.fromJsonString(_googlePayConfig());
      final payClient = Pay({PayProvider.google_pay: config});
      final canPay = await payClient.userCanPay(PayProvider.google_pay);
      
      If canPay returns false, Google Pay is not supported on the device
      eg: 
      if (!canPay) {
        return {
          "status": "FAILURE",
          "message": "Google pay not available",
        };
      }

Step 7: Set payment items, Show Google Pay Button & Get Payment Data

This step defines the payment amount and displays the Google Pay button to the user. When the user completes the payment, the response is returned via onPaymentResult and stored in resultData.

GooglePay Button
final items = [
        PaymentItem(
          label: "Total",
          amount: AMOUNT,	// The amount must be provided as a string in standard decimal format with two digits (e.g., "82.00", not 8200)
          status: PaymentItemStatus.final_price,
        ),
    ];

      Map<String, dynamic>? resultData;

      await showModalBottomSheet(
        context: context,
        isScrollControlled: true,
        builder: (_) {
          return Padding(
            padding: const EdgeInsets.only(bottom: 80),
            child: SizedBox(
              height: 120,
              child: Center(
                child: GooglePayButton(
                  paymentConfiguration: config,
                  paymentItems: items,
                  type: GooglePayButtonType.pay,

                  onPaymentResult: (result) async {
                    resultData = result;
                    Navigator.pop(context);
                  },

                  onError: (e) {
                    Navigator.pop(context);
                  },

                  loadingIndicator: const CircularProgressIndicator(),
                ),
              ),
            ),
          );
        },
      );

Result Data contains the payment response, including the signature, payment token, and encrypted payment data. Once received successfully, this data must be sent to Novalnet for processing and transaction booking.

Step 8: Process Payment with Novalnet SDK

After receiving the payment response from Google Pay, the token must be sent to Novalnet for decryption and transaction processing.

  1. Import the Novalnet SDK in your file: : import 'package:novalnetsdk/novalnetsdk.dart';
  2. Call the SDK function with the payment token: :
novalnet_sdk
var result = await NovalnetSDK.openWalletPaymentHandler(
        context,
        paymentType: "GOOGLEPAY",
        token: resultData ?? {},
      );

The SDK will handle token decryption and complete the transaction processing.

Step 9: Important Notes

  • Google Pay production requires proper merchant configuration
  • Release build must be used for production testing
  • Google Pay is supported only on Android devices
  • Ensure correct merchant ID is used

Reference Implementation (Full Payment Flow)

This example provides a complete end-to-end implementation of Google Pay integration using the Flutter pay plugin. It includes configuration setup, availability check, payment UI handling, and capturing the payment response. Developers can use this as a ready-to-use reference to integrate Google Pay quickly. The returned resultData should be sent to Novalnet for final payment processing.

googlepay_handler.dart
import 'package:flutter/material.dart';
import 'package:pay/pay.dart';
import 'package:novalnetsdk/novalnetsdk.dart';

class GooglePayHandler {
  static Future<Map<String, dynamic>?> startPayment(
    BuildContext context,
  ) async {
    try {
      // Create config
      final config = PaymentConfiguration.fromJsonString(_googlePayConfig());
      // Create Pay client
      final payClient = Pay({PayProvider.google_pay: config});

      // Check availability
      final canPay = await payClient.userCanPay(PayProvider.google_pay);

      if (!canPay) {
        return {"status": "FAILURE", "message": "Google pay not available"};
      }

      // Payment items
      final items = [
        PaymentItem(
          label: "Total",
          amount: AMOUNT,     //The amount must be provided as a string in standard decimal format with two digits (e.g., "82.00" not 8200)
          status: PaymentItemStatus.final_price,
        ),
      ];

      Map<String, dynamic>? resultData;

      // Open UI
      await showModalBottomSheet(
        context: context,
        isScrollControlled: true,
        builder: (_) {
          return Padding(
            padding: const EdgeInsets.only(bottom: 80),
            child: SizedBox(
              height: 120,
              child: Center(
                child: GooglePayButton(
                  paymentConfiguration: config,
                  paymentItems: items,
                  type: GooglePayButtonType.pay,

                  onPaymentResult: (result) async {
                    resultData = result;
                    Navigator.pop(context);
                  },

                  onError: (e) {
                    Navigator.pop(context);
                  },

                  loadingIndicator: const CircularProgressIndicator(),
                ),
              ),
            ),
          );
        },
      );
      
      if (resultData == null || resultData?.isEmpty == true) {
        return {
          "status": "FAILURE",
          "message": "Payment cancelled due to Result data is empty",
        };
      }
      
      var result = await NovalnetSDK.openWalletPaymentHandler(
        context,
        paymentType: "GOOGLEPAY",
        token: resultData ?? {},
      );
      
      return result;
    } catch (e) {
      return {"status": "FAILURE", "message": "Google Pay failed $e"};
    }
  }

  // CONFIG
  static String _googlePayConfig() {
    return '''
{
  "provider": "google_pay",
  "data": {
    "environment": "ENVIRONMENT",
    "apiVersion": 2,
    "apiVersionMinor": 0,

    "allowedPaymentMethods": [
      {
        "type": "CARD",
        "parameters": {
          "allowedAuthMethods": ["PAN_ONLY", "CRYPTOGRAM_3DS"],
          "allowedCardNetworks": ["VISA", "MASTERCARD"]
        },
        "tokenizationSpecification": {
          "type": "PAYMENT_GATEWAY",
          "parameters": {
            "gateway": "GATEWAY",
            "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
          }
        }
      }
    ],

    "transactionInfo": {
      "totalPriceStatus": "FINAL",
      "totalPrice": "TOTAL AMOUNT",
      "currencyCode": "CURRENCY"
    },

    "merchantInfo": {
      "merchantName": "MERCHANT_NAME"
    }
  }
}
''';
  }
}

The following example demonstrates how to initiate Google Pay when the button is pressed. This is a basic example, and you can handle the response and flow based on your application requirements:

Dart File
import 'googlepay_handler.dart';

onPressed: () async {
  var result = await GooglePayHandler.startPayment(context);
  if (result == null) return;
}