sanctions block list
All checks were successful
Deploy / build-deploy (push) Successful in 2m18s

This commit is contained in:
Jorijn van der Graaf 2026-08-15 03:22:25 +02:00
commit b0666841f6
7 changed files with 139 additions and 31 deletions

View file

@ -762,10 +762,11 @@ SafeHtml RenderCheckoutForm(const Product& product,
JsonStr(Form::kNoShippingTemplate),
JsonStr(Form::kTooHeavyTemplate),
JsonStr(Form::kTooHeavyNoneTemplate));
// The destinations checkout refuses, and the sentence that says so. The
// preview has to refuse exactly where the server does — a page that quotes
// a total for an order the server will reject is worse than one that never
// quoted it.
// The destinations checkout refuses, and the sentences that say so — the
// policy list (x/xm) and the sanctions list (s/sm), each with its own
// wording. The preview has to refuse exactly where the server does — a
// page that quotes a total for an order the server will reject is worse
// than one that never quoted it.
// The most units any destination's heaviest bracket can carry, clamped to
// the parsing ceiling. With no table (the wasm fallback path) this stays at
// kMaxQuantity — that page cannot quote a total or reach checkout anyway,
@ -782,7 +783,12 @@ SafeHtml RenderCheckoutForm(const Product& product,
if (i) cc += ',';
cc += JsonStr(Money::NoSaleCountries()[i]);
}
cc += std::format(R"(],"xm":{}}})", JsonStr(Form::kNoSaleMessage));
cc += std::format(R"(],"xm":{},"s":[)", JsonStr(Form::kNoSaleMessage));
for (std::size_t i = 0; i < Money::SanctionedCountries().size(); ++i) {
if (i) cc += ',';
cc += JsonStr(Money::SanctionedCountries()[i]);
}
cc += std::format(R"(],"sm":{}}})", JsonStr(Form::kSanctionsMessage));
// The payment choice. A radio group rather than a <select> because both
// options carry a sentence the buyer should read BEFORE choosing — one
@ -830,7 +836,7 @@ SafeHtml RenderCheckoutForm(const Product& product,
// entered. A three-zone summary next to exact rates was misinformation.
R"(<p class="checkout__shipnote">Shipping is priced per country at carrier )"
R"(rates. Enter your country below and the exact total appears before )"
R"(you order. {}</p>)"
R"(you order. {} {}</p>)"
R"({})"
R"({})"
R"(<form class="form" method="post"{}{} novalidate>)"
@ -882,7 +888,7 @@ SafeHtml RenderCheckoutForm(const Product& product,
R"(<input id="f-country" name="country" type="text" autocomplete="country" )"
R"(maxlength="2" placeholder="NL" required{}>)"
R"(<p class="field__hint">Two-letter code. Decides shipping, and whether the )"
R"(price includes VAT. No US or CA — see the terms.</p>)"
R"(price includes VAT. No US or CA, and no sanctioned countries — see the terms.</p>)"
R"({})"
R"(</div>)"
R"({})"
@ -906,6 +912,7 @@ SafeHtml RenderCheckoutForm(const Product& product,
offerCrypto ? SafeHtml{}
: Raw(": iDEAL, card, or a plain bank transfer, handled by Mollie"),
Escape(Form::kNoSaleMessage),
Escape(Form::kSanctionsMessage),
CustomsNote(),
formError,
Url("action", "/shop/" + product.slug + "#buy"),
@ -973,11 +980,12 @@ export RenderedPage RenderProduct(const Product& product,
// Every destination this listing may advertise: the carrier has a rate
// for a single boxed unit, and the shop is willing to sell there.
// US and CA drop out by policy, not oversight (Money::SellsTo):
// listing a shipping rate to a country checkout refuses would publish
// an offer that cannot be accepted, and feed it to shopping crawlers
// as an invitation to buy from there. Everywhere else drops out
// because no carrier rate exists — which is now the same sentence.
// US and CA drop out by policy and the sanctioned countries by law,
// not oversight (Money::SellsTo denies both): listing a shipping rate
// to a country checkout refuses would publish an offer that cannot be
// accepted, and feed it to shopping crawlers as an invitation to buy
// from there. Everywhere else drops out because no carrier rate
// exists — which is now the same sentence.
struct FeedDest { std::string cc; std::int64_t rate; Money::Zone zone; };
std::vector<FeedDest> dests;
for (const Money::ShipRates& r : liveShipping) {
@ -2001,9 +2009,10 @@ inline constexpr std::string_view kGeoPriceHintScript =
"var qty=qe?parseInt(qe.value,10)||1:1;"
"var k=(ke&&ke.value?ke.value:\"\").replace(/\\s/g,\"\").toUpperCase();"
"if(k.length!==2||!unit||qty<1||qty>d.q){if(out)out.hidden=true;return}"
// Refused destination: say so where the total would have been, instead of
// pricing an order the server will decline.
// Refused destination — policy or sanctions: say so where the total would
// have been, instead of pricing an order the server will decline.
"if(d.x&&d.x.indexOf(k)>-1){if(out){out.textContent=d.xm;out.hidden=false}return}"
"if(d.s&&d.s.indexOf(k)>-1){if(out){out.textContent=d.sm;out.hidden=false}return}"
"var eu=ecc.indexOf(k)>-1,line=unit*qty;"
"var goods=eu?line:Math.floor((line*10000+6050)/12100);"
// No ladder for this destination: there is no price, and saying so beats