subscription.checkout
End-to-end subscription checkout with Stripe — validates plan/org, creates a Stripe Checkout Session, activates on payment webhook.
End-to-end subscription checkout via Stripe.
Uses context.session (SQLAlchemy session injected by the engine) to read and write subscription-domain models from ltinteg-workflow-business-library directly. No application-layer plugin or adapter is required — any project whose DB contains the standard LTINTEG subscription tables can use this workflow as-is.
States: initiated → validating → checkout_created → payment_received → activating → completed canceled | failed (terminal)
Required env var: STRIPE_SECRET_KEY
Overview
| Property | Value |
|---|---|
| Workflow type | Linear |
| Library | App-subscription |
| Version | 2.0 |
Triggers
| Source | Endpoint / Event | Description |
|---|---|---|
| API | POST /api/subscriptions/create-checkout-session | User initiates checkout |
| WEBHOOK | checkout.session.completed | Stripe webhook confirms payment |
Input Schema
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
organization_uuid | uuid | Yes | — | Organization UUID to subscribe |
plan_uuid | uuid | Yes | — | Plan UUID to subscribe to |
user_uuid | uuid | No | — | User initiating checkout |
success_url | string | Yes | — | Redirect URL after successful payment |
cancel_url | string | Yes | — | Redirect URL if user cancels |
selected_price_ref | string | No | — | Stripe price reference for a pre-selected billing cycle |
user_seats | integer | No | — | Initial paid user seat quantity for the Stripe subscription |
key_seats | integer | No | — | Initial paid agent/key seat quantity for the Stripe subscription |
trace_ref | string | No | — | Trace reference for request correlation |
Output Schema
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
organization_uuid | uuid | No | — | — |
plan_uuid | uuid | No | — | — |
user_uuid | uuid | No | — | — |
success_url | string | No | — | — |
cancel_url | string | No | — | — |
selected_price_ref | string | No | — | — |
user_seats | integer | No | — | — |
key_seats | integer | No | — | — |
trace_ref | string | No | — | — |
app_uuid | uuid | No | — | — |
app_code | string | No | — | — |
plan_name | string | No | — | — |
stripe_product_ref | string | No | — | — |
organization_email | string | No | — | — |
stripe_customer_ref | string | No | — | — |
region | string | No | — | — |
billing_currency | string | No | — | — |
trial_period_days | integer | No | — | — |
initiated_at | string | No | — | — |
initiated_by | string | No | — | — |
validated_at | string | No | — | — |
stripe_checkout_session_ref | string | No | — | — |
checkout_url | string | No | — | — |
stripe_price_ref | string | No | — | — |
price_currency | string | No | — | — |
price_amount | integer | No | — | — |
checkout_created_at | string | No | — | — |
pending_subscription_uuid | uuid | No | — | — |
stripe_subscription_ref | string | No | — | — |
current_period_start | string | No | — | — |
current_period_end | string | No | — | — |
subscription_status | string | No | — | — |
payment_received_at | string | No | — | — |
subscription_uuid | uuid | No | — | — |
activated_at | string | No | — | — |
seat_items_persisted | json | No | — | — |
billing_items_persisted | list | No | — | — |
completed_at | string | No | — | — |
canceled_at | string | No | — | — |
cancel_reason | string | No | — | — |
failed_at | string | No | — | — |
failure_reason | string | No | — | — |
failure_type | string | No | — | — |
failed_action | string | No | — | — |
failed_at_state | string | No | — | — |
failed_step | string | No | — | — |
failed_layer | string | No | — | — |
error | string | No | — | — |
error_type | string | No | — | — |
States
| State | Initial | Terminal | Success | Auto-advance | Description |
|---|---|---|---|---|---|
initiated | Yes | No | — | validate_checkout | Checkout request received |
activating | No | No | — | complete | Activating subscription in database |
checkout_created | No | No | — | — | Stripe session created — awaiting payment webhook |
payment_received | No | No | — | activate_subscription | Payment confirmed by Stripe webhook |
validating | No | No | — | create_checkout_session | Validating plan and organization |
canceled | No | Yes | No | — | User canceled checkout |
completed | No | Yes | Yes | — | Subscription activated successfully |
failed | No | Yes | No | — | Checkout failed |
State Diagram
Transitions
| From | Action | To | Description |
|---|---|---|---|
initiated | validate_checkout | validating | Validate plan and org |
validating | create_checkout_session | checkout_created | Create Stripe session |
checkout_created | payment_received | payment_received | Payment confirmed |
payment_received | activate_subscription | activating | Activate subscription |
activating | complete | completed | Mark complete |
checkout_created | cancel | canceled | User canceled |
* (any state) | fail | failed | Catch-all failure handler |
Outcomes
| Outcome | Type | Description | State Data Keys |
|---|---|---|---|
completed | SUCCESS | Subscription activated successfully | subscription_uuid, stripe_subscription_ref, completed_at |
canceled | FAILURE | User canceled checkout | canceled_at, cancel_reason |
failed | FAILURE | Checkout failed — see failure_reason | failed_at, failure_reason |
Business Errors
| Code | Message Template |
|---|---|
BILLING_AUTHORIZATION_REQUIRED | |
BILLING_ITEM_INPUT_ERROR | |
PLAN_NOT_FOUND | Plan {plan_uuid} not found or inactive |
PLAN_NOT_LINKED | Plan {plan_uuid} not linked to a Stripe product |
ORGANIZATION_NOT_FOUND | Organization {organization_uuid} not found |
EXISTING_SUBSCRIPTION | Organization already has an active subscription for app |
NO_PRICE_FOUND | No active recurring price found for product reference {product_ref} in {currency} or USD |
MIXED_CURRENCY | Seat price {seat_price_ref} is in {seat_currency} but the base price for {product_ref} is in {base_currency}; Stripe cannot combine currencies in one checkout session |
State Timeouts
| State | Timeout | Action |
|---|---|---|
checkout_created | 1440 min | fail |
Alarms
| Name | Type | State | Severity | Description |
|---|---|---|---|---|
checkout_failed | state_entered | failed | warning | Subscription checkout failed |
checkout_pending | state_timeout | checkout_created | info | Checkout session pending for over 1 hour |
API Usage
bash
# Step 1: Start the workflow
POST /api/workflows/start
Content-Type: application/json
{
"workflow_type": "subscription.checkout",
"initial_data": {
"organization_uuid": "value",
"plan_uuid": "value",
"success_url": "value",
"cancel_url": "value"
}
}
# Step 2: Webhook triggers transition (checkout.session.completed)
POST /api/workflows/{workflow_id}/transition
Content-Type: application/json
{ "action": "...", ... webhook payload ... }