Webhook Processing

Overview

Midtrans sends HTTP POST notifications to your webhook endpoint when payment statuses change. The subscriptions package handles the entire webhook lifecycle:

  1. Receive — Log the raw payload in WebhookLog

  2. Verify — Validate the SHA-512 signature

  3. Deduplicate — Skip already-processed notifications

  4. Process — Update payment and subscription status

  5. Notify — Create NotificationLog entries

Setup

1. Configure the Webhook URL

Set the notification URL in your .env:

MIDTRANS_NOTIFICATION_URL=https://yourdomain.com/api/subscriptions/webhook/notification/

2. Include the URL

# urls.py
urlpatterns = [
    path("api/subscriptions/", include("subscriptions.urls")),
]

The webhook endpoint is at POST /api/subscriptions/webhook/notification/.

3. Register in Midtrans Dashboard

Go to Midtrans Dashboard → Settings → Configuration → Payment Notification URL.

Set your public URL (e.g., https://yourdomain.com/api/subscriptions/webhook/notification/).

For local development, use a tunnel like ngrok:

ngrok http 8000
# Set MIDTRANS_NOTIFICATION_URL=https://xxxx.ngrok.io/api/subscriptions/webhook/notification/

Signature Verification

Every Midtrans notification includes a signature_key field. The package verifies it using:

SHA512(order_id + status_code + gross_amount + server_key)

If the signature is invalid, the webhook log is marked as invalid and processing stops.

from subscriptions.client import MidtransClient

is_valid = MidtransClient.verify_signature(
    order_id="SUB-A1B2C3D4E5F6",
    status_code="200",
    gross_amount="49000.00",
    signature_key="sha512hash...",
)

Processing Flow

Payment Notifications (order_id starts with SUB-)

Transaction Status

Action

capture (credit card, fraud=accept)

Activate subscription, mark invoice paid

settlement

Activate subscription, mark invoice paid

pending

Create pending notification

deny / cancel / expire

Mark subscription past due

refund / partial_refund

Update refund amount, create notification

Top-Up Notifications (order_id starts with TOPUP-)

Transaction Status

Action

capture / settlement

Mark top-up as success, credit wallet

deny / cancel

Mark top-up as failed

expire

Mark top-up as expired

WebhookLog Statuses

Status

Description

received

Payload received, not yet processed

processed

Successfully processed

failed

Processing error occurred

invalid

Invalid signature

duplicate

Same order_id + transaction_status already processed

Security

  • The webhook endpoint has no authentication (AllowAny) — Midtrans needs to call it

  • Signature verification prevents spoofed notifications

  • The sender IP is logged in WebhookLog.ip_address

  • CSRF is exempt on the webhook view

Testing Webhooks Locally

The example app includes a webhook simulator at /example/webhook-simulator/ that sends test notifications to your local webhook endpoint.

You can also test via curl:

curl -X POST http://localhost:8000/api/subscriptions/webhook/notification/ \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "SUB-TEST123",
    "transaction_status": "settlement",
    "status_code": "200",
    "gross_amount": "49000.00",
    "signature_key": "computed-sha512-hash",
    "payment_type": "bank_transfer",
    "transaction_id": "test-txn-id"
  }'