In this guide, we are going to show you how to integrate a hosted backend in Back4App with Stripe API. The best architecture choice to build it on Back4App is using a very powerful feature called Cloud Functions. Once you finish the cloud function integration with Stripe you will be able to use this integration for all your front-end projects (Web, iOS, Android). This guide presents a complete Stripe integration using a web example.
Integrate Stripe on a web project using Back4App Cloud Functions.
What is Stripe?
Stripe is a tech company operating in over 25 countries, which allows both individuals and businesses to accept payments over the Internet. Stripe focuses on providing the technical, fraud prevention, and banking infrastructure required to operate online payment systems.
This tutorial will walk you through the steps of creating functions and integrating Stripe API to your Parse Server into your web app.
More specifically, in this guide, we will create an environment in which a user can log in or sign up, register credit cards, and generate example purchases with them via Stripe Payments. Also, you will be guided on how to set up the whole process as well as test if the connection to the server and the Stripe API is working properly.
1 - Create a Stripe Account
Go to Stripe and click on the sign up to create an account. There, you just need to provide your personal information and to which country your account belongs.
Next, verify your Stripe account (you will receive an email containing a verification link from Stripe). Click on that link, and then follow the steps to confirm your Stripe email address.
2 - Setting up your database classes
After configuring the Stripe environment for Step 1, go to your Back4App app dashboard so you can set up your database. This step is not obligatory since Parse will automatically create the classes while the cloud functions try to create a new object, but we will go through them to explain which fields will be created and why.
There will be two classes that will hold your app Stripe related data: PaymentMethod and Payment:
Here is how the classes are laid out:
PaymentMethod
type(String): its value will always be “card”;
card(Object): will hold the complete Stripe data regarding the registered card;
stripeID(String): id referencing this PaymentMethod on the Stripe backend;
user(Pointer to Parse.User): direct reference to which Use this PaymentMethod belongs.
Payment
data(Object): will hold the complete Stripe data regarding the payment;
user(Pointer to Parse.User): direct reference to which User this Payment belongs.
We will also add two new String value columns in your app default User class called setupSecret and customerId that will contain the Stripe ids that relate the User to its Stripe counterpart.
3 - Implementing Cloud Code
Let’s configure the Cloud Code functions in the app, installing the Stripe module and deploying the Code.
If you want to better understand the Cloud Code enviroment, check this guide.
3.1 - Get your Stripe Key
Now, open your Stripe dashboard, navigate to the Developers page at the top and then select API Keys on the left menu. In that section, you will be able to see your Publishable key and your Secret key.
Write down these keys as you will need them later.
3.2 - Cloud Code files
On your computer, create the following files, that will be responsible to install the module and adding your Cloud Code functions to Back4App.
JSON
1{2"dependencies":{3"stripe":"*"4}5}
Add the code below to a new file and don’t forget to paste your Stripe secret key on the top.
main.js
1constSTRIPE_SECRET_KEY=2"YOUR_STRIPE_SECRET_KEY";3const stripe =require("stripe")(STRIPE_SECRET_KEY);45// Stripe needs an unique customer id to create payments, so after6// signing up, this function should be called to do this operation7 Parse.Cloud.define("createStripeCustomer",async(request)=>{8// Get Parse.User object9const userQuery =newParse.Query(Parse.User);10 userQuery.equalTo("objectId", request.params.userId);11let user =await userQuery.first();1213const customer =await stripe.customers.create({email: user.get("email")});14// Creates an stripe setupIntent, that will enable the stripe lib to perform15// a singel operation related to payments16const intent =await stripe.setupIntents.create({17customer: customer.id,18});19// Set and save the stripe ids to the Parse.User object20 user.set({21customerId: customer.id,22setupSecret: intent.client_secret,23});24returnawait user.save(null,{useMasterKey:true});25});2627// Creates new payment method for a registered customer28 Parse.Cloud.define("addNewPaymentMethod",async(request)=>{29// Get Parse.User object30const userQuery =newParse.Query(Parse.User);31 userQuery.equalTo("objectId", request.params.userId);32let user =await userQuery.first();3334// Retrieve complete stripe payment method by its id35const stripePaymentMethod =await stripe.paymentMethods.retrieve(36 request.params.paymentMethodId
37);3839// Create a new SetupIntent so the customer can add a new method next time.40const intent =await stripe.setupIntents.create({41customer:`${stripePaymentMethod.customer}`,42});43 user.set("setupSecret", intent.client_secret);44 user =await user.save(null,{useMasterKey:true});4546// Creates a new Parse object in the PaymentMethod class47let PaymentMethod =newParse.Object("PaymentMethod");48 PaymentMethod.set({49user: user,50type:"card",51stripeId: stripePaymentMethod.id,52card: stripePaymentMethod.card,53});54 PaymentMethod.save();55returntrue;56});5758// Creates a new payment using a valid payment method59 Parse.Cloud.define("createNewPayment",async(request)=>{60// Get Parse.User object61const userQuery =newParse.Query(Parse.User);62 userQuery.equalTo("objectId", request.params.userId);63let user =await userQuery.first();6465const{ amount, currency, payment_method }= request.params.paymentData;6667// Look up the Stripe customer id.68const customer = user.get("customerId");6970// Create a charge using an unique idempotency key71// to protect against double charges.72const idempotencyKey =newDate().getTime();73const payment =await stripe.paymentIntents.create(74{75 amount,76 currency,77 customer,78 payment_method,79off_session:false,80confirm:true,81confirmation_method:"manual",82},83{ idempotencyKey }84);85// If the result is successful, write it back to the database.86let Payment =newParse.Object("Payment");87 Payment.set({88user: user,89data: payment,90});91await Payment.save();9293returntrue;94});
4 - Upload functions to Cloud Code
Go to the Back4App website, log in and then find your app. After that, click on Dashboard link and you will end up on the page shown below. To deploy your Cloud Code, click on the + ADD button and find the main.js and package.json files that you created in the previous step, then click on the DEPLOY button.
You have just configured Cloud Code functions that you can use on any platform! Check the Deploy & call functions guide to learn how to call them. On the next step, you will work with a JavaScript project that calls them.
5 - Integrating a JavaScript app with Cloud Code
Now you will see an example of a straightforward HTML page with JavaScript that has three main features: signing in or up a user on Parse, creating valid payment methods (credit cards), and creating new payments, charging these methods belonging to the user.
Go ahead and create a new directory in your computer and a new HTML file with the following code inside it:
In this file you will find two main sections, the first one being the authentication one, which will be rendered by default if the user is not logged in yet. After logging in, the payment section will be shown, containing all the forms responsible for creating data on Stripe and also communicating with the Cloud Code functions on Back4App.
We now need to create a JavaScript function containing the code that ties it all together, called index.js:
index.js
1// Define and initialize Parse and Stripe libs2constPARSE_APP_ID="YOUR_PARSE_ID";3constPARSE_JS_KEY="YOUR_PARSE_JS_KEY";4 Parse.initialize(PARSE_APP_ID,PARSE_JS_KEY);5 Parse.serverURL ="https://parseapi.back4app.com/";67constSTRIPE_PUBLISHABLE_KEY="YOUR_STRIPE_PUBLISHABLE_KEY";8const stripe =Stripe(STRIPE_PUBLISHABLE_KEY);910// Holds the currentUser complete Parse object11let currentUser =null;12constsetCompleteCurrentUser=async()=>{13// Called when user is already signed in, completing14// the currentUser object with the full one15 currentUser =awaitnewParse.Query(Parse.User)16.equalTo("objectId", Parse.User.current().id)17.first();18// Retrieve and render user cards and payments19retrieveCurrentUserPaymentMethods();20retrieveCurrentUserPayments();21};2223constretrieveCurrentUserPaymentMethods=async()=>{24// Query and render user PaymentMethods25const PMQuery =newParse.Query("PaymentMethod");26 PMQuery.equalTo("user", Parse.User.current());27 paymentMethods =await PMQuery.find();28renderPaymentMethodOptions(paymentMethods);29};3031constretrieveCurrentUserPayments=async()=>{32// Query and render user Payments33const paymentsQuery =newParse.Query("Payment");34 paymentsQuery.equalTo("user", Parse.User.current());35 payments =await paymentsQuery.find();36renderPayments(payments);37};3839constrenderPaymentMethodOptions=async(paymentMethods)=>{40for(let paymentMethod of paymentMethods){41const optionId =`card-${paymentMethod.get("stripeId")}`;42let optionElement = document.getElementById(optionId);4344// Add a new option if one doesn't exist yet.45if(!optionElement){46 optionElement = document.createElement("option");47 optionElement.id = optionId;48 document
49.querySelector("select[name=payment-method]")50.appendChild(optionElement);51}5253 optionElement.value = paymentMethod.get("stripeId");54 optionElement.text =`${paymentMethod.get("card").brand} •••• ${55 paymentMethod.get("card").last4
56} | Expires ${paymentMethod.get("card").exp_month}/${57 paymentMethod.get("card").exp_year
58}`;59}60};6162constrenderPayments=(payments)=>{63for(let payment of payments){64let liElement = document.getElementById(`payment-${payment.id}`);65if(!liElement){66 liElement = document.createElement("li");67 liElement.id =`payment-${payment.id}`;68}6970const paymentData = payment.get("data");71let content ="";72if(73 paymentData.status ==="new"||74 paymentData.status ==="requires_confirmation"75){76 content =`Creating Payment for ${formatAmount(77 paymentData.amount,78 paymentData.currency
79)}`;80}elseif(paymentData.status ==="succeeded"){81const card = paymentData.charges.data[0].payment_method_details.card;82 content =`Payment for ${formatAmount(83 paymentData.amount,84 paymentData.currency
85)} on ${card.brand} card •••• ${card.last4}${paymentData.status}!`;86}else{87 content =`Payment for ${formatAmount(88 paymentData.amount,89 paymentData.currency
90)}${paymentData.status}`;91}92 liElement.innerText = content;93 document.querySelector("#payments-list").appendChild(liElement);94}95};9697// Checks if user is already signed in on Parse98if(Parse.User.current()!==null){99setCompleteCurrentUser();100// Hide auth screen and show payment fields101 document.getElementById("auth").style.display ="none";102 document.getElementById("content").style.display ="block";103}104105// Toggle signin and signup forms106 document
107.querySelector("#signup-toggle")108.addEventListener("click",async(_event)=>{109 document.getElementById("auth-signin-form").style.display ="none";110 document.getElementById("auth-signup-form").style.display ="block";111clearAuthFormFields();112});113 document
114.querySelector("#signin-toggle")115.addEventListener("click",async(_event)=>{116 document.getElementById("auth-signup-form").style.display ="none";117 document.getElementById("auth-signin-form").style.display ="block";118clearAuthFormFields();119});120121// Clear auth form fields122constclearAuthFormFields=()=>{123 document
124.querySelector("#auth")125.querySelectorAll("input")126.forEach((input)=>(input.value =""));127};128129// Handle auth forms130 document
131.querySelector("#auth-signin-form")132.addEventListener("submit",async(event)=>{133 event.preventDefault();134toggleAllButtonsEnabled(false);135const form =newFormData(event.target);136const email = form.get("email");137const password = form.get("password");138139// Try to signin on Parse140try{141let user =await Parse.User.logIn(email, password);142if(user !==null){143 currentUser = user;144// Hide auth screen and show payment fields145 document.getElementById("auth").style.display ="none";146 document.getElementById("content").style.display ="block";147clearAuthFormFields();148}149}catch(error){150alert(error);151}152toggleAllButtonsEnabled(true);153});154155 document
156.querySelector("#auth-signup-form")157.addEventListener("submit",async(event)=>{158 event.preventDefault();159toggleAllButtonsEnabled(false);160const form =newFormData(event.target);161const email = form.get("email");162const password = form.get("password");163164// Try to signup on Parse165try{166let user =await Parse.User.signUp(email, password,{email: email });167// Cloud code to create Stripe user and intent168 user =await Parse.Cloud.run("createStripeCustomer",{userId: user.id });169if(user !==null){170 currentUser = user;171// Hide auth screen and show payment fields172 document.getElementById("auth").style.display ="none";173 document.getElementById("content").style.display ="block";174clearAuthFormFields();175}176}catch(error){177alert(error);178}179toggleAllButtonsEnabled(true);180});181182// Signout from Parse183 document.querySelector("#signout").addEventListener("click",async(_event)=>{184await Parse.User.logOut();185 currentUser =null;186// Show auth screen and hide payment fields187 document.getElementById("auth").style.display ="block";188 document.getElementById("content").style.display ="none";189});190191// Creates stripe card UI element192const elements = stripe.elements();193const cardElement = elements.create("card");194 cardElement.mount("#card-element");195196// Handle add new card form197 document
198.querySelector("#payment-method-form")199.addEventListener("submit",async(event)=>{200 event.preventDefault();201toggleAllButtonsEnabled(false);202if(!event.target.reportValidity()){203return;204}205const form =newFormData(event.target);206const cardholderName = form.get("name");207208const result =await stripe.confirmCardSetup(209 currentUser.get("setupSecret"),210{211payment_method:{212card: cardElement,213billing_details:{214name: cardholderName,215},216},217}218);219220if(result.error){221alert(result.error.message);222toggleAllButtonsEnabled(true);223returnnull;224}225226let setupIntent = result.setupIntent;227228// Cloud code to add new payment method229let cloudCodeResult =await Parse.Cloud.run("addNewPaymentMethod",{230userId: currentUser.id,231paymentMethodId: setupIntent.payment_method,232});233234toggleAllButtonsEnabled(true);235alert("Success on creating a new payment method!");236237// Update payment method options238retrieveCurrentUserPaymentMethods();239});240241// Handles new payment form242 document
243.querySelector("#payment-form")244.addEventListener("submit",async(event)=>{245 event.preventDefault();246toggleAllButtonsEnabled(false);247const form =newFormData(event.target);248const amount =Number(form.get("amount"));249const currency = form.get("currency");250// Gets selected card option id251const paymentMethod = form.get("payment-method");252const paymentData ={253payment_method: paymentMethod,254 currency,255amount:formatAmountForStripe(amount, currency),256status:"new",257};258// Cloud code to create new payment259let cloudCodeResult =await Parse.Cloud.run("createNewPayment",{260userId: currentUser.id,261paymentData: paymentData,262});263264toggleAllButtonsEnabled(true);265alert("Success on creating a new payment!");266267retrieveCurrentUserPayments();268});269270// Helper functions271consttoggleAllButtonsEnabled=(enabledValue)=>{272 document
273.querySelectorAll("button")274.forEach((button)=>(button.disabled =!enabledValue));275};276277constformatAmount=(amount, currency)=>{278 amount =zeroDecimalCurrency(amount, currency)279? amount
280:(amount /100).toFixed(2);281returnnewIntl.NumberFormat("en-US",{282style:"currency",283 currency,284}).format(amount);285};286287// Format amount for Stripe288constformatAmountForStripe=(amount, currency)=>{289returnzeroDecimalCurrency(amount, currency)290? amount
291: Math.round(amount *100);292};293294// Check if we have a zero decimal currency295// https://stripe.com/docs/currencies#zero-decimal296constzeroDecimalCurrency=(amount, currency)=>{297let numberFormat =newIntl.NumberFormat(["en-US"],{298style:"currency",299currency: currency,300currencyDisplay:"symbol",301});302const parts = numberFormat.formatToParts(amount);303let zeroDecimalCurrency =true;304for(let part of parts){305if(part.type ==="decimal"){306 zeroDecimalCurrency =false;307}308}309return zeroDecimalCurrency;310};
Make sure to add your Stripe publishable key and also your Parse app ID and JS key to the top of the file. These are some of the key elements to check out and understand in this script:
Check the usage of the Parse.User.current method when loading the script for the first time to render the correct part of the page;
The form submit action listeners that will perform actions on Parse, like signing in or up and calling the Cloud Code functions to create the Stripe related objects and save on your Back4App database;
The “retrieve” and “return” methods that make queries on Parse to retrieve the current user’s Payment and PaymentMethod objects.
Before testing the app, add the following stylesheet in a CSS file called app.css inside the same directory: