{
  "docVersion": 1,
  "id": "template-checkout-no-double-charge",
  "name": "Checkout — no double charge",
  "purpose": "Checkout flow that survives crashes and retries without ever charging a customer twice: idempotent API, outbox event publication, saga compensation on payment failure.",
  "nodes": {
    "web": {
      "type": "browser",
      "label": "Storefront",
      "x": 20,
      "y": 230,
      "runtime": "web + mobile"
    },
    "api": {
      "type": "k8s",
      "label": "Checkout API",
      "x": 280,
      "y": 230,
      "runtime": "idempotency-key dedupe",
      "color": "blue",
      "patterns": [
        "stateless",
        "autoscale"
      ],
      "notes": "Accepts checkout; the Idempotency-Key header makes client retries safe. Writes order + outbox row in ONE transaction."
    },
    "db": {
      "type": "database",
      "label": "Orders DB",
      "x": 560,
      "y": 120,
      "runtime": "orders + outbox table",
      "patterns": [
        "backup"
      ],
      "notes": "Order row and outbox event commit atomically — the outbox is what kills the dual-write bug."
    },
    "relay": {
      "type": "worker",
      "label": "Outbox Relay",
      "x": 560,
      "y": 340,
      "runtime": "polls outbox → publishes"
    },
    "bus": {
      "type": "queue",
      "label": "OrderPlaced",
      "x": 820,
      "y": 340,
      "runtime": "at-least-once"
    },
    "pay": {
      "type": "worker",
      "label": "Payment Service",
      "x": 1080,
      "y": 230,
      "runtime": "saga step · compensable",
      "color": "green",
      "notes": "Charges via PSP. On failure emits PaymentFailed → compensation releases stock and cancels the order."
    },
    "psp": {
      "type": "payment",
      "label": "PSP / Gateway",
      "x": 1340,
      "y": 230,
      "runtime": "external — Stripe/Adyen"
    },
    "stock": {
      "type": "worker",
      "label": "Inventory",
      "x": 1080,
      "y": 480,
      "runtime": "reserve → release on failure"
    },
    "dlq": {
      "type": "dlq",
      "label": "Dead letters",
      "x": 820,
      "y": 520,
      "runtime": "poison events → humans",
      "color": "amber"
    }
  },
  "edges": {
    "e1": {
      "from": "web",
      "to": "api",
      "fromSide": "right",
      "toSide": "left",
      "label": "POST /checkout · Idempotency-Key",
      "animated": true,
      "patterns": [
        "idempotent",
        "timeout"
      ]
    },
    "e2": {
      "from": "api",
      "to": "db",
      "fromSide": "right",
      "toSide": "left",
      "label": "order + outbox, one txn",
      "animated": true
    },
    "e3": {
      "from": "db",
      "to": "relay",
      "fromSide": "bottom",
      "toSide": "top",
      "label": "poll new events"
    },
    "e4": {
      "from": "relay",
      "to": "bus",
      "fromSide": "right",
      "toSide": "left",
      "label": "publish OrderPlaced",
      "style": "dashed"
    },
    "e5": {
      "from": "bus",
      "to": "pay",
      "fromSide": "right",
      "toSide": "left",
      "label": "consume",
      "style": "dashed",
      "animated": true,
      "patterns": [
        "dedup",
        "ordered"
      ]
    },
    "e6": {
      "from": "pay",
      "to": "psp",
      "fromSide": "right",
      "toSide": "left",
      "label": "charge",
      "patterns": [
        "retry",
        "timeout",
        "circuit-breaker"
      ]
    },
    "e7": {
      "from": "bus",
      "to": "stock",
      "fromSide": "bottom",
      "toSide": "left",
      "label": "reserve stock",
      "style": "dashed"
    },
    "e8": {
      "from": "bus",
      "to": "dlq",
      "fromSide": "bottom",
      "toSide": "top",
      "label": "after N failures",
      "style": "dashed"
    },
    "e9": {
      "from": "pay",
      "to": "stock",
      "fromSide": "bottom",
      "toSide": "top",
      "label": "PaymentFailed → release",
      "style": "dashed"
    }
  },
  "boxes": {
    "b1": {
      "x": 520,
      "y": 40,
      "w": 560,
      "h": 620,
      "label": "one blast radius — everything at-least-once inside",
      "color": "blue",
      "opacity": 0.04
    }
  },
  "texts": {
    "t1": {
      "x": 20,
      "y": 430,
      "text": "why no double charge:\n1. retry hits the same Idempotency-Key → same response\n2. outbox means the event exists iff the order exists\n3. consumer dedupes on event id before charging",
      "color": "green",
      "size": 12
    }
  },
  "meta": {
    "createdAt": "2026-07-13T09:00:00.000Z",
    "modifiedAt": "2026-07-13T09:00:00.000Z"
  }
}