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. Due to the extensiveness of this tutorial, I'm leaving the link to my website.
If you don't have a project, you can copy mine here:
vue-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.
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.
<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.
<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.
// 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.
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.
<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.
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.
<!-- 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.
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
Top comments (0)