{
  "info": {
    "name": "Amazon Pay Offline APIs",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "description": "Collection for Amazon Pay Offline APIs - Generate QR, Status Check, and Cancel"
  },
  "variable": [
    { "key": "hostname", "value": "amazonpay.amazon.in", "description": "Amazon Pay API hostname" },
    { "key": "mid", "value": "", "description": "Merchant ID provided by Amazon Pay" },
    { "key": "store_id", "value": "", "description": "Merchant store identifier" },
    { "key": "access_key", "value": "", "description": "Access key for API authentication" },
    { "key": "secret_key", "value": "", "description": "Secret key used for signing requests" },
    { "key": "last_charge_id", "value": "", "description": "Auto-populated orderId from the last Generate QR request" },
    { "key": "last_refund_id", "value": "", "description": "Auto-populated refundId from the last Create Refund request" }
  ],
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "const CryptoJS = require('crypto-js');",
          "",
          "// Generate x-amz-date",
          "const now = new Date();",
          "const pad = (n) => n.toString().padStart(2, '0');",
          "const amzDate = now.getUTCFullYear().toString() + pad(now.getUTCMonth() + 1) + pad(now.getUTCDate()) + 'T' + pad(now.getUTCHours()) + pad(now.getUTCMinutes()) + pad(now.getUTCSeconds()) + 'Z';",
          "const currentDate = amzDate.split('T')[0];",
          "",
          "// Get credentials from collection variables",
          "const secretKey = pm.variables.get('secret_key');",
          "const accessKey = pm.variables.get('access_key');",
          "const mid = pm.variables.get('mid');",
          "const hostname = pm.variables.get('hostname');",
          "",
          "if (!secretKey || !accessKey || !mid) {",
          "    console.warn('Missing credentials. Set mid, secret_key, and access_key in collection variables.');",
          "    return;",
          "}",
          "",
          "// Auto-generate unique IDs based on request name",
          "const requestName = pm.info.requestName;",
          "const randomId = CryptoJS.lib.WordArray.random(16).toString();",
          "if (requestName === '1. Generate QR') {",
          "    const chargeId = 'Charge_' + randomId.substring(0, 8);",
          "    pm.variables.set('charge_id', chargeId);",
          "    pm.collectionVariables.set('last_charge_id', chargeId);",
          "    // Generate expiryTimestamp (2 minutes from now)",
          "    const exp = new Date(now.getTime() + 120000);",
          "    const expiryTimestamp = exp.getUTCFullYear() + '-' + pad(exp.getUTCMonth()+1) + '-' + pad(exp.getUTCDate()) + ' ' + pad(exp.getUTCHours()) + ':' + pad(exp.getUTCMinutes()) + ':' + pad(exp.getUTCSeconds());",
          "    pm.variables.set('expiry_timestamp', expiryTimestamp);",
          "    console.log('charge_id: ' + chargeId);",
          "} else if (requestName === '4. Create Refund') {",
          "    const refundId = 'AMZ-refund-' + randomId.substring(0, 21);",
          "    pm.variables.set('refund_id', refundId);",
          "    pm.collectionVariables.set('last_refund_id', refundId);",
          "    console.log('refund_id: ' + refundId);",
          "}",
          "",
          "// Utility functions",
          "function hash(data) { return CryptoJS.SHA384(data).toString(CryptoJS.enc.Hex); }",
          "function hmacSha384(data, key) { return CryptoJS.HmacSHA384(data, key); }",
          "",
          "// Headers for signing",
          "const header = {",
          "    'x-amz-client-id': mid,",
          "    'x-amz-source': 'Server',",
          "    'x-amz-user-ip': '198.0.0.1',",
          "    'x-amz-user-agent': 'Postman',",
          "    'x-amz-algorithm': 'AWS4-HMAC-SHA384',",
          "    'x-amz-date': amzDate,",
          "    'x-amz-expires': '900'",
          "};",
          "",
          "// Extract URI path",
          "const urlObj = pm.request.url;",
          "const uri = '/' + urlObj.path.map(seg => pm.variables.replaceIn(seg)).join('/');",
          "const httpMethod = pm.request.method;",
          "",
          "// Extract query parameters",
          "const queryParam = {};",
          "if (urlObj.query && urlObj.query.count()) {",
          "    urlObj.query.each((param) => {",
          "        if (param.key && !param.disabled) {",
          "            let val = pm.variables.replaceIn(param.value || '');",
          "            queryParam[param.key] = val;",
          "        }",
          "    });",
          "}",
          "",
          "// Build canonical request",
          "let canonicalRequest = httpMethod + '\\n' + hostname + uri + '\\n';",
          "",
          "// Query parameters (sorted, URL-encoded key=value pairs joined by &)",
          "let sortedQKeys = Object.keys(queryParam).sort();",
          "let qStr = '';",
          "sortedQKeys.forEach(k => { qStr += encodeURIComponent(k) + '=' + encodeURIComponent(queryParam[k]) + '&'; });",
          "if (qStr.endsWith('&')) qStr = qStr.slice(0, -1);",
          "canonicalRequest += qStr + '\\n';",
          "",
          "// Headers (sorted, URL-encoded key=value pairs joined by &)",
          "let sortedHKeys = Object.keys(header).sort();",
          "let hStr = '';",
          "sortedHKeys.forEach(k => { hStr += encodeURIComponent(k) + '=' + encodeURIComponent(header[k]) + '&'; });",
          "if (hStr.endsWith('&')) hStr = hStr.slice(0, -1);",
          "canonicalRequest += hStr + '\\n';",
          "",
          "// Payload (sorted, URL-encoded key=value pairs joined by &)",
          "// Arrays are converted to string format like '[VALUE]' to match Python signing logic",
          "const payload = {};",
          "if (httpMethod === 'POST' && pm.request.body && pm.request.body.raw) {",
          "    try {",
          "        const bodyStr = pm.variables.replaceIn(pm.request.body.raw);",
          "        const bodyObj = JSON.parse(bodyStr);",
          "        Object.keys(bodyObj).forEach(k => {",
          "            const val = bodyObj[k];",
          "            if (Array.isArray(val)) {",
          "                // Convert arrays to bracketed string: ['QR_IMAGE'] -> '[QR_IMAGE]'",
          "                payload[k] = '[' + val.join(',') + ']';",
          "            } else {",
          "                payload[k] = String(val);",
          "            }",
          "        });",
          "    } catch (e) {",
          "        console.warn('Could not parse body for signing: ' + e.message);",
          "    }",
          "}",
          "let sortedPKeys = Object.keys(payload).sort();",
          "let pStr = '';",
          "sortedPKeys.forEach(k => { pStr += encodeURIComponent(k) + '=' + encodeURIComponent(payload[k]) + '&'; });",
          "if (pStr.endsWith('&')) pStr = pStr.slice(0, -1);",
          "canonicalRequest += pStr;",
          "",
          "console.log('--- Canonical Request ---');",
          "console.log(canonicalRequest);",
          "",
          "// Create string to sign",
          "const credentialScope = currentDate + '/eu-west-1/AmazonPay/aws4_request';",
          "const hashedCanonical = hash(canonicalRequest);",
          "const stringToSign = 'AWS4-HMAC-SHA384\\n' + amzDate + '\\n' + credentialScope + '\\n' + hashedCanonical;",
          "",
          "console.log('--- String To Sign ---');",
          "console.log(stringToSign);",
          "",
          "// Derive signing key",
          "const dateKey = hmacSha384(currentDate, 'AWS4' + secretKey);",
          "const regionKey = hmacSha384('eu-west-1', dateKey);",
          "const serviceKey = hmacSha384('AmazonPay', regionKey);",
          "const signingKey = hmacSha384('aws4_request', serviceKey);",
          "",
          "// Generate signature (base64url encoded, no padding)",
          "const signature = hmacSha384(stringToSign, signingKey);",
          "const encodedSignature = CryptoJS.enc.Base64.stringify(signature).replace(/=+$/, '').replace(/\\+/g, '-').replace(/\\//g, '_');",
          "const authHeader = 'AMZ+' + accessKey + ':' + encodedSignature;",
          "console.log('Authorization: ' + authHeader);",
          "",
          "// Inject headers directly into the request",
          "pm.request.headers.upsert({ key: 'Authorization', value: authHeader });",
          "pm.request.headers.upsert({ key: 'x-amz-date', value: amzDate });",
          "pm.request.headers.upsert({ key: 'x-amz-client-id', value: mid });",
          "pm.request.headers.upsert({ key: 'x-amz-source', value: 'Server' });",
          "pm.request.headers.upsert({ key: 'x-amz-user-ip', value: '198.0.0.1' });",
          "pm.request.headers.upsert({ key: 'x-amz-user-agent', value: 'Postman' });",
          "pm.request.headers.upsert({ key: 'x-amz-algorithm', value: 'AWS4-HMAC-SHA384' });",
          "pm.request.headers.upsert({ key: 'x-amz-expires', value: '900' });"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "1. Generate QR",
      "request": {
        "method": "POST",
        "header": [
          { "key": "Content-Type", "value": "application/json" }
        ],
        "url": {
          "raw": "https://{{hostname}}/v2/pay/token",
          "protocol": "https",
          "host": ["{{hostname}}"],
          "path": ["v2", "pay", "token"]
        },
        "body": {
          "mode": "raw",
          "raw": "{\n  \"merchantId\": \"{{mid}}\",\n  \"storeIdType\": \"MERCHANT_STORE_ID\",\n  \"storeId\": \"{{store_id}}\",\n  \"currency\": \"INR\",\n  \"clientId\": \"04\",\n  \"distributionType\": [\"QR_IMAGE\"],\n  \"expiryTimestamp\": \"{{expiry_timestamp}}\",\n  \"amount\": \"1.00\",\n  \"orderId\": \"{{charge_id}}\",\n  \"qrMedium\": \"03\"\n}"
        }
      }
    },
    {
      "name": "2. Status Check",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "https://{{hostname}}/v2/pay/token?merchantId={{mid}}&merchantOrderId={{last_charge_id}}",
          "protocol": "https",
          "host": ["{{hostname}}"],
          "path": ["v2", "pay", "token"],
          "query": [
            { "key": "merchantId", "value": "{{mid}}" },
            { "key": "merchantOrderId", "value": "{{last_charge_id}}" }
          ]
        }
      }
    },
    {
      "name": "3. Cancel",
      "request": {
        "method": "POST",
        "header": [
          { "key": "Content-Type", "value": "application/json" }
        ],
        "url": {
          "raw": "https://{{hostname}}/v2/pay/cancel",
          "protocol": "https",
          "host": ["{{hostname}}"],
          "path": ["v2", "pay", "cancel"]
        },
        "body": {
          "mode": "raw",
          "raw": "{\n  \"merchantId\": \"{{mid}}\",\n  \"chargeIdType\": \"MerchantTxnId\",\n  \"chargeId\": \"{{last_charge_id}}\",\n  \"cancelIntent\": [\"CANCEL_TOKEN\"],\n  \"cancellationReason\": \"USER_CANCELLATION\",\n  \"noteToCustomer\": \"Customer chose payment by cash\"\n}"
        }
      }
    },
    {
      "name": "4. Create Refund",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "const refundId = pm.variables.get('refund_id');",
              "if (refundId) {",
              "    pm.collectionVariables.set('last_refund_id', refundId);",
              "    console.log('Saved last_refund_id: ' + refundId);",
              "}"
            ]
          }
        }
      ],
      "request": {
        "method": "POST",
        "header": [
          { "key": "Content-Type", "value": "application/json" }
        ],
        "url": {
          "raw": "https://{{hostname}}/v1/payments/refund",
          "protocol": "https",
          "host": ["{{hostname}}"],
          "path": ["v1", "payments", "refund"]
        },
        "body": {
          "mode": "raw",
          "raw": "{\n  \"amount\": \"1.00\",\n  \"merchantId\": \"{{mid}}\",\n  \"chargeId\": \"{{last_charge_id}}\",\n  \"chargeIdType\": \"MerchantTxnId\",\n  \"currencyCode\": \"INR\",\n  \"noteToCustomer\": \"Refund for order\",\n  \"refundId\": \"{{refund_id}}\",\n  \"softDescriptor\": \"refunded\"\n}"
        },
        "description": "Initiate a refund for a previously charged transaction.\n\nBody params:\n- chargeId: The orderId/chargeId of the original charge\n- chargeIdType: MerchantTxnId or AmazonTxnId\n- refundId: Auto-generated unique refund identifier\n- amount: Refund amount (partial or full)"
      }
    },
    {
      "name": "5. Refund Status",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "https://{{hostname}}/v1/payments/refund?merchantId={{mid}}&txnId={{last_refund_id}}&txnIdType=MerchantTxnId",
          "protocol": "https",
          "host": ["{{hostname}}"],
          "path": ["v1", "payments", "refund"],
          "query": [
            { "key": "merchantId", "value": "{{mid}}" },
            { "key": "txnId", "value": "{{last_refund_id}}", "description": "The refundId used when creating the refund" },
            { "key": "txnIdType", "value": "MerchantTxnId", "description": "MerchantTxnId or AmazonTxnId" }
          ]
        },
        "description": "Check the status of a refund transaction.\n\nQuery params:\n- txnId: The refundId you used when creating the refund\n- txnIdType: MerchantTxnId (your ID) or AmazonTxnId (Amazon's ID)"
      }
    }
  ]
}
