1. Home
  2. Sms Campaign Project
  3. Project Setup
  4. 03. Subscription activate

03. Subscription activate


1. Stripe Account Setup

গল্প শুরু: John paid plan কিনতে চায়

John তার free account দিয়ে কাজ করছিল। কিন্তু এখন তার business বড় হয়েছে। সে Gold plan কিনতে চায় – $99/month, 500 credits।

তুমি (developer) John এর জন্য Stripe subscription system বানাবে।

Step-by-Step (একদম বিস্তারিত, যেন তুমি পাশে বসে দেখাচ্ছি)

  1. Stripe এ নতুন account খোলা
  • Browser খোলো
  • যাও: https://dashboard.stripe.com/register
  • “Sign up” button click করো
  • তোমার email দাও (যেমন: yourname@gmail.com)
  • Password দাও (strong password)
  • “Create account” click করো
  1. Email verify করো
  • Gmail/Yahoo খোলো
  • Stripe থেকে verification email আসবে
  • “Verify email address” button click করো
  • Redirect হয়ে Stripe dashboard এ চলে আসবে
  1. Business details দাও (প্রথমবার login করলে চাইবে)
  • Business name: “Pizza Palace” (John এর business)
  • Business type: Individual
  • Country: United States (বা তোমার country)
  • Phone number দাও
  • “Continue” click করো
  1. Test Mode এ থাকো (development এর জন্য)
  • Dashboard এর উপরে ডানদিকে “Test mode” toggle on থাকবে
  • এটা on রাখো – production এ off করবে
  1. API Keys নাও (সবচেয়ে গুরুত্বপূর্ণ!)
  • Dashboard এর left sidebar এ “Developers” click করো
  • “API keys” click করো
  • এখানে দেখবে:
    • Publishable keypk_test_51PYacs... (frontend এ লাগবে)
    • Secret keysk_test_51PYacs... (backend এ লাগবে)
  • দুটোই copy করে .env file এ রাখো (পরে দেখাবো)
  1. Webhook Setup করো (এটা না করলে subscription কাজ করবে না!)
  • Dashboard → Developers → Webhooks click করো
  • “Add endpoint” button click করো
  • Endpoint URL দাও:
    • Local testing এ: http://localhost:8000/api/subscription/stripewebhook/
    • Production এ: https://yourdomain.com/api/subscription/stripewebhook/
  • “Select events” click করো
  • নিচের events গুলো select করো:
    • checkout.session.completed (payment success)
    • customer.subscription.updated
    • customer.subscription.deleted
    • invoice.payment_failed
  • “Add endpoint” click করো
  • Endpoint তৈরি হবে
  • “Reveal signing secret” click করো → whsec_... copy করো (এটা .env এ রাখো)
  1. Products & Prices তৈরি করো (এটা না করলে checkout কাজ করবে না)
  • Dashboard → Products click করো
  • “Add product” button click করো
  • Product name দাও: “Gold Plan”
  • Description: “500 credits per month”
  • “Add pricing” click করো
  • Pricing model: Recurring
  • Interval: Monthly
  • Price: $99.00
  • “Create price” click করো
  • Price তৈরি হবে
  • Price ID দেখবে: price_1Qe... → এটা copy করো একইভাবে Silver ($49, price ID), Diamond ($149, price ID) তৈরি করো।

হয়ে গেল! Stripe account ready।


2. Environment Variables

গল্প: John payment করবে, কিন্তু backend keys secure রাখতে হবে

তুমি keys গুলো code এ hardcode করবে না। .env file এ রাখবে।

.env file বানাও (project root এ)

# Stripe Keys (উপরের step থেকে copy করো)
STRIPE_SECRET_KEY=sk_test_51PYacsILVDkqxLbQKcFEGRI9IbUTx2lPBrTQXVwlLkf4iVbhvym9ER2iTy9gemk9U40PYokYqIU6EEnBI52utwQV00yOFoGCQf
STRIPE_PUBLISHABLE_KEY=pk_test_51PYacsILVDkqxLbQ9gmxoY67ibUS1EtIX5b4dAVDDxEixRpy6C5npAj3PJz6YcPXdaH8BruIhzq9jQS8KQMscYS900ysRbOyYR
STRIPE_WEBHOOK_SECRET=whsec_63jnaaq8U8qm3pT5Luevtq3joCpZrrYd

# Frontend URL (success/cancel page এ redirect এর জন্য)
FRONTENDURL=http://localhost:3000/

# Email (to send emails)
EMAIL_HOST_USER=yourgmail@gmail.com
EMAIL_HOST_PASSWORD=yourapppassword

Install করো:

pip install python-dotenv stripe

3. Settings.py Configuration

গল্প: John payment করলে backend Stripe এর সাথে কথা বলবে

import os
from pathlib import Path
from dotenv import load_dotenv

load_dotenv()  # .env file load করে

BASE_DIR = Path(__file__).resolve().parent.parent

# Stripe Configuration
STRIPE_SECRET_KEY = os.getenv("STRIPE_SECRET_KEY")
STRIPE_PUBLISHABLE_KEY = os.getenv("STRIPE_PUBLISHABLE_KEY")
STRIPE_WEBHOOK_SECRET = os.getenv("STRIPE_WEBHOOK_SECRET")
FRONTEND_URL = os.getenv("FRONTENDURL", "http://localhost:3000/")

# Email Configuration (invitation/password reset এর জন্য)
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD")

# Templates folder (email template এর জন্য)
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],  # templates folder যোগ করা
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

4. Models

গল্প: John payment করলে database এ কী কী save হবে

from django.db import models
from django.contrib.auth import get_user_model
from django.utils import timezone
from datetime import timedelta

User = get_user_model()

# User model এ যোগ করো (existing file এ)
# class User(AbstractBaseUser):
#     # তোমার existing fields...
#     stripe_customer_id = models.CharField(max_length=100, blank=True, null=True)

class SubscriptionPlan(models.Model):
    """
    Admin থেকে plan তৈরি করা হবে। Stripe থেকে price ID নিয়ে এখানে রাখতে হবে।
    """
    plan_name = models.CharField(max_length=100)  # e.g. Silver, Gold
    amount = models.DecimalField(max_digits=10, decimal_places=2)  # e.g. 49.99
    monthly_credit = models.DecimalField(max_digits=10, decimal_places=3, default=0)  # e.g. 100.000
    stripe_price_id = models.CharField(max_length=100)  # Stripe থেকে copy
    is_active = models.BooleanField(default=True)

    def __str__(self):
        return f"{self.plan_name} - ${self.amount}/month"

class UserSubscriptionPlan(models.Model):
    """
    User কোন plan কিনেছে তার record। Webhook থেকে তৈরি হবে।
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    subscription_plan = models.ForeignKey(SubscriptionPlan, on_delete=models.CASCADE)
    stripe_subscription_id = models.CharField(max_length=100, blank=True, null=True)
    payment_status = models.CharField(max_length=20, default="pending")  # success/pending/fail
    is_active = models.BooleanField(default=False)
    expire_date = models.DateTimeField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.user.email} - {self.subscription_plan.plan_name}"

class CreditBalance(models.Model):
    """
    User এর current credit balance
    """
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    balance = models.DecimalField(max_digits=10, decimal_places=3, default=0)

    def __str__(self):
        return f"{self.user.email} - {self.balance} credits"

class CreditHistory(models.Model):
    """
    Credit add/deduct এর history
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    credit = models.DecimalField(max_digits=10, decimal_places=3)
    remark = models.CharField(max_length=255, blank=True)
    is_added = models.BooleanField(default=True)  # True = add, False = deduct
    date = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-date']

    def __str__(self):
        return f"{self.user.email} {'+' if self.is_added else '-'}{self.credit}"

5. Serializers

গল্প: Frontend থেকে data আসবে, backend validate করবে

from rest_framework import serializers
from .models import SubscriptionPlan, UserSubscriptionPlan, CreditBalance, CreditHistory

class SubscriptionPlanSerializer(serializers.ModelSerializer):
    class Meta:
        model = SubscriptionPlan
        fields = '__all__'

class UserSubscriptionPlanSerializer(serializers.ModelSerializer):
    subscription_plan = SubscriptionPlanSerializer()
    class Meta:
        model = UserSubscriptionPlan
        fields = '__all__'

class CreditBalanceSerializer(serializers.ModelSerializer):
    class Meta:
        model = CreditBalance
        fields = ['balance']

class CreditHistorySerializer(serializers.ModelSerializer):
    class Meta:
        model = CreditHistory
        fields = ['credit', 'remark', 'is_added', 'date']

6. Views

গল্প: John payment করলে backend কী কী করবে

import stripe
from django.conf import settings
from django.utils import timezone
from datetime import timedelta
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework import status
from .models import SubscriptionPlan, UserSubscriptionPlan, CreditBalance, CreditHistory, User
from .serializers import SubscriptionPlanSerializer, UserSubscriptionPlanSerializer, CreditBalanceSerializer, CreditHistorySerializer

stripe.api_key = settings.STRIPE_SECRET_KEY

class PlanListView(APIView):
    def get(self, request):
        plans = SubscriptionPlan.objects.filter(is_active=True)
        serializer = SubscriptionPlanSerializer(plans, many=True)
        return Response(serializer.data)

class CreateCheckoutSessionView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        plan_id = request.data.get('plan_id')
        plan = SubscriptionPlan.objects.get(id=plan_id)

        if not request.user.stripe_customer_id:
            customer = stripe.Customer.create(email=request.user.email)
            request.user.stripe_customer_id = customer.id
            request.user.save()

        session = stripe.checkout.Session.create(
            customer=request.user.stripe_customer_id,
            payment_method_types=['card'],
            line_items=[{'price': plan.stripe_price_id, 'quantity': 1}],
            mode='subscription',
            success_url=settings.FRONTEND_URL + 'subscription/success',
            cancel_url=settings.FRONTEND_URL + 'subscription/cancel',
        )
        return Response({"url": session.url})

class StripeWebhookView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        payload = request.body
        sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')

        try:
            event = stripe.Webhook.construct_event(payload, sig_header, settings.STRIPE_WEBHOOK_SECRET)
        except:
            return Response(status=400)

        if event.type == 'checkout.session.completed':
            session = event.data.object
            user = User.objects.get(stripe_customer_id=session.customer)
            plan = SubscriptionPlan.objects.get(stripe_price_id=session.display_items[0].price.id)

            UserSubscriptionPlan.objects.update_or_create(
                user=user,
                defaults={
                    'subscription_plan': plan,
                    'stripe_subscription_id': session.subscription,
                    'payment_status': 'success',
                    'is_active': True,
                    'expire_date': timezone.now() + timedelta(days=30),
                }
            )

            balance, _ = CreditBalance.objects.get_or_create(user=user)
            balance.balance += plan.monthly_credit
            balance.save()

            CreditHistory.objects.create(
                user=user,
                credit=plan.monthly_credit,
                remark=f"Monthly credits from {plan.plan_name}",
                is_added=True
            )

        return Response(status=200)

class ActiveSubscriptionView(APIView):
    permission_classes = [IsAuthenticated]
    def get(self, request):
        sub = UserSubscriptionPlan.objects.filter(user=request.user, is_active=True).first()
        if sub:
            serializer = UserSubscriptionPlanSerializer(sub)
            return Response(serializer.data)
        return Response({"message": "No active subscription"})

class CreditBalanceView(APIView):
    permission_classes = [IsAuthenticated]
    def get(self, request):
        balance, _ = CreditBalance.objects.get_or_create(user=request.user)
        serializer = CreditBalanceSerializer(balance)
        return Response(serializer.data)

class CreditHistoryView(APIView):
    permission_classes = [IsAuthenticated]
    def get(self, request):
        history = CreditHistory.objects.filter(user=request.user)
        serializer = CreditHistorySerializer(history, many=True)
        return Response(serializer.data)

class CancelSubscriptionView(APIView):
    permission_classes = [IsAuthenticated]
    def post(self, request):
        sub = UserSubscriptionPlan.objects.filter(user=request.user, is_active=True).first()
        if sub and sub.stripe_subscription_id:
            stripe.Subscription.delete(sub.stripe_subscription_id)
            sub.is_active = False
            sub.save()
            return Response({"message": "Subscription cancelled"})
        return Response({"message": "No active subscription"}, status=400)

7. URLs

from django.urls import path
from .views import (
    PlanListView, CreateCheckoutSessionView, StripeWebhookView,
    ActiveSubscriptionView, CreditBalanceView, CreditHistoryView, CancelSubscriptionView
)

urlpatterns = [
    path('subscription/plans/', PlanListView.as_view()),
    path('subscription/create-checkout-session/', CreateCheckoutSessionView.as_view()),
    path('subscription/stripewebhook/', StripeWebhookView.as_view()),
    path('subscription/active/', ActiveSubscriptionView.as_view()),
    path('subscription/credit-balance/', CreditBalanceView.as_view()),
    path('subscription/credit-history/', CreditHistoryView.as_view()),
    path('subscription/cancel/', CancelSubscriptionView.as_view()),
]

8. Admin Panel Setup

গল্প: তুমি admin হয়ে plan তৈরি করবে

from django.contrib import admin
from .models import SubscriptionPlan, UserSubscriptionPlan, CreditBalance, CreditHistory

@admin.register(SubscriptionPlan)
class SubscriptionPlanAdmin(admin.ModelAdmin):
    list_display = ['plan_name', 'amount', 'monthly_credit', 'is_active']
    list_filter = ['is_active']
    search_fields = ['plan_name']

@admin.register(UserSubscriptionPlan)
class UserSubscriptionPlanAdmin(admin.ModelAdmin):
    list_display = ['user', 'subscription_plan', 'is_active', 'expire_date']
    list_filter = ['is_active', 'payment_status']
    search_fields = ['user__email']

@admin.register(CreditBalance)
class CreditBalanceAdmin(admin.ModelAdmin):
    list_display = ['user', 'balance']
    search_fields = ['user__email']

@admin.register(CreditHistory)
class CreditHistoryAdmin(admin.ModelAdmin):
    list_display = ['user', 'credit', 'is_added', 'remark', 'date']
    list_filter = ['is_added']
    search_fields = ['user__email']

Admin এ গিয়ে plan তৈরি করো – stripe_price_id দিয়ে।


9. Complete Flow

গল্পের মতো পুরো ফ্লো

  1. John dashboard খুলল → Plan list দেখল (GET /subscription/plans/)
  2. Gold plan select করল → Checkout session তৈরি হল (POST /subscription/create-checkout-session/)
  3. Stripe checkout page এ গেল → Test card দিয়ে pay করল (4242 4242 4242 4242)
  4. Payment success → Stripe webhook পাঠাল
  5. Webhook এ:
  • Subscription record তৈরি হল
  • 500 credits add হল
  • CreditHistory entry হল
  1. Success page এ redirect হল → Dashboard reload
  2. John দেখল:
  • Active Plan: Gold
  • Credits: 500
  • Credit History তে +500 entry

হয়ে গেল! John এখন paid user।


How can we help?