Skip to content
Payment Emulator LabDocumentationOpen console

Getting started

Payment Emulator Lab development guide

The repository is modular but deliberately not DDD. Do not introduce aggregates,domain events, repository interfaces or an application/domain/infrastructuresplit to implement an or

4 min read

Índice / Index · Español

#Before changing code

  1. Read .agents/README.md and the binding for the application you will touch.
  2. If a provider contract is involved, read contracts/<provider>/ and docs/en/fidelity/<provider>.md before the implementation.
  3. Find the existing owning module and its nearest analogous test.
  4. Check git status --short and preserve unrelated local changes.

The repository is modular but deliberately not DDD. Do not introduce aggregates, domain events, repository interfaces or an application/domain/infrastructure split to implement an ordinary feature.

#Choose the right home

Change Put it here
Provider route, request validation or wire response apps/api/src/providers/<id>/
Rule shared by every provider apps/api/src/core/ or the owning shared module
Payment state or multi-step write apps/api/src/modules/payments/
Accounting movement apps/api/src/modules/ledger/
Control-plane feature apps/api/src/modules/<area>/
Database model apps/api/src/infrastructure/database/entities/ (current deferral)
Schema change New file in apps/api/src/infrastructure/database/migrations/
Console feature apps/console/app/modules/<area>/ + thin page/Nitro route as needed
Shared console behaviour apps/console/app/core/
Checkout-neutral UI apps/checkout/app/components/
Provider checkout identity/flow apps/checkout/providers/<id>/
Built-in scenario scenarios/<provider>/<name>.json

#Add a backend endpoint

For a control-plane endpoint:

  1. Add or reuse a DTO under the owning module's dto/ when it writes data. DTOs use class-validator; do not repeat the same validation by hand in the controller.
  2. Add the route to the owning controller. Declare RolesGuard and ApplicationScopeGuard consistently with sibling routes.
  3. Put behaviour, transactions and persistence in the service.
  4. For a list, use ListQueryDto and paginate()/paginateArray(). Declare an explicit sortable, filterable and searchable allow-list.
  5. Add an integration test. For row-scoped data, include a caller from another tenant and expect 404.
  6. If the console needs it, add the narrowest Nitro proxy route; never call the NestJS API from browser code.

The gateway wildcard controller is registered last in AppModule. New control-plane modules must remain before GatewayModule, or the wildcard may capture their routes.

#Change a provider operation

Start with the provider's official specification and fidelity document. Then:

  1. Update the provider route/contract/translation files only.
  2. Keep canonical operations within CANONICAL_OPERATIONS. If the provider requires a genuinely new universal operation, treat that as an architecture change and update the contract and shared tests deliberately.
  3. Do not inject services into the plugin. Translate to an IntentCommand and let PaymentsService perform the write.
  4. Update static and behavioural conformance cases plus official-SDK tests where the SDK can target the emulator.
  5. Update docs/en/fidelity/<provider>.md, including limitations.

For a wholly new provider, follow Adding a provider.

#Add or change a scenario

Edit only the canonical JSON under scenarios/, then run:

bash
npm run scenario:compile

This regenerates apps/api/src/modules/scenarios/default-scenarios.ts. Build, typecheck and test also compile scenarios first, but running the command immediately gives a clearer validation error.

Use a canonical then.outcome; put provider vocabulary only in then.wire when the exact provider status is the purpose of the test. Do not use arbitrary code. See Scenarios for the supported DSL and current gaps.

#Add a database entity or field

  1. Change or add the entity in apps/api/src/infrastructure/database/entities/ and export it from entities/index.ts when required by discovery.

  2. Add a new, ordered migration. Do not rewrite a migration that may already have run.

  3. Apply it against a disposable PostgreSQL database:

    bash
    npm run migrate
    
  4. Exercise the down: npm run migrate -- --down, then npm run migrate again. A rollback nobody runs is a rollback that does not work.

  5. Add an integration test that relies on the new schema; compilation alone does not prove the migration and entity agree.

Money columns use integer minor units. Provider-varying payloads use JSONB only at the provider/resource or declared metadata boundary. Never add a mutable balance column.

#Add a console feature

The normal path is:

text
page/component -> module model/columns -> core gateway/composable
               -> Nitro server route -> NestJS control-plane endpoint

Reuse createRestGateway, useEntityCollection and RTable rather than assembling query strings or caching in a page. Keep credentials server-side in Nitro. Run both npm run typecheck and npm run build: Nuxt auto-import and SSR resolution problems are sometimes visible only in a real build.

#Add a checkout feature

Decide whether it is provider-neutral or provider identity:

  • Summary, outcome and shell layout go in apps/checkout/app/components/.
  • Provider-specific copy, layout and flow go in apps/checkout/providers/<id>/.
  • API access goes through apps/checkout/server/api/ and server/utils/api.ts.

Do not install or reuse the console's PrimeVue layer. Never accept a customer ID from the confirmation body to choose a wallet; the checkout session owns that identity.

#Add a test

  • Pure transformation/invariant: apps/api/test/*.spec.ts.
  • Database, HTTP, authorization or concurrency: apps/api/test/integration/.
  • Provider-wide guarantee: parameterize the existing conformance suite.
  • Provider SDK behaviour: SDK-specific spec, with telemetry/network disabled.
  • Whole product acceptance: extend scripts/stack-smoke.mjs only for a critical cross-application path.

See Testing for commands and harness behaviour.

#Definition of done

Run from the repository root:

bash
npm run typecheck
npm run lint
npm test
npm run build
npm run smoke

For a change involving migrations, sessions, Nitro routes, the worker or a cross-application flow, also run:

bash
docker compose up --build --wait
npm run smoke:stack

Before handing off, verify documentation links and confirm that every path, command, environment variable and endpoint mentioned still exists.

Payment Emulator Lab · RonuSoftwareMIT