Tab 1
Tab 2
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 (একদম বিস্তারিত, যেন তুমি পাশে বসে দেখাচ্ছি)
- Stripe এ নতুন account খোলা
- Browser খোলো
- যাও: https://dashboard.stripe.com/register
- “Sign up” button click করো
- তোমার email দাও (যেমন: yourname@gmail.com)
- Password দাও (strong password)
- “Create account” click করো
- Email verify করো
- Gmail/Yahoo খোলো
- Stripe থেকে verification email আসবে
- “Verify email address” button click করো
- Redirect হয়ে Stripe dashboard এ চলে আসবে
- Business details দাও (প্রথমবার login করলে চাইবে)
- Business name: “Pizza Palace” (John এর business)
- Business type: Individual
- Country: United States (বা তোমার country)
- Phone number দাও
- “Continue” click করো
- Test Mode এ থাকো (development এর জন্য)
- Dashboard এর উপরে ডানদিকে “Test mode” toggle on থাকবে
- এটা on রাখো – production এ off করবে
- API Keys নাও (সবচেয়ে গুরুত্বপূর্ণ!)
- Dashboard এর left sidebar এ “Developers” click করো
- “API keys” click করো
- এখানে দেখবে:
- Publishable key →
pk_test_51PYacs...(frontend এ লাগবে) - Secret key →
sk_test_51PYacs...(backend এ লাগবে)
- Publishable key →
- দুটোই copy করে .env file এ রাখো (পরে দেখাবো)
- 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/
- Local testing এ:
- “Select events” click করো
- নিচের events গুলো select করো:
checkout.session.completed(payment success)customer.subscription.updatedcustomer.subscription.deletedinvoice.payment_failed
- “Add endpoint” click করো
- Endpoint তৈরি হবে
- “Reveal signing secret” click করো →
whsec_...copy করো (এটা .env এ রাখো)
- 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=yourapppasswordInstall করো:
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
গল্পের মতো পুরো ফ্লো
- John dashboard খুলল → Plan list দেখল (
GET /subscription/plans/) - Gold plan select করল → Checkout session তৈরি হল (
POST /subscription/create-checkout-session/) - Stripe checkout page এ গেল → Test card দিয়ে pay করল (
4242 4242 4242 4242) - Payment success → Stripe webhook পাঠাল
- Webhook এ:
- Subscription record তৈরি হল
- 500 credits add হল
- CreditHistory entry হল
- Success page এ redirect হল → Dashboard reload
- John দেখল:
- Active Plan: Gold
- Credits: 500
- Credit History তে +500 entry
হয়ে গেল! John এখন paid user।