Bind approval to the payment intent

Create a pending transfer record before asking for extra proof. Include its beneficiary, amount, currency, and expiry in the server-side approval context. After the customer approves, alter one field and submit. The server should ask for a fresh approval or reject the changed request. Run the same test for a saved beneficiary, a new beneficiary, and a bulk transfer item. Check that the final ledger event matches exactly what the customer approved.

Test cases and proof

Use test accounts and test data
CaseExpected resultProof to keep
New beneficiary on new deviceRequire extra proofDecision log
Step-up passes then amount changesRequire new approvalTransfer result
Low-risk repeat transferFollow defined policy without surprise loopUser flow

Synthetic example

A customer approves a transfer to saved beneficiary A, then the request changes to new beneficiary B before commit. Bind step-up proof to beneficiary, amount, currency, and action. A proof for A must not approve B.

Evidence to keep

Save the transfer intent before step-up and the committed transfer after it. Compare beneficiary ID, amount, currency, and approval reference field by field. A matching success screen is weak proof if the ledger used different details. Repeat after the approval window ends. Use a synthetic beneficiary and test balance so no real money moves.

Use a transfer that sits just below a risk threshold and one just above it. The policy should make a predictable decision for both. Then change the amount after step-up. This checks the boundary and the binding rule without claiming any threshold is universal across banks.

The server owns the final transfer

A step-up screen can show the right details while a later API request sends different ones. Store the approved intent on the server and compare it at commit. If the amount, payee, or currency changes, require new proof or reject. A risk trigger may also change during the flow, such as a new device being enrolled. Decide whether to restart approval in that case. The final evidence is the committed ledger event matching the approved intent, not a screenshot of the prompt.

Related reading

Why these checks matter

NIST treats reauthentication and session age as part of sensitive account access. OWASP says authorization should be checked on each request. A step-up prompt is useful only if its result is bound to the transfer the customer approved. The synthetic amount-and-beneficiary swap tests that link.

Separate fresh sign-in from payment approval

Fresh authentication proves the caller recently used a factor. It does not by itself prove they approved the exact payment now submitted. Record both checks: an authentication event with method, time, and session; an approval record with intent ID, version, source, destination, amount, currency, fee, and expiry. The payment service validates the current session and the approved version at release.

Synthetic policy asks for extra proof at ₦25,000 or for a new beneficiary. Test ₦24,999 and ₦25,000 with the same saved beneficiary, then a smaller payment to a new one. Those thresholds describe only this fixture. Changing the amount after approval must fail the version check even when the new amount falls below the trigger.

Race the approval use and change

Create intent INT-33 version 1, show it, and obtain APR-33. Send two release calls concurrently. Expect one local execution identity and one provider transfer identity. Then try APR-33 against version 2 with a changed destination. A spent or mismatched approval must not authorize a second execution.

Claim approval and durable execution work in the same local transaction. Do not claim the provider call is inside that transaction. When provider response is uncertain, keep INT-33 and reconcile rather than granting fresh approval for a new reference. Test one unchanged intent as the positive control. Save factor event, displayed version, risk inputs at decision time, approval claim, provider reference, and ledger outcome. OWASP’s transaction authorization guide covers binding to critical payment details.

Source