Integrate stripe payment gateway in React
Today we’ll explain to you how to integrate stripe payment gateway in React. We will split this integration in the two parts such as React (Frontend) and Node.js (Backend).
In this article, will create a react application that contains the product cart and checkout form as shown below.
Demo Application
Stripe payment gateway integration.
- Part 1 – Integrate stripe payment gateway in React (You are here…)
- Part 2 – Confirm a stripe paymentIntent using Node.js
Steps to integrate stripe payment gateway in React
- Create a stripe account
- Collect the API keys
- Create a react application
- Install npm dependency
- Design a checkout page
- Create a checkout form using stripe package
- Submit payment method to server
- Output
1. Create a stripe account
First of all, we have to create a stripe account using the Register Now link. It’s a very straightforward process using the email address.
2. Collect the API keys
After successful login, you will be redirected on the Dashboard page and the API keys are always available on the dashboard. For the testing purpose, we’ll use the test API keys. Check the following links for more information about the API keys.
Manage your API keys to authenticate requests with Stripe
There are two types of API keys.
- Publishable API key: It’s used in places like your Stripe.js, JavaScript code, or in an Android or iPhone app. We’ll use it in the React application.
- Secret API key: It should be kept confidential and only stored on your own servers. We’ll use it in the Node.js application.
3. Create a react application
Let’s create a react application using the create-react-app
package. For that simply run the following command to Create a React App.
1 | npx create-react-app stripe-payment-gateway-react |
4. Install npm dependency
We will use the @stripe/react-stripe-js and @stripe/stripe-js npm packages to integrate the stripe payment gateway in the react app. Run the following single command to install both packages simultaneously.
1 | npm install --save @stripe/react-stripe-js @stripe/stripe-js |
5. Design a checkout page
Now, we will design a checkout page using bootstrap with some sample records. Add the following bootstrap stylesheet to the public.html
page.
1 | <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> |
You can also check How to add Bootstrap in React.
Let’s add the following HTML and CSS of the checkout page to the react component.
App.js
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 | import React, { useState } from 'react'; const successMessage = () => { return ( <div className="success-msg"> <svg width="1em" height="1em" viewBox="0 0 16 16" className="bi bi-check2" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z" /> </svg> <div className="title">Payment Successful</div> </div> ) } const cart = () => { return (<React.Fragment> <h4 className="d-flex justify-content-between align-items-center mb-3"> <span className="text-muted">Your cart</span> <span className="badge bg-secondary badge-pill">3</span> </h4> <ul className="list-group mb-3"> <li className="list-group-item d-flex justify-content-between lh-condensed"> <div> <h6 className="my-0">Product name</h6> <small className="text-muted">Brief description</small> </div> <span className="text-muted">₹1200</span> </li> <li className="list-group-item d-flex justify-content-between lh-condensed"> <div> <h6 className="my-0">Second product</h6> <small className="text-muted">Brief description</small> </div> <span className="text-muted">₹800</span> </li> <li className="list-group-item d-flex justify-content-between lh-condensed"> <div> <h6 className="my-0">Third item</h6> <small className="text-muted">Brief description</small> </div> <span className="text-muted">₹500</span> </li> <li className="list-group-item d-flex justify-content-between bg-light"> <div className="text-success"> <h6 className="my-0">Promo code</h6> <small>EXAMPLECODE</small> </div> <span className="text-success">-₹500</span> </li> <li className="list-group-item d-flex justify-content-between"> <span>Total (INR)</span> <strong>₹2000</strong> </li> </ul> </React.Fragment>) } function App() { const [paymentCompleted, setPaymentCompleted] = useState(false); return ( <div className="container"> <div className="py-5 text-center"> <h4>Stripe Integration - <a href="https://www.cluemediator.com/" target="_blank" rel="noopener noreferrer">Clue Mediator</a></h4> </div> <div className="row s-box"> {paymentCompleted ? successMessage() : <React.Fragment> <div className="col-md-5 order-md-2 mb-4"> {cart()} </div> <div className="col-md-7 order-md-1"> {/* Checkout form */} </div> </React.Fragment>} </div> </div> ); } export default App; |
index.css
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 | body { margin: 20px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } .container { width: 900px; } .s-box { min-height: 433px; border: 1px solid #ddd; border-radius: 10px; padding: 30px; background-color: #f7f7f9; box-shadow: 0 1px 1px rgba(0,0,0,0.15), 0 2px 2px rgba(0,0,0,0.15), 0 4px 4px rgba(0,0,0,0.15), 0 8px 8px rgba(0,0,0,0.15); } label { margin-bottom: 3px; } .spinner-border { width: 1.3rem; height: 1.3rem; border-width: .1em; } .success-msg { color: #0f5132; text-align: center; margin-top: 120px; } .success-msg svg { font-size: 60px; border: 1px solid #0f5132; border-radius: 30px; padding: 10px; } .success-msg .title { font-size: 25px; margin-top: 10px; } |
6. Create a checkout form using stripe package
It’s time to create a checkout form using the stripe package. Here we will use the individual Element
component to create a form. You can also use the CardElement
to create a ready form.
CheckoutForm.js
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 | import React, { useState } from 'react'; import { useStripe, useElements, CardNumberElement, CardExpiryElement, CardCvcElement } from '@stripe/react-stripe-js'; const CARD_ELEMENT_OPTIONS = { style: { base: { lineHeight: "27px", color: "#212529", fontSize: "1.1rem", "::placeholder": { color: "#aab7c4", }, }, invalid: { color: "#fa755a", iconColor: "#fa755a", }, }, }; export default function CheckoutForm(props) { const [loading, setLoading] = useState(false); const [errorMsg, setErrorMsg] = useState(''); const [name, setName] = useState(''); const [email, setEmail] = useState(''); const stripe = useStripe(); const elements = useElements(); const handleSubmit = async (event) => { // We don't want to let default form submission happen here, // which would refresh the page. event.preventDefault(); if (!stripe || !elements) { // Stripe.js has not yet loaded. // Make sure to disable form submission until Stripe.js has loaded. return; } }; return ( <React.Fragment> <h4 className="d-flex justify-content-between align-items-center mb-3"> <span className="text-muted">Pay with card</span> </h4> <form onSubmit={handleSubmit}> <div className="row"> <div className="col-md-6 mb-3"> <label htmlFor="cc-name">Name on card</label> <input id="cc-name" type="text" className="form-control" value={name} onChange={e => setName(e.target.value)} /> </div> <div className="col-md-6 mb-3"> <label htmlFor="cc-email">Email</label> <input id="cc-email" type="text" className="form-control" value={email} onChange={e => setEmail(e.target.value)} /> </div> </div> <div className="row"> <div className="col-md-12 mb-3"> <label htmlFor="cc-number">Card Number</label> <CardNumberElement id="cc-number" className="form-control" options={CARD_ELEMENT_OPTIONS} /> </div> </div> <div className="row"> <div className="col-md-6 mb-3"> <label htmlFor="expiry">Expiration Date</label> <CardExpiryElement id="expiry" className="form-control" options={CARD_ELEMENT_OPTIONS} /> </div> <div className="col-md-6 mb-3"> <label htmlFor="cvc">CVC</label> <CardCvcElement id="cvc" className="form-control" options={CARD_ELEMENT_OPTIONS} /> </div> </div> <hr className="mb-4" /> <button className="btn btn-dark w-100" type="submit" disabled={loading}> {loading ? <div className="spinner-border spinner-border-sm text-light" role="status"></div> : `PAY ₹${props.amount}`} </button> {errorMsg && <div className="text-danger mt-2">{errorMsg}</div>} </form> </React.Fragment> ); } |
To load the CheckoutForm
component, we need to generate the Stripe
object using the publishable API key. Check the following code.
App.js
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 | import React, { useState } from 'react'; import { Elements } from '@stripe/react-stripe-js'; import { loadStripe } from '@stripe/stripe-js'; import CheckoutForm from './CheckoutForm'; // Make sure to call `loadStripe` outside of a component’s render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe("<YOUR_PUBLISHABLE_KEY>"); const successMessage = () => { return ( <div className="success-msg"> ... ... </div> ) } const cart = () => { return ( <React.Fragment> ... ... </React.Fragment> ) } function App() { const [paymentCompleted, setPaymentCompleted] = useState(false); return ( <div className="container"> <div className="py-5 text-center"> <h4>Stripe Integration - <a href="https://www.cluemediator.com/" target="_blank" rel="noopener noreferrer">Clue Mediator</a></h4> </div> <div className="row s-box"> {paymentCompleted ? successMessage() : <React.Fragment> <div className="col-md-5 order-md-2 mb-4"> {cart()} </div> <div className="col-md-7 order-md-1"> <Elements stripe={stripePromise}> <CheckoutForm amount={2000} setPaymentCompleted={setPaymentCompleted} /> </Elements> </div> </React.Fragment>} </div> </div> ); } export default App; |
7. Submit payment method to server
In the last step, we need to create a payment method on button click of the Pay
. After creating the payment method, we have to send that data to the backend like payment method id, name, email, amount, etc.
So let’s create a stripePaymentMethodHandler
method to submit that data to the backend.
script.js
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 | const API_ENDPOINT = 'http://localhost:4000'; export const stripePaymentMethodHandler = async (data, cb) => { const { amount, result } = data; if (result.error) { // show error in payment form cb(result); } else { const paymentResponse = await stripePayment({ payment_method_id: result.paymentMethod.id, name: result.paymentMethod.billing_details.name, email: result.paymentMethod.billing_details.email, amount: amount }); console.log(paymentResponse); cb(paymentResponse); } } // place backend API call for payment const stripePayment = async data => { const res = await fetch(`${API_ENDPOINT}/pay`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }) return await res.json(); } |
Here, we have used the API_ENDPOINT
as a backend server API endpoint where we will confirm the payment. We’ll see this step in the next article (Confirm a stripe paymentIntent using Node.js).
Let’s call the above method from the CheckoutForm
component.
CheckoutForm.js
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 | import { stripePaymentMethodHandler } from './script'; ... ... export default function CheckoutForm(props) { ... ... const handleSubmit = async (event) => { // We don't want to let default form submission happen here, // which would refresh the page. event.preventDefault(); if (!stripe || !elements) { // Stripe.js has not yet loaded. // Make sure to disable form submission until Stripe.js has loaded. return; } setLoading(true); setErrorMsg(''); const paymentMethodObj = { type: 'card', card: elements.getElement(CardNumberElement), billing_details: { name, email }, }; const paymentMethodResult = await stripe.createPaymentMethod(paymentMethodObj); stripePaymentMethodHandler({ result: paymentMethodResult, amount: props.amount }, handleResponse); }; // callback method to handle the response const handleResponse = response => { setLoading(false); if (response.error) { setErrorMsg(typeof response.error === 'string' ? response.error : response.error.message); return; } props.setPaymentCompleted(response.success ? true : false); }; return ( <React.Fragment> ... ... </React.Fragment> ); } |
Check this link for the more information of the stripe integration.
8. Output
Run the react application and check the output in the browser.
Note: Here, we have used the backend server API to confirm the payment, which we explained in the next article.
That’s it for today.
Thank you for reading. Happy Coding..!!