docs(18-06): complete customer API and payment gateway layer plan
This commit is contained in:
354
.planning/phases/18-payment-gateway/18-06-SUMMARY.md
Normal file
354
.planning/phases/18-payment-gateway/18-06-SUMMARY.md
Normal file
@@ -0,0 +1,354 @@
|
||||
---
|
||||
phase: 18-payment-gateway
|
||||
plan: 06
|
||||
subsystem: payments
|
||||
tags: [paymentgateway, customer-api, nuxt-layer, vue, i18n, guest-access, jwt, checkout, stripe-redirect, polling, testcontainers, node-test, release]
|
||||
|
||||
requires:
|
||||
- phase: 18-02
|
||||
provides: orders.Service and payments.Service, operators.Registry, cart v2 and the legacy reader, support.GuestToken and Hashids, models.Settings and Shipping
|
||||
- phase: 18-04
|
||||
provides: settings pages (enable_guest, shippings_enabled, the manual transfer columns sanitized on save, order_url_template)
|
||||
- phase: 18-05
|
||||
provides: the Stripe operator's process response shapes (redirect_url, the requires_action bundle) and the sm-pgstripe-plugin repository
|
||||
provides:
|
||||
- customer order API under /_paymentgateway/api/v1 (package api, contract api-research), twelve endpoints with the PHP Orders component's access rules and state guards
|
||||
- throttle bucket paymentgateway-api (120 per minute per frontend user, else per client IP)
|
||||
- new repository vue-paymentgateway-layer (branch main, tag v0.1.0) with the typed client, useOrder/useOrders, nine Pg* components, pg-tokens.css, pg-base.css and pg.* copy in en and pl
|
||||
- the three repositories published on git.golem15.com (sm-paymentgateway-plugin, sm-pgstripe-plugin, vue-paymentgateway-layer)
|
||||
affects: [18-07 unit tests and phase gate, site applications that port a checkout, 18.1 subscriptions]
|
||||
|
||||
actuals:
|
||||
tokens: 58000
|
||||
tasks: 6
|
||||
commits: 0
|
||||
plan_head_before: 25cfb0a4cc5c9a9336529bcf5fa95b29f20e9ddb
|
||||
plan_head_after: 25cfb0a4cc5c9a9336529bcf5fa95b29f20e9ddb
|
||||
|
||||
tech-stack:
|
||||
added:
|
||||
- "No new Go dependency. Layer: @nuxtjs/i18n 10.4.0, nuxt 4.4.8, vue 3.5.35, typescript 5.9.3 (the vue-journal-layer pins; lockfile derived from its resolved set, vue-i18n 11.4.13); no Stripe.js"
|
||||
patterns:
|
||||
- "Customer API handlers resolve the order by hash, check access (owner JWT, constant-time guest_code, order-bound guest_token), then mutate inside lagoon.Transaction on the row locked FOR UPDATE with the state guard re-checked (409) and answer the allowlisted OrderJSON"
|
||||
- "OptionalJWT/RequireJWT: the user plugin's jwt guard runs only for Authorization: Bearer; cookies are never read (no CSRF surface)"
|
||||
- "Layer flows kept in plain TS controllers (createCheckout, createAwaitingPoller) with injected location, timers and visibility so node --test drives them; components stay thin glue"
|
||||
- "Layer tests compile SFCs with vue/compiler-sfc next to the source and render them with vue/server-renderer and stubbed Nuxt globals (testing/load.ts)"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- ../sm-paymentgateway-plugin/api/auth.go
|
||||
- ../sm-paymentgateway-plugin/api/errors.go
|
||||
- ../sm-paymentgateway-plugin/api/order_json.go
|
||||
- ../sm-paymentgateway-plugin/api/orders.go
|
||||
- ../sm-paymentgateway-plugin/api/actions.go
|
||||
- ../sm-paymentgateway-plugin/api/methods.go
|
||||
- ../sm-paymentgateway-plugin/api/items.go
|
||||
- ../sm-paymentgateway-plugin/customer_api_test.go
|
||||
- ../vue-paymentgateway-layer/app/utils/client.ts
|
||||
- ../vue-paymentgateway-layer/app/utils/money.ts
|
||||
- ../vue-paymentgateway-layer/app/utils/checkout.ts
|
||||
- ../vue-paymentgateway-layer/app/utils/poller.ts
|
||||
- ../vue-paymentgateway-layer/app/utils/states.ts
|
||||
- ../vue-paymentgateway-layer/app/composables/usePaymentGatewayApi.ts
|
||||
- ../vue-paymentgateway-layer/app/composables/useOrder.ts
|
||||
- ../vue-paymentgateway-layer/app/composables/useOrders.ts
|
||||
- ../vue-paymentgateway-layer/app/components/Pg/{OrderPage,Checkout,OrderStatus,Cart,PaymentMethods,ShippingMethods,BankTransfer,OrderList,Confirm}.vue
|
||||
- ../vue-paymentgateway-layer/app/assets/css/pg-tokens.css
|
||||
- ../vue-paymentgateway-layer/app/assets/css/pg-base.css
|
||||
- ../vue-paymentgateway-layer/i18n/locales/en.json
|
||||
- ../vue-paymentgateway-layer/i18n/locales/pl.json
|
||||
- ../vue-paymentgateway-layer/testing/load.ts
|
||||
- ../vue-paymentgateway-layer/tests/{client,money,checkout,orderPage,states,i18n}.test.ts
|
||||
- ../vue-paymentgateway-layer/.playground/ (nuxt.config.ts, app/app.vue, app/pages/orders/[hash].vue, app/pages/orders/index.vue)
|
||||
- ../vue-paymentgateway-layer/{package.json,pnpm-lock.yaml,pnpm-workspace.yaml,tsconfig.json,nuxt.config.ts,.gitignore,.nvmrc,README.md,scripts/check-git-install.sh}
|
||||
modified:
|
||||
- ../sm-paymentgateway-plugin/routes.go
|
||||
- ../sm-paymentgateway-plugin/plugin.go
|
||||
- ../sm-paymentgateway-plugin/notification_route_test.go
|
||||
- ../sm-paymentgateway-plugin/README.md
|
||||
|
||||
key-decisions:
|
||||
- "Task 1 checkpoint: customer API contract option api-research (human reply: \"api-research\"), recorded in the api package doc comment"
|
||||
- "Customer cancel in AwaitingPayment uses the PHP Cancel transition (Order::cancel) after canceling the order's open cash and bank transfer payments through the bridge; online operator payments are left to the operator and the sweep"
|
||||
- "Process rolls back on an operator error so the order stays Placed and another method can be tried; the answer is 422 payment_method with the operator's message"
|
||||
- "GET /orders uses api.RequireJWT (the user plugin's jwt guard, Bearer only) instead of the named jwt.auth middleware"
|
||||
- "The layer's auth hook is a Nuxt provide named paymentGatewayAuth; tokens travel as Authorization: Bearer only"
|
||||
- "PgOrderPage polls only AwaitingPayment orders of an online operator; cash and bank transfer have no confirmation on the way"
|
||||
- "Task 5: repositories created by the orchestrator with tea; fixtures: none (human reply verbatim recorded); push and tag approved"
|
||||
- "Task 6: pushed the three main branches and the annotated layer tag v0.1.0 after the human's reply \"push\""
|
||||
|
||||
patterns-established:
|
||||
- "Headless successor of a Winter component: an api package in the plugin with an allowlisted JSON projection plus a Nuxt layer with a typed client over an injected fetcher"
|
||||
|
||||
requirements-completed: [SC-3, D-02, D-06, D-11, D-17]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "Tracer: a guest reads their order by guest code and guest token, a foreign token or code and anonymous callers get 404, the owner reads with a JWT and a stranger gets 404, an invalid Bearer is 401, process answers {status: pending} with a Sent cash payment and AwaitingPayment, a second process is 409, the JSON leaks no guest_code, return_url, cart, details or secure parameter"
|
||||
requirement: "D-06"
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "../sm-paymentgateway-plugin/customer_api_test.go#TestCustomerAPITracer (Postgres, run and passed)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "Every endpoint against owner, guest code, guest token, foreign user and anonymous; GET /orders with the DATA-10 envelope and pagination; payment methods and shippings lists; shipping selection with the gross total; cash rules; bank transfer with sanitized HTML; cancel from Placed and AwaitingPayment; refund request; exact piece totals 39.84 -> 79.68 -> 39.84 net; subtract floors at 1; last item 422; every mutation outside Placed 409"
|
||||
requirement: "D-06"
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "../sm-paymentgateway-plugin/customer_api_test.go#TestCustomerAPI (9 subtests, Postgres, run and passed)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "PgCheckout redirect contract (backstop): a redirect answer calls location.assign once and stays busy, a second click while busy sends nothing, requires_action without the slot shows pg.error.payment_action, an error shows pg.error.process and re-enables; methods saved before process; field errors with focus; 409 stale and 429 throttled"
|
||||
requirement: "D-17"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "../vue-paymentgateway-layer/tests/checkout.test.ts (node --test)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D4
|
||||
description: "PgOrderPage polling (backstop): polls every 3 s, stops at Paid, stops after 20 attempts with the slow state, pauses while hidden and resumes when visible; the page routes Placed and other states and renders the not-found panel and pg.error.load"
|
||||
requirement: "D-06"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "../vue-paymentgateway-layer/tests/orderPage.test.ts (node --test)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D5
|
||||
description: "Layer client paths and bodies, composable base and bearer, money formatting of the exact string, state tone map, i18n parity and key usage, PgCart last-item rule"
|
||||
requirement: "D-06"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "../vue-paymentgateway-layer/tests/{client,money,states,i18n,checkout}.test.ts (48 tests, 0 fail)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D6
|
||||
description: "Visual rendering of the layer's checkout, status and list screens in a browser (tokens, radio cards, dialog, tone bar, narrow-screen scroll)"
|
||||
requirement: "D-06"
|
||||
verification: []
|
||||
human_judgment: true
|
||||
rationale: "No browser runner in the layer (node --test only); an SSR smoke of the built playground against a fake API rendered the markup, but the visual check of the UI-SPEC L1 to L8 contracts needs a human"
|
||||
- id: D7
|
||||
description: "Three repositories published and the layer tagged v0.1.0 after the human's reply"
|
||||
verification:
|
||||
- kind: other
|
||||
ref: "git ls-remote origin refs/heads/main equals local main in all three; ls-remote --tags shows v0.1.0 on the layer; cat-file -t v0.1.0 = tag"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
|
||||
duration: about 70min
|
||||
completed: 2026-10-13
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 18 Plan 06: Customer order API and vue-paymentgateway-layer Summary
|
||||
|
||||
**Headless successor of the Winter Orders component: twelve endpoints under `/_paymentgateway/api/v1` with owner JWT, constant-time guest code and order-bound guest token access, locked state-guarded mutations and the D-17 process answers, plus the new `vue-paymentgateway-layer` (v0.1.0) whose PgOrderPage checks out with a Stripe Checkout redirect by default and polls the order status; all three repositories are now published.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** about 70 minutes (Tasks 1 to 4 about 50 minutes, then the two release checkpoints)
|
||||
- **Tasks:** 6 of 6 (two human checkpoints: Task 5 approval, Task 6 push)
|
||||
- **Completed:** 2026-10-13
|
||||
|
||||
## Task 1: decision (checkpoint:decision)
|
||||
|
||||
- **Option chosen:** `api-research` (human reply verbatim: "api-research"). Recorded in the package doc comment of `../sm-paymentgateway-plugin/api/order_json.go`. The alternative `api-flat-money` was not chosen.
|
||||
- **Contract sheet (verbatim from the package comment):**
|
||||
|
||||
```
|
||||
GET /orders frontend JWT; the user's orders, newest first,
|
||||
{data, meta{current_page,last_page,per_page,total}}
|
||||
GET /orders/{hash} owner | ?guest_code= | ?guest_token=
|
||||
GET /payment-methods {data: [{code, name, logo, mode, currencies}]}
|
||||
GET /shippings {data: [{id, name, cost, gross_cost, tax, logo,
|
||||
is_payable_with_cash}]}, empty when shippings are off
|
||||
POST /orders/{hash}/payment-method {payment_method}; Placed only
|
||||
POST /orders/{hash}/shipping {shipping_id}; Placed only
|
||||
POST /orders/{hash}/process Placed only; {redirect_url} |
|
||||
{requires_action, client_secret, payment_intent_id,
|
||||
payment_uuid, publishable_key} | {status: "pending"}
|
||||
POST /orders/{hash}/cancel Placed | AwaitingPayment
|
||||
POST /orders/{hash}/refund-request Paid | Shipped
|
||||
POST /orders/{hash}/items/{item}/pieces Placed; one more piece
|
||||
DELETE /orders/{hash}/items/{item}/pieces Placed; one piece less, never below 1
|
||||
DELETE /orders/{hash}/items/{item} Placed; never the last item
|
||||
```
|
||||
|
||||
- **Money:** `{"amount":"2999.9700","amount_minor":299997,"currency":"PLN"}`.
|
||||
- **Order JSON:** hash_id, state, currency, native_currency, total, total_net, total_tax, native_total, items[id, description, count, net_price, gross_price, tax, tax_value, total_net, total_gross], shipping{id, name, net_cost, gross_cost, tax} or null, payment_method (code) or null, can_edit_items, can_cancel, can_refund, bank_transfer{name, receiver_address, bank_acc_no, swift, bank_details_html} or null, payments[{state, operator}], created_at (`+00:00`).
|
||||
- **Errors:** 401 for an invalid Bearer; 404 `{"error":"Order not found"}` for unknown and forbidden orders alike; 409 `{"error":"Order state changed"}`; 422 `{"error":"Validation failed","errors":{field:[msg]}}`; 429 from surf.
|
||||
|
||||
## Commits
|
||||
|
||||
Repository `../sm-paymentgateway-plugin` (branch `main`, now on `origin`); measured `git rev-list --count e4fbd3b..ef72b85` = 2:
|
||||
|
||||
| Task | Commit | Subject |
|
||||
|------|--------|---------|
|
||||
| 2 | c818edb | feat(18-06): customer API tracer: guest and owner order read and process under /_paymentgateway/api/v1 (task 2) |
|
||||
| 3 | ef72b85 | feat(18-06): the remaining customer API endpoints with the authorization matrix, state guards and the README section (task 3) |
|
||||
|
||||
Repository `../vue-paymentgateway-layer` (new, branch `main`, now on `origin`, tag `v0.1.0`); measured `git rev-list --count main` = 2:
|
||||
|
||||
| Task | Commit | Subject |
|
||||
|------|--------|---------|
|
||||
| 2 | 67b8e9f | feat(18-06): payment gateway layer tracer: client, order page and checkout with the redirect default (task 2) |
|
||||
| 4 | 14d4522 | feat(18-06): the complete payment gateway layer: status with polling, cart, methods, bank transfer, order list, confirm, playground and README (task 4) |
|
||||
|
||||
Framework repository `summercms.go`: no code change (`commits: 0` measured from `25cfb0a`); this SUMMARY and the state updates only. summercms.go was not pushed (plans 01 and 07 own that).
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **Task 2 (tracer):**
|
||||
- Package `api`: `OptionalJWT`, `canActOnOrder`, the `OrderJSON` allowlist with `MoneyJSON`, `Show` and `Process` with the D-17 answers, and the error writers.
|
||||
- `paymentgateway-api` bucket.
|
||||
- `TestCustomerAPITracer`.
|
||||
- New layer repository with the client, money formatting, composables, PgOrderPage and PgCheckout (redirect default), tokens, base CSS, the complete `pg.*` copy in en and pl, `testing/load.ts`, and the client, money and checkout tests.
|
||||
- Tracer gate: interactive run, `human_verify_mode` unset (end-of-phase default), automated-only verify re-run green, expanded.
|
||||
- **Task 3:**
|
||||
- `Index` (RequireJWT, 20 per page), `PaymentMethods`, `Shippings`, `SelectPaymentMethod`, `SelectShipping`, `Cancel`, `RefundRequest`, `AddPiece`, `RemovePiece`, `RemoveItem`. Each uses its own `Where` constraints, a locked transaction and the state guard.
|
||||
- The README Customer API section, including the `http.cors.paths` note.
|
||||
- `TestCustomerAPI`.
|
||||
- **Task 4:**
|
||||
- Full state routing and polling in PgOrderPage (`visibilitychange`).
|
||||
- PgCheckout with the methods, cart, bank transfer and cancel.
|
||||
- PgOrderStatus with the tone bar, PgCart (stepper and last-item rule), PgPaymentMethods, PgShippingMethods, PgBankTransfer, PgOrderList and PgConfirm (`showModal`).
|
||||
- The playground, the layer README, and the orderPage, states and i18n tests.
|
||||
|
||||
## Task 5: repositories, fixtures, approval (checkpoint:human-action)
|
||||
|
||||
- **Human reply verbatim:** "1. you should be able to create repositories on git.golem15.com with "tea" command. 2. fixtures: none, there were some old apps but on very old payment gateway, not worth it. 3. approved"
|
||||
- **Repository URLs.** The orchestrator created them with `tea repos create --owner golem15`, public and with no initial commit:
|
||||
- https://git.golem15.com/golem15/sm-paymentgateway-plugin
|
||||
- https://git.golem15.com/golem15/sm-pgstripe-plugin
|
||||
- https://git.golem15.com/golem15/vue-paymentgateway-layer
|
||||
- **Fixtures:** none. Plan 07 keeps the synthetic legacy fixtures and records the gap (D-11 point 2).
|
||||
- **Verify:** printed `repos-ready`.
|
||||
|
||||
## Task 6: push and tag (checkpoint:human-action)
|
||||
|
||||
- **Preparation:**
|
||||
- `origin` was added in each repository as `git@git.golem15.com:golem15/<name>.git`.
|
||||
- Working trees were clean and the remote `main` branches empty.
|
||||
- The verify printed `push-sheet-ok` and the push sheet was presented.
|
||||
- **Human reply verbatim:** "push"
|
||||
- **Pushed** with `git push -u origin main`, no force-push:
|
||||
|
||||
| Repository | Pushed `main` head | Commits |
|
||||
|------------|--------------------|---------|
|
||||
| sm-paymentgateway-plugin | `ef72b8556e1daeec2be3ca83a0c09a2b6bba1f09` | 12 (plans 02 to 06) |
|
||||
| sm-pgstripe-plugin | `a8bc0b5c45ccf8bf44a51c56a61f61a71359f868` | 3 (plan 05) |
|
||||
| vue-paymentgateway-layer | `14d45221ecf49362e76a755bdee35e9f42c5cb9a` | 2 (plan 06) |
|
||||
|
||||
- **Tag:** the annotated `v0.1.0` ("vue-paymentgateway-layer v0.1.0") is tag object `1783d3373a525fcbd27f4ebaacc6d930139dc50e`, pointing at `14d4522` and pushed with `git push origin v0.1.0`.
|
||||
- **Acceptance:**
|
||||
- Each local `main` equals `ls-remote origin refs/heads/main`.
|
||||
- `ls-remote --tags` shows `v0.1.0` and `v0.1.0^{}` = 14d4522.
|
||||
- `cat-file -t v0.1.0` = `tag`, and `describe --tags` prints `v0.1.0`.
|
||||
- Every `git remote -v` names git.golem15.com/golem15.
|
||||
- **Release note carried forward:**
|
||||
- Both plugins' `go.mod` require summercms v0.1.24 with `replace ../summercms.go`, and sm-pgstripe-plugin requires sm-paymentgateway-plugin v0.0.0 through a replace.
|
||||
- A consumer builds them only after the framework tag v0.1.25 (held until plan 07) and the go.mod version bumps.
|
||||
- The layer depends only on HTTP.
|
||||
|
||||
## Verification
|
||||
|
||||
- **Go:**
|
||||
- `go vet` and `go test -short ./...` are green in sm-paymentgateway-plugin at each commit.
|
||||
- `TestCustomerAPITracer` and `TestCustomerAPI` (9 subtests) RUN and PASS on Postgres via testcontainers.
|
||||
- Full `go test -count=1 ./...` is green in sm-paymentgateway-plugin and in sm-pgstripe-plugin, which compiles the changed gateway.
|
||||
- No Docker suite recorded NOT RUN in plans 02 to 06.
|
||||
- **Layer:**
|
||||
- `pnpm test` gives 48 pass and 0 fail; there are 9 Pg components.
|
||||
- `dev:prepare` and `build:playground` succeed.
|
||||
- An SSR smoke of the built playground against a fake API rendered the EN checkout, the PL Paid status and the not-found panel.
|
||||
- **Acceptance greps all pass:**
|
||||
- `_paymentgateway/api/v1` appears in routes.go, and `subtle.ConstantTimeCompare` appears once in api/auth.go.
|
||||
- `amount_minor` is present; `guest_code` and `return_url` appear only in comments.
|
||||
- `location.assign` appears once in Checkout.vue, and `Number(` does not appear in money.ts.
|
||||
- The locale key sets are equal.
|
||||
- There are 12 route registrations, a `## Customer API` section and the `cors.paths` note.
|
||||
- `visibilitychange` and `showModal` are present.
|
||||
- Components carry no hex colours.
|
||||
- The README names `v0.1.0`, and nothing names a consuming application.
|
||||
- `payment-action` appears in Checkout.vue and the README.
|
||||
- No Stripe package is in package.json.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
1. **[Rule 1, spec arithmetic] The fixture totals.** The fixture item is 39.84 PLN net at 23% VAT, so `total` is 49.0000 (4900), not 39.8400.
|
||||
- The tests assert `total_net` 39.8400/3984 and `total` 49.0000/4900.
|
||||
- The piece test checks total_net 39.84 → 79.68 → 39.84 and total 49.00 → 98.00 → 49.00, plus `amount_stored` and `native_amount_stored`.
|
||||
- This is the same issue 18-05 recorded. Commits c818edb, ef72b85.
|
||||
2. **[Rule 3] TestNotificationRoute scoped.** The plan 02 route test required every `/_paymentgateway/` route to carry only the notify throttle. It now skips `APIPrefix`. Commit c818edb.
|
||||
3. **[Rule 3] GET /orders behind `api.RequireJWT`, not the named `jwt.auth`.** surf assembly failed without the user plugin's middleware in the plugin list (the no-DB route test), and `jwt.auth` would also read cookies. It is the same `jwt` guard, on Bearer only. Commit ef72b85.
|
||||
4. **[Rule 2] Cancel in AwaitingPayment uses the PHP Cancel transition.** It first cancels the open cash and bank transfer payments, and the bridge cancels the order. CancelNotActive stays the sweep's transition. Commit ef72b85.
|
||||
5. **[Rule 2] Process rolls back on an operator error.** Otherwise the order would be stuck in AwaitingPayment with a rejected payment while process accepts only Placed. Other Purchase errors (for example pgstripe `ErrNoSecretKey`) are logged and answered 422 `payment_method` "The payment could not be started." Commit c818edb.
|
||||
6. **[Rule 2] Additive order JSON fields.** `total_tax`, `items[].gross_price` and `items[].tax_value`, so the layer never computes VAT. `payments` shipped in Task 2.
|
||||
7. **[Rule 2] The cash rule holds in both directions.** Choosing a non-cash shipping on a cash order is 422 `shipping_id`.
|
||||
8. **Process requires a delivery method only when shippings are on and at least one row exists.** PHP required one whenever shippings were enabled, which would block every order when no rows exist.
|
||||
9. **Operator `logo` is null.** The PHP asset paths are not served by the Go plugins.
|
||||
10. **The process return URL** is `order_url_template` plus the guest credential the request used. Without a template, the consumer's stored return URL stays.
|
||||
11. **Layer files beyond the plan list:**
|
||||
- `app/utils/checkout.ts` and `app/utils/poller.ts` (the controllers the components use; `location.assign` stays in Checkout.vue and `visibilitychange` in OrderPage.vue).
|
||||
- `.playground/app/app.vue`.
|
||||
- `.nvmrc`.
|
||||
12. **Lockfile source.** The lockfile is derived from vue-journal-layer's, because offline pnpm resolution otherwise picked an unpublished vue-i18n 11.4.14. The resolved set equals the journal's (vue-i18n 11.4.13).
|
||||
13. **Auth hook.** It is a Nuxt `provide` named `paymentGatewayAuth` instead of an app-defined `usePaymentGatewayAuth` composable, because overriding an auto-import between layer and app is not reliable.
|
||||
14. **Copy placeholders** use vue-i18n `{name}` instead of the UI-SPEC's `:name`. New keys:
|
||||
- `pg.cart.item`
|
||||
- `pg.bank.{name,address,account,swift}`
|
||||
- `pg.orders.{label,page,previous,next}`
|
||||
- `pg.not_found.home`
|
||||
15. **Polling scope.** Only AwaitingPayment orders with an online operator are polled (`shouldPoll`).
|
||||
16. **Test approach.**
|
||||
- The checkout backstop drives `createCheckout` with a stub `assign`, because there is no DOM under node --test.
|
||||
- PgCart and PgOrderPage are SFC-compiled and rendered on the server.
|
||||
- vue-journal-layer has no composable loader, so `testing/load.ts` follows the 17-06 FAQ and menu layer pattern.
|
||||
17. **`scripts/check-git-install.sh` adapted.** It builds a scratch app mounting PgOrderPage and PgOrderList, has no SSR fixture smoke, and accepts a `REPO_URL` override.
|
||||
|
||||
**Total deviations:** 17 (3 Rule 1 or Rule 3 fixes, 5 Rule 2 correctness or safety changes, 9 discretionary choices recorded). **Impact:** the published contract is the Task 1 sheet plus additive fields; nothing is architectural.
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None.
|
||||
|
||||
## Threat Flags
|
||||
|
||||
None beyond the plan's register.
|
||||
|
||||
T-18-37 to T-18-45 and T-18-SC are mitigated and tested:
|
||||
- 404 matrix and constant-time compare.
|
||||
- A guest token cannot reach another order and never becomes a user identity.
|
||||
- Only method codes and ids are accepted; an unknown operator is 422; piece totals are exact; mutations outside Placed are 409.
|
||||
- The last item is refused, the piece floor is 1, and every mutation holds the row lock.
|
||||
- The API throttle bucket.
|
||||
- The JSON allowlist (tests check for no guest_code, return_url, cart, details, user_id or secure parameter).
|
||||
- Bank details HTML is sanitized again (tested with a script tag).
|
||||
- Bearer or query credentials only, no cookies.
|
||||
- The busy guard plus the server-side 409 against double submit.
|
||||
- Layer dependencies are only the pins.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None more: the human created the repositories (through the orchestrator) and approved the release.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- **Plan 07 (unit tests and phase gate) should:**
|
||||
- add pure tests for `api` helpers (`canActOnOrder` cases, `returnURL`, `requireState`, `writeMutationError`);
|
||||
- include `TestCustomerAPITracer` and `TestCustomerAPI` and the layer's `pnpm test` in the phase gate;
|
||||
- record fixtures: none for D-11 point 2;
|
||||
- tag the framework (v0.1.25) and bump the plugins' go.mod requirements.
|
||||
- **Site applications** mount `<PgOrderPage :hash>` and `<PgOrderList>` from `vue-paymentgateway-layer#v0.1.0` and provide `paymentGatewayAuth` for logged-in customers.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- Files exist: every path in `key-files` under `../sm-paymentgateway-plugin` and `../vue-paymentgateway-layer`.
|
||||
- Commits exist and are on origin:
|
||||
- paymentgateway: c818edb, ef72b85 (head ef72b85).
|
||||
- layer: 67b8e9f, 14d4522 (head 14d4522, tag v0.1.0).
|
||||
- pgstripe: head a8bc0b5.
|
||||
Reference in New Issue
Block a user