Open source · MIT · v0.1

Stripe pre-authorization, as code.

Authorize the card at signup. Capture when the trial ends. An open-source primitive you can drop in today — or self-host free. No dashboard hype, no take-rate surprise, just the boring payments infrastructure done right.

No credit card required. Self-host is free forever.

10 min
to integrate
$0.50
per capture
0%
take rate
// On signup — from your server
await fetch("https://trial2pay.com/api/v1/trials", {
  method: "POST",
  headers: { Authorization: `Bearer ${API_KEY}` },
  body: JSON.stringify({
    trial_config_id: "cfg_pro_7d",
    customer_email: "alex@acme.dev",
  }),
});

// → 201 Created
// {
//   "trial_id": "tr_01hr...",
//   "checkout_url": "https://trial2pay.com/c/tr_01hr...",
//   "capture_at": "2026-04-24T17:00:00Z"
// }
Real response · test mode200 OK · 148 ms
Cards never touch our servers
Entered into Stripe Elements (iframe). We only see a pm_ token.
Your Stripe. Your money.
Funds flow end-user → Stripe → your balance. Nothing in between.
Every call, logged
Open source + per-trial audit log. Auditable by design.

The problem

Your trial is too easy to forget.

SaaS trials convert at 12–18% on average. Not because the product is bad — because signing up costs nothing, so nothing is on the line. By the time the trial ends, the user has already moved on.

A card held at signup changes the mental model: only committed users proceed, conversion compounds, and payment is automatic the moment the trial expires. The catch? Building it yourself means wiring up PaymentIntents, capture scheduling, webhooks, idempotency, retry logic, audit logs, and PCI scope considerations.

trial2pay is that pipeline, already built and boring.

Typical trial
12%
convert to paid
Card-on-file trial
45-60%
convert to paid

Source: ProfitWell / Paddle — 2023 aggregated SaaS trial data. Range varies by price point; lower ACVs (<$20) see smaller lift.

How it works

Three moves, all the infrastructure.

You write three API calls. We handle the PaymentIntent lifecycle, capture scheduling, webhook retries, idempotency, and the full audit trail.

Authorize at signup

Your signup calls POST /api/v1/trials. We create a PaymentIntent with capture_method: 'manual'. The card is validated and funds are held.

await trial2pay.trials.create({
  trial_config_id: "cfg_pro_7d",
  customer_email: user.email,
});
// → status: "authorized"

Hold for the trial

Funds remain authorized for up to 7 days (Stripe's hard limit). Customer uses your product. You handle usage, we handle the hold.

// Nothing on your side.
// Stripe keeps the auth live.
// You get webhook: trial.authorized

Capture or release

pg_cron triggers capture at trial end. Cancel to void. Every state change is HMAC-signed, retried on failure, logged.

// Auto-capture at trial_ends_at
// → webhook: trial.captured

// Or refund before:
await trial2pay.trials.cancel(id);

Why open source matters for payments

You wouldn't route card data through an AI-branded black‑box. Neither do we.

Closed payment SaaS ask you to trust a growth dashboard with the most sensitive part of your business. trial2pay is the opposite: a thin, auditable layer on top of Stripe — so the only thing between you and your customer's bank is Stripe itself.

Your Stripe, not ours
You paste a restricted Stripe key during onboarding. Every Stripe call runs on your account, with your keys, against your balance. trial2pay never holds funds.
Every call, inspectable
Each Stripe API call is logged with endpoint, duration, and Stripe request ID — viewable per-trial in your dashboard. You can reproduce every charge.
Fork it, run it, change it
MIT licensed. Self-host on Supabase + Vercel in one afternoon. No vendor lock-in: swap hosted for self-hosted in an hour, or vice versa.
Leave any time
Your merchant data, trial configs, and audit log export cleanly to SQL. Canceling stops captures; it doesn't ransom your data.

Integrations

Drop in wherever your checkout lives.

Three embedded SDKs keep the card form on your domain. One hosted redirect handles zero-JS stacks. All four flow through the same server pipeline, so you can change frontends without touching anything else.

@trial2pay/react
<Trial2PayCheckout trialId={...} />
Recommended
@trial2pay/vue
Composable wrapper · same props
Vue 3
@trial2pay/core
One <script> tag, any framework
Vanilla · CDN
Hosted redirect
res.redirect(trial.checkout_url)
Zero JS

Pricing

Pay when we convert for you.

No MRR bands. No take rate on the plan amount. No surprise overages. $0.50 the moment a trial converts — nothing the moment it doesn't.

Self-host

$0forever

MIT licensed. Fork it, run it.

  • Full source on GitHub
  • Self-host on Supabase + Vercel
  • All SDKs + hosted checkout page
  • Community support on GitHub Discussions
  • No trial cap, no feature gates
Read on GitHub ↗

Hosted

Recommended
$0.50per captured trial

We run the infra. You write three API calls.

  • 99.9% uptime SLA (target)
  • Managed capture scheduler + Stripe webhook retries
  • Rate-limited + audited API
  • Priority webhook delivery
  • Email support
  • Free until your first capture succeeds
Start free →
When hosted makes sense: trials with ACV of $20/mo or higher. At $0.50 per capture, effective take is ~0.1% on a $500 plan, ~2.5% on a $20 plan. For sub-$10 plans we honestly recommend self-hosting — the fixed fee doesn't pencil out and the self-host setup is a single afternoon.

FAQ

Questions worth asking about a payments tool.

What actually happens to my customer's card data?

It enters Stripe's iframe directly via Stripe Elements — trial2pay never sees the raw card number. Your server receives only a `pm_...` token and Stripe holds the card. PCI scope stays at SAQ-A (the lowest bucket) because we never transit or store card data.

Where does the money actually go?

Customer → Stripe → your Stripe balance. trial2pay is never a payee. We use your restricted Stripe key to orchestrate the PaymentIntent, but funds settle to your account, under your Stripe business profile.

Can I audit every Stripe call you make on my behalf?

Yes. Every Stripe API call hits `stripe_audit_log` with endpoint, request ID, duration, and response status. Per-trial view is on the trial detail page. Full-index viewer is on the roadmap; the data is there today via SQL.

What if trial2pay goes down?

New trials stop authorizing until we recover. Existing authorizations are already in Stripe — they capture on schedule even if our scheduler is down (Stripe retries). You can also self-host in parallel and failover by swapping a DNS record. No vendor lock-in, ever.

Why wouldn't I just build this myself?

You can. It's a good week of work: PaymentIntent with `capture_method: manual`, Vault or KMS for keys, pg_cron or a queue for capture, HMAC on outbound webhooks, exponential backoff, idempotency keys, plus the audit log. trial2pay is that week, already debugged and MIT-licensed — so you can crib whatever you want.

Do you charge if the trial auth fails?

No. $0.50 is charged only on `trial.captured` — the moment money actually moves into your Stripe balance. Auth failures, voluntary cancels, expired holds, and refunds within the same billing period are not billable.

Ship your trial flow today

The hard payments stuff, already debugged.

10 minutes to your first authorized trial. Free self-host for the skeptical. $0.50 per capture if you'd rather we ran the pipeline.