REST
REST is one way your backend calls the provider: an HTTP request to a named URL, with JSON in the response. A binding is the protocol’s exact mapping of an operation to that request format.
For example, your backend asks for Alice’s entitlements and gets the products she may use. Choose REST when it fits your existing stack; it has the same purchase and account rules as GraphQL.
Every operation lives under /commerce/v1: queries are GET with query parameters, mutations are POST with a JSON body, and credentials travel only in the Authorization header. Use the provider's base URL and check its declared profiles before calling an operation.
The contract stays; the backend can differ
openiap-commerce-protocol-example
A small Fetch handler looks up each request in the published HTTP manifest, validates it, and calls a JavaScript handler backed by SQLite. It implements all six REST operations.
Read code: fetchHono registers the same manifest’s routes and sends validated input to shared handlers backed by Convex and store services. It implements all six operations and also exposes GraphQL.
Read code: HTTP_BINDING route registrationHow to check this behavior
Example. The dashboard calls real local HTTP endpoints, validates their responses, and checks the purchase flow. Run npm test. Inspect reference
IAPKit. The portable runner drives real route code with substituted store/database I/O and compares both bindings. Inspect reference
Read current access#
Run this from your authenticated backend. Set the complete Authorization header value in COMMERCE_SERVER_AUTH and select COMMERCE_USER_ID from the backend's session and ownership policy.
export COMMERCE_BASE_URL='https://your-provider.example'
curl --fail-with-body --get "$COMMERCE_BASE_URL/commerce/v1/entitlements" \
-H "Authorization: $COMMERCE_SERVER_AUTH" \
--data-urlencode "userId=$COMMERCE_USER_ID"Read productIds for current access. An empty list grants nothing; a failed request must not be treated as a successful access decision. Follow verify, bind, and read for the complete flow, or run the local example with fixture evidence.
The package's generated/openapi/commerce-protocol.openapi.json defines requests and responses. The operation table lists paths, profiles, and required roles from the generated HTTP manifest.
Failures#
Every failure is the status the shared error model assigns, with one envelope:
{ "error": { "code": "VERIFICATION_FAILED", "message": "..." } }11 error codes share one open space across both bindings; the manifest carries the code-to-status table.