One worked quote with a fixed deadline

Use synthetic quote Q-01 for source NGN and target USD. It sends 15,000 NGN at 1 USD per 1,500 NGN, so the recipient gets 10.00 USD. A 300 NGN fee makes the customer’s total debit 15,300 NGN. The quote is issued at 11:59:00 UTC and expires at 12:00:00 UTC. In this fixture, funding must arrive on the server before expiry to lock the quote. Funding at 11:59:59 qualifies; funding at 12:00:00 or 12:00:01 needs a new quote. These figures are invented and describe a test, not a market rate.

Bind Q-01 to the customer, source amount, target amount, currency pair, fee, rate source, and expiry. The customer confirms those stored values. The server must not recalculate them from a live rate table after consent. For a real product, use the contract’s locking event: some rules lock on acceptance, others on funding. Save that rule with the fixture before testing.

Change one approved field at a time

Change only the source amount to 14,850 NGN while keeping Q-01. Reject the changed request; it cannot inherit the old 10.00 USD target. Repeat with a different recipient, currency, and fee. Each field is part of the approved quote. A staff tool must face the same rule. A request that changes only the live rate table must leave an already locked Q-01 unchanged. Keep the original snapshot and attempted change so a reviewer can see exactly which field was checked.

Test fee and rounding separately

Keep the 15,000 NGN send amount but change the global fee schedule from 300 to 400 NGN after approval. Q-01 must still debit 15,300 NGN under its stored schedule. Next, create Q-02 for 14,850 NGN before fees: at the same synthetic rate, the target is 9.90 USD. Use fixed-decimal arithmetic or integer minor units and state the rounding rule for values below one cent. Check the customer display, stored quote, provider request, and receipt against the same result. A correct rate with an unstated fee is an incomplete assertion.

Recover delayed funding without another transfer

Let Q-01 qualify at 11:59:59, submit its transfer, then lose the response. Query or retry under the same intended operation and provider key rule; a new quote is not permission to resend a transfer with an unknown outcome. Crash after quote consumption but before the worker sends. Recovery must resume that operation once or expose an owned exception. It must not make the quote reusable for another payment.

In another run, funding arrives at 12:00:10, after expiry. Keep the received funds visible in a pending allocation record while offering a new quote or the contract’s refund path. Do not silently apply the new rate. The next acceptance needs a new quote ID and clear target amount.

Compare final settlement with the accepted quote

When the provider reports completion, compare its source debit, fee, and target credit with Q-01. If it reports 9.99 USD instead of 10.00 USD, open a mismatch exception and apply the documented customer remedy. Do not mark the payment complete only because the provider returned success. Retain quote version, acceptance time, funding time, provider reference, and final amounts. PostgreSQL’s isolation guidance supports checking concurrent local updates; OWASP’s logging guide supports retaining useful records. Neither source defines this product’s exchange-rate contract.

Primary sources

Next step

Accept a quote near expiry while funding is delayed, then check the stored rate and the amount finally sent. Read the related guide. For a review of your own system, request a security review.