diff --git a/README.md b/README.md
index 9c1a938..9d68e85 100644
--- a/README.md
+++ b/README.md
@@ -17,9 +17,21 @@ carries the full merchant surface:
invoice automatically.
- **Hosted checkout:** a zero-JS `/pay/` page (server-rendered
Askama + one CSS file + a server-generated QR SVG at ECC level H with an
- optional GoblinPay-mark center logo), live status via ``,
- and a manual slatepack fallback (paste S1 -> offline `receive_tx` -> copy the
- S2 back) on every page. The same renderer serves embedded and hosted use.
+ optional GoblinPay-mark center logo) with live status via
+ ``. It offers two first-class ways to pay:
+ - **Goblin Wallet (Nostr):** scan the `nprofile` QR (or copy it) and the
+ payment auto-receives over Nostr.
+ - **Slatepack (`grin1`):** pay from any Grin wallet, no Nostr needed. The
+ page shows the wallet's stable index-0 Slatepack address (`grin1...`) plus
+ its QR and the exact amount to send. The payer sends that amount to the
+ address using their wallet's Slatepack/file method, pastes the resulting S1
+ into the page (offline `receive_tx`), then copies the returned S2 back into
+ their wallet to finalize and broadcast it. There is no Tor listener; the
+ `grin1` address is stable and reused across invoices, and the existing
+ invoice matcher and on-chain confirmation handle the received payment like
+ any other. The Slatepack option only appears when a wallet is loaded.
+
+ The same renderer serves embedded and hosted use.
- **Per-user endpubs:** an admin assigns one receiving identity per user
(a derived child keyed by `(user_id, epoch)`; only public keys and the
rotation clock are stored, never private keys), with optional rolling rotation
diff --git a/crates/gp-server/src/checkout.rs b/crates/gp-server/src/checkout.rs
index ba691fe..d860038 100644
--- a/crates/gp-server/src/checkout.rs
+++ b/crates/gp-server/src/checkout.rs
@@ -1,13 +1,15 @@
//! The hosted, zero-JS checkout: the `/pay/` page (shared renderer for
-//! embedded and hosted use), its live status, and the manual-slatepack
-//! fallback.
+//! embedded and hosted use), its live status, and the Slatepack receive flow.
//!
-//! The page shows the amount, a server-generated QR SVG of the recipient
-//! `nprofile`, the `nprofile`/`npub` strings, live status via a
-//! `` while open, and a `
{% else %}
Waiting for payment…
-
{{ info.qr_svg|safe }}
-
Scan with your Goblin Wallet, or copy the address below.
-
-
{{ info.nprofile }}
+
+
Pay with Goblin Wallet
+
{{ info.qr_svg|safe }}
+
Scan with your Goblin Wallet, or copy the address below.
+
+
{{ info.nprofile }}
+
-
- Can't scan? Pay manually with a slatepack
- {% if wallet_available %}
+ {% if let Some(grin1) = info.slatepack_address %}
+
+
Pay by Slatepack (grin1)
+
Pay from any Grin wallet, no Nostr needed. Send {{ info.amount_display }} to the address below.
+ {% if let Some(grin1_qr) = info.slatepack_qr_svg %}
{{ grin1_qr|safe }}
{% endif %}
+
+
{{ grin1 }}
-
In your wallet, send {{ info.amount_display }} using the manual / slatepack option.
-
Paste the generated S1 slatepack below and submit.
-
Copy the response slatepack we return, back into your wallet to finalize and post.
+
In your Grin wallet, send {{ info.amount_display }} to this address using the Slatepack / file method, then paste the Slatepack it produces below.
+
The pasted S1 Slatepack is received here and a response Slatepack is returned.
+
Paste that response back into your wallet to finalize and broadcast it, which completes the payment.
- {% else %}
-
Manual receive is unavailable on this instance.
- {% endif %}
-
+
+ {% endif %}
{% endif %}
{% if let Some(memo) = info.memo %}