This repository was archived by the owner on Apr 29, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathserver.js
More file actions
179 lines (154 loc) · 5.34 KB
/
server.js
File metadata and controls
179 lines (154 loc) · 5.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// Replace if using a different env file or config.
require("dotenv").config({ path: "./.env" });
const express = require("express");
const app = express();
const { resolve } = require("path");
const bodyParser = require("body-parser");
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const client = require("twilio")(
process.env.ACCOUNT_SID,
process.env.AUTH_TOKEN
);
// For demo purposes we're hardcoding the amount and currency here.
// Replace this with your own inventory/cart/order logic.
const purchase = {
amount: 1099,
currency: "USD"
};
const createPurchase = items => {
// Extend this function with your logic to validate
// the purchase details server-side and prevent
// manipulation of price details on the client.
return purchase;
};
app.use(express.static(process.env.STATIC_DIR));
// Use JSON parser for all non-webhook routes.
app.use((req, res, next) => {
if (req.originalUrl === "/webhook") {
next();
} else {
bodyParser.json()(req, res, next);
}
});
app.get("/config", (req, res) => {
res.send({
publishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
purchase
});
});
app.get("/", (req, res) => {
const path = resolve(process.env.STATIC_DIR + "/index.html");
res.sendFile(path);
});
app.post("/create-customer", async (req, res) => {
const { phone, email } = req.body;
try {
// Validate the phone number input
const number = await client.lookups.phoneNumbers(phone).fetch();
// Create a new customer object
const customer = await stripe.customers.create({
phone: number.phoneNumber,
email
});
// Create a CheckoutSession to set up our payment methods recurring usage
const checkoutSession = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
mode: "setup",
customer: customer.id,
success_url: `${req.headers.origin}?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${req.headers.origin}/`
});
res.send({ customer, checkoutSession });
} catch (error) {
res.status(400).send({ error });
}
});
app.get("/checkout-session/:id", async (req, res) => {
const { id } = req.params;
const checkoutSession = await stripe.checkout.sessions.retrieve(id, {
expand: ["customer", "setup_intent.payment_method"]
});
res.send({ checkoutSession });
});
app.post("/start-twilio-verify", async (req, res) => {
const { customerId } = req.body;
// Retrieve the customer object
const customer = await stripe.customers.retrieve(customerId);
// Start Twilio verify
const verification = await client.verify
.services(process.env.VERIFY_SERVICE_SID)
.verifications.create({ to: customer.phone, channel: "sms" });
// Send the PaymentIntent client_secret to the client.
const status = verification.status;
res.send({ status });
});
app.post("/check-twilio-verify", async (req, res) => {
const { customerId, code, items } = req.body;
try {
// Retrieve the customer object
const customer = await stripe.customers.retrieve(customerId);
// Check Twilio verify code
const verificationCheck = await client.verify
.services(process.env.VERIFY_SERVICE_SID)
.verificationChecks.create({ to: customer.phone, code });
// If successful, create the payment with the stored card
if (verificationCheck.status === "approved") {
// Get the stored payment method
const paymentMethods = await stripe.paymentMethods.list({
customer: customerId,
type: "card"
});
if (paymentMethods.data.length !== 1) {
throw new Error("Too few or too many methods on customer!");
}
// Charge the stored method
const { amount, currency } = createPurchase(items);
const paymentIntent = await stripe.paymentIntents.create({
amount,
currency,
customer: customerId,
payment_method: paymentMethods.data[0].id,
off_session: false, // Customer is on-session during checkout
confirm: true // Confirm and charge payment immediately
});
// Return the payment result
res.send({ paymentIntent });
} else {
res
.status(400)
.send({ error: { message: "Incorrect code. Please try again!" } });
}
} catch (error) {
res.status(400).send({ error });
}
});
// Stripe requires the raw body to construct the event.
app.post(
"/webhook",
bodyParser.raw({ type: "application/json" }),
(req, res) => {
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
req.headers["stripe-signature"],
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (error) {
// On error, log and return the error message
console.log(`❌ Error message: ${error.message}`);
return res.status(400).send(`Webhook Error: ${error.message}`);
}
if (event.type === "payment_intent.succeeded") {
// Funds have been captured
// Fulfill any orders, e-mail receipts, etc
// To cancel the payment after capture you will need to issue a Refund (https://stripe.com/docs/api/refunds)
console.log("💰 Payment captured!");
} else if (event.type === "payment_intent.payment_failed") {
console.log("❌ Payment failed.");
}
// Return a response to acknowledge receipt of the event
res.json({ received: true });
}
);
app.listen(4242, () => console.log(`Node server listening on port ${4242}!`));