ShipNext

Entitlement Model

How ShipNext represents DURATION, QUOTA, user_entitlements, and quota_transactions for subscriptions and credits.

This page builds on SaaS Model. ShipNext models "whether a user can use a resource" as an entitlement.

Current entitlement types:

TypeMeaningTypical use
DURATIONUnlimited usage while valid, usually balance = -1Unlimited downloads or feature access during subscription
QUOTANumeric balance that can be consumedAI credits, generation count

Business types live in src/modules/billing/types.ts. Database tables are defined in the SQLite and PostgreSQL payment schemas.

Configuration

Plan entitlements are configured in src/modules/billing/config/plan.ts under prices[].entitlements:

{
  entitlementType: EntitlementType.Quota,
  balance: 500,
  priority: 10,
  isRollOver: false,
  resourceType: EntitlementResourceType.Credit,
  refreshInterval: RefreshInterval.Monthly,
}

Built-in resource types:

export enum EntitlementResourceType {
  Storage = "STORAGE",
  Credit = "CREDIT",
}

To add a resource such as IMAGE_GENERATION, extend the enum and implement grant, consume, and display logic.

user_entitlements

Stores the user's currently available entitlements.

FieldDescription
user_idOwner user
typeDURATION or QUOTA
resource_typeResource such as CREDIT or STORAGE
balanceRemaining balance; DURATION usually uses -1
priorityLower numbers are consumed first
valid_fromStart timestamp
valid_untilEnd timestamp; null means no expiration
sourceSUBSCRIPTION, TOPUP, or LIFETIME
source_idSubscription ID, order ID, or other source ID

quota_transactions

Audit log for quota changes.

actionMeaning
BUY_TOPUPPurchased additional quota
SUB_GRANTSubscription grant or renewal
CONSUMEQuota consumed
REFUNDQuota returned
EXPIREQuota expired or reset

Status can be PENDING, SUCCESS, or FAILED. Long-running jobs should reserve quota first, then commit or roll back depending on the result.

Grant flow

Webhook -> processPaymentWebhook
        -> payment_subscriptions / payment_orders
        -> grantSubscriptionEntitlement / handleSubscriptionRenewal
        -> user_entitlements + quota_transactions

Rules:

  • New subscription: create entitlements from the current price.
  • Renewal: if isRollOver = true, add balance and extend validity; otherwise reset to the new cycle amount.
  • Upgrade: update by resource under the same SUBSCRIPTION/sourceId to avoid duplicate grants in the same cycle.
  • One-time purchase: use TOPUP source, usually without expiration unless your product defines one.

Consume flow

Call prepareQuota(userId, resourceType, amount):

  1. Check for a valid DURATION entitlement. If present, allow usage without writing quota deduction.
  2. Query valid QUOTA entitlements by priority ASC and expiration.
  3. If balance is enough, write PENDING transactions and reserve balance.
  4. On success, call commitQuota(transactionId).
  5. On failure, call rollbackQuota(transactionId).

This is useful for AI generation, rendering, or batch jobs that may fail after work starts.

Common models

Subscription + quota

The built-in starter/pro examples grant CREDIT on each cycle.

Subscription + unlimited usage

Use EntitlementType.Duration with balance = -1.

One-time purchase + quota

Use a BillingModel.OneTime price and grant source = "TOPUP" when payment succeeds.

Storage quota

plan.storageLimit displays storage capacity in Dashboard. API enforcement depends on EntitlementResourceType.Storage. If only storageLimit is configured, users may see a limit while the upload API does not enforce it.

Checklist

  • Provider Price IDs match plan.ts.
  • Every paid price has expected entitlements.
  • Successful subscription creates user_entitlements.
  • Renewal rollover/reset behavior matches product rules.
  • Consuming features use prepare -> commit / rollback.
  • Displayed limits and enforced API limits come from aligned rules.

On this page