# 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`: ```env MIDTRANS_NOTIFICATION_URL=https://yourdomain.com/api/subscriptions/webhook/notification/ ``` ### 2. Include the URL ```python # 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](https://dashboard.midtrans.com) → 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](https://ngrok.com): ```bash 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. ```python 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: ```bash 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" }' ```