Skip to content

feat(subscription): organization subscriptions through Stripe Checkout - #680

Open
samwel141 wants to merge 2 commits into
Greenstand:keycloakfrom
samwel141:feat-stripe-subscription-1261
Open

samwel141 wants to merge 2 commits into
Greenstand:keycloakfrom
samwel141:feat-stripe-subscription-1261

Conversation

@samwel141

@samwel141 samwel141 commented Oct 17, 2026 •

Copy link
Copy Markdown

Part of Greenstand/treetracker-admin-client#1261

Problem

An organization has no way to pay for premium.

Change

POST /api/organizations/{id}/subscription/session   creates a Checkout Session, returns its url
GET  /api/organizations/{id}/subscription           status and period end

Nothing is stored. The session tags subscription_data.metadata.organization_id, and status is read back from Stripe by that metadata.

Stripe's search index lags up to a minute, so the GET also accepts the session_id Stripe appends to the return url and retrieves the session directly, which is immediately consistent.

Both endpoints sit behind requireRole(Role.ORGANIZATION) and compare the caller's organization_id claim with the path id. The secret key never leaves the API.

New env: STRIPE_SECRET_KEY, STRIPE_PRICE_ID, STRIPE_RETURN_URL.

Verification

13 new tests across the controller and the service. Full suite 92 passing, tsc --noEmit clean, eslint clean.

Exercised end to end against the Stripe sandbox with test card 4242: Checkout completed, the return read the session, and the renewal date appeared immediately.

Related

Greenstand/treetracker-admin-client#1267 adds the panel side and needs this deployed first.

Add two endpoints so an organization can pay for premium: one creates a
Stripe Checkout Session, the other reports status and period end.

Nothing is stored. The session tags the subscription with the organization
id, and status is read back from Stripe by that metadata. Stripe's search
lags, so the status endpoint also accepts the session id from the return
url, which is immediately consistent.

Both endpoints require the organization role and check the caller's claim
against the path id.
The SDK default is 80s and the home page waits on this call.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant