Configuration

SUBSCRIPTION_SETTINGS

All settings are configured via a single SUBSCRIPTION_SETTINGS dictionary in your Django settings.py.

SUBSCRIPTION_SETTINGS = {
    # Grace period in days after subscription expires before disabling access
    "GRACE_PERIOD_DAYS": 3,

    # Auto-retry failed payments
    "AUTO_RETRY_FAILED_PAYMENTS": True,

    # Max retry attempts for failed subscription charges
    "MAX_RETRY_ATTEMPTS": 3,

    # Days between retry attempts
    "RETRY_INTERVAL_DAYS": 1,

    # Invoice auto-generation on each charge
    "AUTO_GENERATE_INVOICE": True,

    # Invoice number prefix
    "INVOICE_NUMBER_PREFIX": "INV",

    # Send email notifications via Celery
    "SEND_EMAIL_NOTIFICATIONS": True,

    # Days before expiry to send reminder notifications
    "EXPIRY_REMINDER_DAYS": [7, 3, 1],

    # Default currency code
    "CURRENCY": "IDR",

    # Default payment expiry in minutes
    "PAYMENT_EXPIRY_MINUTES": 60,

    # Paths requiring active subscription (middleware)
    "PROTECTED_PATHS": [],

    # Redirect URL for unauthenticated/unsubscribed users
    "SUBSCRIPTION_REQUIRED_REDIRECT": "/pricing/",
}

Setting Reference

Setting

Type

Default

Description

GRACE_PERIOD_DAYS

int

3

Days after period end before access is revoked for past-due subscriptions

AUTO_RETRY_FAILED_PAYMENTS

bool

True

Automatically retry failed payments via Celery task

MAX_RETRY_ATTEMPTS

int

3

Maximum number of retry attempts for failed subscription charges

RETRY_INTERVAL_DAYS

int

1

Number of days between retry attempts

AUTO_GENERATE_INVOICE

bool

True

Generate an invoice automatically for each charge

INVOICE_NUMBER_PREFIX

str

"INV"

Prefix for invoice numbers (e.g., INV-202501-00001)

SEND_EMAIL_NOTIFICATIONS

bool

True

Enable email notifications via Celery task

EXPIRY_REMINDER_DAYS

list[int]

[7, 3, 1]

Send reminder notifications N days before subscription expiry

CURRENCY

str

"IDR"

Default currency code for new wallets

PAYMENT_EXPIRY_MINUTES

int

60

Payment expiry window in minutes

PROTECTED_PATHS

list[str]

[]

URL path prefixes that require an active subscription

SUBSCRIPTION_REQUIRED_REDIRECT

str

"/pricing/"

Redirect URL for HTML requests when subscription is required

Midtrans Settings

These settings configure the Midtrans payment gateway connection:

# settings.py
from decouple import config

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_NOTIFICATION_URL = config("MIDTRANS_NOTIFICATION_URL", default="")

# Computed: do not set manually
MIDTRANS_API_BASE_URL = (
    "https://api.midtrans.com" if MIDTRANS_IS_PRODUCTION
    else "https://api.sandbox.midtrans.com"
)

Setting

Description

MIDTRANS_SERVER_KEY

Server key from Midtrans Dashboard → Settings → Access Keys

MIDTRANS_CLIENT_KEY

Client key for frontend tokenization

MIDTRANS_MERCHANT_ID

Your Midtrans merchant ID

MIDTRANS_IS_PRODUCTION

False for sandbox, True for production

MIDTRANS_NOTIFICATION_URL

Webhook URL Midtrans will call for payment notifications

MIDTRANS_API_BASE_URL

Auto-computed from MIDTRANS_IS_PRODUCTION

Warning

Never commit real Midtrans keys to version control. Always use environment variables.

Celery Settings

Required Celery configuration:

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_ACCEPT_CONTENT = ["json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
CELERY_TIMEZONE = "Asia/Jakarta"
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"

REST Framework Settings

Recommended DRF configuration:

REST_FRAMEWORK = {
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 20,
    "DEFAULT_FILTER_BACKENDS": [
        "django_filters.rest_framework.DjangoFilterBackend",
        "rest_framework.filters.SearchFilter",
        "rest_framework.filters.OrderingFilter",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticated",
    ],
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.SessionAuthentication",
        "rest_framework.authentication.BasicAuthentication",
    ],
}

Environment Variables (.env)

Complete .env template with all values empty by default:

# Django
DJANGO_SECRET_KEY=
DJANGO_DEBUG=True
DJANGO_ALLOWED_HOSTS=*,localhost,127.0.0.1

# Database (optional, defaults to SQLite)
DB_ENGINE=django.db.backends.sqlite3
DB_NAME=
DB_USER=
DB_PASSWORD=
DB_HOST=
DB_PORT=

# Celery / Redis
CELERY_BROKER_URL=redis://localhost:6379/0
CELERY_RESULT_BACKEND=redis://localhost:6379/0

# Midtrans
MIDTRANS_SERVER_KEY=
MIDTRANS_CLIENT_KEY=
MIDTRANS_MERCHANT_ID=
MIDTRANS_IS_PRODUCTION=False
MIDTRANS_NOTIFICATION_URL=