feat(aws-marketplace): sell several listings from one integration through a product catalog

Merged
WA
3d ago
+406-140

Summary

  • One integration now sells any number of AWS Marketplace listings. Each listing's behavior is a plan in code (aws-marketplace/products.ts); which product codes exist is deployment configuration, AWS_MARKETPLACE_PRODUCTS=<code>=<plan>,.... Every path resolves a license's product from the code AWS returned or the code on its row, so adding, repricing, or retiring a listing is a config change, not a branch
  • Plans: contract (unchanged behavior: any current entitlement grants access) and platform-with-usage (access requires a current platform entitlement; usage dimensions never grant access on their own)
  • entitlementCount still counts every dimension AWS returned, so a license with usage but no platform reads inactive, not pending
  • Registration routes by the code ResolveCustomer returns, since all listings share the one fulfillment URL; a code this deployment doesn't sell is refused as before
  • Reconcile and the daily re-verify span every configured code and verify each license against its own product; agreement renewals, advisories and endings match across listings (agreement IDs are unique anyway)
  • The team's "organizations already linked to this AWS account" hint now matches across listings — a buyer moving to a new listing usually wants the organization they already have — and team notices name the listing
  • config.ts is the single parse point (memoized); a malformed AWS_MARKETPLACE_PRODUCTS throws an error naming the bad entry on first use. env.ts stays free of billing code
  • Reconcile resolves the configured products once per run and the repository returns each license paired with its product, so no loop re-looks-up or skips

Deploy prerequisite: this replaces AWS_MARKETPLACE_PRODUCT_CODE. Before the release carrying this PR deploys, the production env secret must hold AWS_MARKETPLACE_PRODUCTS=<existing code>=contract; otherwise the Marketplace surfaces turn off. The new listing is added to that value (and to the infra event rule) once this is live.

Type of Change

  • New feature

Testing

  • lifecycle.integration.ts + checkout-guard.integration.ts on real Postgres (bun run test:integration): 75 pass, including two new ones — a usage-listing buyer is admitted only on a current platform entitlement (a usage-only license is recorded inactive), and a queued License Updated event for the usage listing is applied under its own plan (a lapsed platform ends access while usage is current). Both go red with the dimension filter removed
  • entitlements.test.ts: 10 pass; the 3 new required-dimension cases go red with the filter removed
  • tsc --noEmit, bun run lint, and check:boundaries, check:client-boundary, check:utils, check:file-names, check:comment-hygiene, check:source-conventions, check:explicit-any; full audits and suites run in CI

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing (new tests pass the test-audit authoring gate)
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)