← Files InsForgeARCHIVED FILE
skills/insforge-cli/references/payments/stripe.md
7.31 KB · Oct 4, 2026 · 12:28 UTC
# npx @insforge/cli payments stripe
Use this reference when configuring or inspecting Stripe payment infrastructure. For app checkout code, load `skills/insforge/payments/stripe.md`.
## Setup Flow
Always start with status:
```bash
npx @insforge/cli payments stripe status
```
If Stripe is unconfigured, add the environment key. `config set` validates the key and automatically syncs provider state when the key or account changes. Use `status` again after setup to verify key/account/sync/webhook health:
```bash
npx @insforge/cli payments stripe config set --environment test sk_test_xxx
npx @insforge/cli payments stripe status
```
Use `sync` later to manually refresh mirrored provider data or retry a failed sync:
```bash
npx @insforge/cli payments stripe sync --environment test
```
Use `--environment test` while building. Use `live` only after explicit production approval. Do not store Stripe secret keys with generic `secrets` commands.
## Webhooks
Stripe webhook registration is automated by InsForge when the backend has a public URL:
```bash
npx @insforge/cli payments stripe webhooks configure --environment test
```
InsForge configures these Stripe events:
- `customer.created`
- `customer.updated`
- `customer.deleted`
- `checkout.session.completed`
- `checkout.session.async_payment_succeeded`
- `checkout.session.async_payment_failed`
- `checkout.session.expired`
- `invoice.paid`
- `invoice.payment_failed`
- `payment_intent.succeeded`
- `payment_intent.payment_failed`
- `charge.refunded`
- `refund.created`
- `refund.updated`
- `refund.failed`
- `customer.subscription.created`
- `customer.subscription.updated`
- `customer.subscription.deleted`
- `customer.subscription.paused`
- `customer.subscription.resumed`
Durable fulfillment belongs on `payments.webhook_events`, not Checkout success URLs and not `payments.transactions`.
## Catalog
Stripe catalog concepts:
- Product: sellable thing or plan family.
- Price: amount/currency/recurrence attached to a Product.
- Subscription checkout uses recurring Prices.
Commands:
```bash
npx @insforge/cli payments stripe catalog --environment test
npx @insforge/cli payments stripe products list --environment test
npx @insforge/cli payments stripe products get prod_123 --environment test
npx @insforge/cli payments stripe products create --environment test --name "Pro Plan"
npx @insforge/cli payments stripe products update prod_123 --environment test --description "Updated"
npx @insforge/cli payments stripe products delete prod_123 --environment test -y
npx @insforge/cli payments stripe prices list --environment test
npx @insforge/cli payments stripe prices create --environment test --product prod_123 --currency usd --unit-amount 2000
npx @insforge/cli payments stripe prices create --environment test --product prod_123 --currency usd --unit-amount 2000 --interval month
npx @insforge/cli payments stripe prices update price_123 --environment test --active false
npx @insforge/cli payments stripe prices archive price_123 --environment test
```
Stripe Price amount/currency/interval are immutable. Create a new Price and archive the old one instead of trying to mutate billing terms.
## Admin Reads
Use these for inspection and debugging:
```bash
npx @insforge/cli payments stripe customers --environment test
npx @insforge/cli payments stripe subscriptions --environment test
npx @insforge/cli payments stripe subscriptions --environment test --subject-type team --subject-id team_123
npx @insforge/cli payments stripe transactions --environment test
npx @insforge/cli payments stripe transactions --environment test --limit 20 --json
```
`--subject-type` and `--subject-id` are app billing subjects passed to InsForge, such as `team:team_123` or `user:user_123`. They are not Stripe customer, payment, price, or subscription IDs.
## RLS For Runtime App Code
Before building subscription checkout or Billing Portal UI, add app-specific RLS to the Stripe runtime authorization tables:
- `payments.stripe_checkout_sessions`: `INSERT` for creating Checkout attempts, `SELECT` for retry/idempotency reads.
- `payments.stripe_customer_portal_sessions`: `INSERT` for creating portal attempts, `SELECT` when the app reads attempts.
Example shape:
```sql
ALTER TABLE payments.stripe_checkout_sessions ENABLE ROW LEVEL SECURITY;
ALTER TABLE payments.stripe_customer_portal_sessions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "team admins create stripe checkout"
ON payments.stripe_checkout_sessions
FOR INSERT
TO authenticated
WITH CHECK (
subject_type = 'team'
AND public.is_team_billing_admin(subject_id)
);
CREATE POLICY "team admins read stripe checkout"
ON payments.stripe_checkout_sessions
FOR SELECT
TO authenticated
USING (
subject_type = 'team'
AND public.is_team_billing_admin(subject_id)
);
CREATE POLICY "team admins create stripe portal"
ON payments.stripe_customer_portal_sessions
FOR INSERT
TO authenticated
WITH CHECK (
subject_type = 'team'
AND public.is_team_billing_admin(subject_id)
);
```
If app checkout sends `idempotencyKey`, include a matching `SELECT` policy on `payments.stripe_checkout_sessions` because retries may reuse an existing row.
## Fulfillment Trigger
Create triggers on `payments.webhook_events` and update app-owned tables:
```sql
CREATE OR REPLACE FUNCTION public.fulfill_stripe_billing_event()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.provider = 'stripe'
AND NEW.processing_status = 'processed'
AND NEW.event_type IN ('checkout.session.completed', 'invoice.paid') THEN
-- Update public.orders, public.team_entitlements, or an app-owned outbox.
NULL;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER fulfill_stripe_billing_event
AFTER INSERT OR UPDATE ON payments.webhook_events
FOR EACH ROW
EXECUTE FUNCTION public.fulfill_stripe_billing_event();
```
Make fulfillment idempotent. For email, warehouse, CRM, or other external side effects, write an app-owned outbox row and process it asynchronously.
Stripe gives no ordering guarantee across events: `invoice.paid` can be processed before `checkout.session.completed` creates the `payments.customer_mappings` row. For subscription events, resolve the billing subject from the payload first (`payload -> 'data' -> 'object' -> 'parent' -> 'subscription_details' -> 'metadata' ->> 'insforge_subject_id'` on invoices) and use `payments.customer_mappings` only as a fallback. See the `insforge` app-integration skill's Stripe guide for a complete subscription fulfillment trigger.
### Subscription Cancellation Fields
When mirroring subscription state into app-owned tables, store `cancel_at` as well as boolean flags. Stripe can schedule future cancellation by setting `cancel_at` while `cancel_at_period_end` remains `false`; `canceled_at` can be the cancellation request time, not the access end time. Use `status <> 'canceled' AND cancel_at IS NOT NULL` for "will cancel".
## Common Mistakes
| Mistake | Fix |
|---------|-----|
| Running root payments commands without a provider | Use `payments stripe ...` |
| Using provider-prefixed price fields in SDK checkout code | Use `priceId` |
| Marking orders paid from success URL | Fulfill from `payments.webhook_events` |
| Adding only `INSERT` RLS for idempotent checkout | Add matching `SELECT` |
| Checking only `cancel_at_period_end` for scheduled cancellation | Also read and store `cancel_at` |
| Expecting Razorpay Items or Plans | Use Stripe Products and Prices |
SHA-256: ae561d06ba83eaf6352f19392e3833c1766241e6c6628cb582b01fec2642c5a0