{
  "info": {
    "name": "Amazon Pay APL APIs",
    "description": "# Amazon Pay Later (APL) APIs\n\n## Setup\n\n1. Import this collection into Postman\n2. Go to **Collection Variables** and fill in the following:\n\n- `mid` — Your Merchant ID\n- `secret_key` — Your Secret Key\n- `access_key` — Your Access Key\n- `client_id` — Your OAuth Client ID\n  - Android: AmazonPay.startAuthorize() returns clientID\n  - iOS: PayAuthorizeCallbackHandler() returns clientID\n  - Web: Developer Account → Web Settings → click Show\n- `client_secret` — Your OAuth Client Secret (Developer Account → Web Settings → click Show)\n- `redirect_uri` — Your Redirect URI\n  - Android: AmazonPay.startAuthorize() returns redirectUri\n  - iOS: PayAuthorizeCallbackHandler() returns redirectUri\n  - Web: The URL added under Developer Account → Web Settings → Allowed Return URLs\n- `code_verifier` — PKCE Code Verifier (Mobile App flow only)\n  - Android: AmazonPay.startAuthorize() returns code_verifier\n  - iOS: PayAuthorizeCallbackHandler() returns code_verifier\n- `auth_code` — Authorization code from OAuth flow\n  - Android: AmazonPay.startAuthorize() returns authCode\n  - iOS: PayAuthorizeCallbackHandler() returns authCode\n  - Web: LoginWithAmazon returns authCode\n- `callback_url` — URL where the user is redirected after payment via Charge API\n- `paymentMetaData` — *(Optional)* Required only for Android app transactions\n\n## Usage\n\n1. Run **Get Access Token** first — this saves `access_token` and `refresh_token` automatically\n2. All other APIs will auto-sign requests using your credentials\n3. `charge_id` and `refund_id` are auto-generated when you run Create Charge / Create Refund\n4. Status check APIs automatically use the last generated charge/refund ID\n\n## Notes\n\n- Set `hostname` to `amazonpay-sandbox.amazon.in` for sandbox testing\n- For **Website** flow: use Get Access Token (Website) with `client_id` + `client_secret`\n- For **Mobile App** flow: use Get Access Token (Mobile App) with `client_id` + `code_verifier`",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "mid",
      "value": "",
      "type": "string",
      "description": "Your Merchant ID"
    },
    {
      "key": "secret_key",
      "value": "",
      "type": "string",
      "description": "Your Secret Key for signing\nObtained from Amazon Pay Merchant Integration Dashboard"
    },
    {
      "key": "access_key",
      "value": "",
      "type": "string",
      "description": "Your Access Key for authorization\nObtained from Amazon Pay Merchant Integration Dashboard"
    },
    {
      "key": "client_id",
      "value": "",
      "type": "string",
      "description": "Your OAuth Client ID"
    },
    {
      "key": "client_secret",
      "value": "",
      "type": "string",
      "description": "Your OAuth Client Secret\nWeb: Developer Account → Web Settings → click Show"
    },
    {
      "key": "redirect_uri",
      "value": "",
      "type": "string",
      "description": "Your Redirect URI"
    },
    {
      "key": "code_verifier",
      "value": "",
      "type": "string",
      "description": "PKCE Code Verifier (Mobile App flow only)"
    },
    {
      "key": "access_token",
      "value": "",
      "type": "string",
      "description": "Access token (auto-populated after running Get Access Token)"
    },
    {
      "key": "refresh_token",
      "value": "",
      "type": "string",
      "description": "Refresh token (auto-populated after running Get Access Token)"
    },
    {
      "key": "hostname",
      "value": "amazonpay.amazon.in",
      "type": "string",
      "description": "Production: amazonpay.amazon.in | Sandbox: amazonpay-sandbox.amazon.in"
    },
    {
      "key": "auth_code",
      "value": "",
      "type": "string",
      "description": "Authorization code from OAuth flow"
    },
    {
      "key": "callback_url",
      "value": "",
      "type": "string",
      "description": "URL where the user is redirected after payment via Charge API"
    },
    {
      "key": "paymentMetaData",
      "value": "",
      "type": "string",
      "description": "(Optional) Required only for Android app transactions\nThe value returned by amazonPayTransactionMetadataRequest method from the client-side SDK"
    },
    {
      "key": "sellerStoreName",
      "value": "",
      "type": "string",
      "description": "(Optional) The seller store name displayed to the customer during payment"
    }
  ],
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "const CryptoJS = require('crypto-js');",
          "",
          "// Skip signing for OAuth token endpoints",
          "const currentUrl = pm.variables.replaceIn(pm.request.url.toString());",
          "if (currentUrl.includes('api.amazon.co.uk') || currentUrl.includes('api.amazon.com')) {",
          "    return;",
          "}",
          "",
          "// 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 only for Charge and Refund create calls",
          "const requestName = pm.info.requestName;",
          "if (requestName === 'Create Charge') {",
          "    const randomId = CryptoJS.lib.WordArray.random(16).toString();",
          "    const chargeId = 'AMZ-' + randomId;",
          "    const referenceId = 'AMZ-R-' + randomId;",
          "    pm.variables.set('charge_id', chargeId);",
          "    pm.variables.set('reference_id', referenceId);",
          "    pm.collectionVariables.set('last_charge_id', chargeId);",
          "    console.log('charge_id: ' + chargeId);",
          "} else if (requestName === 'Create Refund') {",
          "    const randomId = CryptoJS.lib.WordArray.random(16).toString();",
          "    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': 'Browser',",
          "    '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;",
          "        }",
          "    });",
          "}",
          "// Override accessToken with raw value for correct signing",
          "const rawAccessToken = pm.variables.get('access_token');",
          "if (rawAccessToken && queryParam['accessToken']) {",
          "    queryParam['accessToken'] = rawAccessToken;",
          "}",
          "",
          "// 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 &)",
          "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 => { payload[k] = String(bodyObj[k]); });",
          "    } 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.split('+')[0] + '\\n' + credentialScope + '\\n' + hashedCanonical;",
          "",
          "// 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)",
          "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: 'Browser' });",
          "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' });",
          "",
          "// Resolve accessToken for URL query params",
          "const accessToken = pm.variables.get('access_token');",
          "if (accessToken && urlObj.query && urlObj.query.count()) {",
          "    urlObj.query.each((param) => {",
          "        if (param.key === 'accessToken') {",
          "            param.value = accessToken.replace(/\\|/g, '%7C');",
          "        }",
          "    });",
          "}"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "Auth",
      "item": [
        {
          "name": "Get Access Token (Website)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "try {",
                  "    const resp = pm.response.json();",
                  "    console.log('Response:', JSON.stringify(resp));",
                  "    if (resp.access_token) {",
                  "        pm.collectionVariables.set('access_token', resp.access_token);",
                  "        console.log('access_token saved: ' + resp.access_token.substring(0, 30) + '...');",
                  "    }",
                  "    if (resp.refresh_token) {",
                  "        pm.collectionVariables.set('refresh_token', resp.refresh_token);",
                  "        console.log('refresh_token saved');",
                  "    }",
                  "    if (resp.error) {",
                  "        console.error('Error: ' + resp.error + ' - ' + resp.error_description);",
                  "    }",
                  "} catch (e) {",
                  "    console.error('Failed to parse response: ' + e.message);",
                  "    console.log('Raw response: ' + pm.response.text());",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/x-www-form-urlencoded"
              }
            ],
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                { "key": "grant_type", "value": "authorization_code" },
                { "key": "code", "value": "{{auth_code}}" },
                { "key": "client_id", "value": "{{client_id}}" },
                { "key": "client_secret", "value": "{{client_secret}}" },
                { "key": "redirect_uri", "value": "{{redirect_uri}}" }
              ]
            },
            "url": {
              "raw": "https://api.amazon.co.uk/auth/o2/token",
              "protocol": "https",
              "host": ["api", "amazon", "co", "uk"],
              "path": ["auth", "o2", "token"]
            },
            "description": "Exchange authorization code for access_token and refresh_token (Website flow).\n\nUses client_id + client_secret for authentication."
          }
        },
        {
          "name": "Get Access Token (Mobile App)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "try {",
                  "    const resp = pm.response.json();",
                  "    console.log('Response:', JSON.stringify(resp));",
                  "    if (resp.access_token) {",
                  "        pm.collectionVariables.set('access_token', resp.access_token);",
                  "        console.log('access_token saved: ' + resp.access_token.substring(0, 30) + '...');",
                  "    }",
                  "    if (resp.refresh_token) {",
                  "        pm.collectionVariables.set('refresh_token', resp.refresh_token);",
                  "        console.log('refresh_token saved');",
                  "    }",
                  "    if (resp.error) {",
                  "        console.error('Error: ' + resp.error + ' - ' + resp.error_description);",
                  "    }",
                  "} catch (e) {",
                  "    console.error('Failed to parse response: ' + e.message);",
                  "    console.log('Raw response: ' + pm.response.text());",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/x-www-form-urlencoded"
              }
            ],
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                { "key": "grant_type", "value": "authorization_code" },
                { "key": "code", "value": "{{auth_code}}" },
                { "key": "client_id", "value": "{{client_id}}" },
                { "key": "code_verifier", "value": "{{code_verifier}}" },
                { "key": "redirect_uri", "value": "{{redirect_uri}}" }
              ]
            },
            "url": {
              "raw": "https://api.amazon.co.uk/auth/o2/token",
              "protocol": "https",
              "host": ["api", "amazon", "co", "uk"],
              "path": ["auth", "o2", "token"]
            },
            "description": "Exchange authorization code for access_token and refresh_token (Mobile App flow with PKCE)."
          }
        },
        {
          "name": "Refresh Access Token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "try {",
                  "    const resp = pm.response.json();",
                  "    console.log('Response:', JSON.stringify(resp));",
                  "    if (resp.access_token) {",
                  "        pm.collectionVariables.set('access_token', resp.access_token);",
                  "        console.log('access_token refreshed and saved');",
                  "    }",
                  "    if (resp.refresh_token) {",
                  "        pm.collectionVariables.set('refresh_token', resp.refresh_token);",
                  "        console.log('refresh_token updated');",
                  "    }",
                  "    if (resp.error) {",
                  "        console.error('Error: ' + resp.error + ' - ' + resp.error_description);",
                  "    }",
                  "} catch (e) {",
                  "    console.error('Failed to parse response: ' + e.message);",
                  "    console.log('Raw response: ' + pm.response.text());",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/x-www-form-urlencoded"
              }
            ],
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                { "key": "grant_type", "value": "refresh_token" },
                { "key": "refresh_token", "value": "{{refresh_token}}" },
                { "key": "client_id", "value": "{{client_id}}" },
                { "key": "client_secret", "value": "{{client_secret}}" }
              ]
            },
            "url": {
              "raw": "https://api.amazon.co.uk/auth/o2/token",
              "protocol": "https",
              "host": ["api", "amazon", "co", "uk"],
              "path": ["auth", "o2", "token"]
            },
            "description": "Refresh an expired access token."
          }
        }
      ]
    },
    {
      "name": "Instruments",
      "item": [
        {
          "name": "Get Payment Instruments (Pay Later)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "https://{{hostname}}/v1/payments/instruments?merchantId={{mid}}&accessToken={{access_token}}&instrumentTypes=AmazonPayLater&amount=100",
              "protocol": "https",
              "host": ["{{hostname}}"],
              "path": ["v1", "payments", "instruments"],
              "query": [
                {
                  "key": "merchantId",
                  "value": "{{mid}}"
                },
                {
                  "key": "accessToken",
                  "value": "{{access_token}}"
                },
                {
                  "key": "instrumentTypes",
                  "value": "AmazonPayLater"
                },
                {
                  "key": "amount",
                  "value": "100",
                  "description": "Transaction amount to check eligibility"
                }
              ]
            },
            "description": "Retrieve Amazon Pay Later instrument for a customer.\n\n- amount: Transaction amount to check eligibility"
          }
        }
      ]
    },
    {
      "name": "Charge",
      "item": [
        {
          "name": "Create Charge",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const chargeId = pm.variables.get('charge_id');",
                  "if (chargeId) {",
                  "    pm.collectionVariables.set('last_charge_id', chargeId);",
                  "    console.log('Saved last_charge_id: ' + chargeId);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"intent\": \"Capture\",\n  \"amount\": \"1.00\",\n  \"currencyCode\": \"INR\",\n  \"callbackUrl\": \"{{callback_url}}\",\n  \"accessToken\": \"{{access_token}}\",\n  \"chargeId\": \"{{charge_id}}\",\n  \"referenceId\": \"{{reference_id}}\",\n  \"merchantId\": \"{{mid}}\",\n  \"attributableProgram\": \"S2SPay\",\n  \"noteToCustomer\": \"Payment for order\",\n  \"customData\": \"data\",\n  \"timeoutInSecs\": \"900\",\n  \"selectedPaymentInstrumentType\": \"AmazonPayLater\",\n  \"paymentMetaData\": \"{{paymentMetaData}}\",\n  \"sellerStoreName\": \"{{sellerStoreName}}\"\n}"
            },
            "url": {
              "raw": "https://{{hostname}}/v1/payments/charge",
              "protocol": "https",
              "host": ["{{hostname}}"],
              "path": ["v1", "payments", "charge"]
            },
            "description": "Initiate a charge/payment transaction using Amazon Pay Later.\n\n- paymentMetaData: (Optional) Required only for Android app transactions."
          }
        },
        {
          "name": "Get Charge Status",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "https://{{hostname}}/v1/payments/charge?merchantId={{mid}}&txnId={{last_charge_id}}&txnIdType=MerchantTxnId",
              "protocol": "https",
              "host": ["{{hostname}}"],
              "path": ["v1", "payments", "charge"],
              "query": [
                {
                  "key": "merchantId",
                  "value": "{{mid}}"
                },
                {
                  "key": "txnId",
                  "value": "{{last_charge_id}}",
                  "description": "The chargeId used when creating the charge"
                },
                {
                  "key": "txnIdType",
                  "value": "MerchantTxnId",
                  "description": "MerchantTxnId or AmazonTxnId"
                }
              ]
            },
            "description": "Check the status of a charge transaction."
          }
        }
      ]
    },
    {
      "name": "Refund",
      "item": [
        {
          "name": "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"
              }
            ],
            "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}"
            },
            "url": {
              "raw": "https://{{hostname}}/v1/payments/refund",
              "protocol": "https",
              "host": ["{{hostname}}"],
              "path": ["v1", "payments", "refund"]
            },
            "description": "Initiate a refund for a previously charged transaction."
          }
        },
        {
          "name": "Get 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."
          }
        }
      ]
    }
  ]
}
