diff --git a/.planning/phases/18-payment-gateway/18-06-SUMMARY.md b/.planning/phases/18-payment-gateway/18-06-SUMMARY.md new file mode 100644 index 0000000..94ab577 --- /dev/null +++ b/.planning/phases/18-payment-gateway/18-06-SUMMARY.md @@ -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/.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 `` and `` 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.