Cookbook
Recipes
Worked examples for the patterns that come up most often when building Vincia plugins. Each recipe is short — adapt them, don't copy wholesale.
Idempotency keys
Any side-effecting call your plugin makes should be safe to retry. The simplest mechanism is an idempotency key: a deterministic value derived from the request that the downstream provider uses to deduplicate.
Pattern
async charge(ctx, params) {
const idem = `${ctx.tenant_id}:${params.order_id}`;
const res = await ctx.outbound.fetch('https://api.example.com/charge', {
method: 'POST',
headers: { 'idempotency-key': idem },
body: JSON.stringify(params),
});
return await res.json();
}
Why
- Network blips cause retries
- Sidecar restarts may replay queued tasks
- Users hit "submit" twice
A deterministic idempotency key turns "did this charge twice?" into "no, the provider deduped." Without it you carry the burden in your storage code.
Webhook reconciliation
Most payment providers deliver a webhook callback per state change (charged, refunded, disputed). The wrong pattern is to mutate state from the webhook directly; the right pattern is to use the webhook as a signal and reconcile against the provider's authoritative API.
Pattern
async webhook(ctx, event) {
// Don't trust the webhook body for state. Use it as a hint.
const remote = await ctx.outbound.fetch(
`https://api.example.com/charges/${event.id}`,
{ headers: { authorization: `Bearer ${ctx.secrets.get('api_key')}` } },
);
const truth = await remote.json();
// Now update local storage with the provider's view, not the webhook's.
await ctx.storage.put('charges', event.id, truth);
}
Why
- Webhooks can arrive out of order
- Webhooks can be replayed by attackers
- The provider's API is the system of record