01.software Docs
Specialist Integrations

Membership

Prepare paid subscriptions and deliver signed charge results.

Membership

Use this integration only in the stated caller context. Configure credentials first; the SDK README owns the package surface.

Enable customers and membership for this flow. Select ecommerce separately if you also need products and orders.

server.memberships.subscribe() prepares a paid membership; it does not accept a provider payment identifier and it does not activate the membership. Use a server-held Secret Key, then branch on the response union. A paid result is incomplete with the exact amount, currency, period start, expiry, and firstCharge.idempotencyKey for the workspace PSP. Charge the PSP using that key and post its outcome to the existing signed charge-result webhook. A trial result is immediately trialing and has no first-charge instruction. Before a paid subscribe, configure an active delegated billing connection for the workspace; otherwise Console returns 409 conflict without lifecycle writes.

import { createServerClient } from '@01.software/sdk/server'

type Server = ReturnType<typeof createServerClient>

type Prepared = Awaited<ReturnType<Server['memberships']['subscribe']>>
type FirstCharge = Extract<Prepared, { status: 'incomplete' }>['firstCharge']

export async function prepareSubscription(
  server: Server,
  input: Parameters<Server['memberships']['subscribe']>[0],
  tenantPsp: { charge: (charge: FirstCharge) => Promise<void> },
) {
  const prepared = await server.memberships.subscribe(input)
  if (prepared.status === 'incomplete') {
    const expiresAt = Date.parse(prepared.firstCharge.expiresAt)
    if (!Number.isFinite(expiresAt) || Date.now() >= expiresAt) {
      throw new Error('First charge instruction expired or invalid')
    }
    await tenantPsp.charge(prepared.firstCharge)
  }
  return prepared
}

Only the signed result webhook or its existing reconciliation query determines activation. Reuse the same subscribe idempotency key while the first charge is pending; a replay after settlement or expiresAt receives 409 rather than a new payment instruction. Do not initiate the PSP payment if expiresAt is invalid or the current time is at or after it. The workspace PSP must deduplicate on firstCharge.idempotencyKey; a late paid result is retained as evidence but does not activate the Membership, so the workspace must handle the resulting refund outside this API.

Next Actions

On this page