McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Build Paystack Subscriptions in Vue
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Build Paystack Subscriptions in Vue

To build Paystack subscriptions in Vue, create a plan on your backend using the Paystack Plans API, then initialize a transaction with the plan code. Paystack handles the recurring billing automatically. Your backend listens for subscription webhooks (subscription.create, invoice.payment_failed, subscription.disable) and updates your database. Your Vue frontend displays the subscription status by querying your backend API.

In this article

  1. 1. How Paystack Subscriptions Work
  2. 2. Create Plans on Your Backend
  3. 3. Subscription Checkout in Vue
  4. 4. Backend Subscribe Endpoint
  5. 5. Handle Subscription Webhooks
  6. 6. Display Subscription Status in Vue
  7. 7. Let Customers Cancel
  8. 8. Ship Payments Faster

How Paystack Subscriptions Work

Paystack subscriptions follow a simple model. You create a plan that defines the amount, interval (daily, weekly, monthly, annually), and currency. When a customer pays for the first time using a transaction initialized with a plan code, Paystack creates a subscription and charges them automatically on the next billing date.

The flow for a Vue application:

  1. Your backend creates a plan on Paystack (one-time setup per pricing tier).
  2. Your Vue app shows the available plans by fetching them from your backend.
  3. When the customer clicks "Subscribe," Vue calls your backend to initialize a transaction with the plan code.
  4. Vue opens the Paystack popup. The customer pays.
  5. Paystack creates the subscription and starts recurring billing.
  6. Your backend receives webhooks for each billing event and updates the subscription status in your database.
  7. Your Vue app displays the current subscription status by querying your backend.

Vue handles steps 2, 3, 4, and 7. Everything else is backend work.

Create Plans on Your Backend

Create plans using the Paystack Plans API from your backend:

// server.js (Express)
var PAYSTACK_SECRET = process.env.PAYSTACK_SECRET_KEY;

app.post('/api/plans', async function(req, res) {
  var response = await fetch('https://api.paystack.co/plan', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + PAYSTACK_SECRET,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      name: req.body.name,
      amount: req.body.amount, // in kobo
      interval: req.body.interval, // daily, weekly, monthly, quarterly, biannually, annually
      currency: 'NGN',
    }),
  });

  var data = await response.json();

  if (data.status) {
    // Save plan_code to your database
    res.json({ plan_code: data.data.plan_code, name: data.data.name });
  } else {
    res.status(400).json({ error: data.message });
  }
});

// Fetch available plans for Vue to display
app.get('/api/plans', async function(req, res) {
  // Return plans from your database
  var plans = [
    { name: 'Basic', plan_code: 'PLN_xxxbasic', amount: 500000, interval: 'monthly' },
    { name: 'Pro', plan_code: 'PLN_xxxpro', amount: 1500000, interval: 'monthly' },
  ];
  res.json({ plans: plans });
});

You typically create plans once (through the Paystack dashboard or a seed script) and store the plan codes in your database. Your Vue app fetches the list of plans from your backend, not from Paystack directly.

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Subscription Checkout in Vue

Initialize a transaction with the plan code and open the Paystack popup:

<script setup>
import { ref, onMounted } from 'vue';

var API_URL = import.meta.env.VITE_API_URL || 'http://localhost:4000';
var plans = ref([]);
var loading = ref(false);
var selectedPlan = ref(null);
var email = ref('');

onMounted(async function() {
  var script = document.createElement('script');
  script.src = 'https://js.paystack.co/v2/inline.js';
  document.head.appendChild(script);

  var res = await fetch(API_URL + '/api/plans');
  var data = await res.json();
  plans.value = data.plans;
});

async function subscribe(plan) {
  if (!email.value) return;
  loading.value = true;
  selectedPlan.value = plan;

  var res = await fetch(API_URL + '/api/subscribe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      email: email.value,
      plan_code: plan.plan_code,
    }),
  });

  var data = await res.json();

  if (!data.access_code) {
    alert(data.error || 'Failed to initialize');
    loading.value = false;
    return;
  }

  var popup = new window.PaystackPop();
  popup.checkout({
    accessCode: data.access_code,
    onSuccess: async function(transaction) {
      var verifyRes = await fetch(
        API_URL + '/api/verify?reference=' + transaction.reference
      );
      var result = await verifyRes.json();

      if (result.verified) {
        alert('Subscription activated!');
      }
      loading.value = false;
    },
    onCancel: function() {
      loading.value = false;
    },
  });
}
</script>

<template>
  <div>
    <input v-model="email" type="email" placeholder="Your email" />
    <div v-for="plan in plans" :key="plan.plan_code">
      <h3>{{ plan.name }}</h3>
      <p>NGN {{ (plan.amount / 100).toLocaleString() }} / {{ plan.interval }}</p>
      <button @click="subscribe(plan)" :disabled="loading || !email">
        Subscribe
      </button>
    </div>
  </div>
</template>

Backend Subscribe Endpoint

The subscribe endpoint initializes a transaction with the plan code:

app.post('/api/subscribe', async function(req, res) {
  var email = req.body.email;
  var planCode = req.body.plan_code;

  var response = await fetch('https://api.paystack.co/transaction/initialize', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + PAYSTACK_SECRET,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      email: email,
      plan: planCode,
      // amount is not needed when a plan is provided
    }),
  });

  var data = await response.json();

  if (data.status) {
    res.json({
      access_code: data.data.access_code,
      reference: data.data.reference,
    });
  } else {
    res.status(400).json({ error: data.message });
  }
});

When you pass a plan to the Initialize Transaction endpoint, you do not need to pass an amount. Paystack pulls the amount from the plan. After the customer pays, Paystack automatically creates a subscription and schedules the next charge.

Handle Subscription Webhooks

Subscription lifecycle events arrive as webhooks. Your backend must handle these events to keep your database in sync:

var crypto = require('crypto');

app.post('/api/webhooks/paystack', express.raw({ type: 'application/json' }), function(req, res) {
  var hash = crypto
    .createHmac('sha512', PAYSTACK_SECRET)
    .update(req.body)
    .digest('hex');

  if (hash !== req.headers['x-paystack-signature']) {
    return res.status(400).send('Invalid signature');
  }

  var event = JSON.parse(req.body);

  if (event.event === 'subscription.create') {
    // New subscription created
    var sub = event.data;
    // Save: sub.subscription_code, sub.plan.plan_code, sub.customer.email, sub.next_payment_date
  }

  if (event.event === 'invoice.payment_failed') {
    // Recurring charge failed
    // Update subscription status to 'past_due'
    // Notify the customer via email
  }

  if (event.event === 'subscription.not_renew') {
    // Customer cancelled, subscription will not renew
    // Keep access until current period ends
  }

  if (event.event === 'subscription.disable') {
    // Subscription fully disabled
    // Revoke access
  }

  if (event.event === 'charge.success') {
    // Could be initial or recurring charge
    // Update payment history
  }

  res.sendStatus(200);
});

Vue cannot receive webhooks. This endpoint runs on your backend. See Handle Paystack Webhooks in Vue for the architectural explanation.

Display Subscription Status in Vue

Create a composable that fetches the subscription status from your backend:

// src/composables/useSubscription.js
import { ref, onMounted } from 'vue';

var API_URL = import.meta.env.VITE_API_URL || 'http://localhost:4000';

export function useSubscription() {
  var subscription = ref(null);
  var loading = ref(true);

  onMounted(async function() {
    try {
      var res = await fetch(API_URL + '/api/subscription', {
        headers: { Authorization: 'Bearer ' + localStorage.getItem('token') },
      });
      var data = await res.json();
      subscription.value = data.subscription;
    } catch (err) {
      subscription.value = null;
    } finally {
      loading.value = false;
    }
  });

  return { subscription, loading };
}

Use it in a subscription management page:

<script setup>
import { useSubscription } from '../composables/useSubscription';

var { subscription, loading } = useSubscription();
</script>

<template>
  <div v-if="loading">Loading...</div>
  <div v-else-if="subscription">
    <p>Plan: {{ subscription.planName }}</p>
    <p>Status: {{ subscription.status }}</p>
    <p>Next billing: {{ subscription.nextPaymentDate }}</p>
    <a :href="subscription.manageUrl" target="_blank">Manage Subscription</a>
  </div>
  <div v-else>
    <p>No active subscription.</p>
  </div>
</template>

Let Customers Cancel

Paystack provides two ways to let customers manage subscriptions:

  1. Paystack-hosted management link: When you create a subscription, Paystack returns a manage_url that the customer can visit to cancel or update their card. Store this link and show it in your Vue UI.
  2. API cancellation: Your backend calls the Disable Subscription endpoint:
app.post('/api/subscription/cancel', async function(req, res) {
  var response = await fetch('https://api.paystack.co/subscription/disable', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + PAYSTACK_SECRET,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      code: req.body.subscription_code,
      token: req.body.email_token,
    }),
  });

  var data = await response.json();
  res.json(data);
});

The email_token is sent in the subscription creation webhook. Store it alongside the subscription code. After cancellation, the customer keeps access until the current billing period ends.

Ship Payments Faster

This guide is part of the Paystack integration guides by language and framework series.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Subscription logic lives on the backend. Vue displays the subscription state and collects the initial payment.
  • ✓Create plans on your backend using the Paystack Plans API. Do not hardcode plan codes in frontend code.
  • ✓Initialize the subscription checkout by passing the plan code when initializing a transaction. Paystack handles recurring billing after the first charge.
  • ✓Listen for subscription webhooks on your backend: subscription.create, invoice.payment_failed, invoice.update, and subscription.not_renew.
  • ✓Show subscription status in Vue by querying your backend API. Use a computed property or composable for clean state management.
  • ✓Customers can manage their subscription (cancel, update card) through a Paystack-hosted link or through your own UI backed by the Subscriptions API.

Frequently Asked Questions

Can I create Paystack plans from the Vue frontend?
No. The Plans API requires your secret key. Create plans from your backend or from the Paystack dashboard. Your Vue app fetches the available plans from your backend API.
Does Paystack charge the customer automatically after the first payment?
Yes. When you initialize a transaction with a plan code, Paystack creates a subscription after the first successful charge and handles all future billing automatically based on the plan interval.
How do I know if a recurring charge failed?
Paystack sends an invoice.payment_failed webhook to your backend. Listen for this event and update the subscription status in your database. You should also notify the customer so they can update their card.
Can the customer update their payment card?
Yes. Paystack provides a manage_url with every subscription that customers can visit to update their card details. You can also use the Update Subscription endpoint on your backend to change the card programmatically.
What happens when a subscription is cancelled?
The customer keeps access until the end of the current billing period. Paystack will not charge them again. You receive a subscription.not_renew webhook when the customer cancels and a subscription.disable webhook when the subscription is fully deactivated.

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account

Keep reading

Paystack Integration Guides by Language and Framework

A complete directory of Paystack integration guides organized by programming language and framework. Each guide covers accepting payments, verifying transactions, handling webhooks, and building subscriptions in your specific stack.

Read more

Verify Paystack Payments in Svelte and SvelteKit

Verify Paystack transactions in SvelteKit using server endpoints for popup verification and load functions for redirect callback verification. Covers amount validation, idempotent fulfillment, and SSR verification for instant results.

Read more

Handle Paystack Webhooks in Flask

Receive and verify Paystack webhooks in your Flask application. This guide covers accessing request.data for raw bytes, HMAC SHA-512 signature verification, processing charge.success events, and making handlers idempotent.

Read more

Build Paystack Subscriptions in ASP.NET Core

Implement Paystack recurring billing in ASP.NET Core. This guide covers creating plans, subscribing customers through transaction initialization, handling renewal webhooks with C#, and cancelling subscriptions.

Read more

Building a Fully Custom Checkout on the Paystack Charge API

When the hosted checkout does not fit your product, you can build every piece of the payment experience yourself. This guide walks through designing the form, posting card or bank data to your server, running the Charge API flow, and shipping a production-ready checkout.

Read more

Paystack Apple Pay Domain Verification Failing

Apple Pay on Paystack requires domain verification. Apple sends a request to a specific file path on your domain, and if it cannot find the file or the response is wrong, verification fails silently. The fix depends on your hosting platform, and each one has its own gotcha.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.