Skip to content

Commit 532e509

Browse files
dmengeltjamesor
andauthoredNov 13, 2024··
IDX web templates (#317)
chore: Basic and advanced Google Pay button web examples for use in Project IDX. --------- Co-authored-by: james <[email protected]>
1 parent 0cd56f2 commit 532e509

14 files changed

+924
-1
lines changed
 

‎.vscode/settings.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99
"editor.formatOnSave": true,
1010
"editor.codeActionsOnSave": {
1111
"source.fixAll.eslint": true
12-
}
12+
},
13+
"angular.enable-strict-mode-prompt": false
1314
}

‎README.md

+27
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,33 @@ See Google Pay in action:
99

1010
This repository contains Google Pay button implementations for compatible with popular website frameworks even easier.
1111

12+
## Web
13+
14+
This is a bare bones, plain vanilla JavaScript implementation of the Google Pay button. These examples are designed to
15+
launch into a Project IDX Workspace, ready to run.
16+
17+
- [Example code basic](./examples/html/gpay-web-101/)<br>
18+
<a href="https://idx.google.com/new?template=https%3A%2F%2Fgithub.com%2Fgoogle-pay%2Fgoogle-pay-button%2Ftree%2Fmain%2Fexamples%2Fhtml%2Fgpay-web-101">
19+
<picture> <source
20+
media="(prefers-color-scheme: dark)"
21+
srcset="https://cdn.idx.dev/btn/open_dark_32.svg"> <source
22+
media="(prefers-color-scheme: light)"
23+
srcset="https://cdn.idx.dev/btn/open_light_32.svg"> <img
24+
height="24"
25+
alt="Open in IDX"
26+
src="https://cdn.idx.dev/btn/open_purple_32.svg"> </picture> </a>
27+
28+
- [Example code advanced](./examples/html/gpay-web-201/)<br>
29+
<a href="https://idx.google.com/new?template=https%3A%2F%2Fgithub.com%2Fgoogle-pay%2Fgoogle-pay-button%2Ftree%2Fmain%2Fexamples%2Fhtml%2Fgpay-web-201">
30+
<picture> <source
31+
media="(prefers-color-scheme: dark)"
32+
srcset="https://cdn.idx.dev/btn/open_dark_32.svg"> <source
33+
media="(prefers-color-scheme: light)"
34+
srcset="https://cdn.idx.dev/btn/open_light_32.svg"> <img
35+
height="24"
36+
alt="Open in IDX"
37+
src="https://cdn.idx.dev/btn/open_purple_32.svg"> </picture> </a>
38+
1239
## Web component
1340

1441
[![npm version](https://badge.fury.io/js/%40google-pay%2Fbutton-element.svg)][npm-element]

‎examples/html/gpay-web-101/README.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Google Pay for Web
2+
3+
<!--- Open in IDX button -->
4+
<a href="https://idx.google.com/new?template=https%3A%2F%2Fgithub.com%2Fgoogle-pay%2Fgoogle-pay-button%2Ftree%2Fmain%2Fexamples%2Fhtml%2Fgpay-web-101">
5+
<picture>
6+
<source
7+
media="(prefers-color-scheme: dark)"
8+
srcset="https://cdn.idx.dev/btn/open_dark_32.svg">
9+
<source
10+
media="(prefers-color-scheme: light)"
11+
srcset="https://cdn.idx.dev/btn/open_light_32.svg">
12+
<img
13+
height="32"
14+
alt="Open in IDX"
15+
src="https://cdn.idx.dev/btn/open_purple_32.svg">
16+
</picture>
17+
</a>
18+
19+
## About
20+
21+
This project is a minimum viable integration of Google Pay for Web using HTML and JavaScript. For a more complete
22+
integration, refer to [`gpay-web-201/`][17] project template.
23+
24+
## Learning
25+
26+
To learn about the code in this project template, follow to the [Google Pay API for Web 101: Basic][10] codelab.
27+
Learning paths help you get the most of your integration by taking you through a guided developer journey from start to
28+
finish.
29+
30+
## Create a merchant account
31+
32+
While a merchant ID isn't required for testing your integration in a `TEST` enviornment, you will need one when you
33+
deploy to a `PRODUCTION` environment. Register with the [Google Pay & Wallet Console][15] to receive your merchant ID.
34+
It's quick and easy! Get yours now.
35+
36+
## Community
37+
38+
- Join the conversation in the [#payments channel on Discord][12]
39+
- Follow [@GooglePayDevs on X][13] for announcements about more content like this
40+
- Subscribe to the [Google for Developers][14] YouTube channel
41+
42+
## Support
43+
44+
- Question about this template? Ask in the [discussions][16] section of the Google Pay button repo.
45+
- For assistance with your implementation, create a support ticket from the [Google Pay & Wallet Console][15].
46+
47+
[10]: https://codelabs.developers.google.com/codelabs/gpay-web-101
48+
[12]: https://goo.gle/payments-dev-community
49+
[13]: https://x.com/GooglePayDevs
50+
[14]: https://goo.gle/developers
51+
[15]: https://goo.gle/3Cg8KxJ
52+
[16]: https://github.com/google-pay/google-pay-button/discussions
53+
[17]: https://github.com/google-pay/google-pay-button/tree/main/examples/html/gpay-web-201/

‎examples/html/gpay-web-101/dev.nix

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Copyright 2024 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
# To learn more about how to use Nix to configure your environment
18+
# see: https://developers.google.com/idx/guides/customize-idx-env
19+
{ pkgs, ... }: {
20+
# Which nixpkgs channel to use.
21+
channel = "stable-24.05"; # or "unstable"
22+
23+
# Use https://search.nixos.org/packages to find packages
24+
packages = [
25+
pkgs.python311
26+
];
27+
28+
# Sets environment variables in the workspace
29+
env = {};
30+
idx = {
31+
workspace = {
32+
onCreate = {
33+
default.openFiles = [
34+
"README.md"
35+
"index.html"
36+
];
37+
};
38+
};
39+
40+
previews = {
41+
enable = true;
42+
previews = {
43+
web = {
44+
command = ["python3" "-m" "http.server" "$PORT" "--bind" "0.0.0.0"];
45+
manager = "web";
46+
};
47+
};
48+
};
49+
};
50+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!DOCTYPE html>
2+
<!--
3+
Copyright 2024 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<html lang="en">
18+
<head>
19+
<meta charset="UTF-8" />
20+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
21+
<title>Google Pay API for Web</title>
22+
</head>
23+
24+
<body>
25+
<div id="gpay-container"></div>
26+
<p>Transaction info and errors will be logged to the console.</p>
27+
<script type="text/javascript" src="main.js"></script>
28+
<script async src="https://pay.google.com/gp/p/js/pay.js" onload="onGooglePayLoaded()"></script>
29+
</body>
30+
</html>
+220
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
//=============================================================================
18+
// Configuration
19+
//=============================================================================
20+
21+
// The DOM element that the Google Pay button will be rendered into
22+
const GPAY_BUTTON_CONTAINER_ID = 'gpay-container';
23+
24+
// Update the `merchantId` and `merchantName` properties with your own values.
25+
// These fields are optional when the environment is `TEST`.
26+
// Get your merchant Id at https://goo.gle/3Cg8KxJ
27+
const merchantInfo = {
28+
merchantId: '12345678901234567890',
29+
merchantName: 'Example Merchant',
30+
};
31+
32+
/**
33+
* This is the base configuration for all Google Pay requests. This
34+
* configuration will be cloned, modified, and used for all Google Pay requests.
35+
*
36+
* @see {@link https://developers.google.com/pay/api/web/guides/test-and-deploy/integration-checklist}
37+
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects}
38+
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#gateway}
39+
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#MerchantInfo}
40+
*/
41+
const baseGooglePayRequest = {
42+
apiVersion: 2,
43+
apiVersionMinor: 0,
44+
allowedPaymentMethods: [
45+
{
46+
type: 'CARD',
47+
parameters: {
48+
allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
49+
allowedCardNetworks: ['AMEX', 'DISCOVER', 'INTERAC', 'JCB', 'MASTERCARD', 'VISA'],
50+
},
51+
tokenizationSpecification: {
52+
type: 'PAYMENT_GATEWAY',
53+
parameters: {
54+
gateway: 'example',
55+
gatewayMerchantId: 'exampleGatewayMerchantId',
56+
},
57+
},
58+
},
59+
],
60+
merchantInfo,
61+
};
62+
63+
// Prevent accidental edits to the base configuration. Mutations will be
64+
// handled by cloning the config using deepCopy() and modifying the copy.
65+
Object.freeze(baseGooglePayRequest);
66+
67+
//=============================================================================
68+
// Google payments client singleton
69+
//=============================================================================
70+
71+
/**
72+
* A variable to store the Google Payments Client instance.
73+
* Initialized to null to indicate it hasn't been created yet.
74+
*/
75+
let paymentsClient = null;
76+
77+
/**
78+
* Gets an instance of the Google Payments Client.
79+
*
80+
* This function ensures that only one instance of the Google Payments Client
81+
* is created and reused throughout the application. It lazily initializes
82+
* the client if it hasn't been created yet.
83+
*
84+
* @see {@link https://developers.google.com/pay/api/web/reference/client#PaymentsClient}
85+
* @return {google.payments.api.PaymentsClient} Google Payments Client instance.
86+
*/
87+
function getGooglePaymentsClient() {
88+
// Check if the paymentsClient has already been initialized.
89+
if (paymentsClient === null) {
90+
// If not, create a new instance of the Google Payments Client.
91+
paymentsClient = new google.payments.api.PaymentsClient({
92+
// Set the environment for the client ('TEST' or 'PRODUCTION').
93+
// `TEST` is default.
94+
environment: 'TEST',
95+
// Add the merchant information (optional)
96+
merchantInfo,
97+
});
98+
}
99+
100+
return paymentsClient;
101+
}
102+
103+
//=============================================================================
104+
// Helpers
105+
//=============================================================================
106+
107+
/**
108+
* Creates a deep copy of an object.
109+
*
110+
* This function uses JSON serialization and deserialization to create a deep
111+
* copy of the provided object. It's a convenient way to clone objects without
112+
* worrying about shared references.
113+
*
114+
* @param {Object} obj - The object to be copied.
115+
* @returns {Object} A deep copy of the original object.
116+
*/
117+
const deepCopy = obj => JSON.parse(JSON.stringify(obj));
118+
119+
/**
120+
* Renders the Google Pay button to the DOM.
121+
*
122+
* This function creates a Google Pay button using the Google Pay API and adds
123+
* it to the container element specified by `GPAY_BUTTON_CONTAINER_ID`.
124+
* When clicked, button triggers the `onGooglePaymentButtonClicked` handler.
125+
*
126+
* @see {@link https://developers.google.com/pay/api/web/reference/client#createButton}
127+
* @returns {void}
128+
*/
129+
function renderGooglePayButton() {
130+
// Create a Google Pay button using the PaymentsClient.
131+
const button = getGooglePaymentsClient().createButton({
132+
// Set the click handler for the button to the onGooglePaymentButtonClicked
133+
onClick: onGooglePaymentButtonClicked,
134+
});
135+
// Add the Google Pay button to the container element on the page.
136+
document.getElementById(GPAY_BUTTON_CONTAINER_ID).appendChild(button);
137+
}
138+
139+
//=============================================================================
140+
// Event Handlers
141+
//=============================================================================
142+
143+
/**
144+
* Google Pay API loaded handler
145+
*
146+
* This function will be called by the script tag in index.html when the pay.js
147+
* script has finished loading. Once the script is loaded, it will first check
148+
* to see if the consumer is ready to pay with Google Pay. If they are ready,
149+
* the next thing it does is add the Google Pay button to the page. Otherwise,
150+
* it logs an error to the console.
151+
*
152+
* @see {@link https://developers.google.com/pay/api/web/reference/client#isReadyToPay}
153+
* @returns {void}
154+
*/
155+
function onGooglePayLoaded() {
156+
// Create a deep copy of the base Google Pay request object.
157+
// This ensures that any modifications made to the request object
158+
// do not affect the original base request.
159+
const req = deepCopy(baseGooglePayRequest);
160+
161+
// Get an instance of the Google Payments Client.
162+
getGooglePaymentsClient()
163+
// Check if the user is ready to pay with Google Pay.
164+
.isReadyToPay(req)
165+
// Handle the response from the isReadyToPay() method.
166+
.then(function (res) {
167+
// If the user is ready to pay with Google Pay...
168+
if (res.result) {
169+
// Render the Google Pay button to the page.
170+
renderGooglePayButton();
171+
} else {
172+
// If the user is not ready to pay with Google Pay, log
173+
// an error to the console.
174+
console.log('Google Pay is not ready for this user.');
175+
}
176+
})
177+
// Handle any errors that occur during the process.
178+
.catch(console.error);
179+
}
180+
181+
/**
182+
* Google Pay button click handler
183+
*
184+
* @see {@link https://developers.google.com/pay/api/web/reference/client#loadPaymentData}
185+
* @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentMethodTokenizationData}
186+
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo}
187+
* @returns {void}
188+
*/
189+
function onGooglePaymentButtonClicked() {
190+
// Create a new request data object for this request
191+
const req = {
192+
...deepCopy(baseGooglePayRequest),
193+
transactionInfo: {
194+
countryCode: 'US',
195+
currencyCode: 'USD',
196+
totalPriceStatus: 'FINAL',
197+
totalPrice: (Math.random() * 999 + 1).toFixed(2),
198+
},
199+
};
200+
201+
// Write the data to console for debugging
202+
console.log('onGooglePaymentButtonClicked', req);
203+
204+
// Get an instance of the Google Payments Client.
205+
getGooglePaymentsClient()
206+
// Load the payment data in console for the transaction.
207+
.loadPaymentData(req)
208+
// If the payment is successful, process the payment
209+
.then(function (res) {
210+
// show returned data for debugging
211+
console.log(res);
212+
// @todo pass payment token to your gateway to process payment
213+
// @note DO NOT save the payment credentials for future transactions,
214+
// unless they're used for merchant-initiated transactions with user
215+
// consent in place.
216+
paymentToken = res.paymentMethodData.tokenizationData.token;
217+
})
218+
// If there is an error, log it to the console.
219+
.catch(console.error);
220+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "Google Pay API for Web",
3+
"description": "Minimum viable web integration of the Google Pay API on Web.",
4+
"categories": ["Web app", "Misc"],
5+
"icon": "https://www.gstatic.com/images/branding/productlogos/google_pay_round/v6/192px.svg",
6+
"publisher": "Google Pay DevRel",
7+
"website": "https://codelabs.developers.google.com/codelabs/gpay-web-101"
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Copyright 2024 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
{ pkgs, ... }: {
18+
bootstrap = ''
19+
mkdir "$out"
20+
mkdir -p "$out/.idx/"
21+
cp -rf ${./dev.nix} "$out/.idx/dev.nix"
22+
cp -f ${./README.md} "$out/README.md"
23+
shopt -s dotglob; cp -r ${./dev}/* "$out"
24+
chmod -R +w "$out"
25+
'';
26+
}

‎examples/html/gpay-web-201/README.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Google Pay for Web
2+
3+
<!--- Open in IDX button -->
4+
<a href="https://idx.google.com/new?template=https%3A%2F%2Fgithub.com%2Fgoogle-pay%2Fgoogle-pay-button%2Ftree%2Fmain%2Fexamples%2Fhtml%2Fgpay-web-201">
5+
<picture>
6+
<source
7+
media="(prefers-color-scheme: dark)"
8+
srcset="https://cdn.idx.dev/btn/open_dark_32.svg">
9+
<source
10+
media="(prefers-color-scheme: light)"
11+
srcset="https://cdn.idx.dev/btn/open_light_32.svg">
12+
<img
13+
height="32"
14+
alt="Open in IDX"
15+
src="https://cdn.idx.dev/btn/open_purple_32.svg">
16+
</picture>
17+
</a>
18+
19+
## About
20+
21+
This project is a more complete integration of Google Pay API for Web using HTML and JavaScript. For a more bare bones
22+
integration, refer to [`gpay-web-101/`][17] project template.
23+
24+
## Learning
25+
26+
To learn about the code in this project template, follow to the [Google Pay API for Web 201: Advance][10] codelab.
27+
Learning paths help you get the most of your integration by taking you through a guided developer journey from start to
28+
finish.
29+
30+
## Create a merchant account
31+
32+
While a merchant ID isn't required for testing your integration in a `TEST` enviornment, you will need one when you
33+
deploy to a `PRODUCTION` environment. Register with the [Google Pay & Wallet Console][15] to receive your merchant ID.
34+
It's quick and easy! Get yours now.
35+
36+
## Community
37+
38+
- Join the conversation in the [#payments channel on Discord][12]
39+
- Follow [@GooglePayDevs on X][13] for announcements about more content like this
40+
- Subscribe to the [Google for Developers][14] YouTube channel
41+
42+
## Support
43+
44+
- Question about this template? Ask in the [discussions][16] section of the Google Pay button repo.
45+
- For assistance with your implementation, create a support ticket from the [Google Pay & Wallet Console][15].
46+
47+
[10]: https://codelabs.developers.google.com/codelabs/gpay-web-201
48+
[12]: https://goo.gle/payments-dev-community
49+
[13]: https://x.com/GooglePayDevs
50+
[14]: https://goo.gle/developers
51+
[15]: https://goo.gle/3Cmv497
52+
[16]: https://github.com/google-pay/google-pay-button/discussions
53+
[17]: https://github.com/google-pay/google-pay-button/tree/main/examples/html/gpay-web-101/

‎examples/html/gpay-web-201/dev.nix

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Copyright 2024 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
# To learn more about how to use Nix to configure your environment
18+
# see: https://developers.google.com/idx/guides/customize-idx-env
19+
{ pkgs, ... }: {
20+
# Which nixpkgs channel to use.
21+
channel = "stable-24.05"; # or "unstable"
22+
23+
# Use https://search.nixos.org/packages to find packages
24+
packages = [
25+
pkgs.python311
26+
];
27+
28+
# Sets environment variables in the workspace
29+
env = {};
30+
idx = {
31+
workspace = {
32+
onCreate = {
33+
default.openFiles = [
34+
"README.md"
35+
"index.html"
36+
];
37+
};
38+
};
39+
40+
previews = {
41+
enable = true;
42+
previews = {
43+
web = {
44+
command = ["python3" "-m" "http.server" "$PORT" "--bind" "0.0.0.0"];
45+
manager = "web";
46+
};
47+
};
48+
};
49+
};
50+
}
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!DOCTYPE html>
2+
<!--
3+
Copyright 2024 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
18+
<html lang="en">
19+
<head>
20+
<meta charset="UTF-8" />
21+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
22+
<title>Google Pay API for Web</title>
23+
</head>
24+
25+
<body>
26+
<div id="gpay-container"></div>
27+
<p>Transaction info and errors will be logged to the console.</p>
28+
<script type="text/javascript" src="main.js"></script>
29+
<script async src="https://pay.google.com/gp/p/js/pay.js" onload="onGooglePayLoaded()"></script>
30+
</body>
31+
</html>
+340
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
//=============================================================================
18+
// Configuration
19+
//=============================================================================
20+
21+
// The DOM element that the Google Pay button will be rendered into
22+
const GPAY_BUTTON_CONTAINER_ID = 'gpay-container';
23+
24+
// Update the `merchantId` and `merchantName` properties with your own values.
25+
// These fields are optional when the environment is `TEST`.
26+
// Get your merchant Id at https://goo.gle/3Cmv497
27+
const merchantInfo = {
28+
merchantId: '12345678901234567890',
29+
merchantName: 'Example Merchant',
30+
};
31+
32+
/**
33+
* This is the base configuration for all Google Pay requests. This
34+
* configuration will be cloned, modified, and used for all Google Pay requests.
35+
*
36+
* @see {@link https://developers.google.com/pay/api/web/guides/test-and-deploy/integration-checklist}
37+
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects}
38+
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#gateway}
39+
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#MerchantInfo}
40+
*/
41+
const baseGooglePayRequest = {
42+
apiVersion: 2,
43+
apiVersionMinor: 0,
44+
allowedPaymentMethods: [
45+
{
46+
type: 'CARD',
47+
parameters: {
48+
allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
49+
allowedCardNetworks: ['AMEX', 'DISCOVER', 'INTERAC', 'JCB', 'MASTERCARD', 'VISA'],
50+
},
51+
tokenizationSpecification: {
52+
type: 'PAYMENT_GATEWAY',
53+
parameters: {
54+
gateway: 'example',
55+
gatewayMerchantId: 'exampleGatewayMerchantId',
56+
},
57+
},
58+
},
59+
],
60+
merchantInfo,
61+
};
62+
63+
// Prevent accidental edits to the base configuration. Mutations will be
64+
// handled by cloning the config using deepCopy() and modifying the copy.
65+
Object.freeze(baseGooglePayRequest);
66+
67+
//=============================================================================
68+
// Google payments client singleton
69+
//=============================================================================
70+
71+
/**
72+
* A variable to store the Google Payments Client instance.
73+
* Initialized to null to indicate it hasn't been created yet.
74+
*/
75+
let paymentsClient = null;
76+
77+
/**
78+
* Gets an instance of the Google Payments Client.
79+
*
80+
* This function ensures that only one instance of the Google Payments Client
81+
* is created and reused throughout the application. It lazily initializes
82+
* the client if it hasn't been created yet.
83+
*
84+
* @see {@link https://developers.google.com/pay/api/web/reference/client#PaymentsClient}
85+
* @return {google.payments.api.PaymentsClient} Google Payments Client instance.
86+
*/
87+
function getGooglePaymentsClient() {
88+
// Check if the paymentsClient has already been initialized.
89+
if (paymentsClient === null) {
90+
// If not, create a new instance of the Google Payments Client.
91+
paymentsClient = new google.payments.api.PaymentsClient({
92+
// Set the environment for the client ('TEST' or 'PRODUCTION').
93+
// `TEST` is default.
94+
environment: 'TEST',
95+
// Add the merchant information (optional)
96+
merchantInfo,
97+
paymentDataCallbacks: {
98+
onPaymentAuthorized: onPaymentAuthorized,
99+
onPaymentDataChanged: onPaymentDataChanged,
100+
},
101+
});
102+
}
103+
104+
return paymentsClient;
105+
}
106+
107+
//=============================================================================
108+
// Helpers
109+
//=============================================================================
110+
111+
/**
112+
* Creates a deep copy of an object.
113+
*
114+
* This function uses JSON serialization and deserialization to create a deep
115+
* copy of the provided object. It's a convenient way to clone objects without
116+
* worrying about shared references.
117+
*
118+
* @param {Object} obj - The object to be copied.
119+
* @returns {Object} A deep copy of the original object.
120+
*/
121+
const deepCopy = obj => JSON.parse(JSON.stringify(obj));
122+
123+
/**
124+
* Renders the Google Pay button to the DOM.
125+
*
126+
* This function creates a Google Pay button using the Google Pay API and adds
127+
* it to the container element specified by `GPAY_BUTTON_CONTAINER_ID`.
128+
* When clicked, button triggers the `onGooglePaymentButtonClicked` handler.
129+
*
130+
* @see {@link https://developers.google.com/pay/api/web/reference/client#createButton}
131+
* @returns {void}
132+
*/
133+
function renderGooglePayButton() {
134+
// Create a Google Pay button using the PaymentsClient.
135+
const button = getGooglePaymentsClient().createButton({
136+
// Set the click handler for the button to the onGooglePaymentButtonClicked
137+
onClick: onGooglePaymentButtonClicked,
138+
// Set the allowed payment methods for the button.
139+
allowedPaymentMethods: baseGooglePayRequest.allowedPaymentMethods,
140+
});
141+
// Add the Google Pay button to the container element on the page.
142+
document.getElementById(GPAY_BUTTON_CONTAINER_ID).appendChild(button);
143+
}
144+
145+
/**
146+
* Prefetches Google Pay payment data to improve the payment flow performance.
147+
*
148+
* This function creates a payment data request object and uses the Google Pay client
149+
* to prefetch payment data. This can help speed up the payment process when the user
150+
* actually initiates a transaction. The prefetched data is cached and can be used
151+
* for subsequent payment requests.
152+
*
153+
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo}
154+
* @returns {void}
155+
*/
156+
function prefetchGooglePaymentData() {
157+
// Create a deep copy of the base Google Pay request object.
158+
// This ensures that the original request object is not modified.
159+
const req = deepCopy(baseGooglePayRequest);
160+
161+
// Set the transactionInfo property on the request object.
162+
// This is required for prefetching, even though the values
163+
// are not used for caching.
164+
req.transactionInfo = {
165+
totalPriceStatus: 'NOT_CURRENTLY_KNOWN',
166+
currencyCode: 'USD',
167+
};
168+
169+
// Get an instance of the Google Pay client and use it to
170+
// prefetch the payment data.
171+
getGooglePaymentsClient().prefetchPaymentData(req);
172+
}
173+
174+
//=============================================================================
175+
// Event Handlers
176+
//=============================================================================
177+
178+
/**
179+
* Google Pay API loaded handler
180+
*
181+
* This function will be called by the script tag in index.html when the pay.js
182+
* script has finished loading. Once the script is loaded, it will first check
183+
* to see if the consumer is ready to pay with Google Pay. If they are ready,
184+
* the next thing it does is add the Google Pay button to the page. Otherwise,
185+
* it logs an error to the console.
186+
*
187+
* @see {@link https://developers.google.com/pay/api/web/reference/client#isReadyToPay}
188+
* @returns {void}
189+
*/
190+
function onGooglePayLoaded() {
191+
// Create a deep copy of the base Google Pay request object.
192+
// This ensures that any modifications made to the request object
193+
// do not affect the original base request.
194+
const req = deepCopy(baseGooglePayRequest);
195+
196+
// Get an instance of the Google Payments Client.
197+
getGooglePaymentsClient()
198+
// Check if the user is ready to pay with Google Pay.
199+
.isReadyToPay(req)
200+
// Handle the response from the isReadyToPay() method.
201+
.then(function (res) {
202+
// If the user is ready to pay with Google Pay...
203+
if (res.result) {
204+
// Render the Google Pay button to the page.
205+
renderGooglePayButton();
206+
// Prefetch the payment data to improve performance.
207+
prefetchGooglePaymentData();
208+
} else {
209+
// If the user is not ready to pay with Google Pay, log
210+
// an error to the console.
211+
console.log('Google Pay is not ready for this user.');
212+
}
213+
})
214+
// Handle any errors that occur during the process.
215+
.catch(console.error);
216+
}
217+
218+
/**
219+
* Google Pay button click handler
220+
*
221+
* @see {@link https://developers.google.com/pay/api/web/reference/client#loadPaymentData}
222+
* @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentMethodTokenizationData}
223+
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo}
224+
* @returns {void}
225+
*/
226+
function onGooglePaymentButtonClicked() {
227+
// Create a new request data object for this request
228+
const req = {
229+
...deepCopy(baseGooglePayRequest),
230+
transactionInfo: {
231+
countryCode: 'US',
232+
currencyCode: 'USD',
233+
totalPriceStatus: 'FINAL',
234+
totalPrice: (Math.random() * 999 + 1).toFixed(2),
235+
},
236+
callbackIntents: ['PAYMENT_AUTHORIZATION', 'SHIPPING_ADDRESS', 'SHIPPING_OPTION', 'OFFER'],
237+
shippingAddressRequired: true,
238+
shippingOptionRequired: true,
239+
shippingOptionParameters: {
240+
defaultSelectedOptionId: 'shipping-001',
241+
shippingOptions: [
242+
{
243+
id: 'shipping-001',
244+
label: '$0.00: Free shipping',
245+
description: 'Free Shipping delivered in 5 business days.',
246+
},
247+
{
248+
id: 'shipping-002',
249+
label: '$1.99: Standard shipping',
250+
description: 'Standard shipping delivered in 3 business days.',
251+
},
252+
{
253+
id: 'shipping-003',
254+
label: '$1000: Express shipping',
255+
description: 'Express shipping delivered in 1 business day.',
256+
},
257+
],
258+
},
259+
};
260+
261+
// Write the data to console for debugging
262+
console.log('onGooglePaymentButtonClicked', req);
263+
264+
// Get an instance of the Google Payments Client.
265+
getGooglePaymentsClient()
266+
// Load the payment data in console for the transaction.
267+
.loadPaymentData(req)
268+
// If the payment is successful, process the payment
269+
.then(function (res) {
270+
// show returned data for debugging
271+
console.log(res);
272+
// @todo pass payment token to your gateway to process payment
273+
// @note DO NOT save the payment credentials for future transactions,
274+
// unless they're used for merchant-initiated transactions with user
275+
// consent in place.
276+
paymentToken = res.paymentMethodData.tokenizationData.token;
277+
})
278+
// If there is an error, log it to the console.
279+
.catch(console.error);
280+
}
281+
282+
function onPaymentAuthorized(paymentData) {
283+
return new Promise(function (resolve, reject) {
284+
// Write the data to console for debugging
285+
console.log('onPaymentAuthorized', paymentData);
286+
287+
// Do something here to pass token to your gateway
288+
289+
// To simulate the payment processing, there is a 70% chance of success
290+
const paymentAuthorizationResult =
291+
Math.random() > 0.3
292+
? { transactionState: 'SUCCESS' }
293+
: {
294+
transactionState: 'ERROR',
295+
error: {
296+
intent: 'PAYMENT_AUTHORIZATION',
297+
message: 'Insufficient funds',
298+
reason: 'PAYMENT_DATA_INVALID',
299+
},
300+
};
301+
302+
resolve(paymentAuthorizationResult);
303+
});
304+
}
305+
306+
function onPaymentDataChanged(intermediatePaymentData) {
307+
return new Promise(function (resolve, reject) {
308+
let paymentDataRequestUpdate = {};
309+
310+
// Write the data to console for debugging
311+
console.log('onPaymentDataChanged', intermediatePaymentData);
312+
313+
switch (intermediatePaymentData.callbackTrigger) {
314+
case 'INITIALIZE':
315+
// Handle initialize
316+
break;
317+
case 'SHIPPING_ADDRESS':
318+
// Read intermediatePaymentData.transactionInfo
319+
// Read intermediatePaymentData.shippingAddress
320+
// Update paymentDataRequestUpdate.newTransactionInfo
321+
break;
322+
case 'SHIPPING_OPTION':
323+
// Read intermediatePaymentData.transactionInfo
324+
// Read intermediatePaymentData.shippingOptionData
325+
// Update paymentDataRequestUpdate.newTransactionInfo
326+
// Update paymentDataRequestUpdate.newShippingOptionParameters
327+
break;
328+
case 'OFFER':
329+
// Read intermediatePaymentData.offerData
330+
// Read intermediatePaymentData.transactionInfo
331+
// Update paymentDataRequestUpdate.newTransactionInfo
332+
// Update paymentDataRequestUpdate.newOfferInfo
333+
break;
334+
default:
335+
// Update paymentDataRequestUpdate.error
336+
}
337+
338+
resolve(paymentDataRequestUpdate);
339+
});
340+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "Google Pay API for Web complete",
3+
"description": "A more complete integration of the Google Pay API on Web.",
4+
"categories": ["Web app", "Misc"],
5+
"icon": "https://www.gstatic.com/images/branding/productlogos/google_pay_round/v6/192px.svg",
6+
"publisher": "Google Pay DevRel",
7+
"website": "https://codelabs.developers.google.com/codelabs/gpay-web-201"
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Copyright 2024 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
{ pkgs, ... }: {
18+
bootstrap = ''
19+
mkdir "$out"
20+
mkdir -p "$out/.idx/"
21+
cp -rf ${./dev.nix} "$out/.idx/dev.nix"
22+
cp -f ${./README.md} "$out/README.md"
23+
shopt -s dotglob; cp -r ${./dev}/* "$out"
24+
chmod -R +w "$out"
25+
'';
26+
}

0 commit comments

Comments
 (0)
Please sign in to comment.