Skip to content

Commit d581019

Browse files
committed
Use ejs to render data-user-id-token
1 parent 849759c commit d581019

File tree

4 files changed

+102
-112
lines changed

4 files changed

+102
-112
lines changed

save-payment-method/client/app.js

Lines changed: 81 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,94 @@
1-
const main = async () => {
2-
const user = localStorage.getItem("user");
3-
const response = await fetch(`/api/id-token?user=${user}`);
4-
const data = await response.json();
5-
const script = document.createElement("script");
6-
script.src = "https://www.paypal.com/sdk/js?client-id=test";
7-
script.setAttribute("data-user-id-token", data.id_token);
8-
script.addEventListener("load", () => renderButtons());
9-
document.head.appendChild(script);
10-
};
1+
window.paypal
2+
.Buttons({
3+
async createOrder() {
4+
try {
5+
const response = await fetch("/api/orders", {
6+
method: "POST",
7+
headers: {
8+
"Content-Type": "application/json",
9+
},
10+
// use the "body" param to optionally pass additional order information
11+
// like product ids and quantities
12+
body: JSON.stringify({
13+
cart: [
14+
{
15+
id: "YOUR_PRODUCT_ID",
16+
quantity: "YOUR_PRODUCT_QUANTITY",
17+
},
18+
],
19+
}),
20+
});
1121

12-
main();
22+
const orderData = await response.json();
1323

14-
const renderButtons = () =>
15-
window.paypal
16-
.Buttons({
17-
async createOrder() {
18-
try {
19-
const response = await fetch("/api/orders", {
20-
method: "POST",
21-
headers: {
22-
"Content-Type": "application/json",
23-
},
24-
// use the "body" param to optionally pass additional order information
25-
// like product ids and quantities
26-
body: JSON.stringify({
27-
cart: [
28-
{
29-
id: "YOUR_PRODUCT_ID",
30-
quantity: "YOUR_PRODUCT_QUANTITY",
31-
},
32-
],
33-
}),
34-
});
35-
36-
const orderData = await response.json();
37-
38-
if (orderData.id) {
39-
return orderData.id;
40-
} else {
41-
const errorDetail = orderData?.details?.[0];
42-
const errorMessage = errorDetail
43-
? `${errorDetail.issue} ${errorDetail.description} (${orderData.debug_id})`
44-
: JSON.stringify(orderData);
24+
if (orderData.id) {
25+
return orderData.id;
26+
} else {
27+
const errorDetail = orderData?.details?.[0];
28+
const errorMessage = errorDetail
29+
? `${errorDetail.issue} ${errorDetail.description} (${orderData.debug_id})`
30+
: JSON.stringify(orderData);
4531

46-
throw new Error(errorMessage);
47-
}
48-
} catch (error) {
49-
console.error(error);
50-
resultMessage(
51-
`Could not initiate PayPal Checkout...<br><br>${error}`,
52-
);
32+
throw new Error(errorMessage);
5333
}
54-
},
55-
async onApprove(data, actions) {
56-
try {
57-
const response = await fetch(`/api/orders/${data.orderID}/capture`, {
58-
method: "POST",
59-
headers: {
60-
"Content-Type": "application/json",
61-
},
62-
});
34+
} catch (error) {
35+
console.error(error);
36+
resultMessage(`Could not initiate PayPal Checkout...<br><br>${error}`);
37+
}
38+
},
39+
async onApprove(data, actions) {
40+
try {
41+
const response = await fetch(`/api/orders/${data.orderID}/capture`, {
42+
method: "POST",
43+
headers: {
44+
"Content-Type": "application/json",
45+
},
46+
});
6347

64-
const orderData = await response.json();
65-
// Three cases to handle:
66-
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
67-
// (2) Other non-recoverable errors -> Show a failure message
68-
// (3) Successful transaction -> Show confirmation or thank you message
48+
const orderData = await response.json();
49+
// Three cases to handle:
50+
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
51+
// (2) Other non-recoverable errors -> Show a failure message
52+
// (3) Successful transaction -> Show confirmation or thank you message
6953

70-
const errorDetail = orderData?.details?.[0];
71-
72-
if (errorDetail?.issue === "INSTRUMENT_DECLINED") {
73-
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
74-
// recoverable state, per https://developer.paypal.com/docs/checkout/standard/customize/handle-funding-failures/
75-
return actions.restart();
76-
} else if (errorDetail) {
77-
// (2) Other non-recoverable errors -> Show a failure message
78-
throw new Error(
79-
`${errorDetail.description} (${orderData.debug_id})`,
80-
);
81-
} else if (!orderData.purchase_units) {
82-
throw new Error(JSON.stringify(orderData));
83-
} else {
84-
// (3) Successful transaction -> Show confirmation or thank you message
85-
// Or go to another URL: actions.redirect('thank_you.html');
86-
const transaction =
87-
orderData?.purchase_units?.[0]?.payments?.captures?.[0] ||
88-
orderData?.purchase_units?.[0]?.payments?.authorizations?.[0];
89-
resultMessage(
90-
`Transaction ${transaction.status}: ${transaction.id}<br><br>See console for all available details`,
91-
);
92-
93-
localStorage.setItem(
94-
"user",
95-
orderData.payment_source.paypal.attributes.vault.customer.id,
96-
);
54+
const errorDetail = orderData?.details?.[0];
9755

98-
console.log(
99-
"Capture result",
100-
orderData,
101-
JSON.stringify(orderData, null, 2),
102-
);
103-
}
104-
} catch (error) {
105-
console.error(error);
56+
if (errorDetail?.issue === "INSTRUMENT_DECLINED") {
57+
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
58+
// recoverable state, per https://developer.paypal.com/docs/checkout/standard/customize/handle-funding-failures/
59+
return actions.restart();
60+
} else if (errorDetail) {
61+
// (2) Other non-recoverable errors -> Show a failure message
62+
throw new Error(`${errorDetail.description} (${orderData.debug_id})`);
63+
} else if (!orderData.purchase_units) {
64+
throw new Error(JSON.stringify(orderData));
65+
} else {
66+
// (3) Successful transaction -> Show confirmation or thank you message
67+
// Or go to another URL: actions.redirect('thank_you.html');
68+
const transaction =
69+
orderData?.purchase_units?.[0]?.payments?.captures?.[0] ||
70+
orderData?.purchase_units?.[0]?.payments?.authorizations?.[0];
10671
resultMessage(
107-
`Sorry, your transaction could not be processed...<br><br>${error}`,
72+
`Transaction ${transaction.status}: ${transaction.id}<br><br>See console for all available details.<br>
73+
<a href='/?${orderData.payment_source.paypal.attributes.vault.customer.id}'>See the return buyer experience</a>
74+
`,
75+
);
76+
77+
console.log(
78+
"Capture result",
79+
orderData,
80+
JSON.stringify(orderData, null, 2),
10881
);
10982
}
110-
},
111-
})
112-
.render("#paypal-button-container");
83+
} catch (error) {
84+
console.error(error);
85+
resultMessage(
86+
`Sorry, your transaction could not be processed...<br><br>${error}`,
87+
);
88+
}
89+
},
90+
})
91+
.render("#paypal-button-container");
11392

11493
// Example function to show a result to the user. Your site's UI library can be used instead.
11594
function resultMessage(message) {

save-payment-method/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"license": "Apache-2.0",
1515
"dependencies": {
1616
"dotenv": "^16.3.1",
17+
"ejs": "^3.1.9",
1718
"express": "^4.18.2",
1819
"node-fetch": "^3.3.2"
1920
},

save-payment-method/server/server.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import express from "express";
22
import fetch from "node-fetch";
33
import "dotenv/config";
4-
import path from "path";
54

65
const { PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PORT = 8888 } = process.env;
76
const base = "https://api-m.sandbox.paypal.com";
87
const app = express();
98

9+
app.set("view engine", "ejs");
10+
app.set("views", "./server/views");
11+
1012
// host static files
1113
app.use(express.static("client"));
1214

@@ -143,11 +145,6 @@ async function handleResponse(response) {
143145
}
144146
}
145147

146-
app.get("/api/id-token", async (req, res) => {
147-
const { jsonResponse } = await authenticate(req.query.user);
148-
res.json(jsonResponse);
149-
});
150-
151148
app.post("/api/orders", async (req, res) => {
152149
try {
153150
// use the cart information passed from the front-end to calculate the order amount detals
@@ -164,16 +161,25 @@ app.post("/api/orders/:orderID/capture", async (req, res) => {
164161
try {
165162
const { orderID } = req.params;
166163
const { jsonResponse, httpStatusCode } = await captureOrder(orderID);
164+
console.log("capture response", jsonResponse);
167165
res.status(httpStatusCode).json(jsonResponse);
168166
} catch (error) {
169167
console.error("Failed to create order:", error);
170168
res.status(500).json({ error: "Failed to capture order." });
171169
}
172170
});
173171

174-
// serve index.html
175-
app.get("/", (req, res) => {
176-
res.sendFile(path.resolve("./client/checkout.html"));
172+
// render checkout page with client id & user id token
173+
app.get("/", async (req, res) => {
174+
try {
175+
const { jsonResponse } = await authenticate(req.query.customerID);
176+
res.render("checkout", {
177+
clientId: PAYPAL_CLIENT_ID,
178+
userIdToken: jsonResponse.id_token,
179+
});
180+
} catch (err) {
181+
res.status(500).send(err.message);
182+
}
177183
});
178184

179185
app.listen(PORT, () => {

save-payment-method/client/checkout.html renamed to save-payment-method/server/views/checkout.ejs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
<head>
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6-
<title>PayPal JS SDK Standard Integration</title>
6+
<title>PayPal JS SDK Save Payment Method Integration</title>
77
</head>
88
<body>
99
<div id="paypal-button-container"></div>
1010
<p id="result-message"></p>
11+
<script
12+
src="https://www.paypal.com/sdk/js?client-id=<%= clientId %>"
13+
data-user-id-token="<%= userIdToken %>"
14+
></script>
1115
<script src="app.js"></script>
1216
</body>
1317
</html>

0 commit comments

Comments
 (0)