7 min read
Original source

Introduction to Stripe with React

Nowadays, a lot of web applications accept online payments. Although this is not straightforward to implement, there are some ready-to-use solutions that we…

Nowadays, a lot of web applications accept online payments. Although this is not straightforward to implement, there are some ready-to-use solutions that we can take advantage of. In this article, we look into Stripe. It serves as a payment processing platform that does the heavy lifting for us. To implement it, we use NestJS and React. Getting up and running with Stripe To start using Stripe, we first need to sign up. After verifying our email address, we can go straight to the API keys page. On the above page, there are two important things. The first of them is the publishable key. We use it on the frontend part of our application. We can safely expose it in our JavaScript code. The second one is the secret key. We keep it on the backend side and use it to perform API requests to Stripe. It can be used to create charges or perform refunds, for example. Therefore, we need to keep it confidential. As a best practice, we make sure not to commit neither of the above keys to the repository. We keep them in the environment variables. If we want to start accepting real payments with Stripe, we need to activate our account. To do that, we need to answer some questions about our business and provide bank details. Since this is optional for development and testing purposes, we can skip it for now. Using Stripe with NestJS If we would intend only to have simple one-time payments, we could use the prebuilt checkout page. In the upcoming articles, we want to implement features such as saving credit cards for later use. Therefore, we implement a simple custom payment flow: A user creates an account through our NestJS API. Under the hood, we create a Stripe customer for the user and save the id for later. The user provides the details of the credit card through the React application. We send it straight to the Stripe API. Stripe API responds with a payment method id. Our frontend app sends it to our NestJS API. Our NestJS API gets the request and charges the user using the Stripe API. There are various ways to integrate NestJS and Stripe. Although there are some ready-to-use libraries, they don’t have many weekly downloads. Therefore, in this article, we implement Stripe into our NestJS application ourselves. Fortunately, this is a straightforward process. Let’s start by adding some environment variables we will need. app.module.ts import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import * as Joi from '@hapi/joi'; @Module({ imports: [ ConfigModule.forRoot({ validationSchema: Joi.object({ STRIPE_SECRET_KEY: Joi.string(), STRIPE_CURRENCY: Joi.string(), FRONTEND_URL: Joi.string(), // ... }) }), // ... ], // ... }) export class AppModule {} .env STRIPE_SECRET_KEY=sk_test_... STRIPE_CURRENCY=usd FRONTEND_URL=http://localhost:3500 # ...The STRIPE_SECRET_KEY is the secret key we copied from our Stripe dashboard. Although we could support payments in various currencies, we store the currency in the STRIPE_CURRENCY variable in this simple example. We want our React application to send authenticated requests to our API. Therefore, we need to set up Cross-Origin Resource Sharing. If you want to know more about CORS, check out Cross-Origin Resource Sharing. Avoiding Access-Control-Allow-Origin CORS error main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { ConfigService } from '@nestjs/config'; async function bootstrap() { const app = await NestFactory.create(AppModule); const configService = app.get(ConfigService); app.enableCors({ origin: configService.get('FRONTEND_URL'), credentials: true }); // ... await app.listen(3000); } bootstrap(); Setting up Stripe We don’t have to make requests to the Stripe API manually. There is a library that can do that for us.npm install stripeWe need to provide the above package with the secret Stripe key. To do that, let’s create a service. stripe.service.ts import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import Stripe from 'stripe'; @Injectable() export default class StripeService { private stripe: Stripe; constructor( private configService: ConfigService ) { this.stripe = new Stripe(configService.get('STRIPE_SECRET_KEY'), { apiVersion: '2020-08-27', }); } }Above, we use the STRIPE_SECRET_KEY variable we’ve defined before. We also need to define the version of the Stripe API. Stripe sometimes makes changes to their API that isn’t backward-compatible. To avoid issues, we can define the version of the API we want to use. At the time of writing this article, the current API version is 2020-08-27. If you want to know more about the changes to the API, check out the API changelog Creating a customer In our application, we want only the authenticated users to be able to make payments. Because of that, we can create a Stripe customer for each of our users. To do that, let’s add the createCustomer method to our StripeService. stripe.service.ts import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import Stripe from 'stripe'; @Injectable() export default class StripeService { private stripe: Stripe; constructor( private configService: ConfigService ) { this.stripe = new Stripe(configService.get('STRIPE_SECRET_KEY'), { apiVersion: '2020-08-27', }); } public async createCustomer(name: string, email: string) { return this.stripe.customers.create({ name, email }); } }The stripe.customers.create function calls the Stripe API and returns the data bout the Stripe customer. We need to save it in our database. To do able to do that, let’s modify our UserEntity. user.entity.ts import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; import { Exclude } from 'class-transformer'; @Entity() class User { @PrimaryGeneratedColumn() public id: number; @Column({ unique: true }) public email: string; @Column() public name: string; @Column() @Exclude() public password: string; @Column() public stripeCustomerId: string; // ... } export default User;Now, we can use all of the above in the UsersService: users.service.ts import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import User from './user.entity'; import CreateUserDto from './dto/createUser.dto'; import StripeService from '../stripe/stripe.service'; @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository, private stripeService: StripeService ) {} async create(userData: CreateUserDto) { const stripeCustomer = await this.stripeService.createCustomer(userData.name, userData.email); const newUser = await this.usersRepository.create({ ...userData, stripeCustomerId: stripeCustomer.id }); await this.usersRepository.save(newUser); return newUser; } // ... } If you want to know more about authentication, check out API with NestJS #3. Authenticating users with bcrypt, Passport, JWT, and cookies In the upcoming articles in this series, we’ll be able to use the stripeCustomerId to, for example, save credit cards for the user and retrieve them. Charging the user The last part of our NestJS API is the logic for charging the user. Let’s start with adding the charge method to our StripeService: stripe.service.ts import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import Stripe from 'stripe'; @Injectable() export default class StripeService { private stripe: Stripe; constructor( private configService: ConfigService ) { this.stripe = new Stripe(configService.get('STRIPE_SECRET_KEY'), { apiVersion: '2020-08-27', }); } public async createCustomer(name: string, email: string) { return this.stripe.customers.create({ name, email }); } public async charge(amount: number, paymentMethodId: string, customerId: string) { return this.stripe.paymentIntents.create({ amount, customer: customerId, payment_method: paymentMethodId, currency: this.configService.get('STRIPE_CURRENCY'), confirm: true }) } }There a few important things happening above: the paymentMethodId is an id sent by our frontend app after saving the credit card details, the customerId is the Stripe customer id of a user that is making the payment, the confirm flag is set to true to indicate that we want to confirm the payment immediately. Instead of setting the confirm flag to true, we can also confirm the payment separately. To use the above logic, let’s create the ChargeController: charge.controller.ts import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common'; import JwtAuthenticationGuard from '../authentication/jwt-authentication.guard'; import CreateChargeDto from './dto/createCharge.dto'; import RequestWithUser from '../authentication/requestWithUser.interface'; import StripeService from '../stripe/stripe.service'; @Controller('charge') export default class ChargeController { constructor( private readonly stripeService: StripeService ) {} @Post() @UseGuards(JwtAuthenticationGuard) async createCharge(@Body() charge: CreateChargeDto, @Req() request: RequestWithUser) { await this.stripeService.charge(charge.amount, charge.paymentMethodId, request.user.stripeCustomerId); } } createCharge.dto.ts import { IsString, IsNotEmpty, IsNumber } from 'class-validator'; export class CreateChargeDto { @IsString() @IsNotEmpty() paymentMethodId: string; @IsNumber() amount: number; } export default CreateChargeDto; Using Stripe with React To ta

Introduction to Stripe with React | NestJS.io