We just tagged v1.0.1-alpha-32. This release hardens the payments library (idempotent off-session charges, boot-time credential validation, webhook correlation), adds tooling and scaffolding that keep the ports convention from eroding, fixes a boundary-tools packaging bug, and ships a new sizing & scaling guide.
Added: idempotency keys for off-session payments
IPaymentProvider/create-off-session-payment now accepts an optional :idempotency-key — the stable business identity of a charge, e.g. "incasso-<subscription>-<period>" (BOU-82). A retry after a crash or lost response returns the original charge instead of double-charging.
-
OffSessionPaymentRequestschema gains the optional:idempotency-key. -
The Stripe adapter sends it as the
Idempotency-Keyheader onPOST /v1/payment_intents. -
The mock provider caches results per key (per instance) and replays the first result for a repeated key; a new
make-mock-providerfactory is used by wiring and tests. -
Mollie is unchanged — off-session still throws
:not-implemented.
Changed: fail-fast on missing payment credentials
ig/init-key :boundary/payment-provider previously constructed adapters without checking credentials (BOU-77). With STRIPE_API_KEY / STRIPE_WEBHOOK_SECRET (or MOLLIE_API_KEY) unset, Aero #env resolved to nil and the system booted fine — the failure only surfaced at runtime: the first charge hit Stripe with Bearer null (401), and the first webhook verified HMAC against a nil secret and silently rejected events. A forgotten env var shipped a payment system that took no money.
Credentials are now validated at boot: :stripe requires :api-key + :webhook-secret, :mollie requires :api-key, :mock requires nothing. nil and blank both count as missing, and all missing keys are reported together via ex-info {:type :config-error …} naming each key and env var.
|
Note
|
This is a behaviour change — a deploy that was previously booting with missing payment credentials (and failing only on first use) will now refuse to start. Set the credentials before upgrading. |
Fixed: webhooks can be correlated to their checkout
process-webhook surfaced the adapter-internal UUID as :provider-checkout-id, but that UUID was never returned at creation — consumers stored the cs_/tr_ session id and could never match an incoming webhook to it (BOU-78). The Mollie adapter had the same defect.
The fix is port-level and provider-agnostic: CheckoutResult gains a required :correlation-id and WebhookResult an optional one. Adapters echo the internal UUID (already embedded in PSP payment metadata) on both creation and webhook, so consumers correlate every webhook to a stored checkout using only CheckoutResult fields — no extra API calls. Webhook :provider-checkout-id now means the genuine session id only (absent for Stripe payment_intent.* events) instead of aliasing the UUID.
Added: bb check:ports hexagonal boundary gate
Documentation alone won’t keep the ports convention intact — boundary-license proved it erodes silently (BOU-79). check:fcis only guards the pure-core side; nothing verified the hexagonal side. The new check:ports gate (static ns-form analysis, BOU-81) enforces three rules:
-
Module completeness — a directory with both
core/andshell/must define aports.cljwith at least onedefprotocol. -
No cross-module shell coupling — a
boundary.X.shell.*namespace must not require another module’sshell.persistence/shell.service; cross-module access goes throughboundary.Y.ports. -
Web/HTTP layers never require
.shell.persistence directly.*
Legitimate exceptions use ^:boundary/allow-direct ns metadata or a .boundary/check-ports.edn allowlist. It’s wired into bb check (incl. --quick), CI, and the pre-commit hook. Validated against boundary-license, it reproduces the BOU-79 audit exactly (28 violations); the monorepo passes clean (21 modules, 0 violations).
Added: project template mandating ports.clj
New Boundary projects had no agent-guidance file, so the ports convention was invisible to developers and coding agents (BOU-80). generate-project now emits an AGENTS.md documenting the FC/IS + ports architecture with ports.clj marked REQUIRED, plus a thin CLAUDE.md pointing at it. The generated bb.edn now also wires the check:ports task, so the gate the template mandates is actually runnable in a fresh project. bb ai docs --type agents learned a matching "Ports & Protocols" section.
Fixed: boundary-tools jar shipped without its error catalogue
boundary.tools.help realized the error catalogue at namespace-load time and threw when boundary/devtools/error_catalog.edn was absent (BOU-76). Because bb.edn :requires loads help eagerly, every bb task died at startup in consumer projects that depend on boundary-tools without boundary-devtools. The catalogue read is now pure (nil → {}, never throws) and lazy (a delay), help-error degrades to a "catalogue not available" message, and build.clj copies error_catalog.edn into the boundary-tools jar so consumers get it.
Fixed: pom scm tag aligned with git tag for cljdoc
libs/*/build.clj wrote <scm><tag>v<version> while git tags were bare semver, so cljdoc re-clones pointed at a non-existent ref (BOU-75). All build files now emit the bare version as the scm tag, matching the published git tags — alpha-32 poms are bare.
Docs: sizing & scaling guide
A new architecture guide covers three scaling axes — vertical (config knobs), horizontal (N replicas), and functional decomposition (slicing modules into independent services) — with a horizontal-readiness matrix, deployment topologies, per-module sliceability, and a production checklist (BOU-84). Read it under Architecture → Sizing & Scaling.
Version alignment
All 25 libraries bumped to v1.0.1-alpha-32 to maintain lockstep versioning.
Upgrade
Re-run the installer to pick up the latest release:
curl -fsSL https://get.boundary-app.org | bash
The payments schema additions (:idempotency-key, :correlation-id) are additive. The one behaviour change to watch is boot-time credential validation: if a deploy was running with missing payment credentials, set them before upgrading or the system will refuse to start.
Feedback and issues welcome on GitHub.