/* catcrafts.net Copyright (C) 2026 Catcrafts The source code of this website is made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works. */ // The wire-amount parser (Mollie quotes amounts as strings) and the Mollie // payment parser — the line between "the buyer paid" and "the provider said // something we did not understand". Both refuse rather than guess. import std; import Catcrafts.Shared; import Catcrafts.Server; using namespace Catcrafts; namespace { int failures = 0; void Check(bool ok, std::string_view what, std::string_view got = {}) { if (ok) return; ++failures; std::println(std::cerr, "FAIL: {}{}{}", what, got.empty() ? "" : " got: ", got); } } // namespace int main() { // ── the wire-amount parser ──────────────────────────────────────── using Server::ParseAmountToMinor; Check(ParseAmountToMinor("614.00") == 61400, "amount: normal"); Check(ParseAmountToMinor("614") == 61400, "amount: no fraction"); Check(ParseAmountToMinor("614.5") == 61450, "amount: one fraction digit"); Check(ParseAmountToMinor("0.01") == 1, "amount: one cent"); Check(!ParseAmountToMinor("614.005").has_value(), "amount: three decimals rejected"); Check(!ParseAmountToMinor("-1.00").has_value(), "amount: negative rejected"); Check(!ParseAmountToMinor("+1.00").has_value(), "amount: sign rejected"); Check(!ParseAmountToMinor("1e3").has_value(), "amount: exponent rejected"); Check(!ParseAmountToMinor("1.").has_value(), "amount: trailing dot rejected"); Check(!ParseAmountToMinor(".5").has_value(), "amount: bare fraction rejected"); Check(!ParseAmountToMinor("").has_value(), "amount: empty rejected"); Check(!ParseAmountToMinor("1 000.00").has_value(), "amount: separator rejected"); // ── the Mollie payment parser ───────────────────────────────────── { const auto p1 = Server::ParseMolliePayment(R"({ "resource":"payment","id":"tr_7UhSN1zuXS","status":"open","method":null, "amount":{"value":"578.30","currency":"EUR"}, "_links":{"checkout":{"href":"https://www.mollie.com/checkout/select-method/7UhSN1zuXS","type":"text/html"}}})"); Check(p1.has_value(), "mollie: open payment parses"); if (p1) { Check(p1->id == "tr_7UhSN1zuXS", "mollie: id"); Check(p1->status == "open", "mollie: status"); Check(p1->amountMinor == 57830, "mollie: amount to cents"); Check(p1->checkoutUrl == "https://www.mollie.com/checkout/select-method/7UhSN1zuXS", "mollie: checkout link"); Check(p1->method.empty(), "mollie: null method is empty"); } const auto p2 = Server::ParseMolliePayment(R"({ "id":"tr_x","status":"paid","method":"ideal", "amount":{"value":"578.30","currency":"EUR"},"_links":{}})"); Check(p2 && p2->status == "paid" && p2->method == "ideal", "mollie: paid payment carries the method"); const auto p3 = Server::ParseMolliePayment(R"({ "id":"tr_y","status":"paid","amount":{"value":"578.30","currency":"USD"}})"); Check(p3 && p3->amountMinor == 0, "mollie: non-EUR amount refuses to count"); Check(!Server::ParseMolliePayment("garbage").has_value(), "mollie: malformed payload rejected"); Check(!Server::ParseMolliePayment(R"({"status":"open"})").has_value(), "mollie: missing id rejected"); } if (failures != 0) { std::println(std::cerr, "{} check(s) failed", failures); return 1; } return 0; }