> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotency

> Use idempotency keys to prevent duplicate charges and ensure payment security.

export const DoAndDont = ({type = "correct", title, items = []}) => {
  const isCorrect = type === "correct";
  const defaultTitle = isCorrect ? "Correct approach" : "Incorrect approach";
  const label = title ?? defaultTitle;
  const renderText = text => {
    const parts = text.split(/(`[^`]+`)/g);
    return parts.map((part, i) => {
      if (part.startsWith("`") && part.endsWith("`")) {
        return <code key={i} className="bg-slate-100 dark:bg-neutral-800 px-2 rounded text-slate-900 dark:text-slate-100 text-[13px] font-mono">
            {part.slice(1, -1)}
          </code>;
      }
      return part;
    });
  };
  const leftPanelStyle = {
    background: isCorrect ? "repeating-linear-gradient(-45deg, var(--do-stripe-a, #ccfbf1), var(--do-stripe-a, #ccfbf1) 4px, var(--do-stripe-b, #f0fdfa) 4px, var(--do-stripe-b, #f0fdfa) 12px)" : "repeating-linear-gradient(-45deg, var(--dont-stripe-a, #fee2e2), var(--dont-stripe-a, #fee2e2) 4px, var(--dont-stripe-b, #fef2f2) 4px, var(--dont-stripe-b, #fef2f2) 12px)",
    borderRight: isCorrect ? "1px solid var(--do-divider, #14b8a6)" : "1px solid var(--dont-divider, #f87171)"
  };
  return <div className={`do-and-dont-card flex rounded-xl border overflow-hidden bg-white dark:bg-neutral-950 ${isCorrect ? "border-teal-500 dark:border-teal-600" : "border-red-400 dark:border-red-700"}`}>
      <style>{`
        .dark .do-and-dont-card {
          --dont-stripe-a: rgba(127, 29, 29, 0.38);
          --dont-stripe-b: rgba(69, 10, 10, 0.32);
          --dont-divider: #b91c1c;
          --do-stripe-a: rgba(20, 184, 166, 0.16);
          --do-stripe-b: rgba(20, 83, 45, 0.18);
          --do-divider: #0f766e;
        }
      `}</style>
      {}
      <div className={`flex items-start justify-center w-14 shrink-0 pt-4 ${isCorrect ? "dark:bg-teal-950/30" : "dark:bg-red-950/20"}`} style={leftPanelStyle}>
        {isCorrect ? <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M16 26C21.5228 26 26 21.5228 26 16C26 10.4772 21.5228 6 16 6C10.4772 6 6 10.4772 6 16C6 21.5228 10.4772 26 16 26ZM11 16.295L12.0093 15.2857L14.5714 17.8471L19.9893 12.4286L21 13.4393L14.5714 19.8671L11 16.295Z" fill="#38C793" />
          </svg> : <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
            <path d="M26 16C26 10.4772 21.5229 6 16 6C10.4772 6 6 10.4771 6 16C6 21.5228 10.4771 26 16 26C21.5228 26 26 21.5229 26 16ZM12.6727 11.6393L16 14.9868L19.3273 11.6393L20.3405 12.6464L17.0071 16L20.3405 19.3536L19.3273 20.3607L16 17.0132L12.6727 20.3607L11.6595 19.3536L14.9929 16L11.6595 12.6464L12.6727 11.6393Z" fill="#E93544" />
          </svg>}
      </div>

      {}
      <div className="flex-1 p-4 bg-white dark:bg-neutral-900/60">
        <p className="font-semibold text-base text-slate-900 dark:text-slate-100 mb-3 mt-0">
          {label}
        </p>
        <ul className="flex flex-col gap-3 m-0 list-none p-0">
          {items.map((item, i) => <li key={i} className="flex gap-2 items-start text-slate-600 dark:text-slate-300 text-sm leading-5">
              <span>{renderText(item)}</span>
            </li>)}
        </ul>
      </div>
    </div>;
};

Idempotency ensures that when the same request is executed multiple times, the result is the same as executing it only once. In payment scenarios, this prevents duplicate charges caused by retries triggered by network timeouts.

## Idempotency keys

Waffo uses `paymentRequestId` (payments) and `subscriptionRequest` (subscriptions) as idempotency keys.

### Rules

* Maximum length: 32 characters
* Generate a unique value for each new request
* **Must be persisted to the database before sending the request**
* When retrying, **reuse the original idempotency key**; do not generate a new one

## Pattern

```typescript theme={null}
// 1. Generate a unique idempotency key, 32 characters or fewer
const paymentRequestId = generateUniqueRequestId();

// 2. Persist (before sending the request!)
await db.order.create({ paymentRequestId, status: 'PENDING' });

// 3. Send the request
try {
  const response = await waffo.order().create({ paymentRequestId, ... });
} catch (e) {
  if (e instanceof WaffoUnknownStatusException) {
    // 4. Unknown status: inquire, do not retry create
    const inquiry = await waffo.order().inquiry({ paymentRequestId });
  }
}
```

## Common mistakes

<div className="flex flex-col gap-3">
  <DoAndDont
    type="incorrect"
    items={[
  "After a network timeout, generate a new `paymentRequestId` and retry.",
  "Only write `paymentRequestId` to the database after the request is sent.",
  "Close the order immediately after receiving an unknown status.",
]}
  />

  <DoAndDont
    type="correct"
    items={[
  "Use the original `paymentRequestId`, inquire first, then decide on subsequent handling.",
  "Persist it before sending the request.",
  "Inquire or wait for the Webhook.",
]}
  />
</div>
