About the project
A double click or repeated webhook must not create another order or deliver another code. If the supplier does not respond, the previous request needs to be checked before sending a new one.
Angular catalogue, NestJS API, orders in PostgreSQL. A worker handles payment and delivery. Suppliers are HTTP mocks in the demo. It includes timeout, repeated webhook and order recovery scenarios.
- Payments and providers are explicitly mocked; no money is charged and codes are for demonstration only.
- Up to 20 orders per session and 1,000 new demo orders in total. One-hour TTL, cleanup every five minutes.
- The recovery example deliberately fails the first delivery attempt for one order. It does not change global provider modes.
Screenshots, diagrams and source evidence retain their original language; captions and descriptions are translated.
Architecture and diagrams
- Prices and discounts are calculated on the server and saved in the order.
- A unique index protects Idempotency-Key; replayed input is checked against the original intent.
- Webhooks are saved to a durable inbox first. A separate worker processes payments and the delivery queue.
- The provider requestId remains stable across retries. An ambiguous timeout does not immediately allow a second code to be issued.
Code map: files and dependencies
This map is tied to version 767226bf3c2f. The table lists the screen or API, related module and data.
| Feature | Screen / API | Module | Data / verification |
|---|---|---|---|
| Purchase | Storefront / POST orders | storefront.component; ShopService.createOrder | orders, products, promocodes; race tests |
| Payment | POST payments/simulate → webhooks/payment | PaymentsController; acceptWebhook | payment_events; duplicate event_id |
| Delivery | Order / GET orders/:id | worker.service processDeliveryJob | delivery_jobs, provider_requests, fulfillments; recovery |
| Session | Guard for all public orders | portfolio-demo PortfolioGuard | HMAC cookie, prefix owner; tests/portfolio/run.ts |
| Reset / TTL | POST demo/reset / timer | PortfolioCleanup.clear | portfolio orders only; ownership / expiry tests |
Using the code map
- Before making a change, find the feature and trace its links to the API, state and storage.
- Check inputs, errors, repeated events and dependent workflows.
- After making a change, update the map and affected diagrams alongside the code.
- Run the related checks and link the new evidence to the source version.
Workflows and API
- Choose a product and complete a successful test payment. Wait for the code and inspect the history.
- Replay the same payment event: the code and delivery must remain unique.
- Create an order using the failure scenario, pay and select “Recover my order”.
- Check a declined payment, another session's UUID and resetting your own example.
Verification of the published version
CI: CI ↗ · 767226bf · this demo's version · 2026-09-07T00:49:05Z
CI: Portfolio immutable image ↗ · 767226bf · this demo's version · 2026-09-07T00:49:05Z
- Real HTTP webhook, durable inbox, worker, single fulfillment preserved on event replay
- Foreign GET/recover return 404; other session reset preserves first order
- Payment failure; isolated first-attempt delivery fault; own recovery succeeds
These checks apply to the listed version. Business metrics and model quality on an independent dataset were not measured.
Measurements
- 14 meaningful screenshots of the actual interface retained.
- Each screenshot includes its date, scenario and code revision.
- Peak container RAM in the short test: 218.56 MiB. Shared services are counted separately; the shop includes its own database.
- 6 scenario repetitions measured; median 129.0 ms, maximum 1939 ms. Timing includes the browser and HTTPS; methodology and limitations are included.
Running and operating
The demo and original app use the same order model. PORTFOLIO_DEMO=true enables the demo guard. Without the flag, the original API contracts apply.
GitHub Actions builds a pinned commit SHA. A prebuilt GHCR image is uploaded to the VPS. The shop database remains separate from the new applications.
Start: docker compose --env-file .env.production -f compose.production.yaml up -d. Run prisma migrate deploy before switching releases. Secrets are stored only on the server. Do not use seed as an unconditional reset of a running shop.
Readiness: /api/health/ready checks PostgreSQL. During an incident, compare the order history, inbox and delivery job; retry delivery for the original orderId instead of creating another purchase.
Builds run outside the VPS; the server receives static files and versioned images. Secrets are kept separately. Logs rotate; changing log options requires recreating the container. No server-wide docker prune.
The code map links a screen or API to its module and data. A change needs those links checked, the map updated and the affected flows rerun: normal path, error, retry and reset. Diagrams can go stale, so their version is listed alongside them.
Rollback checked at 2026-09-07T01:07:46.255650+00:00: 20260907-verified → 20260907 → 20260907-verified. HTTPS, readiness and SHA were confirmed after each switch. This checks configuration and artifacts at one verified SHA; no reverse database migration was performed.
Stack
| Angular / TypeScript | Storefront, state signals, HTTP client and order polling. |
|---|---|
| NestJS | Contracts, validation, webhooks, recovery operations and demo limits. |
| PostgreSQL / Prisma | Monetary snapshots, inbox, leased queue, unique indexes and transactions. |
| Node.js worker | Provider HTTP calls outside short transactions, retries and final delivery. |
Materials and source code
Implementation history: 1–3 September 2026. Portfolio preparation: 7 September 2026.
The evidence archive retains its original language.