Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 53 additions & 10 deletions docs/api-reference/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
},
{
"name": "Billing",
"description": "Checkout, usage, and Stripe webhook handling."
"description": "Plan, credit, and subscription status."
},
{
"name": "Account",
Expand Down Expand Up @@ -3418,12 +3418,6 @@
"in": "header",
"name": "X-Demo-User",
"description": "Local development only. Any stable value selects an isolated demo account. Rejected in production."
},
"stripeSignature": {
"type": "apiKey",
"in": "header",
"name": "stripe-signature",
"description": "Stripe-generated signature over the exact raw request body."
}
},
"headers": {
Expand Down Expand Up @@ -6172,8 +6166,8 @@
"plan": {
"type": "string",
"enum": [
"free",
"pro"
"starter",
"builder"
]
},
"includedCredits": {
Expand All @@ -6183,13 +6177,62 @@
"type": "string",
"enum": [
"onboarding",
"monthly"
"billing-cycle"
]
},
"creditBalance": {
"type": "integer"
}
}
},
"BillingSummary": {
"type": "object",
"required": [
"plan",
"status",
"creditBalance",
"includedCredits",
"cancelAtPeriodEnd",
"currentPeriodStart",
"currentPeriodEnd",
"canManageBilling"
],
"properties": {
"plan": {
"type": "string",
"enum": [
"starter",
"builder"
]
},
"status": {
"type": "string"
},
"creditBalance": {
"type": "integer"
},
"includedCredits": {
"type": "integer"
},
"cancelAtPeriodEnd": {
"type": "boolean"
},
"currentPeriodStart": {
"type": [
"integer",
"null"
]
},
"currentPeriodEnd": {
"type": [
"integer",
"null"
]
},
"canManageBilling": {
"type": "boolean"
}
}
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions docs/internals/endpoints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Called by the video2ctx web application or an explicit signed-in account action.
| `POST` | `/api/auth/sign-in/social` | `signInWithSocialProvider` | Start Google sign-in | Public or protocol-signed | Starts an interactive browser sign-in; do not call it with API-key credentials. |
| `POST` | `/api/auth/sign-out` | `signOut` | Revoke the current browser or CLI session | `sessionCookie` or `cliSession` | Revokes the presented session; invoke it only for an explicit logout action and clear the matching local credential after success. |
| `DELETE` | `/v1/account` | `deleteAccount` | Permanently delete the current account | `sessionCookie` or `demoUser` | Destructive account operation; require deliberate confirmation and never automate it for a user. |
| `POST` | `/v1/billing/checkout` | `createBillingCheckout` | Create a Stripe Checkout session | `sessionCookie` or `demoUser` | Starts an external checkout session; the user must review and complete payment with Stripe. |
| `GET` | `/v1/billing` | `getBilling` | Get the signed-in account billing status | `sessionCookie` | Returns subscription state for the current browser account; do not expose another customer’s provider identifiers. |
| `POST` | `/v1/demo/youtube/inspect` | `inspectLandingYouTubeVideo` | Inspect a YouTube video from the landing page | Public or protocol-signed | Public, rate-limited demo route; do not use it as a credentialed bulk-data API. |
| `POST` | `/v1/notification-preferences/confirm-email` | `confirmNotificationEmail` | Confirm monitor email alerts from the signed-in dashboard | `sessionCookie` or `cliSession` or `bearerApiKey` or `apiKey` or `demoUser` | Enables email delivery only for the signed-in account after validating the confirmation token. |
| `DELETE` | `/v1/oauth/youtube` | `disconnectYouTube` | Disconnect the YouTube account | `sessionCookie` or `demoUser` | Mutates the account connection state; require an explicit user action. |
Expand All @@ -40,7 +40,6 @@ Called by an external provider or through a signed link with protocol-specific s

| Method | Path | Operation ID | Summary | Declared access | Safety notes |
| --- | --- | --- | --- | --- | --- |
| `POST` | `/v1/billing/webhook` | `handleStripeWebhook` | Handle a Stripe webhook | `stripeSignature` | Accept only Stripe-signed requests and preserve idempotent event handling. |
| `GET` | `/v1/email/unsubscribe` | `unsubscribeEmail` | Unsubscribe from email digests | Public or protocol-signed | A signed link changes email preferences; do not expose or reuse its token. |
| `POST` | `/v1/email/unsubscribe` | `unsubscribeEmailPost` | Unsubscribe from email digests | Public or protocol-signed | Changes email preferences; validate the signed request and avoid logging its token. |
| `GET` | `/v1/oauth/youtube/callback` | `completeYouTubeOAuth` | Complete YouTube OAuth | Public or protocol-signed | OAuth callback; validate state and consume authorization codes only once. |
Expand Down
2 changes: 1 addition & 1 deletion docs/internals/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This section documents every operation that should not be treated as a normal ex
| Audience | Intended caller |
| --- | --- |
| First-party | The video2ctx web application or an explicit account action |
| Callback | A signed provider link, OAuth provider, email recipient, or Stripe |
| Callback | A signed provider link, OAuth provider, email recipient, or Polar |
| Operator | Authorized video2ctx platform administration |

<Warning>
Expand Down
7 changes: 4 additions & 3 deletions platform/.dev.vars.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
YOUTUBE_OAUTH_ENCRYPTION_KEY=base64-encoded-32-byte-key
TURNSTILE_SECRET=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRO_PRICE_ID=
POLAR_ACCESS_TOKEN=
POLAR_WEBHOOK_SECRET=
POLAR_ENVIRONMENT=sandbox
POLAR_BUILDER_PRODUCT_ID=
# Required in production for the anonymous landing-page inspection quota.
LANDING_DEMO_RATE_LIMIT_MODE=disabled
UPSTASH_REDIS_REST_URL=
Expand Down
22 changes: 22 additions & 0 deletions platform/migrations/0014_polar_billing.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
PRAGMA foreign_keys = ON;

-- Application-owned projection of billing state. Polar remains the payment
-- system of record; this table is the local state used for authorization and UI.
CREATE TABLE billing_accounts (
user_id TEXT PRIMARY KEY REFERENCES user(id) ON DELETE CASCADE,
provider TEXT NOT NULL DEFAULT 'polar' CHECK(provider IN ('polar')),
provider_customer_id TEXT UNIQUE,
provider_subscription_id TEXT UNIQUE,
provider_product_id TEXT,
plan TEXT NOT NULL DEFAULT 'starter' CHECK(plan IN ('starter', 'builder')),
status TEXT NOT NULL DEFAULT 'inactive',
cancel_at_period_end INTEGER NOT NULL DEFAULT 0 CHECK(cancel_at_period_end IN (0, 1)),
current_period_start INTEGER,
current_period_end INTEGER,
provider_updated_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);

CREATE INDEX billing_accounts_subscription_idx
ON billing_accounts(provider, provider_subscription_id);

Loading
Loading