Implementing one time payments using React and Stripe

A few days ago, I was trying to implement one time payments using Stripe, but I was getting an error:

Customer has not linked source with id in Stripe.

The process to solve the problem was:

  1. User introduces card details (client-side)
  2. Stripe generates a token (client-side)
  3. Creating a customer using the token as a source (server-side)
  4. Charging the customer (server-side)

Disclaimer: I'm using React in the client-side and Firebase in the server-side.


Stripe provides 2 packages to work with React. Install them:

npm i --save @stripe/stripe-js @stripe/react-stripe-js

Now it's time to create a payment modal:

import React, { useState, useEffect }                    from 'react';
import { loadStripe }                                    from '@stripe/stripe-js';
import { CardElement, Elements, useStripe, useElements } from '@stripe/react-stripe-js';

const PaymentModal = () => {
    const stripePromise = loadStripe('STRIPE_PUBLIC_API_KEY');
    return  (
        <div className = 'Modal'>
            <Elements stripe = {stripePromise}>

export default PaymentModal;

At this point, we can define the checkout form. The checkout form will have a <CardElement/>. After the user introduces card details and clicks on the button, a token will be generated by Stripe and sent to the server via a fetch() function.

const CheckoutForm = () => {
    const [payment, setPayment] = useState(false);
    const stripe                = useStripe();
    const elements              = useElements();
    const handleSubmit = async (e) => {
        const { error, token } = await stripe.createToken(elements.getElement(CardElement));
            let response = await fetch(fetchUrl, {
                method: 'POST',
                headers: {'Content-Type':'application/json'},
                body: JSON.stringify({
                    name: name,
                    mail: mail,
            if(response.ok) {
                console.log('Payment successful!');
    return (
        <div className = 'Checkout'>
            <form className = 'Modal-Wrap' onSubmit = {handleSubmit}>
                <CardElement options = {{hidePostalCode: true}}/>
                    <button type = 'submit' disabled = {!stripe}>Pay</button>   


The server will receive the token, will create a new customer using the token as a source and finally charge the customer. If everything went well, the server will respond with Ok!. Otherwise, the server will respond with Error!.

const functions  = require('firebase-functions');
const cors       = require('cors')({origin: true});
const nodemailer = require('nodemailer');
const stripe     = require('stripe')('STRIPE_PRIVATE_API_KEY');

exports.oneTimePayment = functions.https.onRequest( (request, response) => {
    return cors(request, response, async () => {
        let name  =;
        let email = request.body.mail;
        let token = request.body.token;
        let customer = await stripe.customers.create({
            name: name,
            email: email,
            source: token
        let charge = await stripe.charges.create({
            amount: 1000,
            currency: 'eur',
            description: 'Charge example',
        ? response.status(200).send('Ok!')
        : response.status(500).send('Error!');

Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.