docs(18-02): complete payment gateway domain core plan
This commit is contained in:
@@ -1113,14 +1113,14 @@ Plans:
|
||||
4. A host test proves the order-create, Stripe-pay and order-paid flow that Horoskopia relies on.
|
||||
5. The new code has unit tests, delivered in the phase's last plan.
|
||||
|
||||
**Plans:** 1/7 plans executed
|
||||
**Plans:** 2/7 plans executed
|
||||
|
||||
Plans:
|
||||
**Wave 1**
|
||||
- [x] 18-01-PLAN.md — Framework (summercms.go): cabana `money` field and list column (D-07, scale-4 decimal strings), write-only `lagoon.Encrypted` secrets, richeditor and tabs on settings forms (D-13, F2-F4), danger record actions, preview Edit rule and the table kit (F5-F7), docs, OpenAPI, dist; framework tag with go-ahead
|
||||
|
||||
**Wave 2** *(blocked on 18-01)*
|
||||
- [ ] 18-02-PLAN.md — sm-paymentgateway-plugin domain core: v2 row-shape gate, exact money, migrations, models, FSMs, Order/Payment services, operators (cash, manual, disabled) and registry, cart with legacy reader and extension registry, hashids, events and bridge, notification route, currencies (NBP, Fixer), `pg:*` commands, `paymentgateway:import-winter`
|
||||
- [x] 18-02-PLAN.md — sm-paymentgateway-plugin domain core: v2 row-shape gate, exact money, migrations, models, FSMs, Order/Payment services, operators (cash, manual, disabled) and registry, cart with legacy reader and extension registry, hashids, events and bridge, notification route, currencies (NBP, Fixer), `pg:*` commands, `paymentgateway:import-winter`
|
||||
|
||||
**Wave 3** *(blocked on 18-02)*
|
||||
- [ ] 18-03-PLAN.md — sm-paymentgateway-plugin admin: Orders, Payments, Quotes, Quote items and Shippings in cabana YAML with partials, record and admin actions, the quote cart element, permissions, navigation and the full EN/PL phrasebook
|
||||
@@ -1340,7 +1340,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 →
|
||||
| 15. Journal plugin | 4/4 | Complete | 2026-10-06 |
|
||||
| 16. grzybyfunkcjonalne.pl on reusable blog views | 5/5 | In Progress| |
|
||||
| 17. Blog companions | 6/6 | In Progress| |
|
||||
| 18. Payment Gateway | 1/7 | In Progress| |
|
||||
| 18. Payment Gateway | 2/7 | In Progress| |
|
||||
| 18.1. Payment Gateway Subscriptions | 0/TBD | Not started | - |
|
||||
| 19. Chat and ChatChannels | 0/TBD | Not started | - |
|
||||
| 19.1. Chat satellites | 0/TBD | Not started | - |
|
||||
|
||||
@@ -4,16 +4,16 @@ milestone: v1.0
|
||||
current_phase: 18
|
||||
current_phase_name: Payment Gateway
|
||||
status: executing
|
||||
stopped_at: Completed 18-01-PLAN.md
|
||||
last_updated: "2026-10-13T09:49:54.928Z"
|
||||
stopped_at: Completed 18-02-PLAN.md
|
||||
last_updated: "2026-10-13T11:20:09.050Z"
|
||||
last_activity: 2026-10-13
|
||||
last_activity_desc: Phase 18 execution started
|
||||
state_head: 1e841b4281c0e4280ae5af2f5fed338242f0f7d6
|
||||
state_head: a22bd8a934f9b91820e75a95ce0b8a71a2c460b1
|
||||
progress:
|
||||
total_phases: 40
|
||||
completed_phases: 14
|
||||
total_plans: 175
|
||||
completed_plans: 169
|
||||
completed_plans: 170
|
||||
gsd_state_version: "1.0"
|
||||
milestone_name: milestone
|
||||
---
|
||||
@@ -30,7 +30,7 @@ See: .planning/PROJECT.md (updated 2026-10-12)
|
||||
## Current Position
|
||||
|
||||
Phase: 18 (Payment Gateway) — EXECUTING
|
||||
Plan: 2 of 7
|
||||
Plan: 3 of 7
|
||||
Status: Ready to execute
|
||||
Last activity: 2026-10-13 — Phase 18 execution started
|
||||
|
||||
@@ -226,6 +226,7 @@ Progress: [████░░░░░░] 44%
|
||||
| Phase 17 P05 | 93min | 5 tasks | 103 files |
|
||||
| Phase 17 P06 | 53 min | 4 tasks | 50 files |
|
||||
| Phase 18 P01 | 43min | 4 tasks | 59 files |
|
||||
| Phase 18 P02 | 75min | 4 tasks | 84 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -747,6 +748,9 @@ Recent decisions affecting current work:
|
||||
- [Phase 18]: Phase 18 plan 01 release gate: framework tag v0.1.25 held until the phase gate after plan 07 (human reply: hold); nothing pushed or tagged, plans 02 to 06 build against the checkout through go.work replace — One tag for the whole phase with the unit tests of plan 07 included; the plugin plans use go.work replace and need no published version, so holding costs only a later bump
|
||||
- [Phase 18]: Money is a decimal string on the wire and in the SPA (never a float, never rounded or truncated); MoneyField emits the raw compact text for a malformed or too-precise draft so the server 422 stays the gate — D-07 and D-10 forbid rounding and truncation; emitting the last valid prefix on a bad draft would let Save write a truncated amount
|
||||
- [Phase 18]: Settings secrets are write-only: the GET omits the value and reports meta.secrets.<field> as stored, env or empty; on PUT an absent key or empty string keeps the stored value and JSON null clears it; meta is omitted when there is nothing to report so existing payloads stay byte-identical — The value must never leave the server (D-13, T-18-03); omitempty on meta and danger keeps the Phase 10 conformance fixtures and the PHP-shaped payloads unchanged
|
||||
- [Phase 18]: 18-02: cart/details v2 row format is v2-research (long keys, top-level v:2); legacy JMS rows decoded in place, never rewritten
|
||||
- [Phase 18]: 18-02: notification route verifies (VerifyNotification on the capped raw body) before any database lookup; operators.PaymentContext carries Tx for in-transaction transitions
|
||||
- [Phase 18]: 18-02: NBP uses Table A of the day before with a 5-day fallback (PHP semantics); Fixer keeps the PHP endpoint with optional fixer_access_key
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@@ -825,6 +829,6 @@ Items acknowledged and carried forward from previous milestone close:
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-10-13T09:49:33.284Z
|
||||
Stopped at: Completed 18-01-PLAN.md
|
||||
Last session: 2026-10-13T11:20:08.174Z
|
||||
Stopped at: Completed 18-02-PLAN.md
|
||||
Resume file: None
|
||||
|
||||
276
.planning/phases/18-payment-gateway/18-02-SUMMARY.md
Normal file
276
.planning/phases/18-payment-gateway/18-02-SUMMARY.md
Normal file
@@ -0,0 +1,276 @@
|
||||
---
|
||||
phase: 18-payment-gateway
|
||||
plan: 02
|
||||
subsystem: payments
|
||||
tags: [paymentgateway, money, fsm, operators, cart, legacy-jms, hashids, currencies, nbp, fixer, notification-route, scheduler, import-winter]
|
||||
|
||||
requires:
|
||||
- phase: 18-01
|
||||
provides: framework pieces the plugin reads through go.work (pact, cabana money and settings, the stable framework checkout)
|
||||
- phase: 17
|
||||
provides: plugin repository layout (journal, sitemap), sitemap import-winter precedent, testcontainers harness pattern
|
||||
provides:
|
||||
- new repository ../sm-paymentgateway-plugin (module git.golem15.com/golem15/sm-paymentgateway-plugin, package paymentgateway, plugin ID golem15.paymentgateway, branch main, no remote)
|
||||
- money (int64 x 10^4, MoneyRight arithmetic, Amount4 exact Scanner/Valuer), fsm, models, six raw-SQL migrations plus the settings singleton
|
||||
- payment and order state machines, orders.Service and payments.Service (Make, Create, Purchase, Send, Accept, Cancel, Refund, RecordExternal, PerformTransition, RequestRefund, ...), the 24 typed events and the payment-to-order bridge
|
||||
- operators.Operator, HasOperators, Registry collected over party.Activated, Cash, ManualTransfer, Disabled
|
||||
- cart value objects with the v2 writer (option v2-research) and the permanent legacy JMS reader with golden fixtures and the item-type registry
|
||||
- support (Hashids with legacy-salt fallback, GuestToken, Redact, Urlize/DeUrlize)
|
||||
- currency (ISO table, NBP and Fixer providers, Converter reproducing CurrencyPair::convert, auto-create)
|
||||
- notification route _paymentgateway/{op}[/{mode}[/{result}]] x 5 methods with verify-before-lookup
|
||||
- pg:cancel-sent-payments, pg:cancel-created-payments, pg:cancel-placed-orders, pg:finish-orders (daily), pg:update-currencies (10 min), paymentgateway:import-winter, export SQL scripts
|
||||
- README with Cutover and non-ports; decision note for go-hashids
|
||||
affects: [18-03 order admin, 18-04 payment admin and mailers, 18-05 stripe operator, 18-06 site integration and customer API, 18-07 unit tests]
|
||||
|
||||
actuals:
|
||||
tokens: 103193
|
||||
tasks: 4
|
||||
commits: 4
|
||||
plan_head_before: "framework 3572030 (parent of a22bd8a); plugin repository created by this plan (no prior HEAD)"
|
||||
plan_head_after: "framework a22bd8a (before this SUMMARY commit); plugin f725439"
|
||||
|
||||
tech-stack:
|
||||
added:
|
||||
- "github.com/speps/go-hashids/v2 v2.0.1 (decision note .planning/notes/2026-10-13-paymentgateway-hashids-go-hashids.md)"
|
||||
patterns:
|
||||
- "Money is int64 scaled by 10^4; arithmetic operands are exact rationals (tax is Mul(tax, 100)); provider rates are parsed with big.Rat from json.Number, never a float"
|
||||
- "Boot steps registered with registerBootStep in boot.go; later plans add steps from their own files"
|
||||
- "Operators are build-time values collected over party.Activated; the route segment is base64 of the PHP operator code"
|
||||
- "Verify-before-lookup: VerifyNotification runs on the capped raw body before any database access; the payment row is then locked FOR UPDATE and the operator's handler writes the response after commit"
|
||||
- "Console sweeps run each row in its own transaction with FOR UPDATE and a state re-check, through the services so events fire"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- ../sm-paymentgateway-plugin/money/money.go
|
||||
- ../sm-paymentgateway-plugin/money/amount.go
|
||||
- ../sm-paymentgateway-plugin/fsm/fsm.go
|
||||
- ../sm-paymentgateway-plugin/orders/service.go
|
||||
- ../sm-paymentgateway-plugin/orders/bridge.go
|
||||
- ../sm-paymentgateway-plugin/payments/service.go
|
||||
- ../sm-paymentgateway-plugin/payments/notification.go
|
||||
- ../sm-paymentgateway-plugin/operators/operator.go
|
||||
- ../sm-paymentgateway-plugin/cart/v2.go
|
||||
- ../sm-paymentgateway-plugin/cart/legacy.go
|
||||
- ../sm-paymentgateway-plugin/currency/converter.go
|
||||
- ../sm-paymentgateway-plugin/currency/nbp.go
|
||||
- ../sm-paymentgateway-plugin/currency/fixer.go
|
||||
- ../sm-paymentgateway-plugin/console/import_winter.go
|
||||
- ../sm-paymentgateway-plugin/routes.go
|
||||
- ../sm-paymentgateway-plugin/schedule.go
|
||||
- ../sm-paymentgateway-plugin/README.md
|
||||
- .planning/notes/2026-10-13-paymentgateway-hashids-go-hashids.md
|
||||
modified: []
|
||||
|
||||
key-decisions:
|
||||
- "Task 1 checkpoint: v2 row format option v2-research (human reply: \"let's go with v2-research\"), long keys with a top-level \"v\": 2"
|
||||
- "NBP reads Table A of the day before today with a 5-day 404 fallback (PHP NbpCurrencyExchange::getRatio semantics) instead of the latest table; cross rates mid(iso)/mid(base) for a non-PLN default currency"
|
||||
- "Fixer keeps the PHP endpoint https://exchange.viamage.com (per-code latest?base=ISO&symbols=DEFAULT); fixer_access_key is optional, sent as access_key"
|
||||
- "operators.PaymentContext carries Tx so operators apply transitions inside the notification transaction"
|
||||
- "A sweep timeout below one day falls back to the default instead of sweeping every row"
|
||||
|
||||
patterns-established:
|
||||
- "Notification route: three patterns x Get/Post/Put/Patch/Delete with only the throttle middleware"
|
||||
- "Exact provider decimals: json.Number -> big.Rat -> round half away from zero to the column scale"
|
||||
|
||||
requirements-completed: [SC-1, SC-2, SC-3, D-02, D-04, D-05, D-09, D-10, D-11, D-14, D-15]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "Exact money with the PHP MoneyRight vectors and Amount4 Scan/Value"
|
||||
requirement: D-09
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "go test -short ./money -run '^(TestMoneyVectorsSmoke|TestAmount4ScanValue)$'"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "Cash order end to end: Make, Create, Purchase, Accept through machines, bridge and events, exact row text, hash id, replayed Accept refused"
|
||||
requirement: SC-1
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "go test -run '^TestPaymentGatewayTracer$' . (Postgres)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "Refund, reject and RecordExternal paths through the bridge"
|
||||
requirement: SC-3
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "go test -run '^TestRefundBridgeSmoke$' . (Postgres)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D4
|
||||
description: "Legacy JMS rows decode against golden fixtures; re-encode is v2; unknown type fails until registered"
|
||||
requirement: D-11
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "go test -short ./cart -run '^TestLegacyCartGoldens$'"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D5
|
||||
description: "Notification route: 15 registrations, padded/unpadded base64, mode/result merge, 404 for unregistered/disabled, 403 before any lookup, 413 cap; signed notification accepts once, replay 200 no-op"
|
||||
requirement: D-14
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "go test -short -run '^(TestNotificationRoute|TestRedactor)$' ."
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "go test -run '^TestNotificationRouteSmoke$' . (Postgres)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D6
|
||||
description: "NBP and Fixer providers with exact parsing and the two conversion vectors"
|
||||
requirement: SC-3
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "go test -short ./currency -run '^TestCurrencyProviders$'"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D7
|
||||
description: "Five pg:* commands touch only rows past their timeout and fire events; import-winter maps the flat PHP keys"
|
||||
requirement: SC-3
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "go test -run '^(TestPGCommandsSmoke|TestImportWinterSmoke)$' . (Postgres)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
|
||||
duration: ~75min (two sessions)
|
||||
completed: 2026-10-13
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 18 Plan 02: Payment Gateway Domain Core Summary
|
||||
|
||||
**New `sm-paymentgateway-plugin`: MoneyRight-exact scale-4 money, the PHP payment and order state machines with 24 typed events and the payment-to-order bridge, build-time operators (cash, manual transfer, disabled), v2 cart rows with a permanent legacy JMS reader, NBP/Fixer currencies, the verify-before-lookup notification route, the five scheduled `pg:*` commands and `paymentgateway:import-winter`.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** about 75 minutes of execution across two sessions (the first hit its limit after Task 3)
|
||||
- **Completed:** 2026-10-13
|
||||
- **Tasks:** 4 of 4
|
||||
- **Files:** 84 tracked files in the plugin repository, plus one decision note in the framework
|
||||
|
||||
## Task 1: decision (checkpoint:decision)
|
||||
|
||||
- **Option chosen:** `v2-research` (human reply verbatim: "let's go with v2-research"). Recorded in the package doc comment of `../sm-paymentgateway-plugin/cart/v2.go`.
|
||||
- **Key list (verbatim from the cart/v2.go doc comment):**
|
||||
|
||||
```
|
||||
cart: {"v":2,"currency":"PLN","items":[{"id","type","description",
|
||||
"count","net_price":{"amount","currency"},"tax","data",
|
||||
"adjustments":[{"id","type":"value","description",
|
||||
"value":{"amount","currency"}},{"id","type":"percent",
|
||||
"description","percent"}]}],
|
||||
"shipping":{"name","cost":{"amount","currency"},"tax"}}
|
||||
details: {"v":2,"operator","description","return_url","email",
|
||||
"shipping_address":{"recipient_first_name",
|
||||
"recipient_last_name","street","building_number",
|
||||
"flat_number","zip_code","city","country"},
|
||||
"parameters":{...}}
|
||||
```
|
||||
|
||||
Items are an ordered array; `shipping` and `shipping_address` are left out when absent; `data` and `parameters` are JSON objects (`{}` when empty); money is the exact four-decimal text plus the ISO code. The address keys are the snake_case of the PHP property names. A v2 row cannot be read by PHP (D-11 point 4); legacy rows are decoded in place.
|
||||
|
||||
## Commits
|
||||
|
||||
Plugin repository `../sm-paymentgateway-plugin` (branch `main`, no remote, nothing pushed):
|
||||
|
||||
| Task | Commit | Subject |
|
||||
|------|--------|---------|
|
||||
| 2 | b4452a9 | feat(18-02): payment gateway domain core: money, state machines, services, cart v2, operators (task 2) |
|
||||
| 3 | f5963bb | feat(18-02): legacy JMS reader with golden fixtures, guest tokens, refund and reject paths, RecordExternal (task 3) |
|
||||
| 4 | f725439 | feat(18-02): notification route, currencies with NBP and Fixer, pg:* commands and schedule, import-winter, README (task 4) |
|
||||
|
||||
Framework repository `summercms.go` (branch `master`):
|
||||
|
||||
| Task | Commit | Subject |
|
||||
|------|--------|---------|
|
||||
| 2 | a22bd8a | docs(18-02): decision note for the go-hashids dependency |
|
||||
| - | (this SUMMARY) | docs(18-02): complete payment gateway domain core plan |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **Task 2 (tracer):** repository, `go.mod` (summercms v0.1.24 with the `go.work` replace, sm-user-plugin v0.1.1, go-hashids v2.0.1), `go.work` (framework and `../sm-summercmsio-app/plugins/golem15/user`), plugin wiring with boot steps (hashids salt check, operator registry over `party.Activated`, services, bridge), money with the PHP vectors, fsm, models, the six migrations with the PHP names and Postgres types, cart core and the v2 writer, operators, the 24 events, both services and the bridge, hashids with the `KEIOS.PAYMENTGATEWAY` fallback, the Postgres harness and `TestPaymentGatewayTracer`.
|
||||
- **Task 3:** adjustable items, shipping method and address, the token-walk legacy reader (`DecodeLegacyCart`, `DecodeLegacyDetails`), `RegisterItemType` / `RegisterDetailsParams`, five golden fixtures (the one real row with every string replaced, three generated by `tests/php/gen-fixtures.php` with the plugin's own JMS serializer), manual transfer and disabled operators, `payments.Service.Wrap`, guest tokens (HMAC-SHA256, constant-time compare), the Reject/refund special cases, `RecordExternal`, `TestLegacyCartGoldens` and `TestRefundBridgeSmoke`.
|
||||
- **Task 4:** the notification route (three patterns x five methods under `throttle:paymentgateway-notify` only; base64 padded or unpadded; 1 MiB body read once; `VerifyNotification` before any database access, 403 on refusal; uuid lookup with `FOR UPDATE`; handler response written after commit; `log_requests` through `support.Redact`), `currency` (ISO table, NBP Table A, Fixer, `Converter` installed as the services' converter at boot with `currency_auto_create`), the five `pg:*` commands through the services and their `Schedule()` entries, `paymentgateway:import-winter` with `scripts/winter-export-settings.sql` and `scripts/winter-export-orders.sql`, and the README with the Cutover section and the non-ports.
|
||||
|
||||
## Exported identifiers per package
|
||||
|
||||
- Root: `PluginID`, `NotifyBucket`, `NotificationPatterns`, `Plugin`.
|
||||
- `money`: `Money`, `FromMinor`, `FromString`, `FromUnits`, `Zero`, `RoundingGAAP`, `Option`, `Amount4`, `ParseAmount4`, `MaxNumeric12`, `MaxNumeric10`, `Scale`, `Basic`, `ErrCurrencyMismatch`, `ErrTooPrecise`, `ErrOverflow`, `ErrInvalid`, `ErrDivideByZero`.
|
||||
- `fsm`: `Machine`, `Transition`, `Guard`, `ErrTransition`.
|
||||
- `models`: `Order`, `Payment`, `Currency`, `Shipping`, `Quoteable`, `QuoteOrder`, `Settings`, `JSONMap`, `Register`, `All`, `LoadSettings`, `DefaultSettings`, `Text`, the state/transition constants and lists, `RateSource*`, `ExchangeRateProvider*`, `PaymentData*`.
|
||||
- `cart`: `Cart`, `Item`, `AdjustableItem`, `ValueAdjustment`, `PercentAdjustment`, `ShippingMethod`, `ShippingAddress`, `Details`, `Purchasable`, `Adjustable`, `Adjustment`, `NewCart`, `NewItem`, `NewDetails`, `Decode`, `DecodeDetails`, `DecodeLegacyCart`, `DecodeLegacyDetails`, `EncodeCart`, `EncodeDetails`, `RegisterItemType`, `RegisterDetailsParams`, `ItemTypes`, `Version`, `EmployeeCreatedParam`, error values.
|
||||
- `operators`: `Operator`, `HasOperators`, `Registry`, `NewRegistry`, `PaymentContext` (now with `Tx`), `Response`, `Status*`, `Mode*`, `Code*`, `AnyCurrency`, `Cash`, `ManualTransfer`, `Disabled`, `SettingsLoader`, `SupportsCurrency`, `ManualTransferFields`, error values.
|
||||
- `events`: 24 structs and 24 `…EventName` constants, `OrderEvent`, `PaymentEvent`.
|
||||
- `orders`: `Service` (`Make`, `Purchase`, `Find`, `FindByHash`, `Lock`, `Reload`, `Wrap`, `Save`, `Can`, `PerformTransition`, `RequestRefund`, `Cancel`, `IsFinal`, `CanBeRefunded`), `New`, `WithQuote`, `MakeOption`, `Machine`, `NewMachine`, `ShippingGuard`, `RegisterBridge`, `Subject`, `Wrapped`, `ErrNotConfigured`.
|
||||
- `payments`: `Service` (`Create`, `Context`, `Operator`, `Purchase`, `SaveData`, `Send`, `Accept`, `Cancel`, `Reject`, `Refund`, `Can`, `Find`, `FindByUUID`, `Lock`, `Wrap`, `RecordExternal`, `ForOrderRows`, `ProcessNotification`, `SetConverter`, `Converter`), `New`, `ForOrder`, `Converter`, `DefaultCurrencyOnly`, `MissingFieldError`, `NotificationInput`, `NotificationURL`, `MaxNotificationBytes`, `RouteParam*`, `LoadUser`, error values.
|
||||
- `support`: `Hashids`, `NewHashids`, `Alphabet`, `LegacySalt`, `SaltConfigKey`, `HashIDLength`, `GuestCodeFactor`, `GuestToken`, `NewGuestToken`, `DefaultTTL`, `NewUUID`, `Redact`, `RedactHeaders`, `RedactBody`, `RedactString`, `SensitiveKey`, `FormMap`, `Redacted`, `Urlize`, `DeUrlize`, error values.
|
||||
- `currency`: `Converter`, `NewConverter`, `Provider`, `Providers`, `ProvidersFromConfig`, `ConfigReader`, `NBP`, `NBPTable`, `Fixer`, `Rate`, `ISO`, `ISOCurrency`, `Lookup`, `Known`, `ParseRatio`, `RoundRat4`, `ConvertWithRatio`, `InvertRatio`, `Apply`, `MaxRatio`, `NBPBaseURL`, `NBPFallbackDays`, `FixerBaseURL`, config key constants, error values.
|
||||
- `console`: `CancelSentPayments`, `CancelCreatedPayments`, `CancelPlacedOrders`, `FinishOrders`, `UpdateCurrencies`, `ImportWinter`, `RunCancelPayments`, `RunCancelPlacedOrders`, `RunFinishOrders`, `RunUpdateCurrencies`, `ImportWinterFile`, `ReadWinterSettings`, `WinterOptions`, `WinterReport`, `WinterSettingsItem`.
|
||||
- `updates`: `All`, `Register`.
|
||||
|
||||
## Verification
|
||||
|
||||
- `go vet ./...` and `go test -short ./...` in the plugin: green at every commit (re-checked for Tasks 2 and 3 at the start of this session).
|
||||
- Docker suites, all RUN and PASS on this host (testcontainers Postgres 16): `TestPaymentGatewayTracer`, `TestRefundBridgeSmoke`, `TestNotificationRouteSmoke`, `TestPGCommandsSmoke` (five subtests), `TestImportWinterSmoke`; full `go test -count=1 ./...` green.
|
||||
- Task acceptance greps: no `float64|float32|ParseFloat|big.Float` in money, cart, orders, payments (or currency); 6 migrations, no AutoMigrate; 24 `EventName = `; the legacy salt; the four cart registry and decoder functions; no real e-mail domains in fixtures; `subtle.ConstantTimeCompare` in guest_token.go; 3 `_paymentgateway/{op}` lines in routes.go; one `MaxBytesReader` line in payments/notification.go before the first `FindByUUID`; one `UseNumber` per provider file; 5 `pg:` lines in schedule.go; every command name quoted in console/; both SQL scripts; `## Cutover` in the README; no consuming application named anywhere in the plugin.
|
||||
- Framework repository: no Go code touched by this plan, so its `go vet`/`go test` are unaffected.
|
||||
|
||||
## Fixture generator status
|
||||
|
||||
`tests/php/gen-fixtures.php` ran against the frozen Winter tree's vendor autoloader and the plugin's JMS metadata; the three `synthetic-*.json` fixtures are its output and carry `"_generator": "tests/php/gen-fixtures.php"`. `legacy-real-1-{cart,details}.json` are the one real dump row with every string replaced by placeholders. Shop-type real rows remain to be requested at the plan 06 checkpoint (`scripts/winter-export-orders.sql`) and added in plan 07.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 - Blocking] `operators.PaymentContext` gained a `Tx *gorm.DB` field**
|
||||
- **Found during:** Task 4 (notification route)
|
||||
- **Issue:** `HandleNotification` receives no transaction, so an operator could not apply Accept inside the transaction that holds the payment row lock.
|
||||
- **Fix:** `PaymentContext.Tx`, set by `payments.Service.Context`; the route smoke's operator accepts through it.
|
||||
- **Files modified:** operators/operator.go, payments/service.go
|
||||
- **Commit:** f725439
|
||||
|
||||
**2. [Rule 1 - Spec fidelity] NBP reads the table of the day before, not the latest table**
|
||||
- **Found during:** Task 4
|
||||
- **Issue:** the plan names `tables/A?format=json` (latest), but the frozen PHP `NbpCurrencyExchange::getRatio` uses the rate of the day before with a five-day fallback (Polish VAT rule); D-02 makes the PHP tree the spec.
|
||||
- **Fix:** `exchangerates/tables/A/<yesterday>/?format=json`, stepping back a day on 404 up to `NBPFallbackDays`. A non-PLN default currency gets the exact cross rate (PHP returned the PLN mid regardless of the target, a bug not copied).
|
||||
- **Commit:** f725439
|
||||
|
||||
**3. [Rule 1 - Spec fidelity] Fixer endpoint and access key**
|
||||
- **Issue:** the plan says the access key is "named after the PHP config key read in FixerCurrencyExchange.php", but that class reads no key; it calls `https://exchange.viamage.com/latest?base=<ISO>&symbols=<default>` per currency.
|
||||
- **Fix:** the PHP endpoint and per-code call are kept (`golem15.paymentgateway.fixer_url`); `golem15.paymentgateway.fixer_access_key` is optional and sent as `access_key` for fixer.io. `golem15.paymentgateway.nbp_url` was added the same way so tests use httptest.
|
||||
- **Commit:** f725439
|
||||
|
||||
**4. [Rule 2 - Safety] Sweep timeouts below one day fall back to the default**
|
||||
- **Issue:** PHP casts a missing timeout to 0, which would make a sweep cancel every row in the state.
|
||||
- **Fix:** `deadline` uses the `DefaultSettings` value for a timeout below 1.
|
||||
- **Commit:** f725439
|
||||
|
||||
**5. [Rule 1 - Bug] `pg:update-currencies` also refreshes rows whose `updated_at` is NULL**
|
||||
- **Issue:** PHP `updated_at < today` never selects a NULL row, so such a currency would never update.
|
||||
- **Fix:** `updated_at < today OR updated_at IS NULL`. The command also writes `rate_source` and the NBP table fields, which the PHP command never wrote.
|
||||
- **Commit:** f725439
|
||||
|
||||
**6. [Rule 3] `go.work.sum` added to the plugin's `.gitignore`** (sitemap precedent) so the generated file is not left untracked.
|
||||
|
||||
Provider interface naming: `Provider.Rates(ctx, base, isos)` takes the codes to fetch (Fixer asks one code per call like PHP); `Converter.Ratio(ctx, tx, iso)` replaces the plan's `Ratio(from, to)` because the ratio is always to the default currency. These names are Claude's discretion per CONTEXT.
|
||||
|
||||
## Threat Flags
|
||||
|
||||
None beyond the plan's register: the only new network surface is the notification route (T-18-07, T-18-08, T-18-12, T-18-13), and the outbound calls go to the configured NBP and Fixer endpoints (T-18-15).
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None. `lang/` holds a placeholder translation file by design until plan 03 adds the admin phrases.
|
||||
|
||||
## Next
|
||||
|
||||
Plan 18-03 (order admin screens) builds on the services, models and boot steps.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- Files: cart/v2.go, payments/notification.go, currency/{converter,nbp,fixer,iso,providers_test}.go, console/*.go (7), scripts/*.sql (2), README.md, notification_route_test.go, commands_smoke_test.go and every path in the plan's files_modified exist.
|
||||
- Commits: b4452a9, f5963bb, f725439 in ../sm-paymentgateway-plugin and a22bd8a in summercms.go exist.
|
||||
Reference in New Issue
Block a user