This commit is contained in:
parent
7098ac75cb
commit
df91762271
29 changed files with 3079 additions and 838 deletions
|
|
@ -164,7 +164,8 @@ HTTPResponse RenderPage(std::string_view target) {
|
|||
const ShippingTable ship = CurrentShippingTable();
|
||||
const Views::RenderedPage page =
|
||||
Views::RenderProduct(*product, gContent.rates, ship.perCountry,
|
||||
{}, {}, CryptoPaymentAvailable());
|
||||
{}, {}, CryptoPaymentAvailable(),
|
||||
BankPaymentAvailable());
|
||||
HTTPResponse res;
|
||||
res.status = std::to_string(page.status);
|
||||
ApplyPageHeaders(res, "text/html; charset=utf-8",
|
||||
|
|
@ -266,7 +267,7 @@ HTTPResponse RenderPage(std::string_view target) {
|
|||
// ArrivalPollAllowed. A reload past that renders from the ledger and
|
||||
// lets the reconciler do its job, which is the whole point of having
|
||||
// one. The interval is the ORDER'S rail's, so a crypto order is not
|
||||
// paced by Mollie's cadence or the other way round.
|
||||
// paced by the bank rail's cadence or the other way round.
|
||||
if (order->status == "awaiting_payment") {
|
||||
if (const PaymentRail* rail = gRails.For(order->payChoice);
|
||||
rail && ArrivalPollAllowed(order->token, rail->PollInterval())) {
|
||||
|
|
@ -314,6 +315,15 @@ HTTPResponse RenderPage(std::string_view target) {
|
|||
OrderCryptoPay pay;
|
||||
pay.address = instr->address;
|
||||
pay.amount = instr->amount;
|
||||
// Set only by bank-transfer rails, and what the renderer
|
||||
// switches on. The structured reference is derived from the
|
||||
// order token rather than carried by the rail, so the two
|
||||
// forms the page prints cannot disagree with each other.
|
||||
pay.beneficiary = instr->beneficiary;
|
||||
pay.bic = instr->bic;
|
||||
if (!instr->beneficiary.empty()) {
|
||||
pay.structuredReference = CreditorReferenceFromToken(order->token);
|
||||
}
|
||||
const std::int64_t now =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
|
@ -650,7 +660,7 @@ HTTPResponse HandleCheckout(const HTTPRequest& req, const Route& route) {
|
|||
ApplyPageHeaders(res, "text/html; charset=utf-8", false, true);
|
||||
const Views::RenderedPage page = Views::RenderProduct(
|
||||
*product, gContent.rates, shipTable.perCountry, errors, prev,
|
||||
CryptoPaymentAvailable());
|
||||
CryptoPaymentAvailable(), BankPaymentAvailable());
|
||||
res.body = Views::RenderDocument(page, Views::RenderNav(RouteKind::Product),
|
||||
Views::RenderFooter(), {}, gCssHref);
|
||||
return res;
|
||||
|
|
@ -861,7 +871,7 @@ HTTPResponse HandleCheckout(const HTTPRequest& req, const Route& route) {
|
|||
|
||||
// Straight to the payment page — the buyer clicked "buy", not "read an
|
||||
// interim status page". The order page stays the receipt/status URL that
|
||||
// Mollie redirects back to afterwards.
|
||||
// a hosted provider would redirect back to afterwards.
|
||||
res.status = "303";
|
||||
res.headers["location"] = order.payUrl;
|
||||
res.headers["cache-control"] = "no-store";
|
||||
|
|
@ -874,7 +884,7 @@ HTTPResponse HandleCheckout(const HTTPRequest& req, const Route& route) {
|
|||
// The gate on the order page's arrival poll.
|
||||
//
|
||||
// Rendering /order/<token> asks the provider whether the payment landed, so a
|
||||
// buyer redirected back from Mollie sees "paid" immediately instead of an
|
||||
// buyer returning to this page sees "paid" immediately instead of an
|
||||
// alarming "awaiting payment" that flips ten seconds later. That is a good
|
||||
// thing to do once. The problem was that it happened on EVERY render: an
|
||||
// outbound HTTPS round trip, on the request thread, holding the rail's mutex,
|
||||
|
|
@ -882,7 +892,7 @@ HTTPResponse HandleCheckout(const HTTPRequest& req, const Route& route) {
|
|||
//
|
||||
// The hole that closes: an attacker places one order — their own, so no token
|
||||
// guessing is involved — and then reloads it in a loop. Every reload spent a
|
||||
// live Mollie API call against the shop's key, and because CreateLink shares
|
||||
// live call against the shop's account, and because CreateLink shares
|
||||
// that same mutex, real buyers' checkouts queued behind the flood. The
|
||||
// listener is thread-per-connection with no cap, so the blocked threads piled
|
||||
// up as well.
|
||||
|
|
@ -1056,6 +1066,8 @@ void ConfigurePayments(PaymentRails rails, std::string redirectBase) {
|
|||
|
||||
bool CryptoPaymentAvailable() { return gRails.crypto != nullptr; }
|
||||
|
||||
bool BankPaymentAvailable() { return gRails.bank != nullptr; }
|
||||
|
||||
namespace {
|
||||
|
||||
// The reconciler: the ONLY thing that moves an order to paid.
|
||||
|
|
|
|||
Loading…
Reference in a new issue