DYNAMIC QR TESTCASES - STATUS TABLE AND HOW EACH ONE SHOULD BEHAVE Project: upi_psp_platform_v2 Precert: precert.nfinite.in (bank MER101) Last updated: 2026-11-12 Updated as testing continues. Testcases not tested yet only have a table row; their detailed section is added when we test them (we need the TSD description for that). ==================================================================================== 1. FULL TABLE (all 18 dynamic QR testcases) ==================================================================================== Test case Request / Response Expected code Status -------------- ---------------------------------------------- -------------- --------------------- MT_125 ReqHbt / Hbt 00 Not tested yet * MT_VALQRDEP ReqValQr / Global QR 00 Not tested yet ACC_IP_MT_01 ReqHbt, RespHbt / Global QR 00 Not tested yet ACC_IP_MT_02 ReqValQr, RespValQr / Global QR 00 Done (reported by you) ACC_IP_MT_03 ReqValQr, RespValQr / Global QR UG3 (U09) Done 2026-11-06 ACC_IP_MT_04 ReqValQr, RespValQr / Global QR PE Done 2026-11-12 ACC_IP_MT_05 ReqValQr, RespValQr / Global QR XW Done 2026-11-12 (2nd try) ACC_IP_01 ReqPay, RespPay / BENEFICIARY 00 Not tested yet ACC_IP_02 ReqPay, RespPay / BENEFICIARY BT Not tested yet ACC_IP_03 ReqPay, RespPay / BENEFICIARY X7 Not tested yet ACC_IP_04 ReqPay, RespPay / BENEFICIARY YH Not tested yet ACC_IP_05 ReqPay Credit, RespPay Credit / BENEFICIARY IV Not tested yet ACC_IP_06 ReqPay Credit, RespPay Credit / BENEFICIARY PE Not tested yet ACC_IP_07 ReqPay Credit, RespPay Credit / BENEFICIARY 00 Not tested yet ACC_IP_08 ReqPay Credit, RespPay Credit / BENEFICIARY X7 Not tested yet ACC_IP_09 ReqPay Credit, RespPay Credit / BENEFICIARY 00 Not tested yet ACC_IP_10 ReqPay Credit, RespPay Credit / BENEFICIARY CS Not tested yet ACC_IP_MT_06 ReqValQr, RespValQr / Global QR 00 (QM1 by iUPI) Done 2026-11-12 (1st try, happy flow) * MT_125: the RespHbt fix (refId) was verified against precert earlier (no error in NPCI's Ack), but it has not been run as a testcase in this round. Static QR testcases (17): table to be added when you send the list. ==================================================================================== 2. BASICS (who is who, and what "Success" means) ==================================================================================== - Precert plays NPCI/UPI and the payer PSP. It SENDS us requests and CHECKS our answers. - WE are the IP (international partner), acting as the payee PSP. - QR validation conversation: Precert -> us : ReqValQr ("validate this scanned QR") us -> precert : Ack (immediate "got it", nothing else) us -> precert : RespValQr (our real answer: SUCCESS, FAILURE + code, or nothing) - Precert's "Execution Result: Success" means "we behaved as the testcase expects". For failure testcases (expected result FAILURE) that is what we want. QR to give precert: VALID QR = QRexpire inside the QR string is in the future. Our QRs last 30 minutes, so generate a fresh one right before the test. EXPIRED QR = QRexpire is in the past. Reuse an old QR (look for QRexpire=... in the string) or wait 30 minutes. The QR box in precert must hold a real "upiGlobal://pay?..." string and nothing else. Before every test: git checkout , restart the server, run the testcase in precert, then check the log lines listed. ==================================================================================== 2B. QR PAYLOAD: WHAT TO CHANGE FOR EACH TESTCASE (and when) ==================================================================================== The QR payload is the "upiGlobal://pay?ver=01&mode=16&...&sign=..." string you paste into precert's QR box. Precert sends it to us unchanged, and our answer depends on it. Rule: change ONLY the one field listed. Leave every other field and the sign as they are. Testcase Change in the QR payload When / how Tested? ------------- ---------------------------------------------- ------------------------------------------- ------------------- ACC_IP_MT_02 NOTHING. Use the QR as generated. The QR must be fresh: QRexpire still in the Reported done future (QRs last 30 min from QRts). ACC_IP_MT_03 NOTHING. Use the QR as generated. The QR must be fresh (QRexpire in the Done future). The test is about our silence, not the QR. ACC_IP_MT_04 Make the QR EXPIRED: QRexpire must be in the Use an old QR from an earlier run (check Done (old QR from past. QRexpire=... in the string) or wait until 5 Nov) QRexpire has passed. Do not edit anything else. ACC_IP_MT_05 Change the Terminal ID: mtid=T001 -> mtid=T999 Start from a FRESH QR (QRexpire in the Done (T999) (any terminal that does not belong to the future), because an expired QR would give merchant). Keep pa=restaurant@mercury. PE before XW. Edit only the mtid value. ACC_IP_MT_06 Change the MCC: mc=5812 -> mc=7995 (the Start from a FRESH QR (QRexpire in the Done (7995) invalid code named in the testcase). Edit only future). Keep pa and mtid correct so the the mc value. merchant is found. Where the fields are in the string (example, shortened): ...&pa=restaurant@mercury&...&mtid=T001&...&QRexpire=2026-11-12T13:11:14+05:30&QRts=...&sign=... pa = merchant VPA mtid = terminal ID QRexpire = when the QR stops being valid (compared with the current time) Notes - You do not need to re-sign the QR (sign) after editing mtid; neither we nor precert rejected it in the MT_05 run. - If the QR box holds anything other than a real "upiGlobal://pay?..." string (for example a pasted git command), we answer errCode 05 "Invalid merchant QR" and the test stays Pending. - Time is IST in the QR (+05:30). Compare QRexpire with the current IST time. Other fields our code checks (from the code, NOT tested as testcases yet; shown so you know what each one would trigger): pa (VPA) not in our merchant table -> answer ZR "Merchant not found" mc (MCC) missing / not 4 digits / blocked (7995) / different from the merchant's MCC -> answer QM1 Add these to the table above when a testcase needs them. ==================================================================================== 2C. THE TESTCASES DONE SO FAR, EXPLAINED SIMPLY (MT_02, MT_03, MT_04, MT_05) ==================================================================================== You are working on the International Partner / IMA side (the payee PSP). These four cases are all about QR VALIDATION, which happens BEFORE any money moves. Nothing is debited yet. Common starting flow (same for all four): Customer scans QR ↓ Payer PSP sends ReqValQr to UPI ↓ UPI forwards the ReqValQr to our Mercury PSP ↓ We send an ACK straight away ↓ What we do next changes with the testcase ------------------------------------------------------------------------------------ ACC_IP_MT_02 - QR is fine, validation succeeds ------------------------------------------------------------------------------------ Scenario The customer scans a valid QR and we confirm it. ReqValQr arrives (valid QR) ↓ We send ACK ↓ We check the QR and the merchant: all fine ↓ We send RespValQr SUCCESS (QR details, merchant details, payable amount) ↓ UPI passes SUCCESS to the payer PSP What our PSP must do Validate, then send RespValQr SUCCESS with the QR, merchant and amount tags. We do respond. We do not stay silent. Final result QR validated. Payer PSP can go on to the payment. Easy way to remember ACC_IP_MT_02 = everything fine -> answer SUCCESS ------------------------------------------------------------------------------------ ACC_IP_MT_03 - Network problem at our side, we stay silent ------------------------------------------------------------------------------------ Scenario The customer scans a valid QR, but the IP cannot answer (network issue). ReqValQr arrives (valid QR) ↓ We send ACK ↓ We send NO RespValQr ↓ UPI waits, gets nothing ↓ UPI declines the transaction itself with UG3 / U09 What our PSP must do Send only the ACK. Do not send RespValQr. (This needs extra code: branch dynamic/acc_ip_mt_03.) The UG3/U09 decline is produced by UPI, not by us. Final result Declined with UG3 / U09. Precert shows Success because the expected result is FAILURE. Easy way to remember ACC_IP_MT_03 = say nothing after the ACK -> UPI declines ------------------------------------------------------------------------------------ ACC_IP_MT_04 - Expired QR ------------------------------------------------------------------------------------ Scenario The customer scans a QR that has already expired. ReqValQr arrives (QRexpire is in the past) ↓ We send ACK ↓ We check QR expiry: expired ↓ We send RespValQr FAILURE with PE ↓ UPI passes FAILURE PE to the payer PSP What our PSP must do Check QRexpire. If now is later than QRexpire, answer FAILURE + PE. We do respond. No extra code is needed. Final result Declined with PE ("payload expired"). Not to be confused with ACC_IP_06 gives the same PE answer, but later, at the CREDIT stage (after the customer was already debited, so UPI then reverses). ACC_IP_MT_04 is at QR validation, before any debit. Easy way to remember ACC_IP_MT_04 = expired QR -> answer PE failure ------------------------------------------------------------------------------------ ACC_IP_MT_05 - Wrong Terminal ID ------------------------------------------------------------------------------------ Scenario The customer scans a QR whose terminal ID (mtid) does not belong to the merchant. ReqValQr arrives (QR has a wrong mtid) ↓ We send ACK ↓ We look up the merchant by VPA + terminal: VPA exists, terminal does not match ↓ We send RespValQr FAILURE with XW ↓ UPI passes FAILURE XW to the payer PSP What our PSP must do Compare the terminal ID with the merchant's. If it does not match, answer FAILURE + XW ("Terminal ID mismatch"). We do respond. No extra code is needed. Final result Declined with XW. Easy way to remember ACC_IP_MT_05 = wrong terminal -> answer XW failure ------------------------------------------------------------------------------------ Side by side ------------------------------------------------------------------------------------ Testcase QR that arrives ACK RespValQr we send What happens next ------------- ------------------ ------ -------------------- --------------------------- ACC_IP_MT_02 Valid Yes SUCCESS Validation succeeds ACC_IP_MT_03 Valid Yes NOTHING (silent) UPI declines UG3 / U09 ACC_IP_MT_04 Expired Yes FAILURE, PE Declined PE ACC_IP_MT_05 Wrong terminal ID Yes FAILURE, XW Declined XW Memory trick: 02 = success, 03 = silent, 04 = expired, 05 = wrong terminal ------------------------------------------------------------------------------------ FINAL TABLE: HOW TO TEST EACH ONE ------------------------------------------------------------------------------------ Testcase Branch to be on QR you give precert We respond Test passes when ------------- ---------------------------- ------------------------------- ------------------ ------------------------- ACC_IP_MT_02 fix/tsd-v16-compliance-26oct Fresh QR, nothing changed RespValQr SUCCESS Precert: Success ACC_IP_MT_03 dynamic/acc_ip_mt_03 Fresh QR, nothing changed ACK only Precert: Success (log: "NOT sending RespValQr") ACC_IP_MT_04 fix/tsd-v16-compliance-26oct EXPIRED QR (QRexpire in the RespValQr Precert: Success, code PE past, e.g. an old QR) FAILURE PE ACC_IP_MT_05 fix/tsd-v16-compliance-26oct Fresh QR with mtid changed RespValQr Precert: Success, code XW (T001 -> T999) FAILURE XW Always: switch to the branch, restart the server, then run the testcase in precert. ==================================================================================== 3. HOW EACH TESTED CASE SHOULD BEHAVE ==================================================================================== ------------------------------------------------------------------------------------ ACC_IP_MT_02 - ReqValQr / RespValQr - expected 00 (success) ------------------------------------------------------------------------------------ What it is: A customer scans a valid international QR and it validates fine. What comes from them: ReqValQr with a valid QR. Do we respond? YES - ack first, then a full RespValQr. What we respond: RespValQr result="SUCCESS", with the details, the details and the payable (dynamic QR must include the amount). When we stay silent: Never in this case. Branch: fix/tsd-v16-compliance-26oct (happy flow, no extra code). What you give precert: A VALID (not expired) QR. How to know it worked: Our log: "QR is still valid - proceeding with normal flow", then a RespValQr with result="SUCCESS" and an line. Precert: Execution Result = Success. What happened: You reported it passed. I have not seen its logs and it has not been re-run since the branches were reorganised. Re-run once. ------------------------------------------------------------------------------------ ACC_IP_MT_03 - ReqValQr / RespValQr - expected UG3 (U09) ------------------------------------------------------------------------------------ What it is: A customer scans a valid QR but the IP (us) cannot answer because of a network problem. UPI gets nothing and declines with UG3/U09. What comes from them: ReqValQr with a valid QR and the note ACC_IP_MT_03. Do we respond? ONLY the ack. We stay SILENT after that. What we respond: Ack only. We send NO RespValQr at all. When we stay silent: Always, for this testcase. The UG3/U09 decline is produced by UPI/precert when our answer never arrives, not by us. Branch: dynamic/acc_ip_mt_03 (extra code: if note = ACC_IP_MT_03, ack only). On the happy-flow branch we WOULD send a RespValQr and the test would not pass. What you give precert: A VALID (not expired) QR. How to know it worked: Our log: "ACC_IP_MT_03: ACK only for ReqValQr ... NOT sending RespValQr", the ack is sent, and there is NO "[RespValQr] Sending to URL" line. About 60 s later a "Bandit.HTTPError Body read timeout" appears; it is harmless. Precert: Execution Result = Success (expected result is FAILURE). What happened: First we sent a full SUCCESS RespValQr, precert answered "TIMEOUT" and the result stayed Pending. After we stopped sending RespValQr it passed (2026-11-06). Same code is now committed on this branch; re-run once there. ------------------------------------------------------------------------------------ ACC_IP_MT_04 - ReqValQr / RespValQr - expected PE ------------------------------------------------------------------------------------ What it is: A customer scans a QR that has already expired. We refuse it with PE ("payload expired"). What comes from them: ReqValQr carrying whatever QR YOU put in the precert QR box. Precert does not expire the QR for us. Do we respond? YES - ack first, then a RespValQr. What we respond: RespValQr result="FAILURE" errCode="PE" (the request values are echoed back). When we stay silent: Never in this case. How we decide: We read QRexpire from the QR. If the current time is later than QRexpire, we answer PE. If the QR is still valid, we would answer SUCCESS instead, so the QR you give decides the result. Branch: fix/tsd-v16-compliance-26oct (happy flow, no extra code). What you give precert: An EXPIRED QR (QRexpire in the past), e.g. one with QRexpire=2026-11-05T13:10:14+05:30 or older. How to know it worked: Our log: a positive "Time diff: ... seconds", "expired_by_window=true", "QR payload expired (PE) - scheduling async PE response", "Sent PE error response to NPCI". Precert: Execution Result = Success, Response Code = PE. What happened: 1) Precert sent a FRESH QR. I forced a PE in code. That was wrong (I did not ask first; the testcase says you give an expired QR). That change was removed. 2) A git command was in the QR box by mistake: we answered errCode 05 "Invalid merchant QR", result stayed Pending. 3) An expired QR (from 5 Nov) was used: we answered PE with no special code and precert showed Success. This is the real result. Open observation: In the PE answer merchantInstCode is empty and Ownership shows PARTNERSHIP (the success answer shows MER123 and PRIVATE). Precert accepted it; not changed. ------------------------------------------------------------------------------------ ACC_IP_MT_05 - ReqValQr / RespValQr - expected XW (Terminal ID mismatch) ------------------------------------------------------------------------------------ What it is: A customer scans an international QR whose Terminal ID (mtid) is wrong. We decline with XW ("Terminal ID mismatch"). TSD steps: Customer scans QR -> payer PSP sends ReqValQr with a QR containing an incorrect Terminal ID -> UPI forwards it -> the IP/sponsor bank answers FAILURE code XW -> UPI sends FAILURE XW to the payer PSP. What comes from them: ReqValQr carrying whatever QR YOU put in the precert QR box. Precert does not break the terminal ID for us. Do we respond? YES - ack first, then a RespValQr. What we respond: RespValQr result="FAILURE" errCode="XW". When we stay silent: Never in this case. How we decide: We look up the merchant by the QR's pa (VPA) and mtid (terminal). If the VPA exists but that terminal ID does not belong to it, we answer XW. If the terminal ID is correct, we answer SUCCESS, so the QR you give decides the result. Branch: fix/tsd-v16-compliance-26oct (happy flow, no extra code; the XW answer for a terminal mismatch is already in the code). What you give precert: A FRESH (not expired) QR whose mtid is WRONG. Take a fresh QR and change only mtid, e.g. mtid=T001 -> mtid=T999 (any terminal that does not exist for restaurant@mercury). Keep pa=restaurant@mercury so the merchant is found but the terminal does not match. Do NOT use an expired QR: the expiry check comes first and would answer PE instead of XW. How to know it worked: Our log: "TID mismatch: VPA restaurant@mercury exists but TID T999 doesn't match", then a RespValQr with result="FAILURE" errCode="XW". Precert: Execution Result = Success, Response Code = XW. What happened: First try (2026-11-12 12:37 IST): the QR given had the CORRECT terminal (mtid=T001). So we validated it and answered SUCCESS; precert showed Failed (it expected FAILURE / XW). No code problem; the input was wrong. Second try (2026-11-12 12:43 IST): a fresh QR with mtid=T999 (other fields unchanged, old sign left as is). Our log showed "TID mismatch: VPA restaurant@mercury exists but TID T999 doesn't match", then we sent RespValQr result="FAILURE" errCode="XW". Precert's ack was clean and the result was Success with Response Code XW. PASSED, with no code change. Notes: - The QR's sign was not re-signed after editing mtid; neither we nor precert rejected it. - As in the MT_04 PE answer, merchantInstCode was empty in the XW answer. Precert accepted it both times; not changed. ------------------------------------------------------------------------------------ ACC_IP_MT_06 - ReqValQr / RespValQr - QM1 (Invalid MCC for the payee) ------------------------------------------------------------------------------------ What it is: A customer scans an international QR whose merchant category code (mc) is invalid (the testcase says to use mc=7995). The IP's response carries that invalid MCC; iUPI then DECLINES with QM1 and sends the IP a negative ack "Invalid MCC for the payee". TSD steps: Payer PSP sends ReqValQr -> UPI forwards it -> the IP/sponsor bank sends a response with an invalid MCC -> iUPI declines, tells the payer PSP QM1 and sends a negative ack to the IP. Testcase note: "Execute the testcase with Invalid Merchant Code: 7995 in QR payload." What comes from them: ReqValQr carrying the QR YOU supply, with mc=7995. Do we respond? YES (as the IP). By the testcase wording, our RespValQr is the normal answer that carries the QR's invalid MCC (precert lists the expected code as 00). The decline with QM1 is done by iUPI / precert, and it comes back to us as a NEGATIVE ACK. When we stay silent: Never in this case. Branch: Start on fix/tsd-v16-compliance-26oct (happy flow) and see what happens. Do not change code before the first run. What you give precert: A FRESH QR with mc=5812 changed to mc=7995 (only that value). How to know it worked: Precert: Execution Result = Success. In our log, the ack that precert returns to our RespValQr should be a negative ack (an err / description like "Invalid MCC for the payee"). WHAT HAPPENED (2026-11-12, happy-flow branch, NO code change needed): Given QR: fresh, mc=7995, pa/mtid correct. ReqValQr arrived, we sent the ACK. Our own QM1 check fired ("mc 7995 is in the blocked MCC list") and we answered RespValQr FAILURE errCode="QM1" (payee code/subCode = 7995). Precert's ack to that RespValQr: err="Invalid Mcc code.." (this is the "negative ack"). Precert: Execution Result = Success, Expected = Success, Response Code 00. So our own QM1 check is accepted; no dynamic/acc_ip_mt_06 branch is needed. Status: DONE. Passes on the happy-flow branch with a fresh QR, mc=7995. ==================================================================================== 4. NEXT (add a section like the ones above as each testcase is tested) ==================================================================================== For every new testcase we need its TSD steps/description. Then we write the same blocks: what comes from them, do we respond or stay silent, what we respond, branch, what you give precert, how to know it worked, what happened.