woo-store-ts-api
v1.0.0
Published
TypeScript client for the WooCommerce Store API (wc/store/v1) — cart, checkout, and storefront catalog. Separate from the admin REST client (woocommerce-rest-ts-api).
Maintainers
Readme
woo-store-ts-api
TypeScript client for the WooCommerce Store API (wc/store/v1) — headless cart, checkout, and storefront catalog.
Not the admin REST client.
For authenticated back-office operations (wc/v3+ consumer keys / OAuth), usewoocommerce-rest-ts-api.
For AI agents over admin REST, usewoo-mcp-server.
| Package | Surface | Auth | Typical use |
|---------|---------|------|-------------|
| woocommerce-rest-ts-api | Admin REST wc/v3 | Consumer key + secret (OAuth) | ERP, ops, inventory, MCP |
| woo-store-ts-api | Store API wc/store/v1 | Cart-Token (preferred) or Nonce | Headless storefront, mobile, BFF |
| woo-mcp-server | MCP tools → admin REST | Same as admin library | Claude / agents |
Hard rule: this client rejects consumerKey / consumerSecret. Store API identity is session-based only. Do not mix packages into one “god client.”
Table of contents
- Install
- Quick start
- When to use which package
- Authentication & session
- Constructor options
- Endpoint coverage matrix
- API reference
- End-to-end examples
- Types
- Errors
- Exports
- Architecture
- Testing & development
- Troubleshooting
- Official Store API docs
- License
Install
pnpm add woo-store-ts-api
# or
npm install woo-store-ts-api
# or
yarn add woo-store-ts-apiRequirements: Node.js ≥ 18. Peer/runtime dependency: axios (bundled as a dependency).
Monorepo (this repo):
pnpm install
pnpm --filter woo-store-ts-api buildimport { WooCommerceStoreApi } from "woo-store-ts-api";
// workspace package: packages/store-apiQuick start
import { WooCommerceStoreApi } from "woo-store-ts-api";
const store = new WooCommerceStoreApi({
url: "https://shop.example",
});
// 1) Bootstrap session — GET /cart; client captures Cart-Token from response headers
await store.ensureSession();
// 2) Storefront catalog (public product shapes — not admin products)
const products = await store.products.list({
per_page: 10,
on_sale: true,
});
// 3) Cart mutations return the full cart envelope
const cart = await store.cart.addItem({
id: products[0]!.id,
quantity: 1,
variation: [], // required empty array for simple products
});
console.log(cart.items_count, cart.totals?.total_price);
console.log("Cart-Token", await store.getCartToken());When to use which package
┌─────────────────────────────────────────────────────────────────┐
│ Your application │
├────────────────────────────┬────────────────────────────────────┤
│ Back-office / ERP / MCP │ Headless storefront / mobile │
│ woocommerce-rest-ts-api │ woo-store-ts-api │
│ wc/v3 + CK/CS │ wc/store/v1 + Cart-Token │
│ orders, refunds, stock… │ cart, checkout, catalog │
└────────────────────────────┴────────────────────────────────────┘| Need | Use |
|------|-----|
| Create/update products as admin, list all orders, refunds, system status | woocommerce-rest-ts-api |
| Guest cart, add to cart, apply coupon, place order from storefront | woo-store-ts-api |
| Claude / agent tools against admin REST | woo-mcp-server |
| Both admin and storefront in one app | Two clients, two configs — never one class |
Authentication & session
Store API does not use API keys. Identity is the cart session.
Preferred: Cart-Token (headless)
- Call
GET /wp-json/wc/store/v1/cart(orstore.ensureSession()/store.cart.get()). - WooCommerce returns a
Cart-Tokenresponse header. - This client stores it and sends
Cart-Token: …on later cart/checkout requests. - When a valid Cart-Token is present, Nonce is not required.
Fallback: Nonce (cookie / same-site)
- Mutating cart + checkout routes may require a
Nonceheader when not using Cart-Token. - Nonce is generated by WordPress (
wc_store_api) and rotates on responses. - The client absorbs
Noncefrom every response and will send it if no Cart-Token is set.
What the client does automatically
| Behavior | Detail |
|----------|--------|
| Capture tokens | Reads Cart-Token / Nonce from every response (case-insensitive) |
| Prefer Cart-Token | Request headers: Cart-Token if set, else Nonce |
| Persist | Default: in-memory. Inject sessionStore for Redis / cookies / localStorage |
| Reject admin keys | Constructor throws if consumerKey or consumerSecret is passed |
Official references:
- Cart tokens
- Nonce tokens
- Monorepo deep dive:
docs/STORE_API.md
Restore a previous session
const store = new WooCommerceStoreApi({
url: "https://shop.example",
cartToken: process.env.CART_TOKEN, // from cookie / localStorage / Redis
});Custom session store
import {
WooCommerceStoreApi,
type SessionStore,
type SessionSnapshot,
} from "woo-store-ts-api";
/** Example: persist Cart-Token across HTTP requests in a BFF */
const cookieBackedStore: SessionStore = {
async get(): Promise<SessionSnapshot> {
return {
cartToken: readCookie("wc_cart_token"), // your helper
nonce: null,
};
},
async set(snapshot: SessionSnapshot): Promise<void> {
if (snapshot.cartToken) {
writeCookie("wc_cart_token", snapshot.cartToken);
}
},
};
const store = new WooCommerceStoreApi({
url: "https://shop.example",
sessionStore: cookieBackedStore,
});Built-in: MemorySessionStore (default).
Constructor options
new WooCommerceStoreApi({
/** Store origin (required). Example: https://shop.example */
url: "https://shop.example",
/** WordPress REST prefix. Default: "wp-json" */
wpAPIPrefix: "wp-json",
/** Store API version path. Default: "wc/store/v1" */
version: "wc/store/v1",
/** Request timeout in ms. Default: 30000 */
timeoutMs: 30_000,
/** Max in-flight HTTP calls (0 = unlimited). Default: 0 */
maxConcurrentRequests: 0,
/** Seed Cart-Token (restore session) */
cartToken: undefined,
/** Seed Nonce (cookie-mode fallback) */
nonce: undefined,
/** Custom SessionStore implementation */
sessionStore: undefined,
/** Inject axios instance (tests / custom agents) */
axiosInstance: undefined,
// ❌ consumerKey / consumerSecret — rejected with StoreApiOptionsError
});Base URL built as:
{url}/{wpAPIPrefix}/{version}/{endpoint}
→ https://shop.example/wp-json/wc/store/v1/cartEndpoint coverage matrix
Every official Store API route is reachable. First-class resource methods cover the cart/checkout/catalog happy path; everything else uses store.request().
| HTTP | Endpoint | Client API |
|------|----------|------------|
| GET | /cart | store.cart.get() · store.ensureSession() |
| POST | /cart/add-item | store.cart.addItem(input) |
| POST | /cart/update-item | store.cart.updateItem({ key, quantity }) |
| POST | /cart/remove-item | store.cart.removeItem(key) |
| POST | /cart/apply-coupon | store.cart.applyCoupon(code) |
| POST | /cart/remove-coupon | store.cart.removeCoupon(code) |
| POST | /cart/update-customer | store.cart.updateCustomer(input) |
| POST | /cart/select-shipping-rate | store.cart.selectShippingRate(input) |
| GET | /cart/items | store.cart.listItems() |
| DELETE | /cart/items | store.cart.clearItems() |
| GET/POST/PUT/DELETE | /cart/items/:key | store.request(…, "cart/items/{key}") |
| GET/POST/DELETE | /cart/coupons | store.request(…, "cart/coupons") |
| GET/DELETE | /cart/coupon/:code | store.request(…, "cart/coupon/{code}") |
| GET | /checkout | store.checkout.get() |
| POST | /checkout | store.checkout.process(payload?) |
| PUT | /checkout | store.checkout.update(payload?) |
| POST | /checkout/:id | store.checkout.payForOrder(orderId, payload?) |
| GET | /order/:id | store.request("GET", "order/{id}") |
| GET | /products | store.products.list(params?) |
| GET | /products/:id | store.products.get(id) |
| GET | /products/collection-data | store.products.collectionData(params?) |
| GET | /products/categories | store.products.listCategories(params?) |
| GET | /products/tags | store.products.listTags(params?) |
| GET | /products/attributes | store.products.listAttributes(params?) |
| GET | /products/attributes/:id | store.request("GET", "products/attributes/{id}") |
| GET | /products/attributes/:id/terms | store.request("GET", "products/attributes/{id}/terms") |
| GET | /products/brands | store.request("GET", "products/brands") |
| GET | /products/reviews | store.products.listReviews(params?) |
| POST | /batch | store.batch(requests) |
Official endpoint catalog (HTTP semantics, params): docs/STORE_API.md.
API reference
Session helpers on the client
| Method | Returns | Description |
|--------|---------|-------------|
| ensureSession(force?: boolean) | Promise<StoreCart> | GET /cart if needed; captures Cart-Token. Subsequent calls no-op unless force or token missing |
| getCartToken() | Promise<string \| null> | Current Cart-Token |
| session | CartSession | Low-level session object (see below) |
| request(method, endpoint, opts?) | Promise<StoreApiResponse<T>> | Escape hatch for unmapped routes |
| batch(requests) | Promise<unknown> | POST /batch with { requests } body |
request options:
await store.request<StoreProduct[]>("GET", "products", {
query: { per_page: 5, on_sale: true },
body: undefined, // POST/PUT body
headers: { "X-Custom": "1" },
skipSessionHeaders: false, // true = do not send Cart-Token / Nonce
});
// → { data, status, headers }store.session — CartSession
| Method | Description |
|--------|-------------|
| getSnapshot() | { cartToken, nonce } from the SessionStore |
| getCartToken() | Cart-Token only |
| getNonce() | Nonce only |
| getRequestHeaders() | { "Cart-Token": … } or { "Nonce": … } (never both; token wins) |
| absorbResponseHeaders(headers) | Merge new tokens from a response (used automatically by HTTP layer) |
| setCartToken(token \| null) | Manually set / clear Cart-Token |
| clear() | Clear both tokens |
Default store: MemorySessionStore. Inject any SessionStore (get / set) for cookies, Redis, etc.
store.cart — cart lifecycle
All mutating cart methods return the full cart object (Store API contract).
| Method | HTTP | Body / notes |
|--------|------|----------------|
| get() | GET /cart | Bootstrap / read cart |
| addItem({ id, quantity, variation?, … }) | POST /cart/add-item | variation defaults to [] for simple products |
| updateItem({ key, quantity }) | POST /cart/update-item | Line key from cart.items[].key |
| removeItem(key) | POST /cart/remove-item | |
| applyCoupon(code) | POST /cart/apply-coupon | |
| removeCoupon(code) | POST /cart/remove-coupon | |
| updateCustomer({ billing_address?, shipping_address?, … }) | POST /cart/update-customer | Address objects |
| selectShippingRate({ package_id, rate_id }) | POST /cart/select-shipping-rate | From cart.shipping_rates |
| listItems() | GET /cart/items | Items collection |
| clearItems() | DELETE /cart/items | Empty cart |
Variable products — pass attribute pairs:
await store.cart.addItem({
id: 100, // variation or parent product id per your catalog
quantity: 1,
variation: [
{ attribute: "pa_color", value: "blue" },
{ attribute: "pa_size", value: "m" },
],
});store.products — storefront catalog
These are Store API product shapes (StoreProduct), not admin REST Products.
| Method | HTTP | Notes |
|--------|------|--------|
| list(params?) | GET /products | Pagination, search, filters |
| get(id) | GET /products/:id | Single product |
| collectionData(params?) | GET /products/collection-data | Aggregations / price ranges |
| listCategories(params?) | GET /products/categories | |
| listTags(params?) | GET /products/tags | |
| listAttributes(params?) | GET /products/attributes | |
| listReviews(params?) | GET /products/reviews | |
Common list params (ProductListParams):
| Param | Type | Example |
|-------|------|---------|
| page | number | 1 |
| per_page | number | 20 (max 100) |
| search | string | "beanie" |
| order | "asc" \| "desc" | "desc" |
| orderby | string | "price", "date", "popularity", "rating", "title", … |
| category | string | category id |
| tag | string | tag id |
| type | string | "simple", "variable", "variation", … |
| on_sale | boolean | true |
| min_price / max_price | string | minor units per store |
| stock_status | string | string[] | "instock", "outofstock", … |
| featured | boolean | |
| slug | string | |
| attribute | string | attribute taxonomy / id |
| attribute_term | string | term id(s) |
| (index signature) | unknown | any extra Store API query param passes through |
const sale = await store.products.list({
on_sale: true,
per_page: 20,
orderby: "price",
order: "asc",
});store.checkout — place order
| Method | HTTP | Notes |
|--------|------|--------|
| get() | GET /checkout | Draft checkout state |
| process(payload?) | POST /checkout | Place order / process payment |
| update(payload?) | PUT /checkout | Update draft fields |
| payForOrder(orderId, payload?) | POST /checkout/:id | Pay for existing order |
Typical CheckoutProcessInput fields:
await store.checkout.process({
billing_address: {
first_name: "Jane",
last_name: "Doe",
address_1: "123 Main",
city: "Austin",
state: "TX",
postcode: "78701",
country: "US",
email: "[email protected]",
},
shipping_address: {
/* same shape */
},
payment_method: "bacs", // or cheque, cod, stripe, …
customer_note: "Leave at door",
create_account: false,
payment_data: [
// gateway-specific key/value pairs when required
],
});Flow that matches Store API practice:
ensureSession()/cart.get()cart.addItem/ coupons /updateCustomer/selectShippingRatecheckout.process({ … })
Low-level request & batch
// Unmapped or experimental routes
const res = await store.request<unknown>("GET", "products/brands", {
query: { per_page: 50 },
});
console.log(res.status, res.data, res.headers);
// Batch (WC Store API batch envelope)
await store.batch([
{
path: "/wc/store/v1/cart/add-item",
method: "POST",
body: { id: 26, quantity: 1 },
},
]);End-to-end examples
Browse → cart → coupon → shipping → checkout
import { WooCommerceStoreApi, StoreApiError } from "woo-store-ts-api";
const store = new WooCommerceStoreApi({ url: process.env.STORE_URL! });
try {
await store.ensureSession();
const [product] = await store.products.list({ per_page: 1, search: "hoodie" });
if (!product) throw new Error("No product found");
let cart = await store.cart.addItem({
id: product.id,
quantity: 1,
variation: [],
});
cart = await store.cart.applyCoupon("SAVE10").catch((err) => {
if (err instanceof StoreApiError) console.warn("coupon:", err.message);
return cart;
});
cart = await store.cart.updateCustomer({
billing_address: {
first_name: "Ada",
last_name: "Lovelace",
address_1: "1 Analytical Engine Way",
city: "London",
postcode: "SW1A 1AA",
country: "GB",
email: "[email protected]",
},
shipping_address: {
first_name: "Ada",
last_name: "Lovelace",
address_1: "1 Analytical Engine Way",
city: "London",
postcode: "SW1A 1AA",
country: "GB",
},
});
const pkg = cart.shipping_rates?.[0];
const rate = pkg?.shipping_rates?.find((r) => r.selected) ?? pkg?.shipping_rates?.[0];
if (pkg && rate) {
cart = await store.cart.selectShippingRate({
package_id: pkg.package_id,
rate_id: rate.rate_id,
});
}
const order = await store.checkout.process({
payment_method: cart.payment_methods?.[0] ?? "bacs",
});
console.log("Order", order.order_id, order.status, order.order_key);
console.log("Persist this token for the shopper:", await store.getCartToken());
} catch (err) {
if (err instanceof StoreApiError) {
console.error(err.status, err.code, err.message, err.isSessionError);
}
throw err;
}ESM vs CJS
// ESM
import { WooCommerceStoreApi } from "woo-store-ts-api";
// CJS
const { WooCommerceStoreApi } = require("woo-store-ts-api");
// or
const Store = require("woo-store-ts-api").default;Admin + Store side by side (correct pattern)
import WooCommerceRestApi from "woocommerce-rest-ts-api";
import { WooCommerceStoreApi } from "woo-store-ts-api";
// Back-office
const admin = new WooCommerceRestApi({
url: process.env.WC_URL!,
consumerKey: process.env.WC_KEY!,
consumerSecret: process.env.WC_SECRET!,
version: "wc/v3",
});
// Storefront session (no keys)
const storefront = new WooCommerceStoreApi({
url: process.env.WC_URL!,
});
const adminProduct = await admin.get("products", { id: 34 });
await storefront.ensureSession();
await storefront.cart.addItem({ id: 34, quantity: 1 });Types
Store domain types are separate from admin models in woocommerce-rest-ts-api. All cart mutations return StoreCart; catalog returns StoreProduct; checkout returns StoreCheckout. Extra WC extension keys are allowed via index signatures ([key: string]: unknown).
Type index
| Type | Role |
|------|------|
| StoreCart | Full cart envelope (items, totals, shipping_rates, …) |
| StoreCartItem | Line item (key, id, quantity, …) |
| StoreCartCoupon | Applied coupon |
| StoreCartTotals / StoreMoney | Money fields (minor units + currency metadata) |
| StoreAddress | Billing / shipping address |
| StoreShippingPackage / StoreShippingRate | Shipping selection |
| StoreProduct | Storefront product |
| StoreProductImage / StoreProductPrices | Media & prices |
| StoreCheckout | Checkout / order result |
| AddToCartInput | addItem payload |
| UpdateCartItemInput | updateItem payload |
| UpdateCustomerInput | Address update |
| SelectShippingRateInput | Shipping selection |
| CheckoutProcessInput | Checkout POST/PUT body |
| ProductListParams | Catalog filters |
| StoreApiResponse<T> | { data, status, headers } for request() |
| SessionSnapshot / SessionStore | Session persistence contract |
| WooCommerceStoreApiOptions | Constructor options |
| StoreApiVersion | "wc/store/v1" \| string |
import type {
StoreCart,
StoreProduct,
CheckoutProcessInput,
SessionStore,
} from "woo-store-ts-api";StoreCart (cart envelope)
| Field | Notes |
|-------|--------|
| items | StoreCartItem[] — each has key (line id for update/remove) |
| coupons | StoreCartCoupon[] |
| totals | StoreCartTotals — amounts as strings in minor units + currency meta |
| shipping_address / billing_address | StoreAddress |
| shipping_rates | StoreShippingPackage[] → nested shipping_rates[] with rate_id, selected |
| payment_methods | Available gateways for this cart |
| needs_payment / needs_shipping | Booleans |
| items_count / items_weight | Aggregates |
| errors / extensions | Validation / plugin data |
Money note: totals.total_price is a string in the store’s minor unit (e.g. cents). Use totals.currency_minor_unit to format.
StoreCartItem
| Field | Notes |
|-------|--------|
| key | Required for updateItem / removeItem |
| id | Product or variation id |
| quantity | Current qty |
| variation | Selected attributes |
| prices / totals | Line money objects |
| quantity_limits | { minimum, maximum, multiple_of, editable } |
StoreProduct (storefront — not admin)
| Field | Notes |
|-------|--------|
| id, name, slug, type, permalink | Identity |
| prices | StoreProductPrices (price, regular_price, sale_price, currency meta) |
| on_sale, is_purchasable, is_in_stock | Commerce flags |
| images | StoreProductImage[] |
| categories / tags / brands | Taxonomies |
| attributes, variations, has_options | Variable products |
| add_to_cart | { text, url, minimum, maximum, multiple_of } |
StoreCheckout
| Field | Notes |
|-------|--------|
| order_id, order_key, status | Placed order |
| billing_address / shipping_address | Addresses on the order |
| payment_method | Gateway id |
| payment_result | { payment_status, payment_details, redirect_url } |
| customer_id, customer_note | Account / notes |
Input payloads
// AddToCartInput
{ id: number; quantity: number; variation?: { attribute: string; value: string }[]; ... }
// UpdateCartItemInput
{ key: string; quantity: number }
// UpdateCustomerInput
{ billing_address?: Partial<StoreAddress>; shipping_address?: Partial<StoreAddress>; ... }
// SelectShippingRateInput
{ package_id: number | string; rate_id: string }
// CheckoutProcessInput
{
billing_address?: Partial<StoreAddress>;
shipping_address?: Partial<StoreAddress>;
payment_method?: string;
customer_note?: string;
create_account?: boolean;
payment_data?: { key: string; value: string }[];
extensions?: Record<string, unknown>;
}
// StoreAddress
{
first_name?, last_name?, company?,
address_1?, address_2?, city?, state?, postcode?, country?,
email?, phone?
}Errors
| Class | When |
|-------|------|
| StoreApiOptionsError | Bad constructor options (missing url, illegal path, admin keys passed) |
| StoreApiError | HTTP ≥ 400 or transport failure |
StoreApiError fields:
| Field | Meaning |
|-------|---------|
| message | Human-readable |
| status | HTTP status if any |
| code | WooCommerce / WP error code |
| data | Raw error payload |
| isSessionError | Hint to re-bootstrap session (nonce / 401 / 403 / token issues) |
import { StoreApiError, isSessionRelatedCode } from "woo-store-ts-api";
try {
await store.cart.addItem({ id: 1, quantity: 1 });
} catch (e) {
if (e instanceof StoreApiError && e.isSessionError) {
await store.ensureSession(true);
// retry once…
}
}Exports
// Client
import WooCommerceStoreApi, {
WooCommerceStoreApi,
type WooCommerceStoreApiOptions,
} from "woo-store-ts-api";
// Session
import {
CartSession,
MemorySessionStore,
type SessionStore,
type SessionSnapshot,
} from "woo-store-ts-api";
// Errors
import {
StoreApiError,
StoreApiOptionsError,
isSessionRelatedCode,
} from "woo-store-ts-api";
// Resources (advanced)
import {
CartResource,
ProductsResource,
CheckoutResource,
} from "woo-store-ts-api";Architecture
WooCommerceStoreApi
├── session: CartSession → SessionStore (Memory by default)
├── cart: CartResource
├── products: ProductsResource
├── checkout: CheckoutResource
└── http: StoreHttpClient
├── builds /wp-json/wc/store/v1/*
├── injects Cart-Token | Nonce
├── absorbs tokens from response headers
├── concurrency throttle (optional)
└── NO oauth-1.0a / NO consumer keysIntentionally not shared with the admin package:
- Auth stack (OAuth vs session headers)
- Domain types (admin product ≠ store product)
- MCP tool registry
OK to mirror (duplicated small utils): URL sanitize, concurrency throttle — not a shared runtime dependency on woocommerce-rest-ts-api.
Package layout:
packages/store-api/
src/
client.ts
session.ts
http.ts
types.ts
errors.ts
utils.ts
resources/
cart.ts
products.ts
checkout.ts
index.ts
tests/
README.mdTesting & development
# from monorepo root
pnpm install
pnpm --filter woo-store-ts-api build
pnpm --filter woo-store-ts-api typecheck
pnpm --filter woo-store-ts-api test # Jest + nock, coverage thresholds
pnpm test:store # same, via root scriptUnit tests cover:
- Session header preference (Cart-Token over Nonce)
- Token capture from response headers (case-insensitive)
- Cart mutations, products list/get, checkout process
- Rejection of admin credentials (
consumerKey/consumerSecret) StoreApiErrorsession flags on 403/nonce failures- URL / version / endpoint sanitization
- Optional concurrency throttle
Coverage thresholds are enforced in jest.config.cjs (statements/branches/functions/lines).
Troubleshooting
| Symptom | Cause | Fix |
|---------|-------|-----|
| Constructor throws about consumerKey | Admin keys passed into Store client | Use woocommerce-rest-ts-api for admin; Store client takes url only |
| 403 / woocommerce_rest_invalid_nonce | Missing or stale Nonce without Cart-Token | Call ensureSession() first; prefer Cart-Token; or ensureSession(true) and retry |
| isSessionError === true | Token/nonce/auth failure | await store.ensureSession(true) then retry the mutation once |
| Cart empty after process restart | Default MemorySessionStore | Persist via sessionStore or seed cartToken |
| 404 on /wp-json/wc/store/v1/… | Pretty permalinks off or WC Blocks Store API disabled | Enable permalinks; update WooCommerce |
| Wrong product shape vs admin docs | Store product ≠ admin product | Use StoreProduct types / this package’s docs |
| Need brands / order by key / coupon collection | No dedicated method yet | store.request("GET", "products/brands") etc. (see coverage matrix) |
| Amounts look 100× too large | Minor units | Divide by 10 ** (totals.currency_minor_unit ?? 2) |
Official Store API docs
| Topic | URL |
|-------|-----|
| Overview | https://developer.woocommerce.com/docs/apis/store-api/ |
| Cart tokens | https://developer.woocommerce.com/docs/apis/store-api/cart-tokens/ |
| Nonce tokens | https://developer.woocommerce.com/docs/apis/store-api/nonce-tokens/ |
| Cart endpoints | https://developer.woocommerce.com/docs/apis/store-api/resources-endpoints/cart/ |
| Checkout | https://developer.woocommerce.com/docs/apis/store-api/resources-endpoints/checkout/ |
| Products | https://developer.woocommerce.com/docs/apis/store-api/resources-endpoints/products/ |
| Monorepo reference | docs/STORE_API.md |
| Issue / design | GitHub #62 |
License
MIT © Yuri Lima
