Skip to main content
Wildo.ai Coming soon

Billing and subscriptions

Keep each customer’s billing separate

Workspace and personal billing use distinct scoped accounts and records. Wildo checks the account behind a request and resolves provider events back to that account, so a purchase stays with the customer it belongs to.

A member working in Acme cannot select Northwind’s account simply by changing an account identifier. Provider events also resolve through the customer mapping rather than trusting an echoed workspace ID.

Keep each customer’s billing separate

Workspace and personal billing use distinct scoped accounts and records. Wildo checks the account behind a request and resolves provider events back to that account, so a purchase stays with the customer it belongs to.

Example — Keep Acme’s invoice in Acme

A member working in Acme cannot select Northwind’s account simply by changing an account identifier. Provider events also resolve through the customer mapping rather than trusting an echoed workspace ID.

For engineers

Application billing uses organization and user variants for accounts, subscriptions, invoices, usage and credits. The frontend chooses the current payer; resource authorization checks the request. Do not replace those operations with an unscoped repository lookup from a browser-supplied ID.

The state service adds a scope check before its internal reads:

The following selected excerpt is from billing-state.backend.service.ts; the surrounding module and explanatory source comments are omitted.

const principalScopeId = (executionContext as unknown as Record<string, unknown> | undefined)?.[variants.scopeKey];
if (principalScopeId !== undefined && principalScopeId !== null && String(principalScopeId) !== String(scopeEntityId)) {
  throw this.errorBuilder.buildError(ErrorType.AUTHORIZATION, executionContext, {
    context: {
      message: 'billing getState scopeEntityId does not match the authenticated principal scope',
      scopeType,
    },
  });
}
const billingAccount = await this.resolveOrCreateBillingAccount(variants, scopeEntityId);

A bound authenticated scope must match the requested scope entity. System/internal contexts without a bound scope can perform legitimate cross-account work; this check does not claim those trusted operations are tenant-restricted.

Check event identity independently of its signature

The provider module binds incoming events through data.object.customer and the local providerCustomerId. Checkout metadata is compared with that authoritative account; it does not choose the owner. Checkout also validates product scope and product/price binding before creating a provider session.

These checks cover distinct paths: a signed webhook proves origin, resource scope authorizes a human request, and the provider-customer mapping identifies the financial owner. The application must retain those paths when adding custom billing operations. A new direct repository or provider call needs its own explicit authorization and account resolution.

Building a B2B product or an internal tool?

Wildo is not self-service yet. Tell us what you have in mind and we will say plainly whether it fits, and what happens next.