The Starter to Pro Upgrade Flow allows Malet Owners to transition their personal Starter Malets into organizational Pro Malets. This process involves the payments, organizations, and malets subgraphs working together to ensure atomic provisioning and data integrity.
Upgrade & Transfer Architecture
When a user upgrades a Starter Malet, the backend must create an Organization, transfer the Malet to that Organization, and attach the Subscription & Billing Architecture โ all atomically.
The process leverages the same Stripe checkout.session.completed webhook pattern used in the initial Malet-First Ownership Architecture, but with the action set to upgrade_and_transfer.
1. Checkout Session Creation
The client requests an upgrade via the createUpgradeCheckoutSession GraphQL mutation. The payments service embeds the necessary context into the Stripe session metadata:
metadata: {
action: 'upgrade_and_transfer',
userId: 'user_123',
planId: 'pro',
maletId: 'malet_456', // The Starter Malet being upgraded
orgName: 'Acme Corp',
orgSlug: 'acme-corp'
}
2. Atomic Provisioning Webhook
When the user completes payment, Stripe fires checkout.session.completed. The WebhookService handles this by:
- Creating the Organization: An HTTP POST to the organizations subgraph (protected by
x-internal-callerguard). - Transferring the Malet: An HTTP POST to the malets subgraph to execute
transferMaletToOrganization, changing ownership from theUSERto the newORGANIZATION. - Updating the Subscription: Stripe subscription metadata is updated with the new
orgId. - Emitting Notifications: The
org_upgraded_and_malet_transferredTCP event is dispatched.
NOTE
Idempotency: If the organization slug (acme-corp) is already taken, the webhook gracefully bails out before creating any entities, preventing orphan records.
Collaborator Migration
Starter Malets often have Outside Collaborators (users with sandboxed access). When upgrading to Pro, these collaborators must be converted into full Organization Members.
This is handled by the convertCollaboratorsToMembers mutation in the organizations subgraph.
Permission Mapping
The convertToMembers service translates sandbox permissions into formal Custom RBAC organization roles:
| Collaborator Permission | New OrgRole |
|---|---|
FULL_ACCESS |
ADMIN |
EDIT_CONTENT |
MEMBER |
MANAGE_ORDERS |
MEMBER |
VIEW_ONLY |
VIEWER |
Tier Limits Enforcement
The migration process strictly enforces the Pro tier limit (maximum 10 members) using the centralized checkTierLimit configuration. If a conversion would exceed the limit, the specific collaborator is skipped, and an error is returned in the CollaboratorConversionResult.
type CollaboratorConversionResult {
converted: Int!
errors: [String!]!
}
Whenever a collaborator is successfully converted to a member, their old Collaborator record is marked as REVOKED, and an OrgAuditEventType.COLLABORATOR_CONVERTED_TO_MEMBER audit event is logged.
Deck UI
The upgrade flow is surfaced in The Deck โ the Malet Owner command center running on ngwenya-deck.
Upgrade CTA
On the /dashboard page, every personal Malet card (ownerType === 'USER') displays a green Upgrade button alongside the existing Manage, Blog, View, and Transfer actions. Clicking it opens the UpgradeModal.
UpgradeModal
The modal presents the Malet Owner with two paths:
- Upgrade & Transfer (recommended) โ The user enters an Organization name, URL handle (auto-derived slug), and optional category, selects Monthly or Yearly billing, and clicks "Upgrade & Transfer". This calls
CREATE_UPGRADE_CHECKOUT_SESSIONfrom@ngwenya/queries/subscriptionsand redirects to Stripe Checkout. - Create New Pro Malet โ Navigates to
/create-malet?tier=pro. The original Starter Malet remains personal and unmodified.
The modal auto-derives the slug from the org name (same pattern as OrgDetailsStep.svelte in the create-malet wizard) and validates minimum lengths before enabling the submit button.
Upgrade Complete Page
After Stripe checkout succeeds, the user lands on /upgrade-complete?orgSlug={slug}. This is a simple confirmation page that:
- Displays a success message with Pro feature highlights
- Provides a CTA to navigate to the new Organization management page (
/orgs/{slug}/manage) - Links back to The Deck dashboard
Shared Queries
The upgrade UI consumes two new GraphQL mutations from @ngwenya/queries:
| Mutation | Package | Description |
|---|---|---|
CREATE_UPGRADE_CHECKOUT_SESSION |
@ngwenya/queries/subscriptions |
Creates a Stripe Checkout session with upgrade_and_transfer metadata |
CONVERT_COLLABORATORS_TO_MEMBERS |
@ngwenya/queries/organizations |
Converts sandbox collaborators into org members post-upgrade |
NOTE
The PLAN_TIERS definition in @ngwenya/queries/subscriptions uses pro as the plan ID (renamed from business for consistency with the backend checkTierLimit configuration).
Related
- Malet-First Ownership Architecture โ The foundational flow for atomic checkout provisioning.
- Outside Collaborators โ Details on sandbox access before upgrading.
- Subscription & Billing Architecture โ How billing models and tier limits are managed.
- Custom RBAC โ Permission matrix unlocked on Pro tier.
- User Guide: Upgrading to Pro