Skip to content

Build a store in an afternoon

This guide starts with an AdonisJS 7 application and ends with a persisted order. It uses SQLite and the manual payment driver so every step runs locally.

This guide starts with an AdonisJS 7 application and ends with a persisted order. It uses SQLite and the manual payment driver so every step runs locally. Swap Stripe in after the flow works.

1. Install

Requirements: Node.js 24+, AdonisJS 7, Lucid 22, and a configured database connection.

Adocommerce Kit is beta. The source tree is aligned to the lockstep 1.2.0 Indonesia release candidate, which is not yet published to the registry. Install matching exact versions of all @adocommercekit/* packages for a controlled deployment, and read stability and compatibility before depending on a surface.

pnpm add @adocommercekit/core @adocommercekit/storefront-api

Register the engine and publish the host-owned schema:

node ace configure @adocommercekit/core
node ace configure @adocommercekit/storefront-api
node ace migration:run

@adocommercekit/core adds its provider and commands, writes config/commerce.ts, and publishes migrations into database/migrations. @adocommercekit/storefront-api publishes controllers, validators, middleware, and the /api/commerce route file. Nothing is mounted invisibly from node_modules.

Commit the generated files. They are part of your application.

2. Check the installation

node ace commerce:doctor

A healthy result confirms required tables, payment driver capabilities, driver health checks, and inventory reservation totals. If a deferred or COD provider is registered, doctor also requires retrieveIntent and, in production, jobs.dispatcher: 'queue'. Application boot has already validated configuration and the model registry. Fix doctor errors before seeding.

3. Seed a catalog

node ace commerce:seed:demo

The demo seeder is idempotent. It creates web/retail channels, a Field Supply category tree, outdoor products with variants and descriptions, USD/EUR/IDR prices, multi-location inventory, and flat-rate shipping methods. In the playground, follow with node ace commerce:id:seed:demo (or pnpm --filter playground demo:reset) for Indonesian customers, orders, payments, shipments, and cart funnel data.

Start Adonis and inspect the storefront contract:

node ace serve --hmr
curl http://localhost:3333/api/commerce/products

Each variant includes a serialized price and an availability object. Product controllers preload every relationship used by the transformer; transformers never issue queries.

4. Create a cart

Keep the cookie jar because the default sessionCartToken() strategy stores the anonymous cart token in the session.

curl -c .cart-cookie -b .cart-cookie \
  http://localhost:3333/api/commerce/cart

Copy a variantId from the product response, then add it:

curl -c .cart-cookie -b .cart-cookie \
  -H 'content-type: application/json' \
  -d '{"variantId":"<variant-id>","quantity":2}' \
  http://localhost:3333/api/commerce/cart/lines

If Shield protects your API mutations, expose its XSRF cookie and send X-XSRF-TOKEN, or exempt token-authenticated API routes in config/shield.ts. The generated payment webhook route is excluded because external providers cannot send a CSRF token.

5. Add checkout details

curl -X PUT -c .cart-cookie -b .cart-cookie \
  -H 'content-type: application/json' \
  -d '{"email":"buyer@example.com"}' \
  http://localhost:3333/api/commerce/cart/email

curl -X PUT -c .cart-cookie -b .cart-cookie \
  -H 'content-type: application/json' \
  -d '{"shipping":{"firstName":"Ari","lastName":"Stone","line1":"18 Market Street","city":"Portland","region":"OR","postalCode":"97205","countryCode":"US"}}' \
  http://localhost:3333/api/commerce/cart/addresses

List eligible shipping methods, then select one:

curl -c .cart-cookie -b .cart-cookie \
  http://localhost:3333/api/commerce/cart/shipping-methods

curl -X PUT -c .cart-cookie -b .cart-cookie \
  -H 'content-type: application/json' \
  -d '{"methodId":"<shipping-method-id>"}' \
  http://localhost:3333/api/commerce/cart/shipping-method

6. Place the first order

curl -X POST -c .cart-cookie -b .cart-cookie \
  -H 'content-type: application/json' \
  -H 'Idempotency-Key: tutorial-order-001' \
  -d '{"payment":{"method":"manual"}}' \
  http://localhost:3333/api/commerce/checkout

The response is 201 with the storefront order transformer and a seven-day accessToken. Repeating the same request with the same key returns the same order. Reusing the key with a different payload returns E_IDEMPOTENCY_CONFLICT.

Read the order without an account:

curl 'http://localhost:3333/api/commerce/orders/<order-number>?token=<access-token>'

The generated controller also authorizes an authenticated user when the order’s customer record has a matching userId. Signed access tokens remain available for guest orders.

7. Move to Stripe

Install and configure the Stripe package:

pnpm add @adocommercekit/stripe
node ace configure @adocommercekit/stripe

Register stripe() under payments.methods in config/commerce.ts, provide the server key and webhook secret through Adonis environment validation, and point Stripe at:

POST /api/commerce/webhooks/payments/stripe

Do not parse and re-stringify webhook JSON. The published raw-body middleware reads Adonis’s preserved request body and passes those exact UTF-8 bytes to the provider verifier.

The generated checkout controller handles synchronous authorization. It does not publish a complete 3DS/SCA client-confirmation flow. Follow the Stripe guide, including its SCA limitation, before enabling Stripe in production.

Selling in Indonesia instead? @adocommercekit/midtrans and @adocommercekit/xendit implement the same payment port with deferred channels such as QRIS, virtual accounts, and e-wallets, and @adocommercekit/id adds kode-unik manual transfer. Those flows return an order in pending_payment with payment instructions rather than an immediate authorization, and production use requires jobs.dispatcher: 'queue'. Start from packages and imports.

8. Upgrade generated files

node ace commerce:upgrade

The command classifies each publication as add, update, unchanged, or conflict. Conflicts are never overwritten: incoming source and three-way-merge instructions are staged under .commercekit/incoming.

Next steps

Navigation

Type to search…

↑↓ navigate↵ selectEsc close