Stripe Payment Api Integration in react js

Hello Fellow Developers in this article we are going to learn how can one integrate payment API in react js.

We are going to make it from scratch  I have divided this task into 2 parts for front end we are going to use react js and for backend we are going to use nodejs.

Now lets start with first part or react js

Create a new application using following command

npx create-react-app stripe-payment-gateway-react

We are going to use stripe for payment integration process so please create a stripe account using following link:

https://dashboard.stripe.com/register

Now after creating a stripe account you will be directly redirected to stripes dashboard collect api keys from there for more information regarding api keys please refer to following links:

https://stripe.com/docs/keys

Install following dependencies in your project:

Now lets design payment integration page for that first:

We are going to use bootstrap for this blog so please link these in your index.html

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">

Now in your app.js

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

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

Now create a CheckoutForm.js and add following code:

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

Now for using checkout form check this code in app.js

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;

In the last step, we need to create a payment method on button click. After creating the payment method, we have to send that data to the backend like payment method  name, email, amount, etc.

Create a stripePaymentMethodHandler method to submit that data to the backend.

Now create a script.js

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

We have used the API_ENDPOINT as a backend server API endpoint where we will confirm the payment.

Let’s call the above method from the CheckoutForm component. Add your publish key in the app component.

This should be it for this blog I will explain backend for this in next blog.

Submit a Comment

Your email address will not be published. Required fields are marked *

Subscribe

Select Categories