This repository should serve as a simple example of how to use Stripe APIs for collecting payments. The goal of this project is to help you get started with using Stripe payment APIs for an ecommerce website.
You can find a working demo of the app hosted on Heroku: Demo
- Note: The demo is running in test mode – use
4242424242424242
as a test card number with any CVC + future expiration date. Read more about testing on Stripe at https://stripe.com/docs/testing.
- Add & remove items from shopping cart
- Payment page with credit card option
- Checkout flow
- Confirmation page
This application has a web client that will be the store front end & a web server that will be responsible to process the payments by making Stripe API calls.
Client side stack:
- React client app bootstrapped from create-react-app
- Redux, for managing state of shopping cart items
- react-stripe-elements for creating payment form
Server side stack:
- Express Node JS webserver
- Stripe node SDK
- The first step for our client is to get our Stripe publishable API key (pk_id). We have an option to store the key using environment variable for REACT apps, however, we are going to get the pk_id from our server once user enters the checkout page.
- Next, to collect payments from users on our e-commerce website we will create a payment form. Here we are going to make use of Stripe Elements to create a form that securely collects our customer’s card information without requiring us to handle sensitive card data. We then get the token as below:
let {token} = await this.props.stripe.createToken({name: "Name"});
- On the checkout page we are collecting users' email address as a mandatory field. This is only done to demonstrate how we can use Stripe's customer endpoint to first create a customer, and subsequently create charges. The email address along with credit card token and other purchase details are sent to our app server. Our app server will then call Stripe's
/v1/customers
endpoint to create a customer record. Stripe will return a customer id if the customer record is created successfully.
var customer = await stripe.customers.create({
email: req.body.email
})
- Once a customer record is created, we then call the
/v1/customers/:id/source
endpoint on Stripe to associate the credit card token with the customer id we received in the previous step.
var source = await stripe.customers.createSource(customer.id, {
source: req.body.token
})
- Finally, we create charges against all items sent by our client to our server. We will use the
/v1/charges
API endpoint on Stripe for the same. We will use the metadata field to pass along the title of the item as a key value pair. Stripe supports adding metadata to some common requests, such as processing charges. Through metadata, we can associate information—meaningful to us—with Stripe activity.
stripe.charges.create({
amount: req.body.total,
currency: 'usd',
description: item.title,
customer: source.customer,
metadata: { title: item.title }
})
- Upon receiving a success response from Stripe, our app server will send a confirmation of charges to our app client, which in turn will take the user to payment confirmation page and display the
ch_id (charge id)
Below sequence diagram explains the flow.
You have two options to run the app locally
- You can use a VPN tunnel, like ngrok, to link your local machine to an actual web address. This handy tool lets you set up a secure tunnel to your localhost, which is a fancy way of saying it opens access to your local app from the internet. You would use this option if you prefer to use HTTPS connections.
- You can simply run the client & server on the localhost. You may be limited to HTTP connections.
This guide only focuses on option 2 above. For more information on how to setup and user ngrok, you can read product documentation here.
- Create a new file in the Server directory of the app called
.env
STRIPE_PUBLIC_KEY=pk_id
STRIPE_SECRET_KEY=sk_id
STRIPE_WEBHOOK_SECRET=whsec_
DOMAIN=http://localhost:4242
STATIC_DIR=../Client/build/
- Next install all app dependencies. Using the terminal, navigate to the Server directory where the app is located and run
npm install
. You will do the same for Client directory. - Finally, execute
npm run dev
from the root directory.
Things to consider before getting ready for production:
-
Identity:
- This sample demo does not capture and save users' identity related information.
- In the future, if our application needs to support capturing user's identity related information, we can use federated identity through Google or Facebook Authentication. On our server we will introduce a database for persisting user's identity. We will save user's customer id as received from Stripe for creating new charges corresponding to the same user.
-
Security:
- We need to ensure all NodeJS Express server API endpoints are only accessible behind HTTPS. We must also consider protecting specific routes using jSON web token.
-
Support for additional payment methods:
- The current iteration of our demo app only uses credit card payment option. Using Stripe APIs we can easily add support for additional payment methods. The payment request button element lets us collect payment and address information from customers using Apple Pay and the Payment Request API.
-
Support for shipping & taxes:
- In order to operate as a full e-commerce solution our app should also support collecting users' address information & calculate shipping based off of that. We must look into using Avalara APIs or other 3rd party tax APIs to collect tax related information. It will be good to keep an eye out on Stripes' roadmap relating to taxes
-
After payment use cases
- Please refer Stripe's documentation for below scenarios that we must consider post payment.
- Support for failed payments
- Handle retries
- Support sending email receipts for payment confirmation
- Handle use cases for disputes, refunds & chargebacks
- Please refer Stripe's documentation for below scenarios that we must consider post payment.
-
Better error handling
- Our app currently does not do any error handling or report back to users in the event of an error during checkout. It is criticaly important to add support to gracefully handle errors in order to ensure we are giving users a delightful experience.
Q: Why did you pick these frameworks?
A: Since the goal of this exercise is to demonstrate the key Stripe calls and concepts, I used the tech stack that is simple to understand & easy to implement.
- https://stripe.com/docs/recipes/elements-react
- https://github.com/stripe/react-stripe-elements#using-the-paymentrequestbuttonelement
- https://stripe.com/docs/charges#creating-charges
- https://stripe.com/docs
- https://stripe.com/docs/api/charges/object
- https://dashboard.stripe.com/test/payments?status%5B%5D=successful
- https://github.com/stripe-samples/checkout-one-time-payments