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:
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:
terminalnpm i --save @stripe/stripe-js @stripe/react-stripe-js
Now it's time to create a payment modal:
jsximport 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}> <CheckoutForm/> </Elements> </div> ); } 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.
jsxconst CheckoutForm = () => { const [payment, setPayment] = useState(false); const stripe = useStripe(); const elements = useElements(); const handleSubmit = async (e) => { e.preventDefault(); const { error, token } = await stripe.createToken(elements.getElement(CardElement)); if(!error){ setPayment('processing'); let response = await fetch(fetchUrl, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ name: name, mail: mail, token: token.id }) }); 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> </form> </div> ); }
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!
.
javascriptconst 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 = request.body.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({ customer: customer.id, amount: 1000, currency: 'eur', description: 'Charge example', }); charge ? 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.