Django Subscription Midtrans
A production-ready Django package for subscription billing with Midtrans Core API integration. Provides a complete subscription management system including recurring payments, invoicing, wallet top-ups, webhook handling, and admin dashboard.
Note: This package uses the Midtrans Core API (server-side charge via
/v2/charge,/v1/subscriptions,/v1/invoices), not Midtrans Snap.
Features
- Subscription Plans — Configurable interval billing (daily/weekly/monthly), trial periods, max billing cycles, feature flags per plan
- Payment Methods — Credit Card, Bank Transfer (BCA/BNI/BRI/Permata/CIMB), GoPay, ShopeePay, QRIS, E-Channel (Mandiri Bill), Wallet
- Automatic Billing — Celery Beat periodic tasks handle recurring charges, payment retries, grace periods, expiration
- Invoice System — Auto-generated invoices with line items, tax/discount support, PDF-ready data
- Wallet & Top-Up — Internal wallet balance system with top-up via any payment method
- Webhook Processing — Secure Midtrans notification handler with SHA-512 signature verification, duplicate detection
- Admin Dashboard — Django Unfold admin with status badges, inline editing, bulk actions, Midtrans sync
- Notification System — Email, webhook, and in-app notification logging with queued delivery
- Access Middleware — Path-based subscription enforcement with grace period support
- Django Signals — 20+ signals for all subscription/payment lifecycle events
- REST API — Full DRF ViewSet API for plans, subscriptions, payments, invoices, wallet, notifications
- Testing — Comprehensive test suite with factories, mocked Midtrans responses
Architecture
| Layer | Components |
|---|---|
| Admin UI | Django Unfold Admin |
| REST API | DRF ViewSets + Serializers |
| Service Layer | SubscriptionService, WalletService |
| Data / Integration | Models/ORM, MidtransClient, Celery Tasks (Beat) |
| External | Midtrans Core API (v2/charge, v1/subscriptions) |
For detailed Mermaid diagrams, see the Architecture Diagrams documentation.
Quick Start
Installation
From PyPI:
pip install django-subscription-midtrans
From source:
git clone https://github.com/rissets/django-subscription-midtrans.git
cd django-subscription-midtrans
pip install -e ".[dev]"
Step 1: Add to INSTALLED_APPS
INSTALLED_APPS = [
# Django Unfold (must be before django.contrib.admin)
"unfold",
"unfold.contrib.filters",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# Third-party
"rest_framework",
"django_celery_beat",
# Subscription package
"subscriptions",
]
Step 2: Configure Environment Variables
Create a .env file in your project root:
# Django
DJANGO_SECRET_KEY=
DJANGO_DEBUG=True
DJANGO_ALLOWED_HOSTS=*,localhost,127.0.0.1
# Database (default: SQLite, configure for PostgreSQL in production)
# DB_ENGINE=django.db.backends.postgresql
# DB_NAME=subscription_db
# DB_USER=
# DB_PASSWORD=
# DB_HOST=localhost
# DB_PORT=5432
# Celery / Redis
CELERY_BROKER_URL=redis://localhost:6379/0
CELERY_RESULT_BACKEND=redis://localhost:6379/0
# Midtrans Credentials (get from https://dashboard.midtrans.com)
MIDTRANS_SERVER_KEY=
MIDTRANS_CLIENT_KEY=
MIDTRANS_MERCHANT_ID=
MIDTRANS_IS_PRODUCTION=False
# Webhook URL (must be publicly accessible)
# For local dev, use ngrok: ngrok http 8000
MIDTRANS_NOTIFICATION_URL=
Step 3: Add Settings
# settings.py
from decouple import config
# Midtrans
MIDTRANS_SERVER_KEY = config("MIDTRANS_SERVER_KEY", default="")
MIDTRANS_CLIENT_KEY = config("MIDTRANS_CLIENT_KEY", default="")
MIDTRANS_MERCHANT_ID = config("MIDTRANS_MERCHANT_ID", default="")
MIDTRANS_IS_PRODUCTION = config("MIDTRANS_IS_PRODUCTION", default=False, cast=bool)
MIDTRANS_API_BASE_URL = (
"https://api.midtrans.com" if MIDTRANS_IS_PRODUCTION
else "https://api.sandbox.midtrans.com"
)
MIDTRANS_NOTIFICATION_URL = config("MIDTRANS_NOTIFICATION_URL", default="")
# Celery
CELERY_BROKER_URL = config("CELERY_BROKER_URL", default="redis://localhost:6379/0")
CELERY_RESULT_BACKEND = config("CELERY_RESULT_BACKEND", default="redis://localhost:6379/0")
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
# REST Framework
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 20,
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.BasicAuthentication",
],
}
# Subscription Settings (all optional, these are defaults)
SUBSCRIPTION_SETTINGS = {
"PROTECTED_PATHS": [],
"SUBSCRIPTION_REQUIRED_REDIRECT": "/pricing/",
"GRACE_PERIOD_DAYS": 3,
"PAYMENT_EXPIRY_MINUTES": 60,
"AUTO_GENERATE_INVOICE": True,
"AUTO_RETRY_FAILED_PAYMENTS": True,
"MAX_RETRY_ATTEMPTS": 3,
"RETRY_INTERVAL_DAYS": 1,
"INVOICE_NUMBER_PREFIX": "INV",
"SEND_EMAIL_NOTIFICATIONS": True,
"EXPIRY_REMINDER_DAYS": [7, 3, 1],
}
Step 4: Include URLs
# urls.py
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("api/subscriptions/", include("subscriptions.urls")),
]
Step 5: Run Migrations and Setup
# Apply database migrations
python manage.py migrate
# Create periodic Celery tasks
python manage.py setup_periodic_tasks
# Seed sample subscription plans (optional, for development)
python manage.py seed_plans
# Create admin superuser
python manage.py createsuperuser
Step 6: Start Services
You need 4 processes running:
# Terminal 1: Redis (message broker)
redis-server
# Terminal 2: Django development server
python manage.py runserver
# Terminal 3: Celery worker (processes async tasks)
celery -A config worker -l info
# Terminal 4: Celery beat (schedules periodic tasks)
celery -A config beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
Development Setup
Prerequisites
- Python 3.10+
- Redis server
- Git
Clone & Install
git clone https://github.com/rissets/django-subscription-midtrans.git
cd django-subscription-midtrans
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # macOS/Linux
# .venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
# Copy environment file
cp .env.example .env
# Edit .env with your Midtrans sandbox credentials
Midtrans Sandbox Setup
- Register at https://dashboard.sandbox.midtrans.com
- Get your Server Key and Client Key from Settings → Access Keys
- Add them to your
.envfile - For webhooks, expose your local server:
# Install ngrok: https://ngrok.com ngrok http 8000 # Copy the HTTPS URL and set MIDTRANS_NOTIFICATION_URL in .env
Run the Example App
The project includes a full example application demonstrating all features:
# Apply migrations
python manage.py migrate
# Seed plans and periodic tasks
python manage.py seed_plans --reset
python manage.py setup_periodic_tasks --reset
# Create a superuser for admin access
python manage.py createsuperuser
# Start all services (in separate terminals)
redis-server
python manage.py runserver
celery -A config worker -l info
celery -A config beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
Then visit:
- Example App: http://localhost:8000/example/
- Admin Dashboard: http://localhost:8000/admin/
- API Root: http://localhost:8000/api/subscriptions/
Test Credentials (Sandbox)
| Payment Method | Test Details |
|---|---|
| Credit Card | Card: 4811 1111 1111 1114, Exp: 01/29, CVV: 123, OTP: 112233 |
| BCA VA | Use the VA number from the response, simulate payment in Midtrans dashboard |
| BNI VA | Use the VA number from the response |
| GoPay | Use the QR code or deeplink from the response |
| QRIS | Scan the QR code with any QRIS-compatible app (sandbox mode) |
Running Tests
# Run all subscription tests
python manage.py test subscriptions
# Run specific test module
python manage.py test subscriptions.tests.test_models
python manage.py test subscriptions.tests.test_services
python manage.py test subscriptions.tests.test_views
python manage.py test subscriptions.tests.test_client
python manage.py test subscriptions.tests.test_middleware
# Run with verbosity
python manage.py test subscriptions -v 2
Documentation
Full Sphinx documentation is available in the docs/ directory:
cd docs
pip install -r requirements.txt
make html
# Open docs/_build/html/index.html
API Reference
Plans
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/subscriptions/plans/ |
No | List all active plans |
GET |
/api/subscriptions/plans/{slug}/ |
No | Get plan details by slug |
Subscriptions
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/subscriptions/subscriptions/ |
Yes | List user's subscriptions |
POST |
/api/subscriptions/subscriptions/ |
Yes | Create subscription |
GET |
/api/subscriptions/subscriptions/{id}/ |
Yes | Get subscription detail |
GET |
/api/subscriptions/subscriptions/active/ |
Yes | Get active subscription |
POST |
/api/subscriptions/subscriptions/{id}/cancel/ |
Yes | Cancel subscription |
POST |
/api/subscriptions/subscriptions/{id}/pause/ |
Yes | Pause subscription |
POST |
/api/subscriptions/subscriptions/{id}/resume/ |
Yes | Resume subscription |
POST |
/api/subscriptions/subscriptions/{id}/change-plan/ |
Yes | Change subscription plan |
GET |
/api/subscriptions/subscriptions/{id}/sync/ |
Yes | Sync with Midtrans |
Payments
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/subscriptions/payments/ |
Yes | List user's payments |
GET |
/api/subscriptions/payments/{id}/ |
Yes | Get payment detail |
POST |
/api/subscriptions/payments/charge/ |
Yes | Create charge for subscription |
POST |
/api/subscriptions/payments/{id}/refund/ |
Yes | Refund a payment |
GET |
/api/subscriptions/payments/{id}/check-status/ |
Yes | Check Midtrans status |
Invoices
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/subscriptions/invoices/ |
Yes | List user's invoices |
GET |
/api/subscriptions/invoices/{id}/ |
Yes | Get invoice detail |
Wallet
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/subscriptions/wallet/me/ |
Yes | Get user's wallet |
GET |
/api/subscriptions/wallet/transactions/ |
Yes | List wallet transactions |
Top-Ups
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/subscriptions/topups/ |
Yes | List user's top-ups |
POST |
/api/subscriptions/topups/ |
Yes | Create a top-up |
GET |
/api/subscriptions/topups/{id}/ |
Yes | Get top-up detail |
Notifications
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/subscriptions/notifications/ |
Yes | List notifications |
GET |
/api/subscriptions/notifications/unread/ |
Yes | Get unread notifications |
POST |
/api/subscriptions/notifications/{id}/mark-read/ |
Yes | Mark notification as read |
Webhook
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/subscriptions/webhook/notification/ |
No | Midtrans webhook handler |
API Usage Examples
Create Subscription (Bank Transfer - BCA)
curl -X POST http://localhost:8000/api/subscriptions/subscriptions/ \
-H "Content-Type: application/json" \
-b "sessionid=<your-session-id>" \
-d '{
"plan_id": "<plan-uuid>",
"payment_type": "bank_transfer",
"customer_details": {
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"phone": "081234567890"
}
}'
Response:
{
"id": "uuid",
"plan": { "name": "Pro Monthly", "price": "299000.00" },
"status": "pending",
"payment_type": "bank_transfer",
"payments": [{
"order_id": "SUB-xxxxx",
"status": "pending",
"payment_details": {
"bank": "bca",
"va_number": "9898123456789"
}
}]
}
Create Subscription (GoPay)
curl -X POST http://localhost:8000/api/subscriptions/subscriptions/ \
-H "Content-Type: application/json" \
-b "sessionid=<your-session-id>" \
-d '{
"plan_id": "<plan-uuid>",
"payment_type": "gopay"
}'
Response includes deeplink/QR for GoPay payment.
Create Subscription (QRIS)
curl -X POST http://localhost:8000/api/subscriptions/subscriptions/ \
-H "Content-Type: application/json" \
-b "sessionid=<your-session-id>" \
-d '{
"plan_id": "<plan-uuid>",
"payment_type": "qris"
}'
Response includes QR code URL for scanning.
Create Subscription (Credit Card)
curl -X POST http://localhost:8000/api/subscriptions/subscriptions/ \
-H "Content-Type: application/json" \
-b "sessionid=<your-session-id>" \
-d '{
"plan_id": "<plan-uuid>",
"payment_type": "credit_card",
"token": "<card-token-from-midtrans-js>"
}'
Create Subscription (Wallet)
curl -X POST http://localhost:8000/api/subscriptions/subscriptions/ \
-H "Content-Type: application/json" \
-b "sessionid=<your-session-id>" \
-d '{
"plan_id": "<plan-uuid>",
"payment_type": "wallet"
}'
Cancel Subscription
curl -X POST http://localhost:8000/api/subscriptions/subscriptions/<uuid>/cancel/ \
-H "Content-Type: application/json" \
-b "sessionid=<your-session-id>" \
-d '{"reason": "Switching provider", "immediate": false}'
Top Up Wallet
curl -X POST http://localhost:8000/api/subscriptions/topups/ \
-H "Content-Type: application/json" \
-b "sessionid=<your-session-id>" \
-d '{
"amount": 500000,
"payment_type": "bank_transfer",
"bank": "bca"
}'
Refund Payment
curl -X POST http://localhost:8000/api/subscriptions/payments/<uuid>/refund/ \
-H "Content-Type: application/json" \
-b "sessionid=<your-session-id>" \
-d '{"amount": 150000, "reason": "Partial refund"}'
Configuration Reference
All settings are configured via SUBSCRIPTION_SETTINGS dict in your Django settings:
SUBSCRIPTION_SETTINGS = {
# Access Control
"PROTECTED_PATHS": ["/api/premium/", "/pro-features/"],
"SUBSCRIPTION_REQUIRED_REDIRECT": "/pricing/",
# Grace Period
"GRACE_PERIOD_DAYS": 3, # Days after failed payment to keep access
# Payment
"PAYMENT_EXPIRY_MINUTES": 60, # Payment token/VA expiry time
# Invoicing
"AUTO_GENERATE_INVOICE": True, # Auto-create invoice on charge
"INVOICE_NUMBER_PREFIX": "INV", # Invoice number format: INV-2026-0001
# Retry Logic
"AUTO_RETRY_FAILED_PAYMENTS": True,
"MAX_RETRY_ATTEMPTS": 3,
"RETRY_INTERVAL_DAYS": 1,
# Notifications
"SEND_EMAIL_NOTIFICATIONS": True,
"EXPIRY_REMINDER_DAYS": [7, 3, 1], # Days before expiry to send reminders
}
Subscription Lifecycle
Subscription states and transitions:
| From | Event | To |
|---|---|---|
| — | create_subscription() |
PENDING |
| PENDING | Payment success | ACTIVE |
| PENDING | Plan has trial days | TRIALING |
| PENDING | Payment expired/failed | EXPIRED |
| TRIALING | Trial ends + payment success | ACTIVE |
| TRIALING | Trial ends + payment failed | EXPIRED |
| ACTIVE | Renewal payment failed | PAST_DUE |
| ACTIVE | User pauses | PAUSED |
| ACTIVE | User cancels (immediate) | CANCELLED |
| ACTIVE | Period ends + cancel_at_period_end | CANCELLED |
| PAST_DUE | Retry payment success | ACTIVE |
| PAST_DUE | Grace period expired | EXPIRED |
| PAUSED | User resumes | ACTIVE |
| PAUSED | User cancels | CANCELLED |
Two Subscription Modes
| Mode | Payment Types | How It Works |
|---|---|---|
| Native Midtrans Subscription | Credit Card, GoPay (with account_id) | Uses /v1/subscriptions API — Midtrans handles automatic recurring billing |
| Manual Charge Subscription | Bank Transfer, QRIS, ShopeePay, E-Channel, Wallet | Each billing cycle creates a new /v2/charge — managed by Celery Beat |
Celery Periodic Tasks
| Task | Schedule | Description |
|---|---|---|
process_due_subscriptions |
Every 1 hour | Charges subscriptions with next_billing_date <= now |
process_expired_subscriptions |
Every 6 hours | Cancels subscriptions marked cancel_at_period_end |
process_past_due_expiration |
Every 12 hours | Expires subscriptions past grace period |
retry_failed_payments |
Every 4 hours | Retries failed charges (up to MAX_RETRY_ATTEMPTS) |
process_wallet_billing |
Every 1 hour | Deducts wallet balance for wallet-based subscriptions |
mark_overdue_invoices |
Daily 1:00 AM | Marks unpaid invoices as overdue |
send_expiry_reminders |
Daily 9:00 AM | Sends reminders N days before subscription expiry |
sync_midtrans_subscriptions |
Every 6 hours | Syncs native Midtrans subscription statuses |
send_pending_email_notifications |
Every 5 min | Processes queued email notifications |
Webhook Setup
Configure Midtrans Dashboard
- Log in at Midtrans Dashboard (or sandbox)
- Go to Settings → Configuration
- Set Payment Notification URL:
https://yourdomain.com/api/subscriptions/webhook/notification/ - Enable notifications for all transaction statuses
Local Development with ngrok
ngrok http 8000
# Example output: https://abc123.ngrok-free.app
# Set in .env:
# MIDTRANS_NOTIFICATION_URL=https://abc123.ngrok-free.app/api/subscriptions/webhook/notification/
Signature Verification
All incoming webhooks are verified using SHA-512:
SHA512(order_id + status_code + gross_amount + server_key) == signature_key
Invalid signatures are rejected and logged. Duplicate notifications are detected and skipped.
Django Signals
Connect to subscription events in your app:
from django.dispatch import receiver
from subscriptions.signals import (
subscription_activated,
payment_success,
payment_failed,
subscription_plan_changed,
)
@receiver(subscription_activated)
def on_subscription_activated(sender, subscription, **kwargs):
# Grant access to premium features
pass
@receiver(payment_success)
def on_payment_success(sender, payment, **kwargs):
# Send receipt email
pass
@receiver(payment_failed)
def on_payment_failed(sender, payment, **kwargs):
# Notify user of failed payment
pass
@receiver(subscription_plan_changed)
def on_plan_changed(sender, subscription, old_plan, new_plan, **kwargs):
# Adjust user features
pass
Available Signals
| Signal | Arguments | Triggered When |
|---|---|---|
subscription_created |
subscription |
New subscription created |
subscription_activated |
subscription |
Subscription becomes active |
subscription_cancelled |
subscription |
Subscription cancelled |
subscription_paused |
subscription |
Subscription paused |
subscription_resumed |
subscription |
Subscription resumed |
subscription_expired |
subscription |
Subscription expired |
subscription_renewed |
subscription |
Billing cycle renewed |
subscription_plan_changed |
subscription, old_plan, new_plan |
Plan changed |
payment_created |
payment |
New payment created |
payment_success |
payment |
Payment settled |
payment_failed |
payment |
Payment failed |
payment_refunded |
payment |
Payment refunded |
invoice_created |
invoice |
Invoice generated |
invoice_paid |
invoice |
Invoice paid |
invoice_overdue |
invoice |
Invoice past due date |
invoice_voided |
invoice |
Invoice voided |
webhook_received |
webhook_log |
Midtrans webhook received |
webhook_processed |
webhook_log |
Webhook processed |
topup_completed |
topup |
Wallet top-up completed |
wallet_credited |
transaction |
Wallet balance increased |
wallet_debited |
transaction |
Wallet balance decreased |
Middleware
Protect routes based on subscription status:
# settings.py
MIDDLEWARE = [
# ... other middleware
"subscriptions.middleware.SubscriptionAccessMiddleware",
]
SUBSCRIPTION_SETTINGS = {
"PROTECTED_PATHS": ["/api/premium/", "/dashboard/pro/"],
"SUBSCRIPTION_REQUIRED_REDIRECT": "/pricing/",
}
Behavior:
- API requests without active subscription →
403 JSONresponse - HTML requests without active subscription → redirect to pricing page
- Staff users always bypass the check
- Grace period is respected (users with
PAST_DUEwithin grace period still have access)
Admin Dashboard
The package uses Django Unfold for a modern admin interface:
- Plan Management — Create/edit plans, activate/deactivate, view subscriber counts
- Subscription Management — Status badges, pause/resume/cancel actions, sync from Midtrans
- Payment Tracking — View payments with fraud status, check/cancel/expire from admin
- Invoice Management — View invoices with line items, mark paid, void
- Webhook Logs — Audit trail of all Midtrans notifications
- Wallet & Top-Up — View user wallets and transaction history
Access at: http://localhost:8000/admin/
Extending the Package
Custom Signal Handlers
# your_app/signals.py
from django.dispatch import receiver
from subscriptions.signals import subscription_activated
@receiver(subscription_activated)
def grant_premium_access(sender, subscription, **kwargs):
user = subscription.user
user.profile.is_premium = True
user.profile.save()
Override Service Methods
from subscriptions.services import SubscriptionService
class CustomSubscriptionService(SubscriptionService):
def activate_subscription(self, subscription):
result = super().activate_subscription(subscription)
send_welcome_package(subscription.user)
return result
Proxy Models
from subscriptions.models import Subscription
class PremiumSubscription(Subscription):
class Meta:
proxy = True
def has_feature(self, feature_name):
return self.plan.features.get(feature_name, False)
Production Deployment
Environment Variables
DJANGO_SECRET_KEY=<strong-random-key>
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
# PostgreSQL (recommended for production)
DB_ENGINE=django.db.backends.postgresql
DB_NAME=subscription_db
DB_USER=
DB_PASSWORD=
DB_HOST=localhost
DB_PORT=5432
# Redis
CELERY_BROKER_URL=redis://localhost:6379/0
CELERY_RESULT_BACKEND=redis://localhost:6379/0
# Midtrans Production
MIDTRANS_SERVER_KEY=
MIDTRANS_CLIENT_KEY=
MIDTRANS_MERCHANT_ID=
MIDTRANS_IS_PRODUCTION=True
MIDTRANS_NOTIFICATION_URL=https://yourdomain.com/api/subscriptions/webhook/notification/
Production Checklist
- Set
DEBUG=False - Use PostgreSQL (not SQLite)
- Set
MIDTRANS_IS_PRODUCTION=True - Use production Midtrans keys (not sandbox)
- Configure
MIDTRANS_NOTIFICATION_URLto your public HTTPS URL - Run
python manage.py collectstatic - Run
python manage.py migrate - Run
python manage.py setup_periodic_tasks - Use a process manager (systemd, supervisor) for Celery workers
- Configure HTTPS (required by Midtrans for production)
- Set up monitoring for Celery tasks and webhook logs
Systemd Services
Celery Worker
# /etc/systemd/system/celery-worker.service
[Unit]
Description=Celery Worker
After=network.target redis.service
[Service]
Type=simple
User=www-data
WorkingDirectory=/path/to/project
ExecStart=/path/to/venv/bin/celery -A config worker -l info
Restart=always
[Install]
WantedBy=multi-user.target
Celery Beat
# /etc/systemd/system/celery-beat.service
[Unit]
Description=Celery Beat Scheduler
After=network.target redis.service
[Service]
Type=simple
User=www-data
WorkingDirectory=/path/to/project
ExecStart=/path/to/venv/bin/celery -A config beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
Restart=always
[Install]
WantedBy=multi-user.target
Docker Compose
docker-compose.yml
version: "3.8"
services:
web:
build: .
command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/app
ports:
- "8000:8000"
env_file: .env
depends_on:
- db
- redis
db:
image: postgres:16
environment:
POSTGRES_DB: ${DB_NAME:-subscription_db}
POSTGRES_USER: ${DB_USER:-dbuser}
POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
celery-worker:
build: .
command: celery -A config worker -l info
env_file: .env
depends_on:
- db
- redis
celery-beat:
build: .
command: celery -A config beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
env_file: .env
depends_on:
- db
- redis
volumes:
pgdata:
Project Structure
| Directory | Description |
|---|---|
config/ |
Django project settings, Celery config, URL routing, WSGI |
subscriptions/ |
Main reusable package (models, services, views, signals, tasks, admin, middleware, client) |
subscriptions/tests/ |
Test suite with factories |
subscriptions/management/commands/ |
seed_plans, setup_periodic_tasks management commands |
example/ |
Example Django app with template-based views and template tags |
templates/example/ |
HTML templates (Tailwind CSS) |
docs/ |
Sphinx documentation |
License
This project is licensed under the MIT License — see the LICENSE file for details.
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Commit your changes:
git commit -am 'Add my feature' - Push to the branch:
git push origin feature/my-feature - Open a Pull Request
Resources
Release files for django-subscription-midtrans 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_subscription_midtrans-0.1.3.tar.gz | 61.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_subscription_midtrans-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 123.9 kB
Release files / django_subscription_midtrans-0.1.3.tar.gz
| Download URL | django_subscription_midtrans-0.1.3.tar.gz |
|---|---|
| Size | 61.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2b19f47f67afbce7e5766056b5d05a64d48412be4e359a5a9ff841e0ff5f0aa3
|
|
BLAKE2b-256 checksum How to use checksums |
9a8e016cbe6ba83df61dadc996590806c2a6224a421185b3996a9dd83f75f9e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / django_subscription_midtrans-0.1.3-py3-none-any.whl
| Download URL | django_subscription_midtrans-0.1.3-py3-none-any.whl |
|---|---|
| Size | 62.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dc00523ef9c09cf3640dc178808832476f777e7b9989993b576831bb6e9e2410
|
|
BLAKE2b-256 checksum How to use checksums |
b74bb5b85a22c6c5b13d10dd7682eed08079949b0a4ebe40d359a0faf38c750f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|