Owner-annotated on the live page: the pay-page header lockup should be
4rem, not a fixed 34px. Only the .brandmark rule carried the size (the
template has no inline height), so the change is that one declaration;
width stays auto and the .brand centering/margins hold at the larger
size (verified headless at a phone-width viewport, no overflow).
The embedded Woo panel header is untouched: its logo height is an
inline height="30" SVG attribute in the plugin, not this rule, and it
reads correctly at panel scale.
Gate: cargo test -p gp-server green.
The pay-URI builder now emits goblin:<nprofile>?amount=&memo= instead of
nostr:, and the Goblin panel gains a clickable "Open in Goblin" pill that
carries it. goblin: is the wallet's registered deep-link scheme for web
buttons (Build ~142+), so tapping opens the wallet straight to a prefilled
review; the QR carries the same goblin: payload (the wallet camera accepts
either scheme, goblin: keeps link and QR in agreement). The bare nprofile
textarea stays as the copy-paste fallback. Adds CheckoutInfo.pay_link.
Replace the left-aligned wordmark + redundant "Pay with Goblin" heading with a
single centered lockup (the white goblin+Pay wordmark) as the page h1, with
generous whitespace and no other heading text. The lockup already reads
"Goblin Pay", so the separate heading is dropped; its alt carries the heading
for assistive tech. Rendered height 34px. Asset is the existing
static/goblinpay-wordmark.svg (byte-identical to the design-bundle
Goblin-Pay-W.svg), already routed and embedded.
GP_GRIN1_RAIL now defaults OFF. Unset/off, GoblinPay behaves like the pre-rail
server: no arti/Tor code path runs, no loopback Foreign API listener, no
invoice slates issued, and the pay page shows ONLY 'Pay with Goblin' (no
switcher, no grin1 UI, footer unchanged) — even for an invoice armed while the
rail was on. Enabled, the two-rail switcher renders with the Goblin tab
default-selected; the switcher chrome appears only when BOTH rails are
available (a single rail renders its panel directly).
- config: default flip + docs; env example + README gain the opt-in block.
- checkout: build_info gates all grin1 UI on the flag; tests for both states
(off => no rail-tab/rail-radio/panel-grin/grin1 strings; on => switcher with
rail-goblin checked).
- sqlx 0.8 -> 0.9 (workspace): its libsqlite3-sys range (<0.38) unifies with
arti's rusqlite, resolving the one-links-per-native-lib conflict that
otherwise forbids sqlx-sqlite + in-process arti in one binary. Feature map:
runtime-tokio + sqlite (bundled, as before); dynamic COLUMNS queries wrapped
in sqlx::AssertSqlSafe (compile-time composed strings).
- invoices.rs: arm_grin1_rail issues a native Grin invoice slate for every
exact-amount invoice at create time (off the async workers; best-effort) and
stores its armored I1 + slate id.
- checkout.rs: build_info surfaces the invoice slatepack (primary Grin method)
and gates the plain-send address panel behind the render gate.
- pay.html: reworked into a CSS-only two-rail switcher — 'Pay with Goblin'
(nprofile) and 'Pay with any Grin wallet' (invoice slatepack primary,
plain-send address only when the gate allows). Zero-JS meta-refresh + wordmark
header + dark theme preserved; plain QRs; no em dashes.
- style.css: rail switcher styles (hidden radios + :checked sibling combinator).
House standard: a payment is final only after GP_CONFIRMATIONS (default 10)
on-chain confirmations. Add a terminal `confirmed` invoice state after `paid`
and drive it from the existing kernel-confirmation poll.
- config: GP_CONFIRMATIONS (default 10, must be >= 1).
- invoice: additive `confirmed` status (open -> paid -> confirmed); `paid`
stays a real, backward-compatible state. mark_confirmed() + a confirmations
reader off the paying payment.
- poll: track live confirmation depth per payment; advance the paying invoice
paid -> confirmed at the threshold and enqueue a payment.confirmed webhook
(once, on the transition). Self-terminating per payment.
- APIs: GET /invoice/{id}, GET /pay/{token}/status and GET /payment/{id} now
expose status + confirmations + confirmations_required.
- checkout: received -> confirming (n of N) -> confirmed, zero-JS meta-refresh
kept live through the confirming state.
- webhook: payment.confirmed carries confirmed_height + confirmations (the woo
and medusa connectors already consume this event).
- migration 0008: payment.confirmations, invoice.confirmed_at.
- tests: threshold transition, config default 10, payload shape, API wiring.
The hosted /pay page now shows the wallet's grin1 Slatepack address (with
a QR and the exact amount) as a payment method alongside the Goblin/Nostr
option. A payer sends the amount from any Grin wallet via the Slatepack or
file method, pastes the S1 into the existing paste box, receives an S2,
and finalizes to complete the payment. Reuses the existing offline
receive_tx flow bound to the invoice token; the Nostr gift-wrap path, the
invoice matcher, and the proof/confirm logic are unchanged. No Tor
listener. The grin1 address is the wallet's stable index-0 address.
The hosted checkout page gains the GoblinPay wordmark header, and the QR
center logo defaults to a new high-contrast GoblinPay mark (dark P on
brand gold) instead of the generic goblin mark. Still overridable via
GP_QR_LOGO (url/path/off); the legacy /static/goblin-mark.svg route is
kept for operators who pinned it.
A self-hostable Grin payment server for shops, creators, and sites: show a
code, Grin lands in your wallet, with a verifiable Grin payment proof on
receive. Workspace crates (gp-core / gp-nostr / gp-server / gp-wallet /
gp-goblin-sender), a WooCommerce connector, a hosted /pay/<token> checkout,
and NIP-44 v3 gift-wrapped payment DMs carried over the Nym mixnet. All
secrets are read from the environment; none are committed.