Marketplace Architecture
The PayPM Business Marketplace is a contract-backed value-chain execution layer. It is not a generic listing catalogue.
Two surfaces during the compatibility period
| Surface | Role |
|---|---|
| Compatibility Marketplace in the wallet backend | Operator Plane B write spine still used by existing operator tools (GET|POST /internal/marketplace/*). Not a live Wallet consumer path. Wallet mobile does not mount a Marketplace tab and does not advertise GET /marketplace/my as a customer contract. |
| Standalone Marketplace API | Separate product service with its own PostgreSQL schema, Identity directory, and transactional outbox. Sandbox supplier web is served at marketplace.paypm.net; browser sign-in on it is currently held (the session read answers 503). The Marketplace API remains ClusterIP (no public API ingress); the SPA same-origin /api proxy reaches it. Business mobile and the Marketplace SPA live-read standalone GET /api/v1/marketplace/my when pointed at this host. |
Do not treat the standalone service as cut over for writes. Compatibility wallet Plane B remains the operator write spine. Do not invent a Marketplace write API. Shadow comparison must exit non-zero on any customer-visible mismatch before cutover.
Ownership boundaries
| Owner | Owns |
|---|---|
ZITADEL (auth.paypm.net) | Subject authentication only |
| Identity product | Person, business, membership, OIDC subject link, optional wallet end-user reference |
| Standalone Marketplace | Programmes, opportunities, participation roles (supplier/operator), assignments, milestones, evidence, contract orchestration, outbox events |
| Wallet / ledger / providers | Balances, Blnk postings, signatures, notifications, settlement adapters |
Marketplace never derives participant authority from phone number or token profile
claims. Standalone /context and /my require an active Identity person/business
context and a Marketplace participant whose role matches
MARKETPLACE_REQUIRED_ROLE (default supplier).
Standalone Identity contract
Code
Sandbox Marketplace uses IDENTITY_DIRECTORY_MODE=http against the private
Identity ClusterIP with a ZITADEL service JWT. Identity does not return product
roles. Compatibility mode remains available as a time-boxed adapter only.
See Identity (Account & Business) for bind and account read/update routes.
Current Marketplace reads
Wallet consumer mobile is money-only. It does not mount Marketplace and
does not use wallet GET /marketplace/my as a live product contract. That
compatibility route is residue on the wallet host, not current Wallet IA.
Standalone Marketplace GET /api/v1/marketplace/my is the Marketplace-owned
live-read for Business mobile and the Marketplace SPA: the resolved
participant, browseable open opportunities, and assignments with their
opportunity projection. It is read-only and tenant-scoped. It is not a Wallet
Plane A contract.
Marketplace SPA OIDC
The standalone web client uses a dedicated public SPA OIDC application against
ZITADEL (authorization code + PKCE). Deployment-time public config carries only
issuer, client ID, redirect URI, and Marketplace API URL. The ZITADEL owner must
approve the exact redirect URI; the Identity owner must bind that subject to the
supplier tenant and marketplace:supplier role before the actor is live.
Compatibility backend bounded contexts
While the compatibility backend remains the operator write spine, operators continue to use the in-backend marketplace registry and Contract Vault spine:
| Context | Owns |
|---|---|
| Marketplace Registry | buyers, suppliers, contractors, verifiers, funders |
| Programmes | value-chain initiatives and opportunity groups |
| Opportunities | tenders, work packages, lots, eligibility |
| Contract Workspace | contract drafts, signed versions, hashes |
| Milestone Ledger | obligations, expected/completed counts, review state |
| Evidence Vault | photos, PDFs, reports, receipts, notes |
| Payment Controls | controlled release instructions into wallet/ledger |
| Payment Executions | provider-neutral gateway handoff attempts and settlement state |
Core flow
Code
Standalone Contract Vault read BFF
The Marketplace product API now exposes OIDC-bound, fail-closed GETs for standalone Contract Vault records, versions, and commitments:
GET /api/v1/marketplace/contract-vault/recordsGET /api/v1/marketplace/contract-vault/:contractRef/versionsGET /api/v1/marketplace/contract-vault/:contractRef/versions/:versionNumberGET /api/v1/marketplace/contract-vault/:contractRef/commitmentsGET /api/v1/marketplace/contract-vault/:contractRef/commitments/:commitmentRef
These are Marketplace SPA OIDC session reads, scoped to tenant + linked
walletEndUserId. They are not on the wallet sandbox Plane A/B catalog
host, and the browser never uses Plane B credentials. Missing link or
store returns unavailable + empty. Writes, signatures, payments, and
evidence remain on wallet-backend.
Compatibility operator API surface
The first contract-vault spine plus marketplace registry path on the compatibility backend includes:
GET|POST /internal/marketplace/programmesGET|POST /internal/marketplace/opportunitiesGET|POST /internal/marketplace/participantsGET|POST /internal/marketplace/assignmentsGET|POST /internal/marketplace/milestonesGET|POST /internal/marketplace/evidenceGET|POST /internal/marketplace/payment-controlsGET|POST /internal/marketplace/payment-executionsPOST /internal/marketplace/payment-executions/provider-events- Contract Vault version, signature, commitment, extraction, and anchor-event
endpoints under
/internal/contract-vault/*
Wallet GET /marketplace/my is compatibility residue, not a live Wallet
consumer contract. Operators can create programmes, opportunities,
participants, and assignments in backoffice, then link contracts from
Contract Vault. Those writes stay on wallet Plane B. Do not invent a
Marketplace write API. Approved evidence can link to payment-control
instructions; approved controls can be followed by payment execution
attempts. Provider callbacks or backoffice provider-event workflows
reconcile gateway or signature receipts without locking the product to one vendor.
Cutover rule
Snapshots exported from the compatibility backend must carry Identity-owned
identityId values; importers never infer identity from phone or email. Replay
of the same snapshotId and content is idempotent; content drift under an
existing ID fails closed. Keep the compatibility API authoritative for operator
writes until shadow comparison, observability, and rollback proof are complete.