crypto payment message
All checks were successful
Deploy / build-deploy (push) Successful in 3m13s

This commit is contained in:
Jorijn van der Graaf 2026-08-20 02:50:28 +02:00
commit 1a38cfe5d7
7 changed files with 169 additions and 17 deletions

View file

@ -143,14 +143,16 @@ void OpenShopLifecycle(TestServer& srv) {
}
}
Check(recorded, "the crypto choice is what the ledger records");
// The order page has to promise what is actually behind the button
// The order page has to promise what is actually behind the choice
// the bank copy on a crypto order would send someone looking for
// iDEAL. (The shell suite pinned 'Lightning' here, CoinGate-era copy
// that had already left the codebase — this is the check that caught
// the drift when the port first ran against an open shop.)
// iDEAL. The fake crypto slot renders instructions like the real
// EURC rail, so the probe is the instructions block. (The shell
// suite pinned 'Lightning' here, CoinGate-era copy that had already
// left the codebase — this is the check that caught the drift when
// the port first ran against an open shop.)
const std::string page = srv.Body(std::format("/order/{}", tokenCrypto));
Check(page.find("completes your crypto payment") != std::string::npos,
"the crypto order page describes the crypto payment");
Check(page.find("Pay with EURC") != std::string::npos,
"the crypto order page renders the payment instructions");
Check(page.find("iDEAL") == std::string::npos,
"the crypto order page does not promise iDEAL");
}
@ -546,6 +548,37 @@ void DonationLifecycle(TestServer& srv) {
"the amount refusal names the bounds");
}
// ── the in-flight state ───────────────────────────────────────────
// Between the wallet's "success" and finality the rail reports the money
// as SEEN but not yet evidence, and the page must say so — the first
// live EURC payment proved that a buyer staring at "awaiting payment"
// after their wallet said done is a support email in the making. The
// fake rail's "<marker>.seen" file is that state's test handle.
{
const std::string tokenSeen =
TokenOf(srv.Post("/shop/donation", "amount=2&pay=crypto"));
Check(!tokenSeen.empty(), "a crypto donation goes through");
if (!tokenSeen.empty()) {
const std::string seenPath = std::format("/order/{}", tokenSeen);
// Before anything is seen: the awaiting page owns the timing
// expectation, so the gap between wallet and shop is explained
// even to a buyer who never reloads.
srv.BodyHas(seenPath, "typically follows in 10 to 25 minutes",
"the awaiting page states the confirmation timing");
WriteFile(std::filesystem::path(
srv.Orders().string() + ".fake-paid.seen"), "");
const std::string page =
srv.WaitForBody(seenPath, "Your payment is on its way");
Check(page.find("Your payment is on its way") != std::string::npos,
"an in-flight payment is acknowledged on the order page");
Check(page.find("awaiting payment") != std::string::npos,
"an in-flight payment is still awaiting, not paid");
std::error_code ec;
std::filesystem::remove(std::filesystem::path(
srv.Orders().string() + ".fake-paid.seen"), ec);
}
}
// ── a donation with no email at all ───────────────────────────────
// Identity is optional: the capability URL is the receipt.
const auto created = srv.Post("/shop/donation", "amount=25");