Create A Subscription System with Stripe and Vue js
Tags: vue, stripe, javascript, tutorial
Published: 2022-07-21 00:07:00

Hello 🖐🏼, in this guide, I will show you how to build a subscription system with Vue.js and Stripe. Our working environment is Vue.js Options API, Vue Router, and Express.js as a backend manager. For the UI/UX, I'm using Vuetify. If you are looking for the Vue 3 Composition API and TypeScript integration, check this tutorial [Create a subscription system with Vue 3, Vite, and Stripe] (https://codingpr.com/create-a-subscription-system-with-vue-3/). Due to the extensiveness of this tutorial, I'm leaving the link to my website.

{% embed https://codingpr.com/vue-stripe-subscriptions/ %}

If you don't have a project, you can copy mine here:
[vue-stripe-subscriptions](https://github.com/Denisse-AB/vue-options-stripe-subscriptions)

## 1. Set Up Stripe.

First, let's set up our environment. Copy in your env file your publishable key from the stripe dashboard; you can find this key in the developer's section of the dashboard. Create two products in the dashboard products section, the Basic plan for five dollars and the Premium plan for ten. Copy the products IDs in the env file.

```bash

      VUE_APP_STRIPE_KEY=<YOUR-PUBLISHABLE-KEY>
      VUE_APP_BASIC_PLAN=<YOUR-BASIC-PLAN>
      VUE_APP_PREMIUM_PLAN=<YOUR-PREMIUM-PLAN>
    
```

- Once you have opened your account with Stripe, copy the script tag into the head of index.html.

```html

    <head>
      <script src="https://js.stripe.com/v3/"></script>
    </head>
    
```

## 2. Integration.

Our first move to integrate Stripe into Vue.js is making the on-click event when the customer wants to sign up for your subscription service. We are collecting the email and the customer's full name; in production, you should collect additional information, like the customer's address.

```html

    <v-card-actions>
      <v-btn
        id="stripeBtn"
        class="mb-2"
        block
        :loading="loading"
        @click="Signup"
      >
        Sign Up
      </v-btn>
    </v-card-actions>
    
```
- In this try and catch block, we are sending, to the backend, the customer personal information that we collected from the sign-up form. If we get a response, push the plan view with the customer id as a parameter. Check Vue Router Docs on how to set up passing parameters between views.

```javascript

    // methods
    import PostService from '../post-service'

    async Signup() {
      const { email, fullname } = this
      try {
        const res = await PostService.createCust(
          email,
          fullname
        )

        if (res.data.customer) {
          this.$router.push({
            name:'Plan',
            params: {
              fullName: fullname,
              customerId: res.data.customer
            },
            props: true
          })
        }

      } catch (error) {
        this.alert1 = true;
        this.alertTxt = 'Error, Try again!'
      }
    }
    
```
- Create a file in the root of the src folder, the job of this file is to send http requests to the backend with Axios.

```javascript

    import axios from 'axios';

    const url = 'http://localhost:3000/api/posts';

    class PostService {
      // Create customer
      static async createCust(email, fullname) {
        const res = await axios.post(url, {
          email, fullname
        });
        return res;
      }
      // Create subscription
      static async createSubs(customerId, priceId) {
        const res = await axios.post(`${url}/subs`, {
          customerId, priceId
        });
        return res;
      }
      // Delete subscription
      static async delete(subscriptionId) {
        const res = await axios.post(`${url}/delete`, {
          subscriptionId,
        });
        return res;
      }
    }
    export default PostService;
    
```

- After receiving the response from the server with the customer id, Vue Router will push the second step; your customer needs to choose a plan. Make two buttons with two different on-click events. One is to subscribe to the five-dollar plan and the other to the ten-dollar plan.

```html

    <v-card-actions>
      <v-btn
        id="btnColor"
        :disabled="disabled"
        class="mx-auto mb-2"
        @click="subsPlan1"
      >
        Select
      </v-btn>
    </v-card-actions>
    <v-card-actions>
      <v-btn
        id="btnColor"
        :disabled="disabled2"
        class="mx-auto mb-2"
        @click="subsPlan2"
      >
        Seclect
      </v-btn>
    </v-card-actions>
    
```

- The create subscription function will receive the parameters of the plan that the customer chooses plus the props from step one. This function will send, to the backend, the customer id and the price id and create the subscription; If the response data is good, the checkout view will be pushed with parameters.

```javascript

    props: {
      fullName: String,
      customerId: String
    },

    data: () => ({
      disabled: false,
      disabled2: false,
      alert2: false,
      alertTxt: '',
    }),

    methods: {
      async createSubscription(priceId, price, plan) {
        const {fullName, customerId } = this
        try {
          const res = await PostService.createSubs(
            customerId,
            priceId,
          )

          if (res.data) {
            const subscriptionId = res.data.subscriptionId
            const clientSecret = res.data.clientSecret
            this.$router.push({
              name: 'Checkout',
              params: {
                fullName,
                price,
                plan,
                clientSecret,
                subscriptionId
              }
            })
          }

        } catch (err) {
          this.alert2 = true
          this.disabled = false
          this.alertTxt = 'An error has occurred. Try again later'
        }
      },

      async subsPlan1() {
        const priceId = process.env.VUE_APP_BASIC_PLAN
        const price = '5.00'
        const plan = 'Basic'
        this.disabled = true
        this.disabled2 = false
        await this.createSubscription(priceId, price, plan)
      },

      async subsPlan2() {
        const priceId = process.env.VUE_APP_PREMIUM_PLAN
        const price = '10.00'
        const plan = 'Premium'
        this.disabled2 = true
        this.disabled = false
        await this.createSubscription(priceId, price, plan)
      }
    }
    
```

## 3. Mounting The Card Element.

This is the last step of the frontend integration, mount the card element and create the submit event.

```html

    <!-- Stripe Element-->
    <div
      ref="card"
      class="inputCard"
    />

    <!-- Error messages in this element -->
    <div
      id="card-errors"
      role="alert"
    />
    <br>
    <v-alert
      v-model="alert"
      color="red"
      dense
      dismissible
      type="error"
    >
      <!-- alertTxt -->
    </v-alert>
    <v-btn
      id="stripeBtn"
      class="my-3"
      block
      :loading="loading"
      @click="Submit"
    >
      Pay with Stripe
    </v-btn>
    
```
- Use the client secret to access the Stripe function "confirm card payment". Inside this function, send the payment method and the customer billing info; Check the list of parameters you can send to stripe . If the subscription is successful, push thank you view with the subscription id as a parameter.

```javascript

    import PostService from '../post-service'

    const stripe = window.Stripe(process.env.VUE_APP_STRIPE_KEY)

    // Create an instance of Elements.
    const elements = stripe.elements()
    const style = {
      base: {
        color: '#32325d',
        fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
        fontSmoothing: 'antialiased',
        fontSize: '16px',
        '::placeholder': {
          color: '#aab7c4',
        },
      },
      invalid: {
        color: '#fa755a',
        iconColor: '#fa755a',
      },
    };

    const card = elements.create('card', { style: style })

    export default {
      props: {
        fullName: String,
        price: String,
        plan: String,
        clientSecret: String,
        subscriptionId: String
      },

      mounted() {
        card.mount(this.$refs.card)

        card.on('change', (event) => {
          this.displayError(event)
        })
      },

      methods: {
        displayError(event) {
          const displayError = document.getElementById('card-errors')
          if (event.error) {
            displayError.textContent = event.error.message
          } else {
            displayError.textContent = ''
          }
        },

        async Submit() {
          this.loading = true
          const { clientSecret, fullName, alert, alertTxt, loading } = this
          const result = await stripe.confirmCardPayment(clientSecret, {
            payment_method: {
              type: 'card',
              card: card,
              billing_details: {
                name: fullName,
              }
            }
          })

          if (result.error) {
            alert = true
            alertTxt = result.error.message
            loading = false
          } else {
            // Successful subscription payment
            // The subscription automatically becomes active upon payment.
            this.$router.push({
              name: 'ThankYou',
              params: {
                subscriptionId: this.subscriptionId
              }
            })
          }
        }
      }
    }
    
```

Check the backend here: [Subscription System With Stripe and Express](https://codingpr.com/express-stripe-subscriptions/)