Works with the AI Agents you already use

    Claude CodeClaude CodeCursorCursorCodex CLICodex CLIMuseMuseDotsDots+22 more

    Developing Features — Security-First Production Code

    1

    Security-first production code: threat questions, a reuse ladder, SOLID, file-size limits, structured logs, data rules.

    Free

    Security scanned
    Developing Features — Security-First Production Code

    Developing Features — Security-First Production Code

    Example session with this skill installed

    Add a payment webhook endpoint. Python 3.12, FastAPI, SQLAlchemy 2.0 (sync Session), PostgreSQL. We already have Base and get_session in app/db.py, and an Order model (id: str, status: str) in app/orders/models.py.

    Provider docs say

    • POST to our URL, JSON body: {"id": "evt_...", "type": "payment.succeeded", "data": {"order_id": "..."}}
    • Header X-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>" with the endpoint secret>
    • Reject if the timestamp is more than 5 minutes off. Deliveries are at-least-once and may repeat.

    On payment.succeeded, mark the order as paid. The secret is in env var PAYMENTS_WEBHOOK_SECRET.

    • Read your context and instructions
    • Compiled the developing features

    Summary. Language and framework: Python 3.12, FastAPI, SQLAlchemy 2.0 sync, PostgreSQL. The endpoint verifies the HMAC over the raw body before it reads any field. It rejects stale timestamps, deduplicates repeated deliveries by event ID, and marks the order paid in the same transaction that records the event.

    Pre-implementation check

    What am I trusting? Nothing in the body or header until the HMAC matches. order_id is read only after verification.
    2.

    Blast radius. A forged event could mark unpaid orders as paid, which means goods ship for free. Signature verification is the only gate.
    3.

    Authoritative source. The provider's signing scheme as given: "<t>.<raw body>", HMAC-SHA256, 5-minute tolerance.
    4.

    Failure path. Any parsing or verification error returns 401 and is never treated as a pass. A missing secret raises an error and is not skipped.
    5.

    Who controls the value? The sender controls the header and body, so both are verified. The secret comes from the environment.
    6. Platforms. No paths or subprocess calls. Timestamps are Unix seconds, so there is no timezone issue.

    Reuse ladder. Stdlib hmac.compare_digest handles the timing-safe compare. FastAPI and Pydantic already validate the payload. PostgreSQL ON CONFLICT DO NOTHING deduplicates atomically. No new dependencies.

    File plan

    app/payments_webhook/
    ├── webhook_constants.py        constants
    ├── webhook_types.py            Pydantic payload, outcome enum, domain errors
    ├── webhook_models.py           ProcessedPaymentEvent ORM model
    ├── signature_validation.py     parse + verify the signature header (pure)
    ├── webhook_repository.py       data access (event dedupe, order update)
    ├── webhook_service.py          business rules
    └── webhook_handler.py          FastAPI route (transport only)
    

    Code

    
    # app/payments_webhook/webhook_constants.py
    SIGNATURE_HEADER = "X-Signature"
    SIGNATURE_TOLERANCE_SECONDS = 300
    MAX_BODY_BYTES = 65_536
    SUPPORTED_EVENT_TYPE = "payment.succeeded"
    ORDER_STATUS_PAID = "paid"
    WEBHOOK_SECRET_ENV = "PAYMENTS_WEBHOOK_SECRET"
    
    
    # app/payments_webhook/webhook_types.py
    from enum import Enum
    
    from pydantic import BaseModel, ConfigDict, Field
    
    class PaymentEventData(BaseModel):
        model_config = ConfigDict(frozen=True)
        order_id: str = Field(min_length=1, max_length=64)
    
    class PaymentEvent(BaseModel):
        model_config = ConfigDict(frozen=True)
        id: str = Field(min_length=1, max_length=128)
        type: str = Field(min_length=1, max_length=64)
        data: PaymentEventData
    
    class WebhookOutcome(str, Enum):
        PROCESSED = "processed"
        DUPLICATE = "duplicate"
        IGNORED = "ignored"
    
    class WebhookSignatureError(Exception):
        def __init__(self, reason: str) -> None:
            super().__init__(reason)
            self.reason = reason
    
    class OrderNotFoundError(Exception):
        def __init__(self, order_id: str) -> None:
            super().__init__(order_id)
            self.order_id = order_id
    
    
    # app/payments_webhook/webhook_models.py
    from datetime import datetime
    
    from sqlalchemy import DateTime, String, func
    from sqlalchemy.orm import Mapped, mapped_column
    
    from app.db import Base
    
    class ProcessedPaymentEvent(Base):
        __tablename__ = "processed_payment_events"
    
        event_id: Mapped[str] = mapped_column(String(128), primary_key=True)
        received_at: Mapped[datetime] = mapped_column(
            DateTime(timezone=True), server_default=func.now()
        )
    
    
    # app/payments_webhook/signature_validation.py
    import hashlib
    import hmac
    
    from app.payments_webhook.webhook_constants import SIGNATURE_TOLERANCE_SECONDS
    from app.payments_webhook.webhook_types import WebhookSignatureError
    
    def parse_signature_header(header: str) -> tuple[int, str]:
        pairs = dict(
            item.strip().split("=", 1) for item in header.split(",") if "=" in item
        )
        try:
            return int(pairs["t"]), pairs["v1"]
        except (KeyError, ValueError) as exc:
            raise WebhookSignatureError("malformed_header") from exc
    
    def verify_signature(raw_body: bytes, header: str, secret: str, now: int) -> None:
        timestamp, received = parse_signature_header(header)
        if abs(now - timestamp) > SIGNATURE_TOLERANCE_SECONDS:
            raise WebhookSignatureError("timestamp_outside_tolerance")
        signed_payload = f"{timestamp}.".encode() + raw_body
        expected = hmac.new(secret.encode(), signed_payload, hashlib.sha256).hexdigest()
        if not hmac.compare_digest(expected, received):
            raise WebhookSignatureError("signature_mismatch")
    
    
    # app/payments_webhook/webhook_repository.py
    from sqlalchemy import update
    from sqlalchemy.dialects.postgresql import insert
    from sqlalchemy.orm import Session
    
    from app.orders.models import Order
    from app.payments_webhook.webhook_constants import ORDER_STATUS_PAID
    from app.payments_webhook.webhook_models import ProcessedPaymentEvent
    
    class PaymentWebhookRepository:
        def __init__(self, session: Session) -> None:
            self._session = session
    
        def record_event_if_new(self, event_id: str) -> bool:
            stmt = (
                insert(ProcessedPaymentEvent)
                .values(event_id=event_id)
                .on_conflict_do_nothing(index_elements=["event_id"])
            )
            return self._session.execute(stmt).rowcount == 1
    
        def mark_order_paid(self, order_id: str) -> bool:
            stmt = update(Order).where(Order.id == order_id).values(status=ORDER_STATUS_PAID)
            return self._session.execute(stmt).rowcount == 1
    
    
    # app/payments_webhook/webhook_service.py
    from typing import Protocol
    
    from app.payments_webhook.webhook_constants import SUPPORTED_EVENT_TYPE
    from app.payments_webhook.webhook_types import OrderNotFoundError, PaymentEvent, WebhookOutcome
    
    class PaymentEventStore(Protocol):
        def record_event_if_new(self, event_id: str) -> bool: ...
        def mark_order_paid(self, order_id: str) -> bool: ...
    
    class PaymentWebhookService:
        def __init__(self, store: PaymentEventStore) -> None:
            self._store = store
    
        def handle(self, event: PaymentEvent) -> WebhookOutcome:
            if event.type != SUPPORTED_EVENT_TYPE:
                return WebhookOutcome.IGNORED
            if not self._store.record_event_if_new(event.id):
                return WebhookOutcome.DUPLICATE
            if not self._store.mark_order_paid(event.data.order_id):
                raise OrderNotFoundError(event.data.order_id)
            return WebhookOutcome.PROCESSED
    
    
    # app/payments_webhook/webhook_handler.py
    import logging
    import os
    import time
    
    from fastapi import APIRouter, Depends, Header, HTTPException, Request
    from pydantic import ValidationError
    from sqlalchemy.orm import Session
    
    from app.db import get_session
    from app.payments_webhook.signature_validation import verify_signature
    from app.payments_webhook.webhook_constants import (
        MAX_BODY_BYTES, SIGNATURE_HEADER, WEBHOOK_SECRET_ENV,
    )
    from app.payments_webhook.webhook_repository import PaymentWebhookRepository
    from app.payments_webhook.webhook_service import PaymentWebhookService
    from app.payments_webhook.webhook_types import (
        OrderNotFoundError, PaymentEvent, WebhookSignatureError,
    )
    
    router = APIRouter()
    logger = logging.getLogger(__name__)
    
    async def read_raw_body(request: Request) -> bytes:
        body = await request.body()
        if len(body) > MAX_BODY_BYTES:
            raise HTTPException(status_code=413)
        return body
    
    def get_webhook_secret() -> str:
        secret = os.environ.get(WEBHOOK_SECRET_ENV)
        if not secret:
            raise RuntimeError(f"{WEBHOOK_SECRET_ENV} is not set")
        return secret
    
    @router.post("/v1/webhooks/payments")
    def receive_payment_webhook(
        raw_body: bytes = Depends(read_raw_body),
        signature: str = Header(default="", alias=SIGNATURE_HEADER),
        secret: str = Depends(get_webhook_secret),
        session: Session = Depends(get_session),
    ) -> dict[str, str]:
        start = time.monotonic()
        try:
            verify_signature(raw_body, signature, secret, now=int(time.time()))
        except WebhookSignatureError as exc:
            logger.warning("payment_webhook.signature_invalid", extra={"reason": exc.reason})
            raise HTTPException(status_code=401) from exc
    
        try:
            event = PaymentEvent.model_validate_json(raw_body)
        except ValidationError as exc:
            logger.warning("payment_webhook.payload_invalid", extra={"error_count": exc.error_count()})
            raise HTTPException(status_code=400) from exc
    
        service = PaymentWebhookService(PaymentWebhookRepository(session))
        try:
            with session.begin():
                outcome = service.handle(event)
        except OrderNotFoundError as exc:
            logger.error(
                "payment_webhook.order_not_found",
                extra={"event_id": event.id, "order_id": exc.order_id},
            )
            raise HTTPException(status_code=404) from exc
    
        logger.info(
            "payment_webhook.handled",
            extra={
                "event_id": event.id,
                "order_id": event.data.order_id,
                "outcome": outcome.value,
                "duration_ms": int((time.monotonic() - start) * 1000),
            },
        )
        return {"status": outcome.value}
    

    Migration (Alembic, same PR). Create processed_payment_events (event_id varchar(128) PRIMARY KEY, received_at timestamptz NOT NULL DEFAULT now()). The primary key is the unique index that dedupe relies on. orders.id is already the primary key, so the update needs no new index.

    Tests

    
    # tests/payments_webhook/test_signature_validation.py
    import hashlib
    import hmac
    
    import pytest
    
    from app.payments_webhook.signature_validation import verify_signature
    from app.payments_webhook.webhook_types import WebhookSignatureError
    
    SECRET = "whsec_test"
    BODY = b'{"id":"evt_1","type":"payment.succeeded","data":{"order_id":"ord_1"}}'
    NOW = 1_760_000_000
    
    def sign(body: bytes, ts: int, secret: str = SECRET) -> str:
        mac = hmac.new(secret.encode(), f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
        return f"t={ts},v1={mac}"
    
    def test_should_accept_when_signature_matches():
        verify_signature(BODY, sign(BODY, NOW), SECRET, now=NOW)
    
    def test_should_reject_when_body_is_tampered():
        with pytest.raises(WebhookSignatureError, match="signature_mismatch"):
            verify_signature(BODY.replace(b"ord_1", b"ord_2"), sign(BODY, NOW), SECRET, now=NOW)
    
    def test_should_reject_when_timestamp_is_older_than_tolerance():
        with pytest.raises(WebhookSignatureError, match="timestamp_outside_tolerance"):
            verify_signature(BODY, sign(BODY, NOW - 301), SECRET, now=NOW)
    
    @pytest.mark.parametrize("header", ["", "t=abc,v1=00", "v1=00", "garbage"])
    def test_should_reject_when_header_is_malformed(header):
        with pytest.raises(WebhookSignatureError, match="malformed_header"):
            verify_signature(BODY, header, SECRET, now=NOW)
    
    
    # tests/payments_webhook/test_webhook_service.py
    import pytest
    
    from app.payments_webhook.webhook_service import PaymentWebhookService
    from app.payments_webhook.webhook_types import OrderNotFoundError, PaymentEvent, WebhookOutcome
    
    class FakeStore:
        def __init__(self, existing_orders: set[str]) -> None:
            self.events: set[str] = set()
            self.orders = existing_orders
            self.paid: list[str] = []
    
        def record_event_if_new(self, event_id: str) -> bool:
            if event_id in self.events:
                return False
            self.events.add(event_id)
            return True
    
        def mark_order_paid(self, order_id: str) -> bool:
            if order_id not in self.orders:
                return False
            self.paid.append(order_id)
            return True
    
    def event(event_type: str = "payment.succeeded", order_id: str = "ord_1") -> PaymentEvent:
        return PaymentEvent(id="evt_1", type=event_type, data={"order_id": order_id})
    
    def test_should_mark_order_paid_when_event_is_new():
        store = FakeStore({"ord_1"})
        assert PaymentWebhookService(store).handle(event()) is WebhookOutcome.PROCESSED
        assert store.paid == ["ord_1"]
    
    def test_should_not_pay_twice_when_event_is_redelivered():
        store = FakeStore({"ord_1"})
        service = PaymentWebhookService(store)
        service.handle(event())
        assert service.handle(event()) is WebhookOutcome.DUPLICATE
        assert store.paid == ["ord_1"]
    
    def test_should_ignore_when_event_type_is_unsupported():
        store = FakeStore({"ord_1"})
        assert PaymentWebhookService(store).handle(event("payment.refunded")) is WebhookOutcome.IGNORED
        assert store.paid == []
    
    def test_should_raise_when_order_does_not_exist():
        with pytest.raises(OrderNotFoundError):
            PaymentWebhookService(FakeStore(set())).handle(event(order_id="ord_missing"))
    

    Validation still to run. None of this was executed here. Run ruff check . && ruff format --check . && mypy app/payments_webhook && pytest tests/payments_webhook. Add one integration test that goes through the route with a real PostgreSQL instance. ON CONFLICT and the rollback on OrderNotFoundError can't be tested with the fake store.

    Decisions & trade-offs

    Verify before parsing. The HMAC is checked on the untouched raw body, and Pydantic runs only after it passes. Parsing first would mean acting on unauthenticated data.

    Dedupe and update in one transaction. If the order update fails, the event row rolls back too. The provider's retry can then succeed, for example when the order commit was racing the webhook.

    Database-level dedupe. ON CONFLICT DO NOTHING on the primary key is atomic across workers. An in-memory or check-then-insert approach would let two simultaneous deliveries both pay.

    404 for an unknown order. It returns non-2xx so the provider retries and the error log alerts someone. The alternative, returning 200 and dropping the event, would hide a real inconsistency.

    • Unsupported types return 200 "ignored". The provider doesn't need to retry events we deliberately don't handle.

    Connects securely to your tools. The creator never sees your data.

    About this skill

    Developing Features is the default skill for writing production code: new features, system design and refactors. Its rule of thumb is secure by default, organized by responsibility, and simple until proven otherwise.

    Problems it solves

    • Code that trusts client input, skips signature checks or fails silently.
    • A second copy of a helper the codebase already had.
    • 1,500-line files that mix validation, SQL and HTTP.
    • Prose log lines with PII in them.
    • N+1 queries and lists without pagination.
    • Code that assumes POSIX paths and shells.

    The workflow

    • Six questions before any code. What am I trusting? What's the blast radius? Is this from the authoritative spec? What happens on failure? Who controls this value? Which platforms must it run on?
    • A reuse ladder. Does it need to exist? Is it already in the codebase? Does the stdlib, a platform feature or an installed dependency do it? Only then write the minimum that works.
    • An 8-step build. State language and framework, plan the file structure, implement incrementally, validate at boundaries, add structured logs, run linters and type checks, run the tests, record learnings.
    • A pre-commit checklist and a fixed output format: code, then tests, then 3–5 bullets on trade-offs.

    References

    • security.md: the full rules, including AI/LLM guardrails, third-party integration and webhook checklists, API security, supply chain.
    • solid.md: SOLID with wrong and right examples.
    • pragmatic-principles.md: DRY, KISS, YAGNI, Law of Demeter.
    • file-structure.md: hard limits (500 LOC, 5 responsibilities per file), layer rules, naming.
    • observability.md: structured logging, levels, correlation IDs.
    • data-layer.md: pooling, indexes, N+1, write scaling, isolation anomalies.
    • untrusted-content.md: prompt-injection rules for the agent itself.
    • A bundled copy of the REST conventions it cites for pagination.

    When it triggers. Any "build production code" task, unless you ask for TDD (use developing-features-tdd) or a bug fix (use fixing-bugs).

    Free and MIT-licensed. Part of the claude-skills-czl collection.

    Source on GitHub: this skill · full repository

    How to use

    Skills on Agensi run inside your AI agent. Copy the prompt, paste it into your agent, and it connects to Agensi and installs this skill for you. If a payment is needed, your agent shows it to you first.

    Reviews

    No reviews yet

    Be one of the first to try it. Every listed skill passes our trust checks below.

    Security scanned

    Passed our 8-point scan before listing

    Fresh listing

    Recently published to Agensi

    Free forever

    No account required to browse

    Trust & safety

    Security scanned

    Verified clean 10 days ago

    • Free to download with an account

    Listed11 days ago
    Updated4 days ago

    What's inside

    Frequently Asked Questions