Creating Charges for Stripe with React and Netlify functions Part 1

Part 1 of a 3 part tutorial - setting up react stripe elements and creating a charge form

Fri, 03 Aug 2018

This is part 1 of a 3 part series that will show you how to set up and charge your customers/clients with Stripe. We will achieve this by setting up our front end with react stripe elements and the backend that we will create is going to be a lambda that interacts with Stripes server to process payments. We’re going to focus on setting up stripe elements in this post. I used Bootstrap 4 to add in extra fields to capture, at the end you should end up with the following modal-screenshot-stripe

React Stripe Elements

React stripe elements is a thin wrapper around Stripe.js and Stripe Elements that allows you to securely gather sensitive card data using some pre built components from Stripe. If you want to see how it works, Stripe built an official JSFiddle that you can play around with here.

yarn add react-stripe-elements

The final portion is to include the stripe script inside your app. If you use GatsbyJS, you can achieve this by editing the default html file located in the .cache folder. Below is the full command to copy the default version of html.js and place it in your src folder. Do note, this command is ran from the root of your project folder and not your src folder

cp .cache/default-html.js src/html.js

Place the following script right before the end of the <body> tag in your your html.js, restart your dev server and Stripe should be injected into your app.

<script src="https://js.stripe.com/v3/"></script>

If you’re using create react app, you can just use the index.html located in the public folder.

Optional

I used Bootstrap for my layout and modal component. If you would like to do the same you can install Reactstrap to get Bootstrap components.

yarn add bootstrap reactstrap

Checkout Modal

We are going to need a few components to get the job done. The first component that will create will be CheckoutModal. This component is responsible for displaying our modal for the checkout form we’re creating.

import React from 'react';
import CheckoutComponent from '../components/CheckoutComponent';
import {StripeProvider} from 'react-stripe-elements';
import {Col, Modal, ModalHeader, ModalBody, Button} from 'reactstrap';

class CheckoutModal extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      modal: false,
      backdrop: true
    };
    this.toggle = this.toggle.bind(this);
  }

  toggle() {
    this.setState({
      modal: !this.state.modal
    });
  }

  render() {
    return (
      <Col>
        <Button color="danger" onClick={this.toggle}>Purchase</Button>
        <Modal isOpen={this.state.modal} toggle={this.toggle} backdrop={this.state.backdrop}>
          <ModalHeader toggle={this.toggle}>Checkout</ModalHeader>
          <ModalBody>
            <StripeProvider apiKey="<pk_test_key>">
              <CheckoutComponent />
            </StripeProvider>
          </ModalBody>
        </Modal>
      </Col>
    );
  }
}

export default CheckoutModal;

Most of the code for this component is just a straight copy paste of the modal example in the Reactstrap docs. The only thing that is worth looking at in the StripeProvider component. To be able to access the Stripe object we need to wrap our CheckoutComponent with a StripeProvider component, which is example what we do inside the ModalBody component.

Checkout Component

Our next component is CheckoutComponent. This is the component that StripeProvider wrapped. Next, we want to wrap the Elements component around your form. This groups the set of Stripe Elements you’re using together, so that Stripe is able to pull data from groups of Elements when you’re tokenizing. Not much to explain code wise for this component.

import React from 'react';
import {Elements} from 'react-stripe-elements';

import CheckoutForm from './CheckoutForm';

class MyStoreCheckout extends React.Component {
  render() {
    return (
      <Elements>
        <CheckoutForm />
      </Elements>
    );
  }
}

export default MyStoreCheckout;

Checkout Form

Now that we have access to Elements, we can start building our checkout form. There is a lot that goes on in this component that I will explain line by line

import React from 'react';
import {injectStripe} from 'react-stripe-elements';
import axios from 'axios';
import AddressSection from './AddressSection';
import CardSection from './CardSection';
import {Button} from 'reactstrap';
import Success from './Success';

class CheckoutForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: undefined,
      email: undefined,
      address: undefined,
      city: undefined,
      state: undefined,
      program: undefined,
      price: undefined,
      success: false
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    const name = event.target.name
    this.setState({[name]: event.target.value});
  }


  handleSubmit = (ev) => {
    ev.preventDefault();

    this.props.stripe.createToken({
        name: this.state.name,
        address_city: this.state.city,
        address_line1: this.state.address,
        address_state: this.state.state,
        address_country: "US"
      }).then(({token}) =>
      {
        const charge = JSON.stringify({
          token,
          charge: {
            amount: this.getPrice(),
            currency: 'usd',
            email: this.state.email,
            number: this.state.number
          },
        })
        axios.post('/.netlify/functions/charge', charge)
        .catch(function (error) {
          console.log(error);
        })
    });
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit} id="form-css" className="w-100">
        { this.state.success === false ?
          <div>
            <AddressSection handleChange={this.handleChange} schedule={this.state.schedule} />
            <CardSection />
          </div>
          :
          <Success />
        }
        <Button className="w-100 h4">Pay $50</Button>
      </form>
    );
  }
}

export default injectStripe(CheckoutForm);

The css for form-css

#form-css h1 {
  color: #32325d;
  font-weight: 400;
  line-height: 50px;
  font-size: 40px;
  margin: 20px 0;
  padding: 0;
}

#form-css label {
  color: #6b7c93;
  font-weight: 300;
  letter-spacing: 0.025em;
}

#form-css button {
  white-space: nowrap;
  border: 0;
  outline: 0;
  display: inline-block;
  height: 40px;
  line-height: 40px;
  padding: 0 14px;
  box-shadow: 0 4px 6px rgba(50, 50, 93, .11), 0 1px 3px rgba(0, 0, 0, .08);
  color: #fff;
  border-radius: 4px;
  font-size: 15px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.025em;
  background-color: #6772e5;
  text-decoration: none;
  -webkit-transition: all 150ms ease;
  transition: all 150ms ease;
  margin-top: 10px;
}

#form-css form {
  margin-bottom: 40px;
  padding-bottom: 40px;
  border-bottom: 3px solid #e6ebf1;
}

#form-css button:hover {
  color: #fff;
  cursor: pointer;
  background-color: #7795f8;
  transform: translateY(-1px);
  box-shadow: 0 7px 14px rgba(50, 50, 93, .10), 0 3px 6px rgba(0, 0, 0, .08);
}

#form-css input,
.StripeElement {
  display: block;
  margin: 10px 0 20px 0;
  max-width: 100%;
  padding: 10px 14px;
  font-size: 1em;
  font-family: 'Source Code Pro', monospace;
  box-shadow: rgba(50, 50, 93, 0.14902) 0px 1px 3px, rgba(0, 0, 0, 0.0196078) 0px 1px 0px;
  border: 0;
  outline: 0;
  border-radius: 4px;
  background: white;
}

#form-css textarea {
  display: block;
  margin: 10px 0 20px 0;
  max-width: 100%;
  padding: 10px 14px;
  font-size: 1em;
  font-family: 'Source Code Pro', monospace;
  box-shadow: rgba(50, 50, 93, 0.14902) 0px 1px 3px, rgba(0, 0, 0, 0.0196078) 0px 1px 0px;
  border: 0;
  outline: 0;
  border-radius: 4px;
  background: white;
}

#form-css select {
  display: block;
  margin: 10px 0 20px 0;
  max-width: 100%;
  padding: 10px 14px;
  font-size: 1em;
  font-family: 'Source Code Pro', monospace;
  box-shadow: rgba(50, 50, 93, 0.14902) 0px 1px 3px, rgba(0, 0, 0, 0.0196078) 0px 1px 0px;
  border: 0;
  outline: 0;
  border-radius: 4px;
  background: white;
}

#form-css textarea::placeholder {
  color: #aab7c4;
}

#form-css input::placeholder {
  color: #aab7c4;
}

#form-css input:focus,
.StripeElement--focus {
  box-shadow: rgba(50, 50, 93, 0.109804) 0px 4px 6px, rgba(0, 0, 0, 0.0784314) 0px 1px 3px;
  -webkit-transition: all 150ms ease;
  transition: all 150ms ease;
}
Loading...
Edward Beazer

Edward Beazer - I just like to build shit. Sometimes I get stuck for hours, even days while trying to figure out how to solve an issue or implement a new feature. Hope my tips and tutorials can save you some time.