commerce

package
v1.801.307 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2026 License: Apache-2.0 Imports: 34 Imported by: 0

Documentation

Overview

activeplan.go answers the subscription paywall's ONE question: does org X hold a LIVE PAID plan? It is the commerce "active-subscription check" — the tier sibling of CheckEntitlement (per-product license). CheckEntitlement deliberately cannot answer this: it filters Status=Active ONLY (a TRIALING subscriber would be mis-read as unentitled) and it is scoped to a single product id, whereas the paywall gates on the PLAN TIER regardless of product. So ActivePaidPlan is a distinct read over the SAME org resolver + subscription store, counting ACTIVE *and* TRIALING subscriptions and classifying the tier through @hanzo/plans (clients/plan.Paid) — the single source of truth for which tiers cost money.

It is an OPTIONAL capability on the in-process client (not on the narrow types.CommerceClient interface): the paywall (routers.PlanChecker) resolves it by type-assertion, mirroring types.ModelLister. A commerce build that cannot answer (split-deploy ZAP client, disabled stub) simply does not implement it, and the paywall fails OPEN — an outage never locks out a subscriber.

client.go is the in-process inter-subsystem commerce client — the REAL implementation of cloud's types.CommerceClient, absorbed here from the retired in-process stub that used to fail closed on entitlement. It answers cloud's licensing/entitlements tier with DIRECT Go calls into the embedded commerce datastore (subscriptions) plus the @hanzo/plans vocabulary (plan → license features) — no HTTP hop, no network.

MONEY-SAFETY. CheckEntitlement NEVER fabricates a grant. It returns Active:true ONLY when a real active, unexpired subscription in the org's own datastore namespace holds a plan tier whose @hanzo/plans license-features actually name the product. Any machinery it cannot resolve (commerce not co-resident, org not resolvable, subscription query error, plans vocabulary unavailable) returns an ERROR — the entitlements gate treats an erroring client as "cannot verify ⇒ 503", the specified secure default, so an unverifiable product is never enabled. A clean "resolved, but no plan licenses this product" is a real Active:false answer (the gate turns it into a 402 upgrade prompt), never an error and never a grant.

mount.go mounts the hanzoai/commerce MODULE into a cloud binary (HIP-0106) via the NATIVE co-residence contract: commerce registers its routes directly on the HOST's zip app (EmbedConfig.App) — one router, one specificity space, zero handler adaptation. This adapter narrows cloud.Deps, boots the embed, and wires the in-process seams. Direction is one-way: cloud → commerce.

PCI SCOPE. Commerce is a LIGHT ROUTER, NOT in PCI-DSS scope: tokens + intent IDs only, NEVER a PAN. PAN-touching paths call the out-of-process Payments / Vault (ZAP-RPC); when those clients are absent the payment handlers fail closed while tenant config + admin stay served — Mount warns loudly at startup.

FAIL-SOFT. A broken Embed does NOT crash the binary: commerce degrades to a 503 on its own prefixes while every co-resident subsystem stays up.

Index

Constants

This section is empty.

Variables

View Source
var Prefixes = []string{
	"/v1/commerce",
	"/_/commerce",

	"/v1/store",

	"/v1/catalog",

	"/v1/plans",

	"/v1/billing/webhooks",

	"/v1/billing/auto-recharge",
}

Prefixes is every root path the commerce surface owns on the shared app. Under the native SharedApp contract most of these are registered by commerce's own setupRoutes; the list is the fail-closed 503 set AND the wire contract prefix_test pins — the route families a session gate or the AI /v1/* catch-all must never swallow.

It is exported because the composition root DECLARES it (apps.Wire's commerce entry, Prefixes: commerce.Prefixes), which is what puts commerce on the light host's manifest. Derived instead, the walk would read the `app.Group("/v1")` this file opens for the store/catalog/plan bundle as a claim on ALL of /v1 and hand commerce every request in the fleet. Same list, one owner, stated once.

Functions

func BalanceCents

func BalanceCents(ctx context.Context, org, subject, currency string, test bool) (int64, error)

BalanceCents returns subject's available prepaid balance (USD cents) in org, read DIRECTLY from the co-resident embedded commerce ledger — no HTTP hop. It is the native twin of the /v1/billing/balance read (billing.zapGetBalance): resolve the org's own datastore namespace, tally the subject's iam-user transactions in the currency, and return Balance - Holds clamped at zero. It reuses the SAME currentEmbedded seam the in-process entitlement client resolves through.

This is the read the money cutover (admin/finance backfill) and the admin cockpit's credit panels use when commerce runs in the SAME binary: the admin commerce HTTP client dials an unroutable in-proc address and reads $0, which would silently migrate/report nothing. When commerce is NOT co-resident this returns an ERROR (never 0), so a caller can tell "not wired" from a real zero balance and fail loud rather than move money on a phantom figure. Subject is lowercased + trimmed; an empty currency defaults to usd.

func Mount

func Mount(app *zip.App, deps cloud.Deps) error

Mount boots commerce ON the shared zip app (native co-residence). commerce's own setupRoutes registers /v1/commerce/* and /_/commerce/* directly; the standalone-only surfaces (bare /healthz, legacy /admin SPA, checkout SPA root catch-all, Listen) are skipped by the SharedApp contract. This adapter registers the remaining wire-contract families with commerce's own gate chains (see Prefixes).

func PublishEmbedded

func PublishEmbedded(e *commercemod.Embedded)

PublishEmbedded records the mounted Embedded as the in-process entitlement source. Mount calls it once; nil un-publishes (tests).

Types

type Client

type Client = types.CommerceClient

Client is the in-process inter-subsystem seam cloud's licensing/entitlements tier calls. It IS cloud's types.CommerceClient — one narrow interface (GetOrgConfig + the real CheckEntitlement), not a second copy — kept as an alias so a value satisfies both names with no adapter. Add methods here only when a consumer needs them; keep it narrow.

func InProcessClient

func InProcessClient(brand string) Client

InProcessClient returns the process-wide, lazily-resolved client cloud's pickCommerceClient wires as deps.Commerce. BuildDeps runs before MountAll, so it resolves the published Embedded per call rather than capturing one; brand answers OrgConfig even before Mount publishes.

Directories

Path Synopsis
Package transport is the ONE seam that lets every cloud subsystem that speaks the commerce billing S2S surface (clients/{billing,account,admin, referrals,authors,affiliates,usage} + the request-edge metering gate in build.go) reach the co-resident, in-process commerce handler with a DIRECT Go call instead of an HTTP hop to the standalone commerce pod (CLOUD_COMMERCE_HTTP_URL, commerce.hanzo.svc:8001).
Package transport is the ONE seam that lets every cloud subsystem that speaks the commerce billing S2S surface (clients/{billing,account,admin, referrals,authors,affiliates,usage} + the request-edge metering gate in build.go) reach the co-resident, in-process commerce handler with a DIRECT Go call instead of an HTTP hop to the standalone commerce pod (CLOUD_COMMERCE_HTTP_URL, commerce.hanzo.svc:8001).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL