← Provider replacement walkthrough

Commerce Protocol Example · composition/commerce-client.mjs

Source snapshot · 2026-09-09

SHA-256 f1f1a056bc76801df25111cb12fdcde501d507a5461ef8b15bfe2f57979cd03c
1import { operation, validate } from "../contract.mjs";2 3// This module runs on the authenticated app backend, which owns the credential.4export function createCommerceClient({ baseUrl, credential }) {5  async function call(name, input) {6    const spec = operation(name);7    if (!spec) throw new Error("Unknown operation");8    if (spec.input && !validate(spec.input, input))9      throw new Error("Invalid operation input");10    const url = new URL(spec.path, baseUrl);11    if (spec.method === "GET" && input)12      for (const [key, value] of Object.entries(input))13        url.searchParams.set(key, value);14    const response = await fetch(url, {15      method: spec.method,16      headers: {17        "content-type": "application/json",18        ...(spec.auth === "none" ? {} : { authorization: credential }),19      },20      ...(spec.method === "POST" ? { body: JSON.stringify(input) } : {}),21      redirect: "error",22      signal: AbortSignal.timeout(5000),23    });24    const result = await response.json();25    if (response.status !== spec.successStatus)26      throw new Error("Commerce operation failed");27    if (!validate(spec.result, result))28      throw new Error("Invalid operation result");29    return result;30  }31 32  async function fulfill(input, { userId, productId }) {33    const evidence = { ...input };34    delete evidence.userId;35    const verdict = await call("verifyPurchase", evidence);36    if (!verdict.isValid) throw new Error("Purchase was not accepted");37    const binding = await call("bindPurchase", { ...evidence, userId });38    if (!binding.bound) throw new Error("Purchase belongs to another user");39    const access = await call("entitlements", { userId });40    if (!access.productIds.includes(productId))41      throw new Error("Requested product is not accessible");42    return access;43  }44 45  return { call, fulfill };46}47