Customization with Google Pay

The Google Pay direct API integration will let you offer the payment method button to the customer for authorization. However, you can extend your code to customize the Google Pay payment sheet for additional operations. For example, the customer need not register in the shop for express checkout and guest checkout options. Instead, they can fulfil their purchase using the data stored on their Google accounts. Add Google Pay to your item or product pages, and enable quick checkout for your customers. This reduces the number of checkout steps and increases conversion rates on single-item purchases.

When you offer Google Pay to your cart or product page, you can offer the customer the following improvements:

All these customizations are facilitated by using the order object in your payment intent request.

Further, you can set up Google Pay buttons based on your business needs, desired button types, colours and display requirements. The button customizations are arranged by using the button object in your payment intent request.

Setup the business name

If you want to setup your merchant name or business name to be rendered in the payment sheet, you can use the order.merchantName property. Use the same business name people will see when they look for the charge on their bank or credit card statement. This provides reassurance that payment is going to the right place. For this, set up the transaction.dynamic_descriptor parameter during the booking when using the /payment API (or /authorize API).

Suppose the order.merchantName property is missing in the payment intent, Novalnet, by default, will show the domain name in which the Google Pay processing takes place, Eg:- for this page, it is developer.novalnet.de.

Display the line items

You can set the order.lineItems property to show a detailed summary of the order line items in your payment intent request. As per the Google API, you can categorize the line items on the types "SUBTOTAL", "LINE_ITEM", and "TAX", so it makes sense to put all purchased items into one "subtotal" item and adds additional items for shipping, tax, discounts, etc., as needed.

Provide the total separately, in the transaction.amount property. Line items aren't required in the API request but can't be empty if they're present.

JAVASCRIPT
{
  lineItems: [
    {
      label: "Bag Subtotal",
      type: "LINE_ITEM",
      amount: 1500
    },
    {
      label: "Free Shipping",
      type: "LINE_ITEM",
      amount: 0
    },
    {
      label: "Estimated Tax",
      type: "TAX",
      amount: 300      
    }
  ]
}

Collect the billing data of the customer

If you specify the order.billing.requiredFields property in your payment intent request, Google will prompt the user to enter or choose their billing address in the payment sheet. Depending upon your customer account creation, you can customize the requiredFields property and require it only if it is essential. The entered billing data will then be available on the authorization response order.billing.contact property passed to the onProcessCompletion callback.

JAVASCRIPT
billing: {
	requiredFields: ["postalAddress", "phone", "email"]
}

Process the shipping-related information

Unlike billing contact, gathering shipping information may trigger a few changes in the payment request and handling. For instance, the customer, after entering the shipping address in the payment sheet, the information needs to be validated if you could cover the delivery to those regions or could enforce a tax change which could change the total altogether. Owing to these requirements, processing the shipping information also might require including additional callbacks onShippingContactChange and onShippingMethodChange.

Collect the shipping data of the customer

In the same way, like gathering the billing information, you can also require shipping related data in your payment intent request. Google will prompt the user to enter or choose their shipping address in the payment sheet. The entered shipping data will then be available to you on the authorization response order.shipping.contact passed to the onProcessCompletion callback.

JAVASCRIPT
shipping: {
	requiredFields: ["postalAddress", "phone"]
}

The registered callback onShippingContactChange in the payment intent will be called when the customer selects a shipping contact in the payment sheet. The callback allows you to dynamically update shipping options and transaction information based on the selected shipping address.

The callback function should have two arguments, the first to receive the entered shipping contact and the second to return the updated transaction information to the payment sheet. It is optional to correct the transaction information, depending on your shipping handling.

Please note that if you are going to offer the shipping options only based on the callback onShippingContactChange instead of setting in the payment intent request, then you have to pre-notify the API by setting order.shipping.methodsUpdatedLater property with value true.

JAVASCRIPT
callbacks: {
    "onShippingContactChange": function(shippingContact, newShippingContactResult) {
    
		let transactionInfoToUpdate = {};
		
		// There could be a situation where the shipping methods differ based on region
		if (shippingContact.countryCode == "DE") {		
			transactionInfoToUpdate.methods = [{
				identifier: "dhlshipping",
				amount: 500,
				detail: "The product will be delivered depends on the executive",
				label: "DHL Shipping"
			}, {
				identifier: "freeshipping",
				amount: 0,
				detail: "Free shipping within Deutschland",				
				label: "Free Shipping"
			}];
		} else {
			transactionInfoToUpdate.methods = [{
				identifier: "expressshipping",
				amount: 750,
				detail: "The product will be dispatched in the same day",				
				label: "Express Shipping"
			}];
		}
		
		// Recalculating the total gross based on the chosen shipping method
		transactionInfoToUpdate.defaultIdentifier = transactionInfoToUpdate.methods[0].identifier;
		transactionInfoToUpdate.amount = transactionInfoToUpdate.methods[0].amount + transactionInformation.amount;		
		transactionInfoToUpdate.lineItems = [{
			  label: "Bag Subtotal",
			  type: "LINE_ITEM",
			  amount: 1500
			},
			{
			  label: transactionInfoToUpdate.methods[0].label,
			  type: "LINE_ITEM",
			  amount: transactionInfoToUpdate.methods[0].amount			 			  
			},
			{
			  label: "Estimated Tax",
              type: "TAX",
              amount: 300
			}
		];
		
		newShippingContactResult(transactionInfoToUpdate);
	}				  							
}

Setup the shipping methods offered

You can let your users choose/change by offering various shipping options in the Google Pay payment sheet. Make use of the order.shipping.methods property to pass on the shipping options as shown in the sample code below. Besides offering the shipping options to the customer to choose from, you can also set the default shipping option by using the optional order.shipping.defaultIdentifier property.

JAVASCRIPT
{
  defaultIdentifier: "dhlshipping",
  methods: [
    {
      identifier: "freeshipping",
	  amount: 0,
	  detail: "Free shipping within Deutschland",				
	  label: "Free Shipping"
	},
	{
	  identifier: "dhlshipping",
	  amount: 500,
	  detail: "The product will be delivered depends on the executive",
	  label: "DHL Shipping"
	}
  ]
}

The registered callback onShippingMethodChange in the payment intent will be called when the customer changes a shipping method in the payment sheet. The callback allows you to update transaction information based on the chosen shipping method dynamically.

The callback function should have two arguments, the first to receive the chosen shipping method and the second to return the updated transaction information to the payment sheet. It is optional to correct the transaction information, depending on your shipping handling.

The chosen shipping method will then be available to you on the authorization response order.shipping.method passed to the onProcessCompletion callback.

Customize the payment button

Styling your payment button involves changing the button texts based on the customer locale to set up the colour, type, and dimensions. As a best practice, follow Google's brand guidelines to customize the payment button accordingly.

Payment sheet customization request and response sample

The entire customization request with the order and button property of the payment intent request is listed below. Beneath that, the example will contain a complete customization request for Google Pay.

Each parameter is marked with attributes Mandatory, Conditional , Optional . Based on your necessity, you can use these parameters accordingly.

JAVASCRIPT
// Setting up the Payment Intent for authentication and payment button processing						
var merchantInformation = {
	countryCode: "DE",
	partnerId: "BCR2DN4T4DTN7FSI"
};

var transactionInformation = {
	amount: 1850,
	currency: "EUR",	
	paymentMethod: "GOOGLEPAY",
	environment: "SANDBOX"
};

var orderInformation = {
    merchantName: "Test Development for Google Pay",    
    lineItems: [{
        label: "Bag Subtotal",
        type: "LINE_ITEM",
        amount: 1500
      },
      {
        label: "Free Shipping",
        type: "SUBTOTAL",
        amount: 0
      },
      {
        label: "Estimated Tax",
        type: "TAX",
        amount: 350      
      }
    ],
    billing: {
    	requiredFields: ["postalAddress", "phone", "email"]
    },
    shipping: {
    	requiredFields: ["postalAddress", "phone"],    	
		methods: [
          {
            identifier: "freeshipping",
	        amount: 0,
	        detail: "Free shipping within Deutschland",				
	        label: "Free Shipping"
	      },
	      {
	        identifier: "dhlshipping",
	        amount: 500,
	        detail: "The product will be delivered depends on the executive",
	        label: "DHL Shipping"
	      }
        ],
		defaultIdentifier: "dhlshipping",	
		methodsUpdatedLater: true
    }
};

var buttonInformation = {
	type: "plain",
	style: "black",
	locale: "en-US",
	boxSizing: "static",
	dimensions: {
		height: 45,
		width: 200
	}
};

var paymentIntent = {
	clientKey: ###YOUR_CLIENT_KEY###,
	paymentIntent: {
		merchant: merchantInformation,
		transaction: transactionInformation,
		order: orderInformation,
		button: buttonInformation,
		callbacks: {
			"onProcessCompletion": function(payLoad, bookingResult) {

				// Handle response here and setup the bookingresult
				if (payLoad.result && payLoad.result.status) {

					// Only on success, we proceed further with the booking   
					if (payLoad.result.status == 'SUCCESS') {

						// Sending the token and amount to Novalnet server for the booking
						$.post('send_server_request.php', {token: payLoad.transaction.token, amount: payLoad.transaction.amount}).done(function(processedResult) {
							
							var parsedResult = $.parseJSON(processedResult);
							
							// "SUCCESS" or "FAILURE" 							
							let result = {};
							result.status = payLoad.result.status;
							result.statusText = payLoad.result.status_text;
							bookingResult(result);							
							
						});

					} else {

						// Upon failure, displaying the error text 
						if (payLoad.result.status_text) {
							alert(payLoad.result.status_text);
						}
					}
				}
			},
			"onShippingContactChange": function(shippingContact, newShippingContactResult) {
    
	         	let transactionInfoToUpdate = {};
	         	
	         	// There could be a situation where the shipping methods differ based on region
	         	if (shippingContact.countryCode == "DE" || shippingContact.countryCode == "US") {		
	         		transactionInfoToUpdate.methods = [{
	         			identifier: "dhlshipping",
	         			amount: 500,
	         			detail: "The product will be delivered depends on the executive",
	         			label: "DHL Shipping"
	         		}, {
	         			identifier: "freeshipping",
	         			amount: 0,
	         			detail: "Free shipping within Deutschland",
	         			label: "Free Shipping"
	         		}];
	         	} else {
	         		transactionInfoToUpdate.methods = [{
	         			identifier: "expressshipping",
	         			amount: 750,
	         			detail: "The product will be dispatched in the same day",				
	         			label: "Express Shipping"
	         		}];
	         	}
	         	
	         	// Recalculating the total gross based on the chosen shipping method
	         	transactionInfoToUpdate.amount = transactionInfoToUpdate.methods[0].amount + transactionInformation.amount;		
	         	transactionInfoToUpdate.lineItems = [{
	         		  label: "Bag Subtotal",
	         		  type: "LINE_ITEM",
	         		  amount: 1500
	         		},
	         		{
	         		  label: transactionInfoToUpdate.methods[0].label,
	         		  type: "SUBTOTAL",
	         		  amount: transactionInfoToUpdate.methods[0].amount			 			  
	         		},
	         		{
	         		  label: "Estimated Tax",
                      type: "TAX",
                      amount: 300
	         		}
	         	];
	         	
	         	newShippingContactResult(transactionInfoToUpdate);
	         },
	         "onShippingMethodChange": function(shippingMethod, newShippingMethodResult) {
                  
                 // There could be a situation where the shipping method can alter total  
	         	let transactionInfoToUpdate = {};
	         	
	         	// Recalculating the total gross based on the chosen shipping method
	         	transactionInfoToUpdate.amount = parseInt(shippingMethod.amount) + transactionInformation.amount;		
	         	transactionInfoToUpdate.lineItems = [{
	         		  label: "Bag Subtotal",
	         		  type: "LINE_ITEM",
	         		  amount: 1500
	         		},
	         		{
	         		  label: shippingMethod.label,
	         		  type: "SUBTOTAL",
	         		  amount: parseInt(shippingMethod.amount)			 			  
	         		},
	         		{
	         		  label: "Estimated Tax",
                      type: "TAX",
                      amount: 300
	         		}
	         	];
	         	
	         	newShippingMethodResult(transactionInfoToUpdate);
	         }	                       		
		}
	}
};

try {
	// Loading the payment instances
	var NovalnetPaymentInstance = NovalnetPayment();
	var googlepayNovalnetPaymentObj = NovalnetPaymentInstance.createPaymentObject();

    // Setting up the payment intent in your object 
	googlepayNovalnetPaymentObj.setPaymentIntent(paymentIntent);

	// Checking for the payment method availability
	googlepayNovalnetPaymentObj.isPaymentMethodAvailable(function(displayGooglePayButton) {
		if (displayGooglePayButton) {
			// Initiating the Payment Request for the Wallet Payment
			googlepayNovalnetPaymentObj.addPaymentButton("#googlepay-container");
		}
	});

} catch (e) {
	// Handling the errors from the payment intent setup
	console.log(e.message);
}

Demonstration

The following demonstrates a basic example of the Google Pay button in action.

Transaction Result
Transaction Result
// Waiting for transaction data...

Loading...