DEV Community

Tallan Groberg for ITNEXT

Posted on • Edited on

Using stripe to make credit card charges part 1

This is a multipart series to help you set up stripe API's within the context of react, express mongoDB applications. In the first section we will make our first test charge to your stripe console. in the second part, we will set up error handling in the testing environment. In the third part, we will deploy our dev environment to heroku and have a functional credit card payment system on the web.

Prerequisites:

  1. Understanding of javascript.

  2. Experience with express, mongoose, mongoDB and node.js.

  3. Understanding of React, including hooks, and context.

  4. Unix command line and use of npm packages. Including nodemon

  5. a bank account, and a stripe account.

  6. deploying applications to heroku.(part 3, not made yet)

  7. you need to have mongoDB compass installed

if you haven't had very much experience with these technologies you could still try and do this tutorial but it would be much better to learn about these things before you try, because this is going to be an advanced tutorial relating to a specific stack and requires a computer already configured for this development environment.

lets go to stripe.com

fill out the information so that you can get to your stripe dashboard.

the dashboard should look something close to this.

Alt Text

right under the small bash teminal is the publishable-api-key.

these keys need to be handled with care to ensure your site stays secure.

we will get back to that in a moment but let's make our express application.

Make a directory for the project. we will start by making our full-stack application.

mkdir payment-system && cd payment-system
Enter fullscreen mode Exit fullscreen mode

make a git repo.

git init
Enter fullscreen mode Exit fullscreen mode

next let's initialize a package.json from the command-line by running this command.

npm init -y
Enter fullscreen mode Exit fullscreen mode

open the directory with your favorite text editor. I'll be using vscode.

make a file in the root of your project you can do this by pressing control and ~ at the same time to start a command-line instance in the root of your project and type into the command line.

touch server.js
Enter fullscreen mode Exit fullscreen mode

since this tutorial needs a production build to make our payment system work we are going to try and make our server ready for deploy from the start so that we don't have to refactor later.

let's start with dependencies.

npm i express morgan mongoose body-parser stripe dotenv
Enter fullscreen mode Exit fullscreen mode
  1. morgan is a dependency for listening and getting better data for our API requests.

  2. body-parser middleware is there so that you can retrieve the payload from the POST request body.

  3. dotenv will set up our testing environment variables. We have to configure another two .env files. one for the back end another for the front end.

  4. stripe is the library to make the serverside calls of this payment system.

before we start adding code. I want to clarify, the only time you will see duplicate pieces of code the function names and boilerplate for them when I have structured functions and given them structure later or when I talk about certain parts of the file then share the whole thing.

let add the code to get our server running in development.

server.js


const express = require('express')
const app = express()
const mongoose = require('mongoose')
require('dotenv').config()
const bodyParser = require('body-parser')
const morgan = require('morgan')
const PORT = process.env.PORT || 4444 

app.use(morgan('dev'))
app.use(bodyParser.text())
app.use(express.json())

mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/paymentSystem', {
  useNewUrlParser: true,
  useFindAndModify: false,
  useCreateIndex: true,
  useUnifiedTopology: true,
}, console.log('db connected...'))

//Route goes here



app.listen(PORT, () => {
  console.log(`app is running on ${PORT}`)
})

Enter fullscreen mode Exit fullscreen mode

Everything on the server.js is the bare essentials for a production deploy with the integration of stripe API. Especially the code before the || or operators.

if everything when well you should see this when you type nodemon server.js into command line inside the root directory.

Alt Text

it might only be repeated once.

Next we define the paymentRouter to make the post request on the backend.

press control c after clicking on the teminal and in the command line lets write

mkdir routes && touch routes/paymentRouter.js  
Enter fullscreen mode Exit fullscreen mode

before we leave the server.js define the route.
server.js


app.use('/charge', require('./routes/paymentRouter'))

Enter fullscreen mode Exit fullscreen mode

since our paymentRouter.js is an empty file, our server is crashing. Add this to the and the server will run normally again.

const express = require('express')

const paymentRouter = express.Router()
const stripe = require('stripe')(process.env.SECRET_LIVE_APIKEY || process.env.SECRET_TEST_APIKEY);


module.exports = paymentRouter
Enter fullscreen mode Exit fullscreen mode

if you're logged into stripe and look at any of the examples in the docs you'll notice that they are nice enough to provide you with your test API keys in the examples.

Rather than adding our keys directly into the code, we will set up environment wide variables to represent our keys so that we can push to public repositories without hackers being able to steal our payment credentials.

now we should make the post request for the POST '/charge' route between the module.exports and the const stripe.


paymentRouter.post('/', async (req,res) => {
  try{
    let {status} = await stripe.charges.create({
      amount: req.body.amount,
      currency: 'usd',
      source: req.body.token,
    })
    return res.json({status})
  } catch(err) {
    console.error(err)
    res.status(500).end()
  }
})

Enter fullscreen mode Exit fullscreen mode

note: you can hard code the amount for testing and choose whatever currency type you are most likely to be receiving payments with but the source key has to get the stripe token from the front end for the value.

The whole file should look like this...

const paymentRouter = express.Router()
const stripe = require('stripe')(process.env.SECRET_LIVE_APIKEY || process.env.SECRET_TEST_APIKEY);


paymentRouter.post('/', async (req,res) => {
  try{
    let {status} = await stripe.charges.create({
      amount: req.body.amount,
      currency: 'usd',
      source: req.body.token,
    })
    return res.json({status})
  } catch(err) {
    console.error(err)
    res.status(500).end()
  }
})

module.exports = paymentRouter

Enter fullscreen mode Exit fullscreen mode

now make and set up a .env file to ensure our secret keys are working.

touch .env .gitignore && echo .env >> .gitignore

Enter fullscreen mode Exit fullscreen mode

the command after the &&, ensures that you don't push anything in the .env files to github which is exactly what we want.

Make sure that the .env looks a little faded out and that it says .env in the .gitignore file before you git add or git commit anything. Remember that we are about to be dealing with real money, real credit card numbers, and real people that deserve your due diligence

Alt Text

just to drive my point home.

now lets our use create-react-app to make a client folder.

create-react-app client
Enter fullscreen mode Exit fullscreen mode

now we install all the dependencies.

npm i axios react-stripe-elements dotenv
Enter fullscreen mode Exit fullscreen mode

lets set up the .env files for the front-end.

Go to your stripe dashboard and get the publishable test key, the one prefixed with pk_test. These are the ones that you can see without typing your password first.

in the terminal for the client directory, make a .env file for the client directory.

touch .env
Enter fullscreen mode Exit fullscreen mode

inside the .env file write...

REACT_APP_PUBLISHABLE_TEST_APIKEY="the publishable key copied from the stripe dashboard"
Enter fullscreen mode Exit fullscreen mode

we have to add the REACT_APP_ part to the apikey because this is the way that create-react-app handles environmental variables.

add the proxy to the package.json for the client folder.

 "proxy": "http://localhost:4444",
Enter fullscreen mode Exit fullscreen mode

make sure that your commas and double quotes, follow proper convention for json objects.

now we need to prepare the index.js in the src folder with the stripe and elements context to be available app wide. You can add this more dynamically to your app but for the sake of scale we will add this where we would put any other piece of context.

your index.js will look like this...

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {StripeProvider, Elements} from 'react-stripe-elements'

ReactDOM.render(
  <StripeProvider apiKey={
    process.env.REACT_APP_PUBLISHABLE_TEST_APIKEY
    ||process.env.REACT_APP_PUBLISHABLE_LIVE_APIKEY
      }>
      <Elements>
        <App />
      </Elements>
  </StripeProvider>

, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

Enter fullscreen mode Exit fullscreen mode

notice that we are adding variables that don't exist yet. that's so that we don't have to refactor for production mode.

you can tell if it worked by opening the dev tools where stripe gives a warning message.

Alt Text

clear out the App.js to make room for the CheckoutForm.js we are about to make.

the new App.js should look like this.

import React from 'react';
import logo from './logo.svg';
import './App.css';
import CheckoutForm from './CheckoutForm'

function App() {
  return (
    <div className="App">
      <header className="App-header">
       <CheckoutForm />
      </header>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

now make the checkout form.

touch CheckoutForm.js
Enter fullscreen mode Exit fullscreen mode

then go to the CheckoutForm and make a skeleton component.

import React from 'react';

const CheckoutForm = (props) => {
  return (
    <div>

    </div>
  );
};

export default CheckoutForm;

Enter fullscreen mode Exit fullscreen mode

we know we are going to make a post request from this component so let's add axios right now

import axios from 'axios'
Enter fullscreen mode Exit fullscreen mode

import the higher order component injectStripe and CardElement from react-stripe-elements and make this component its child by adding these two lines of code

first at the top.

import {CardElement, injectStripe} from 'react-stripe-elements';
Enter fullscreen mode Exit fullscreen mode

and at the export default. pass in the injectStripe higher-order component the CheckoutForm as an argument like this...

export default injectStripe(CheckoutForm);
Enter fullscreen mode Exit fullscreen mode

Now that we have all of the props from context, let's render the card element inside the div.

 <p>enter your card information here. This is for test purposes only. do not enter your real credit card information.</p>
      <CardElement />

Enter fullscreen mode Exit fullscreen mode

now we need to make the function to send the users input to the back-end.

this is a hooks refactor from the stripe docs on how to do this.

import useState at the top.

import React, {useState,} from 'react';
Enter fullscreen mode Exit fullscreen mode

let see if we can hit the the post request from the front-end to the back-end. by adding this code.

a button underneath the < CardElement / >

<button onClick={submit}>Purchase</button>
Enter fullscreen mode Exit fullscreen mode

add the boolean for completed transactions at the top of the functional component.

  const [complete, setComplete] = useState(false)
Enter fullscreen mode Exit fullscreen mode

here is the function to send the request to the backend

  const submit = async (ev) => {
    let {token} = await props.stripe.createToken({name: "Name"});


    await axios.post('/charge', {
          headers: {"Content-Type": "text/plain"},
          token: token.id,
          amount: "2000", 
    }
  ).then(res => {
    if(res.status === 200) {
      console.log(res)
      setComplete(!complete)

      }
    })
    .catch(err => console.log(err))
  }

Enter fullscreen mode Exit fullscreen mode

you can test the system by typing 42 for every number in the transaction like this.

Alt Text

if the payment was successful the complete boolean will be marked as correct and you will see it.

Alt Text

in part 2 we will show you how to make a production deploy to accept real money.

Top comments (1)

Collapse
 
ariadnavilar profile image
Ariadna Vilar

I've done everything exact as the article but I got this error:
Error: Please pass either 'apiKey' or 'stripe' to StripeProvider. If you're using 'stripe' but don't have a Stripe instance yet, pass 'null' explicitly.

Thanks.