Example of how to use Stripe.js and Node.JS libraries.
index.html
- Client side workflow (JS)index.js
- Server side workflow (Node.JS)
Note: Examples are a very rough POC, so please excuse the rough style.
<!doctype html> | |
<html> | |
<head> | |
<title>This is the title of the webpage!</title> | |
<script src="https://js.stripe.com/v3/"></script> | |
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
</head> | |
<body> | |
<p>This is an example paragraph. Anything in the <strong>body</strong> tag will appear on the page, just like this | |
<strong>p</strong> tag and its contents.</p> | |
<div> | |
<button onclick="submitPaymentIntent()"> | |
Submit Payment Intent | |
</button> | |
</div> | |
<form id="payment-form" onsubmit="submitPayment(event)" class="payment-form"> | |
<div id="card-element"> | |
<!-- Elements will create input elements here --> | |
</div> | |
<!-- We'll put the error messages in this element --> | |
<div id="card-errors" role="alert"></div> | |
<button id="submit">Pay</button> | |
</form> | |
<script> | |
var stripe = Stripe('pk_test_51HJpxrGuRfJc1w8oW76xWsJTkrH3m4ZTzBHdFcnl9lV7s6s4JISnkm8mIozF8DROH5n0bTyosxSbSOYQNCVhkRsf008rpptWBx'); | |
var elements = stripe.elements(); | |
var style = { | |
base: { | |
color: "#32325d", | |
} | |
}; | |
// a global | |
var card = elements.create("card", { style: style }); | |
card.mount("#card-element"); | |
var clientSecret = null; | |
function submitPaymentIntent() { | |
console.log("submitPaymentIntent() clicked") | |
const customerInfo = { | |
first_name: "first", | |
last_name: "last", | |
email: "", | |
address: { | |
// required | |
line1: "123 Main St", | |
line2: "Apt 2", | |
city: "Nowhere", | |
state: "AK", | |
postal_code: "90299", | |
// Two-letter country code (ISO 3166-1 alpha-2). | |
country: "us", | |
}, | |
phone: "123-234-2345" | |
}; | |
const products = [ | |
1, 2, 3 | |
]; | |
const body = JSON.stringify({ | |
customerInfo, | |
products, | |
}) | |
console.log(`Start fetch POST /payments/provider/stripe/create-payment-intent | Body: ${body}`) | |
fetch('/payments/provider/stripe/create-payment-intent', { | |
method: 'POST', | |
cache: 'no-cache', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body, | |
}).then(function (response) { | |
console.log('End POST /payments/provider/stripe/create-payment-intent') | |
return response.json(); | |
}).then(function (responseJson) { | |
clientSecret = responseJson.client_secret; | |
// Call stripe.confirmCardPayment() with the client secret. | |
console.log(`clientSecret returned: ${clientSecret}`) | |
}); | |
} | |
function submitPayment(e) { | |
e.preventDefault() | |
console.log("Start submitPayment()") | |
// CF: TO FIGURE OUT | |
// Do the customer/payment_intent here???? ---->> ClientSecret | |
// idea: execute this to a CF API - so theres no page refresh | |
// CF side: CF Lib, Java lib, Azure API | |
stripe.confirmCardPayment(clientSecret, { | |
// Docs: "Use off_session if your customer may or may not be present in your checkout flow." | |
setup_future_usage: "off_session", | |
payment_method: { | |
card: card, | |
} | |
}).then(function (result) { | |
console.log(`confirmCardPayment Completed`) | |
if (result.error) { | |
// Show error to your customer (e.g., insufficient funds) | |
console.log(`confirmCardPayment ErrorMessage: ${result.error.message}, Result: ${JSON.stringify(result)}`); | |
} else { | |
console.log(`confirmCardPayment Success`) | |
// The payment has been processed! | |
if (result.paymentIntent.status === 'succeeded') { | |
console.log(`confirmCardPayment Status: Succeeded`) | |
// Show a success message to your customer | |
// There's a risk of the customer closing the window before callback | |
// execution. Set up a webhook or plugin to listen for the | |
// payment_intent.succeeded event that handles any business critical | |
// post-payment actions. | |
storePaymentIntent(result.paymentIntent) | |
} | |
} | |
}).catch(function (e) { | |
console.log(`confirmCardPayment Catch ${e}`); | |
}); | |
} | |
function storePaymentIntent(paymentIntent) { | |
console.log(`Start fetch POST /payments/provider/stripe/payment-intent-complete. Body: ${JSON.stringify(paymentIntent)}`) | |
fetch('/payments/provider/stripe/payment-intent-complete', { | |
method: 'POST', | |
cache: 'no-cache', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify(paymentIntent) | |
}).then(function (res) { | |
console.log(`End POST /payments/provider/stripe/payment-intent-complete - Status: ${res.status}`) | |
// MUST REDIRECT to NEW PAGE + PREVENT USER FROM CLICKING BUTTON AGAIN! | |
}).catch(function (e) { | |
console.log(`End POST /payments/provider/stripe/payment-intent-complete - Error: ${e}`) | |
}); | |
} | |
</script> | |
<style> | |
.payment-form { | |
width: 400px; | |
border-style: solid; | |
} | |
</style> | |
</body> | |
</html> |
let express = require('express'); | |
let app = express(); | |
app.use(express.json()) | |
app.use(express.static('public')) | |
function isNullOrEmpty(value) { | |
return value == undefined || value == null || value == "" | |
} | |
app.get('/health', function (req, res) { | |
// if (isNullOrEmpty(req.query) || isNullOrEmpty(req.query.echo)) { | |
// console.error('No "echo" query parameter supplied') | |
// res.status(400).send({ error: 'No "echo" query parameter supplied' }) | |
// return; | |
// } | |
console.log('health') | |
res.json({ success: true }); | |
}) | |
app.post('/api', function (req, res) { | |
console.log(`Received message. Body: ${JSON.stringify(req.body)}. Headers: ${JSON.stringify(req.headers)}`) | |
res.status(200).send(new Buffer(`"Hello World"`)); | |
}) | |
// TODO: # Install Stripe via npm | |
// $ npm install --save stripe | |
// Set your secret key. Remember to switch to your live secret key in production! | |
// See your keys here: https://dashboard.stripe.com/account/apikeys | |
const stripe = require('stripe')('sk_test_51HJpxrGuRfJc1w8odYvPTdFMnNT0ieggBP02I0vSp12cGodXZoDRssYLw9Iok8fc6HLGQC0QkPnkNx2BrZkHITsY00ojVJnyK2'); | |
// # Verify your integration in this guide by including the metadata parameter | |
// curl https://api.stripe.com/v1/payment_intents \ | |
// -u sk_test_51HJpxrGuRfJc1w8odYvPTdFMnNT0ieggBP02I0vSp12cGodXZoDRssYLw9Iok8fc6HLGQC0QkPnkNx2BrZkHITsY00ojVJnyK2: \ | |
// -d amount=1099 \ | |
// -d currency=usd \ | |
// -d "metadata[integration_check]"=accept_a_payment | |
app.post('/payments/provider/stripe/create-payment-intent', async (req, res) => { | |
console.log('Start POST /payments/provider/stripe/create-payment-intent') | |
console.log(req.body) | |
const { customerInfo, products } = req.body; | |
if (customerInfo == undefined) throw new Error("customerInfo undefined") | |
if (products == undefined) throw new Error("products undefined") | |
console.log(`Products: ${JSON.stringify(products)}`); | |
console.log(`Customer Info: ${JSON.stringify(customerInfo)}`); | |
customerInfo.name = `${customerInfo.first_name} ${customerInfo.last_name}`; | |
delete customerInfo.first_name | |
delete customerInfo.last_name | |
/// | |
/// CREATE CUSTOMER | |
/// | |
const customer = await stripe.customers.create(customerInfo) | |
.catch(e => { | |
console.log(`Error in stripe.paymentIntents.create(): ${e}`); | |
throw new Error(e) | |
}); | |
console.log(`Stripe Customer Created: ${JSON.stringify(customer)}`); | |
const amount = calculateOrderAmount(products); | |
console.log('Start stripe.paymentIntents.create()') | |
/// | |
/// CREATE PAYMENT INTENT | |
/// | |
const intent = await stripe.paymentIntents.create({ | |
amount, | |
currency: 'usd', | |
customer: customer.id, | |
// Verify your integration in this guide by including this parameter | |
metadata: { | |
integration_check: 'accept_a_payment', | |
// maybe include a customer id? | |
}, | |
}).catch(e => { | |
console.log(`Error in stripe.paymentIntents.create(): ${e}`); | |
throw new Error(e) | |
}); | |
console.log(`End stripe.paymentIntents.create(): ${JSON.stringify(intent)}`) | |
console.log('End POST /payments/provider/stripe/create-payment-intent') | |
res.json({ | |
client_secret: intent.client_secret | |
}); | |
// res.send({ | |
// publishableKey: process.env.STRIPE_PUBLISHABLE_KEY, | |
// clientSecret: paymentIntent.client_secret | |
// }); | |
}); | |
calculateOrderAmount = (items) => 4200; | |
// Result Wrapper + Payment Intent | |
// { | |
// "id": "pi_1HPbH1GuRfJc1w8oAEfAq1vU", | |
// "object": "payment_intent", | |
// "amount": 4200, | |
// "canceled_at": null, | |
// "cancellation_reason": null, | |
// "capture_method": "automatic", | |
// "client_secret": "pi_1HPbH1GuRfJc1w8oAEfAq1vU_secret_jvhEkgUb1urOSiMspm2V7lYKV", | |
// "confirmation_method": "automatic", | |
// "created": 1599688575, | |
// "currency": "usd", | |
// "description": null, | |
// "last_payment_error": null, | |
// "livemode": false, | |
// "next_action": null, | |
// "payment_method": "pm_1HPbHJGuRfJc1w8o5P9cp2at", | |
// "payment_method_types": [ | |
// "card" | |
// ], | |
// "receipt_email": null, | |
// "setup_future_usage": null, | |
// "shipping": null, | |
// "source": null, | |
// "status": "succeeded" | |
// } | |
app.post('/payments/provider/stripe/payment-intent-complete', async (req, res) => { | |
console.log(`POST /payments/provider/stripe/payment-intent-complete : Body: ${JSON.stringify(req.body)}`) | |
res.status(201).end(); | |
}) | |
const endpointSecret = 'whsec_...'; | |
app.post('/payments/provider/stripe/webhook', function (request, response) { | |
const sig = request.headers['stripe-signature']; | |
const body = request.body; | |
let event = null; | |
try { | |
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret); | |
} catch (err) { | |
// invalid signature | |
response.status(400).end(); | |
return; | |
} | |
let intent = null; | |
switch (event['type']) { | |
case 'payment_intent.succeeded': | |
intent = event.data.object; | |
console.log("Succeeded:", intent.id); | |
break; | |
case 'payment_intent.payment_failed': | |
intent = event.data.object; | |
const message = intent.last_payment_error && intent.last_payment_error.message; | |
console.log('Failed:', intent.id, message); | |
break; | |
} | |
response.sendStatus(200); | |
}); | |
var port = (process.env.PORT || 8081); | |
app.listen(port, function () { | |
console.log('Listening on port: ' + port); | |
}); |