- Home
- Skills
- APIs & Backend
- Ledgerlock
More screenshots
Works with the AI Agents you already use
Ledgerlock
Credits and 30-day passes on Postgres and Stripe that never double-charge, double-credit or trap a user in a renewal.
$19
Ledgerlock
Example session with this skill installed
Set up a credit system with a 30-day pass for my Supabase and Next.js app. I need it to handle Stripe webhooks without double-crediting if they retry.
- Read your context and instructions
- Compiled the ledgerlock
Built credits plus a 30-day pass for your Supabase and Next.js app, with Stripe fulfilment that survives retries.
| Piece | What it does |
|---|---|
credit_accounts | one row per user, check (balance >= 0), locked with for update on every spend |
credit_ledger | append-only, a unique idempotency_key on every spend, grant and refund |
passes | expires_at = greatest(now(), expires_at) + 30 days; an active pass makes covered actions cost 0 |
stripe_events | event ids already handled, so a retried webhook is skipped |
-- inside spend_credits (security definer, search_path = '')
select balance into bal from public.credit_accounts where user_id = p_user for update;
if exists (select 1 from public.credit_ledger where idempotency_key = p_key) then return bal; end if;
if bal is null or bal < p_amount then return null; end if;
update public.credit_accounts set balance = balance - p_amount where user_id = p_user;
insert into public.credit_ledger(user_id, delta, reason, idempotency_key) values (p_user, -p_amount, p_reason, p_key);
revoke execute on function public.spend_credits from public, anon, authenticated;
grant execute on function public.spend_credits to service_role;
The webhook verifies the raw body, skips event ids it has seen, and calls the same fulfill(sessionId) as the success page with the key checkout:<session.id>, so a retry grants once. Prices are mapped to Stripe price ids on the server, never taken from the client.
Tests (Vitest against local Supabase): 20 parallel 1-credit spends on a balance of 5 give exactly 5 successes and a balance of 0; the same key twice writes one ledger row; a replayed webhook grants once.
Next steps
- Add
STRIPE_WEBHOOK_SECRETand runstripe listen --forward-to localhost:3000/api/stripe/webhook. - Fill the pricing table with your price ids per region.
- Run
stripe trigger checkout.session.completedto test end to end.
Connects securely to your tools. The creator never sees your data.
About this skill
A user taps twice and pays twice. A Stripe webhook retries and the credits land twice. Two requests race and spend the same credit. Ask your agent to "add credits and a 30-day pass to the app" and Ledgerlock builds billing that counts every credit exactly once: a locked balance row, an append-only ledger, an idempotency key on every spend, grant and refund, and Stripe fulfilment that survives retries.
Why it's different
- Proven, not assumed: a concurrency test fires 20 parallel spends at a balance of 5 and expects exactly 5 successes, and a replayed webhook must grant once.
- Locked down by default: money functions set
search_path, lock the account row and revoke execute from client roles, so no browser can grant itself credits. - Honest billing: price and renewal shown before pay, reminders before any renewal, one-tap cancel, no weekly auto-charge trials.
- Built from real projects: 9 failures that actually happened, each with its guard.
What it does
- Ledger schema: account row with
check (balance >= 0), append-only ledger with unique idempotency keys, passes and a Stripe event table, plus a nightlybalance = sum(delta)check. - Spend and refund flow: deduct before the work, refund on failure with a key derived from the spend, and never refund a pass user who spent nothing.
- Stripe Checkout and webhooks: server-side price mapping, one fulfilment function for the webhook and the success page, raw-body signature check, event dedupe.
- Regional pricing: real per-region prices with
currency_optionsor separate price ids, not just FX conversion. - Refunds and disputes: claw back unspent credits, end the pass, freeze spending on a dispute.
How it works
- Pricing table first: packs, passes and plans per region, with the rule that an active pass makes covered actions free.
- Migration: tables, locked RPCs and grants in one migration.
- Routes: checkout, fulfilment and webhook handlers with idempotent grants.
- Billing UX: price, renewal and cancel shown honestly.
- Prove it: Vitest against local Supabase plus
stripe triggerandstripe listenend to end.
Frameworks & tools
Defaults to Postgres on Supabase, Stripe and Next.js route handlers, and follows your stack if it differs. Tests with Vitest, the Supabase CLI and the Stripe CLI.
Use cases
- AI apps that charge per action with credit packs.
- A 30-day pass that wins over credits while it is active.
- Fixing balances that drift or webhooks that credit twice.
- Pricing the same product fairly in different countries.
Known limitations
Not for in-game currencies traded between users or marketplace payouts (Stripe Connect is a different design). Needs a Stripe account and a Postgres database you control.
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
Money-back guarantee
Doesn't do what its page promises? You get your money back.
Trust & safety
Security scanned
Verified clean today
- Passed all security checks, Safe to install