Integrating Stripe Payments in Ionic Vue JS App with Capacitor and NodeJS

Aaron K Saunders - May 10 '23 - - Dev Community

This code will show you how to setup your Ionic Vue application to support the Payment Sheet From Stripe using the Capacitor Community Stripe Plugin. We will also create a small server application to run locally to create the payment intent secret that is required for the application to work properly with stripe. Finally we will show how to package and deploy the application to a mobile device using Ionic Capacitor

You need to have a stripe account setup for this demo. See documentation for setting up your account here - https://stripe.com/docs/implementation-guides/core-payments/welcome-stripe

Image description

Video

The Vue Client Application

create ionic vue application

ionic start --type vue  
Enter fullscreen mode Exit fullscreen mode

Install the Capacitor Stripe plugin using the following command:

npm install @capacitor-community/stripe
Enter fullscreen mode Exit fullscreen mode

Additional plugin configuration available here https://stripe.capacitorjs.jp/docs/configuration

Create .env in root of project to hold the

VITE_STRIPE_PUBLISHABLE_KEY='pk_test_VALUE'
Enter fullscreen mode Exit fullscreen mode

Initialize the Stripe plugin with your publishable key in your App.vue file:

<template>
  <ion-app>
    <ion-router-outlet />
  </ion-app>
</template>

<script setup lang="ts">
import { IonApp, IonRouterOutlet } from '@ionic/vue';
import { Capacitor } from '@capacitor/core';
import { Stripe } from '@capacitor-community/stripe';

// Initialize the Stripe plugin
if (Capacitor.isPluginAvailable("Stripe")) {
  debugger;
  Stripe.initialize({
    publishableKey : import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY,
  })
}
</script>
Enter fullscreen mode Exit fullscreen mode

Create a new component for a StripeCheckoutButton and add the following code.

Make sure to adjust the IP address for the API call to match the address of your computer so this can be tested appropriately on device.

This code accomplishes two things
1) make API call to get payment intent from the server
2) Present the payment sheet to get credit card information from the user and charge the user.

<template>
    <ion-button @click=handleCheckout :disabled="loading">
      {{loading ? "Loading..." : "Checkout - Payment Sheet"}}
    </ion-button>
</template>
<script setup lang="ts">
import { Stripe } from "@capacitor-community/stripe";
import { ref } from 'vue';
import { IonButton } from "@ionic/vue";

const loading = ref<boolean>(false);

const handleCheckout = async () => {
    loading.value =true;

    try {
      const response = await fetch(
        "http://192.168.1.56:3000/create-payment-intent",
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ amount: 1000, currency: "usd" }),
        }
      );

      const data = await response.json();
      console.log("PaymentIntent client secret:", data.clientSecret);

      await Stripe.createPaymentSheet({
        paymentIntentClientSecret: data.clientSecret,
        merchantDisplayName: "Inclusive Innovation Incubator",
      });

      const { paymentResult } = await Stripe.presentPaymentSheet();
      console.log(paymentResult);

    } catch (error) {
      console.error("Payment failed:", error);
    } finally {
      loading.value = false;
    }
  };
</script>

Enter fullscreen mode Exit fullscreen mode

When the Payment Sheet is displayed, the user can enter their payment information and submit the payment. If the payment succeeds, the paymentIntent object will contain information about the successful payment. If the payment fails, an error will be thrown.

The Server

We need to create a Payment Intent endpoint on a server to create the payment-intent client secret and pass it to the Payment Sheet.

For the purpose of this example we will create a small node server and run it locally.

First create a new directory in your project called server

mkdir server
cd server
Enter fullscreen mode Exit fullscreen mode

Then initialize a node project

npm init
Enter fullscreen mode Exit fullscreen mode

Then install the required libraries

npm install express body-parser cors stripe
Enter fullscreen mode Exit fullscreen mode

First of all, install an npm package called dotenv using the following command in your node.js project root directory;

npm install dotenv --save
Enter fullscreen mode Exit fullscreen mode

dotenv package automatically loads environment variables from .env file into process object in node.js applications.

Create a .env file in your server root directory and add the stripe key, we will use it in the createPaymentIntent method

STRIPE_SECRET_KEY='sk_test_VALUE'
Enter fullscreen mode Exit fullscreen mode

Next create an file called index.js as the main file for your server; it has one route /create-payment-intent.

In this code, we're importing the createPaymentIntent method from the createPaymentIntent.js file, and setting up an Express app that listens for POST requests to the /createPaymentIntent path.

When a POST request is received at this path, the createPaymentIntent function is called with the request and response objects.

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const { createPaymentIntent } = require('./createPaymentIntent');

const app = express();

app.use(bodyParser.json());
app.use(cors());

app.post('/create-payment-intent', createPaymentIntent);

app.listen(3000, () => {
  console.log('Server listening on port 3000');
});
Enter fullscreen mode Exit fullscreen mode

Then create the file that has the code for the /create-payment-intent route.

In this code, we're requiring the stripe package and creating a createPaymentIntent function that takes in a req (request) and res (response) object.

Inside the function, we're extracting the amount and currency values from the request body, and using the Stripe SDK to create a new payment intent with those values.

Once the payment intent is created, we're sending the client secret back to the client in the response body, which can then be used to complete the payment process.

Here we also use the information from the .env file to set the API key

require('dotenv').config();
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

function createPaymentIntent(req, res) {
  const { amount, currency } = req.body;

  stripe.paymentIntents.create({
    amount,
    currency,
    // You can also add additional parameters here, such as a customer ID or payment method ID
    metadata: { integration_check: 'accept_a_payment' },
  })
    .then(paymentIntent => {
      res.json({ clientSecret: paymentIntent.client_secret });
    })
    .catch(error => {
      console.error(error);
      res.status(500).send({ error: 'An error occurred while creating the payment intent' });
    });
}

module.exports = {
  createPaymentIntent,
};
Enter fullscreen mode Exit fullscreen mode

To run the server, navigate to the directory where your index.js file is located, and run node index.js. This will start the server and allow you to test your payment process locally.

You can also use a client like postman or thunderclient to test the API locally

Running the Application

First start the node server

node index.js
Enter fullscreen mode Exit fullscreen mode

Next launch your capacitor application, I am using IOS so I need to add capacitor ios package.

npx cap add ios
Enter fullscreen mode Exit fullscreen mode

To actually build and run the project you then use the following commands

npx cap add ios
npx ionic build
npx cap copy ios;
npx cap run ios --external --no-build --no-sync
Enter fullscreen mode Exit fullscreen mode

Links

Stripe Plugin - https://stripe.capacitorjs.jp/
Capacitor - https://capacitorjs.com/
Ionic Vue - https://ionicframework.com/docs/vue
Stripe Account Setup - https://stripe.com/docs/implementation-guides/core-payments/welcome-stripe

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .