All Articles

Connect Stripe to your web application

Published Jun 17, 2021

2880px stripe logo  revised 2016 svg

Today I want to share with you some of my thoughts about integrating Stripe into a web application. I will not be talking about Stripe pricing, fees, etc. I want to focus on the integration itself. There will be no example project, only abstract Stripe code. So some parts you’ll have to implement by yourself depending on your needs and your backend. Hope you’ll find it useful 🙂 Let’s go!

Overview

Stripe is an online payment processing platform for internet businesses. With help of Stripe you can accept payments on your website or in your mobile application easily.

Stripe also provides Stripe Connect to make integration easier for different types of marketplaces.

Basically, there are three types of connected accounts. Detailed information you can always find here. We are going to use Express account.

Task formulation

Let’s say we have a marketplace. Obviously, we need to have customers (who will buy stuff with credit card) and providers (who will sell stuff). Also a good point will be if we take some fee from the provider for using our marketplace 💸.

So, that could be our flow:

  1. User adds a credit card to buy with in the future
  2. User buys some items with specific credit card
  3. Money goes to the provider and platform also takes some fee

Configure Stripe

First of all, we should connect our marketplace to Stripe. After that, we need to get public and secret keys. You can find them under Developers -> API keys setting in the menu ⚙️. Let’s also find Stripe Client ID under Settings -> Connect -> Settings. Scroll down to the bottom and there it is - Test mode client ID. I also recommend to enable OAuth for Express accounts, because we are going to need that later.

Let’s also add redirect URL (find it in the connect settings) to confirm payments later. You can use something like this BACKEND_URL/stripe/callback.

Let’s save our settings in .env file.

STRIPE_SECRET=stripe_secret
STRIPE_CLIENT_ID=client_id
STRIPE_RETURN_URL=http://localhost:3000/stripe/callback

Also make sure to have Viewing test data checkbox enabled to use Stripe in test mode.

              2021 06 16   18 11 47

After that we can initialize Stripe instance as follows

import Stripe from 'stripe';

export const stripe = new Stripe(process.env.STRIPE_SECRET), {
  apiVersion: '2020-08-27',
});

Connect providers

If we want to send money to the provider after the user buys items we have to create a connected account for each provider. As was mentioned before, we are going to use Express Account.

To create a connected account we just simply redirect the provider to https://connect.stripe.com/express/oauth/authorize?response_type=code&redirect_uri=${STRIPE_RETURN_URL}&client_id=${STRIPE_CLIENT_ID}&scope=read_write. Or we can use manual flow using API, but it requires a lot of effort from our side, because we need to handle all the validation by ourselves.

After the user fills in all the required information he’ll be redirected to STRIPE_RETURN_URL with code parameter in the query. We should grab this code and make an additional API call to finalize account creation.

import { Request, Response } from 'express';

async callbackRoute(req: Request, res: Response) {

  const { code } = req.query;

  const response = await stripe.oauth.token({
     grant_type: 'authorization_code',
     code,
  });
  
  const stripeAccountId = response.stripe_user_id;
  
  // IMPORTANT: create logic to store this account id somewhere in the provider
 
  res.redirect(process.env.FRONTEND_CONNECT_SUCCESS_URL)
}

Important step is to store the Stripe Connected Account ID somewhere in the providers table/collection, to be able to make payments in the future. You should implement this logic by yourself depending on which database you are using. Just associate this ID with the provider.

Create customer

To be able to charge users (buy items) we should make them customers inside Stripe system. To create a customer just call this method:

const customer = await stripe.customers.create({
  email: user.email,
  phone: user.phone
});

All parameters are optional. After the customer has been created we also should associate his ID with the user. For example, we can store this ID in the customer field of user schema.

Add payment method

Usually, each user could have multiple payment cards. To create a payment method just call this method:

const paymentMethod = await stripe.paymentMethods.create({
  type: 'card',
  card: {
    number: '4242424242424242',
    exp_month: 7,
    exp_year: 2025,
    cvc: '123',
  },
});

So we have our first payment method created, but we have a problem. This method is not attached to the customer. To do so we just call another API method:

const paymentMethod = await stripe.paymentMethods.attach(
  'PAYMENT_METHOD_ID',
  { customer: 'CUSTOMER_ID' }
);

But here’s one caveat. If we wanna be able to charge the user that is not present in our flow during payment we should setup the card as off-session.

const customer = await stripe.customers.create({
  email: user.email,
  phone: user.phone
});

const paymentMethod = await stripe.paymentMethods.create({
  type: 'card',
  card: {
    number: '4242424242424242',
    exp_month: 7,
    exp_year: 2025,
    cvc: '123',
  },
});

const intent = await stripe.setupIntents.create({
  customer: customer.id,
  payment_method: paymentMethod.id,
  usage: "off_session",
  confirm: true,
  payment_method_types: ["card"],
  return_url: process.env.STRIPE_RETURN_URL,
});

Obviously, we should create setupIntent for each new payment method.

Here’s one very important step if your customers are from Europe. There’s a thing called Strong Customer Authentication. That means when the user’s making a payment he may be requested to make an additional verification step (usually enter a code sent by his bank). To handle this thing, we should check status of the previously created setup intent.

// if intent requires additional verification 
// we redirect the user to that url
if (
  intent &&
  intent.next_action &&
  intent.next_action.type === "redirect_to_url"
) {
  res.redirect(intent.next_action.redirect_to_url.url)
}

After the user approves or declines authentication he’ll be redirected to return_url (that we added to SetupIntent ) with setup_intent query parameter. So we need to make some checks in there also.

// STRIPE_RETURN_URL route handler

if (setup_intent) {
  let intent;

  try {
    intent = await stripe.setupIntents.retrieve(setup_intent);

    if (intent.status === "requires_confirmation") {
      await stripe.setupIntents.confirm(setup_intent);

      return res.redirect(
        `${process.env.STRIPE_RETURN_URL}?setup_intent=${intent.id}`
      );
    }

    if (intent.status === "succeeded") {
      return res.send("Payment method has been confirmed");
    } else {
      throw new Error("Payment method has been canceled");
    }
  } catch (e) {
    // cancel setup intent
    await stripe.setupIntents.cancel(intent.id);

    return res.status(500).send(e.message);
  }
}

Now, we can charge the user off_session using saved payment methods.

Default payment methods

If the user do not have any payments methods the first one will be set as the default. To set another payment method as default we need to update the customer:

const customer = await stripe.customers.update(
  'CUSTOMER_ID',
  { invoice_settings: { default_payment_method: 'PAYMENT_METHOD_ID' } }
);

If you want, you can also store payment methods IDs in database and associate them with a customer. It could help you in the future.

Make a payment

To make payments we use PaymentIntents API.

const intent = await stripe.paymentIntents.create({
  amount: 1000,
  currency: "eur",
  payment_method,
  off_session: true,
  customer: "CUSTOMER_ID",
  confirm: true,
  payment_method_types: ["card"],
});

Please, notify that Stripe works with cents. So our customer won’t be charged 1000 EUR, but only 10 EUR.

As you can see we did not define any destination. In that case all the money goes to our Platform’s Stripe account. If we want to transfer money directly to the provider we just set the destination parameter as follows.

const intent = await stripe.paymentIntents.create({
  ...
  customer: "CUSTOMER_ID",
  transfer_data: {
    destination: "STRIPE_CONNECTED_ACCOUNT_ID" // provider's stripe connected account
  }
});

Fee

If we want to take some fee from providers for using our marketplace, we can do it also:

const intent = await stripe.paymentIntents.create({
  ...
  customer: "CUSTOMER_ID",
  transfer_data: {
    destination: "STRIPE_CONNECTED_ACCOUNT_ID"
  }
  application_fee_amount: "FEE_IN_CENTS"
});

Transfer money from Platform to the Provider directly

const transfer = await stripe.transfers.create({
  amount: 400,
  currency: 'eur',
  destination: 'STRIPE_CONNECTED_ACCOUNT_ID',
});

Final thoughts

I think that’s enough information for you not to be lost in Stripe API’s big ocean 😂

The point of this article was to guide you on which steps you should take to start integrating a basic payment logic into your application. Because, when I faced payments for the first time, I was kinda lost 🙃 Cause, Stripe has good API documentation, but there are so many methods and concepts in it, that you simply don’t know where to start.

So I just wanted to help you in this crazy world of javascript frameworks… Good luck bro and remember that you can became a master of something only if you try to implement it by yourself!

Useful links