Finds the exact bug.Never guesses.

Stack trace in. Verified root cause + fix out. Under 5 seconds.
Code never leaves India. Open-weight models. No vendor lock-in.

Self-hosted · Privacy-first · Built for Indian engineering teams

Strike
502s on /billing/webhook — duplicate charges in ledger for same Stripe event id.
Root cause: notify_accounting.delay fires inside atomic() before commit; retry replays _post() while task already consumed partial state. Move delay to on_commit().
Analyzing payments/views.py

Correlating stack frames with webhook handler entrypoints…

Suspecting double-post: task enqueue is outside atomic block…

Tracing LedgerEntry.create vs accounting notification ordering…

Hypothesis: race between concurrent identical event retries.

1
"""Stripe webhook → ledger bridge."""
2
from __future__ import annotations
3
4
import hashlib
5
import logging
6
from typing import Any
7
8
from django.conf import settings
9
from django.db import transaction
10
from django.http import HttpRequest, JsonResponse
11
from django.utils import timezone
12
from django.views.decorators.csrf import csrf_exempt
13
14
from apps.billing import tasks
15
from apps.billing.models import LedgerEntry, WebhookEvent
16
from apps.billing.serializers import StripeEventSerializer
17
from apps.core.metrics import increment
18
19
logger = logging.getLogger(__name__)
20
21
22
@csrf_exempt
23
def stripe_webhook(request: HttpRequest) -> JsonResponse:
24
    """Verify signature, persist idempotently, post ledger."""
25
    raw = request.body
26
    sig = request.headers.get('Stripe-Signature', '')
27
28
    if not _verify_stripe_signature(raw, sig):
29
        increment('stripe.webhook.bad_sig')
30
        return JsonResponse({'ok': False}, status=400)
31
32
    payload = StripeEventSerializer.parse(raw)
33
    event_id = payload['id']
34
35
    if WebhookEvent.objects.filter(stripe_id=event_id).exists():
36
        return JsonResponse({'ok': True, 'duplicate': True})
37
38
    WebhookEvent.objects.create(
39
        stripe_id=event_id,
40
        type=payload['type'],
41
        received_at=timezone.now(),
42
    )
43
44
    if payload['type'] != 'charge.succeeded':
45
        return JsonResponse({'ok': True})
46
47
    amount_cents = int(payload['data']['object']['amount'])
48
    currency = payload['data']['object']['currency']
49
    customer_id = payload['data']['object'].get('customer')
50
51
    metadata = {
52
        'event_id': event_id,
53
        'customer': customer_id or '',
54
        'currency': currency,
55
    }
56
57
    def _post() -> None:
58
        LedgerEntry.objects.create(
59
            amount_cents=amount_cents,
60
            metadata=metadata,
61
            source='stripe_webhook',
62
        )
63
64
    with transaction.atomic():
65
        _post()
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
        tasks.notify_accounting.delay(event_id)  # BUG: before commit
88
89
    increment('stripe.webhook.posted')
90
    return JsonResponse({'ok': True})
91
92
93
def _verify_stripe_signature(raw: bytes, sig: str) -> bool:
94
    import hmac
95
    secret = settings.STRIPE_WEBHOOK_SECRET
96
    digest = hmac.new(secret.encode(), raw, hashlib.sha256).hexdigest()
97
    return hmac.compare_digest(digest, sig.split(',')[0])
98
99
def replay_fixture(path: str) -> None:
100
    """Dev-only helper for integration tests."""
101
    with open(path, 'rb') as f:
102
        body = f.read()
103
    stripe_webhook(_fake_request(body))
104
105
106
def _fake_request(body: bytes) -> HttpRequest:
107
    req = HttpRequest()
108
    req._body = body
109
    return req
Strike · verified patch · local workspace

SELF-HOSTED · OPEN-WEIGHT MODELS ONLY · CODE NEVER LEAVES YOUR SERVER · NO OPENAI · NO VENDOR LOCK-IN · MIT MANIPAL RESEARCH

Strike does the debugging. You make the decisions.

01 —— Paste your stack trace

Drop any Django or Python exception. Nothing leaves your server — ever.

POST /debug

02 —— Strike retrieves and verifies

Finds the exact functions in your codebase. Verifies the fix is grounded before showing it.

confidence_score · verified=true

03 —— One-click patch

Root cause, fix, unified diff. Click apply. If it cannot verify — it stays silent.

POST /apply · POST /rollback

THE ONLY RULE

Only shows what it can prove.

Most AI tools hallucinate a fix and move on. Strike withholds the answer until it can verify the fix exists in your actual code. It only gives you the right fix.

Simple pricing. No per-token billing.

SELF-HOSTED FREE

₹0

Run on your own GPU. MIT licensed.

  • Unlimited sessions
  • All open-weight models
  • Full API

HOSTED

₹799/month

Hosted in India. Code never leaves Indian infrastructure.

  • Everything in Free
  • VS Code plugin
  • Priority support

ENTERPRISE

Custom

On-prem, SLA, dedicated infra.

  • Everything in Hosted
  • On-prem deployment
  • Custom SLA

Your next bug is already waiting.Strike finds it first.

Join engineering teams across India on the waitlist.

Request Early Access