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); | |
| }); |