Creating Charges for Stripe with React and Netlify functions Part 2

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

Sat, 04 Aug 2018

I hit a file limit size on my last post and had to split this tutorial up into an addition part. Jumping right in where we left off:

Our constructor sets up the initial state for our form and binds some functions to our component.

  constructor(props) {
    super(props);
    this.state = {
      name: undefined,
      email: undefined,
      address: undefined,
      city: undefined,
      state: undefined,
      program: undefined,
      schedule: undefined,
      price: undefined,
      success: false,
      number: undefined
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

The next block is for form input handling. Whenever someone types inside one of our inputs our state will get updated with whatever they typed(or deleted)

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

After this we have our form submission method. The first line prevents our form from doing its default completion action, which is to refresh the page. If that we were to refresh the page we wouldn’t be able to submit any payment info.

ev.preventDefault();

We will now create a token for a payment request that we will then send to our backend server.

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

These are the custom fields that I decided to capture for my card payments. The only required option here is name, personally I would still collect this extra information for verification and dispute purposes. The card information that we captured from the CardSection component is automatically added to this method. Once we have our token, we then create a promise to send the information to our backend for charging.

const charge = JSON.stringify({
 token,
 charge: {
   amount: 50,
   currency: 'usd',
   email: this.state.email
 },
})

We are now making an object that holds the token we just created and another object that holds the amount we want to charge, the currency of the charge and email to send the customer a receipt. The last portion our of form submission method is to send the charge to our backend via a post request. I host all of my sites on Netlify and make use of their functions feature so I don’t have to create a backend. The third portion of this tutorial will go into detail about the lambda I created for the post request.

 axios.post('/.netlify/functions/charge', charge)
 .catch(function (error) {
   console.log(error);
 })

We are just sending the charge object we previously created to our server endpoint.

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

The way our form flows is as follows, we display the full form for the customer to fill out as well as a button for them to start the submission for the form. If our submission is successful a separate modal appears with a thank you message.

Address Section

The AddressSection component will help us capture custom data that isn’t readily available from Elements. Most of this is just basic form handling. The only thing worth mentioning is that we add the onChange attribute to fire off our handleChange function from CheckoutModal. Since we passed handleChange as a props to AddressSection, we will be able to manipulate the state of CheckoutModal, so each input that gets changed will update the state of whatever the name attribute is on the input.

import React from 'react';
import {CardElement} from 'react-stripe-elements';
import { Row, Col, Input} from 'reactstrap';

class AddressSection extends React.Component {

  render() {
    return (<div>
      <Row>
        <Col xs="6" sm="6" md="6">
          <label className="w-100">
            Name
            <input className="w-100" type="text" name="name" onChange={this.props.handleChange} placeholder="Jane Doe" required />
          </label>
        </Col>
        <Col xs="6" sm="6" md="6">
          <label className="w-100">
            Email
            <input className="w-100" type="email" name="email" onChange={this.props.handleChange} placeholder="jane.doe@example.com" required />
          </label>
        </Col>
      </Row>
      <Row>
        <Col xs="6" sm="6" md="6">
          <label className="w-100">
            Address
            <input className="w-100" type="text" name="address" onChange={this.props.handleChange} placeholder="100 Legends Way" required />
          </label>
        </Col>
      </Row>
      <Row>
        <Col xs="5" sm="5" md="6">
          <label className="w-100">
            Cell
            <input className="w-100" type="tel" name="number" onChange={this.props.handleChange} placeholder="781-111-1111" required />
          </label>
        </Col>
        <Col xs="4" sm="4" md="6">
          <label className="w-100">
            City
            <input className="w-100" type="text" name="city" onChange={this.props.handleChange} placeholder="Boston" required />
          </label>
        </Col>
        <Col xs="3" sm="3" md="6">
          <label className="w-100">
            State
            <input className="w-100" type="text" name="state" onChange={this.props.handleChange} placeholder="MA" required />
          </label>
        </Col>
      </Row>
    </div>
    );
  }
}

export default AddressSection;

Card Section

This component is similar to the above component, the only difference is that we can use the prebuilt components from Elements which handle all of the form handling. All we need to do is put the component inside a label

import React from 'react';
import {CardNumberElement,CardExpiryElement,CardCVCElement,PostalCodeElement} from 'react-stripe-elements';
import { Row, Col} from 'reactstrap';

class CardSection extends React.Component {
  render() {
    return (<div>
      <Row>
        <Col xs="8" sm="8" md="6">
          <label className="w-100">
             Card number
             <CardNumberElement />
           </label>
        </Col>
        <Col xs="4" sm="4" md="6">
          <label className="w-100">
            CVC
            <CardCVCElement />
          </label>
        </Col>
      </Row>
      <Row>
        <Col xs="6" sm="6" md="6">
          <label className="w-100">
            Expiration date
            <CardExpiryElement />
          </label>
        </Col>
        <Col xs="6" sm="6" md="12">
          <label className="w-100">
            Postal code
            <PostalCodeElement />
          </label>
        </Col>
      </Row>
    </div>
    );
  }
}

export default CardSection;

Success

The Success component will display a message if the charge was successful. This component is very similar to our CheckoutModal where we use the standard modal elements that are provided by Reactstrap.

import React from 'react';
import { Modal, ModalHeader, ModalBody, ModalFooter} from 'reactstrap';
import Link from 'gatsby-link'

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

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

  render() {
    return (
      <div>
        <Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className} backdrop={this.state.backdrop}>
          <ModalHeader>Success!!</ModalHeader>
          <ModalBody>
            <p>Thank you for signing up for one of our programs.  A receipt will be emailed to you shortly.  Click the button below to close this message.</p>
          </ModalBody>
          <ModalFooter><Link to="/"><Button color="danger" onClick={this.toggle}>Close</Button></Link></ModalFooter>
        </Modal>
      </div>
    );
  }
}

export default Success;

Wrapping Up

check out Part 3 to finish up.

Buy Me A CoffeeDigitalOcean Referral Badge
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.

DigitalOcean Referral Badge