{
  "info": {
    "name": "INTERNAL — Acquirer Operations",
    "description": "⚠️ **INTERNAL ONLY — NEVER SHARE WITH A MERCHANT.**\n\nDirect, authenticated calls to Mastercard Payment Gateway Services, bypassing shukriapg. They exist for operations work that has no merchant-facing equivalent:\n\n- verifying a merchant's credentials before routing live traffic to them\n- reading the gateway's own view of an order when reconciling a dispute\n- issuing a refund or capture when the application path is unavailable\n\n**Why this is a separate collection**\nMerchants must never hold acquirer credentials or call an acquirer directly. Keeping these requests out of the merchant collection makes that difficult to get wrong by accident — there is no folder to hand over.\n\n**Credentials**\n`mpgs_api_password` can issue refunds. Put it in a Postman **environment**, never in this file, and never in a shared workspace.\n\nRefund and Capture move real money. Check which merchant you are pointed at before running them.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "order_ref",
      "value": "SHK-1042",
      "description": "gateway_order_ref as sent to MPGS"
    },
    {
      "key": "mpgs_host",
      "value": "eu-gateway.mastercard.com",
      "description": "Regional gateway host: eu- / ap- / na-gateway.mastercard.com"
    },
    {
      "key": "mpgs_api_version",
      "value": "version/100"
    },
    {
      "key": "mpgs_merchant_id",
      "value": "YOUR_MERCHANT_ID"
    },
    {
      "key": "mpgs_api_password",
      "value": "YOUR_API_PASSWORD",
      "description": "Sensitive. Prefer a Postman environment over the collection for this."
    },
    {
      "key": "mpgs_webhook_secret",
      "value": "YOUR_WEBHOOK_SECRET"
    },
    {
      "key": "base_url",
      "value": "https://shukriapg.ariticapp.com"
    },
    {
      "key": "pg_prefix",
      "value": "/pgpayments"
    },
    {
      "key": "webhook_secret",
      "value": "YOUR_WEBHOOK_SECRET"
    }
  ],
  "item": [
    {
      "name": "MPGS Gateway Direct",
      "description": "Calls straight to Mastercard, bypassing CloudLayer. Useful for verifying credentials and for reading the gateway's own view of an order when reconciling.\n\nAll use HTTP Basic auth: username `merchant.<merchantId>`, password the API password.",
      "item": [
        {
          "name": "Verify credentials (paymentOptionsInquiry)",
          "request": {
            "auth": {
              "type": "basic",
              "basic": [
                {
                  "key": "username",
                  "value": "merchant.{{mpgs_merchant_id}}"
                },
                {
                  "key": "password",
                  "value": "{{mpgs_api_password}}"
                }
              ]
            },
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json;charset=UTF-8"
              }
            ],
            "url": {
              "raw": "https://{{mpgs_host}}/api/rest/{{mpgs_api_version}}/merchant/{{mpgs_merchant_id}}/paymentOptionsInquiry",
              "protocol": "https",
              "host": [
                "{{mpgs_host}}"
              ],
              "path": [
                "api",
                "rest",
                "{{mpgs_api_version}}",
                "merchant",
                "{{mpgs_merchant_id}}",
                "paymentOptionsInquiry"
              ]
            },
            "description": "The cheapest authenticated call MPGS offers — no payload, creates nothing. The right first request when checking whether credentials work.\n\nEquivalent to `PaymentGatewayApp.Mpgs.verify_credentials(user_id)`.\n\nA `401` means the merchant id or password is wrong. A `200` with `result: SUCCESS` means you are connected."
          },
          "response": []
        },
        {
          "name": "Retrieve order",
          "request": {
            "auth": {
              "type": "basic",
              "basic": [
                {
                  "key": "username",
                  "value": "merchant.{{mpgs_merchant_id}}"
                },
                {
                  "key": "password",
                  "value": "{{mpgs_api_password}}"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": {
              "raw": "https://{{mpgs_host}}/api/rest/{{mpgs_api_version}}/merchant/{{mpgs_merchant_id}}/order/{{order_ref}}",
              "protocol": "https",
              "host": [
                "{{mpgs_host}}"
              ],
              "path": [
                "api",
                "rest",
                "{{mpgs_api_version}}",
                "merchant",
                "{{mpgs_merchant_id}}",
                "order",
                "{{order_ref}}"
              ]
            },
            "description": "The gateway's own record of an order and every transaction against it.\n\nThis is the authority our return handler settles from. When our record and this disagree, this one is right.\n\nCheck `result`, `status`, and each `transaction[].response.gatewayCode` — a payment counts as paid only when the order result **and** the transaction gateway code are both good."
          },
          "response": []
        },
        {
          "name": "Retrieve transaction",
          "request": {
            "auth": {
              "type": "basic",
              "basic": [
                {
                  "key": "username",
                  "value": "merchant.{{mpgs_merchant_id}}"
                },
                {
                  "key": "password",
                  "value": "{{mpgs_api_password}}"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": {
              "raw": "https://{{mpgs_host}}/api/rest/{{mpgs_api_version}}/merchant/{{mpgs_merchant_id}}/order/{{order_ref}}/transaction/{{order_ref}}-1",
              "protocol": "https",
              "host": [
                "{{mpgs_host}}"
              ],
              "path": [
                "api",
                "rest",
                "{{mpgs_api_version}}",
                "merchant",
                "{{mpgs_merchant_id}}",
                "order",
                "{{order_ref}}",
                "transaction",
                "{{order_ref}}-1"
              ]
            },
            "description": "A single transaction within an order. Transaction references follow `<order_ref>-<attempt>`."
          },
          "response": []
        },
        {
          "name": "Create checkout session (INITIATE_CHECKOUT)",
          "request": {
            "auth": {
              "type": "basic",
              "basic": [
                {
                  "key": "username",
                  "value": "merchant.{{mpgs_merchant_id}}"
                },
                {
                  "key": "password",
                  "value": "{{mpgs_api_password}}"
                }
              ]
            },
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json;charset=UTF-8"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"apiOperation\": \"INITIATE_CHECKOUT\",\n  \"partnerSolutionId\": \"CLOUDLAYER_0.1.0\",\n  \"order\": {\n    \"id\": \"POSTMAN-{{$timestamp}}\",\n    \"amount\": \"10.00\",\n    \"currency\": \"AED\",\n    \"description\": \"Postman test\",\n    \"reference\": \"POSTMAN-REF\",\n    \"notificationUrl\": \"https://shukriapg.ariticapp.com/mastercard/callback\"\n  },\n  \"interaction\": {\n    \"operation\": \"PURCHASE\",\n    \"returnUrl\": \"https://shukriapg.ariticapp.com/mastercard/return?txn_id=0\",\n    \"merchant\": {\n      \"name\": \"TechStore\"\n    },\n    \"displayControl\": {\n      \"customerEmail\": \"HIDE\",\n      \"billingAddress\": \"HIDE\",\n      \"paymentTerms\": \"HIDE\",\n      \"shipping\": \"HIDE\"\n    }\n  },\n  \"customer\": {\n    \"email\": \"test@example.com\",\n    \"firstName\": \"Test\",\n    \"lastName\": \"Payer\"\n  },\n  \"transaction\": {\n    \"reference\": \"POSTMAN-TXN\",\n    \"source\": \"INTERNET\"\n  }\n}"
            },
            "url": {
              "raw": "https://{{mpgs_host}}/api/rest/{{mpgs_api_version}}/merchant/{{mpgs_merchant_id}}/session",
              "protocol": "https",
              "host": [
                "{{mpgs_host}}"
              ],
              "path": [
                "api",
                "rest",
                "{{mpgs_api_version}}",
                "merchant",
                "{{mpgs_merchant_id}}",
                "session"
              ]
            },
            "description": "Exactly the request `Mpgs.Checkout.initiate/2` builds.\n\nUseful for validating payload shape against the gateway before trusting the Elixir path. Returns `session.id` and `successIndicator`.\n\nNote `order.id` must be unique — MPGS rejects reuse, hence the timestamp."
          },
          "response": []
        },
        {
          "name": "Refund",
          "request": {
            "auth": {
              "type": "basic",
              "basic": [
                {
                  "key": "username",
                  "value": "merchant.{{mpgs_merchant_id}}"
                },
                {
                  "key": "password",
                  "value": "{{mpgs_api_password}}"
                }
              ]
            },
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json;charset=UTF-8"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"apiOperation\": \"REFUND\",\n  \"partnerSolutionId\": \"CLOUDLAYER_0.1.0\",\n  \"transaction\": {\n    \"amount\": \"10.00\",\n    \"currency\": \"AED\",\n    \"reference\": \"refund-{{order_ref}}-1\"\n  },\n  \"order\": {\n    \"notificationUrl\": \"https://shukriapg.ariticapp.com/mastercard/callback\",\n    \"reference\": \"{{order_ref}}\"\n  }\n}"
            },
            "url": {
              "raw": "https://{{mpgs_host}}/api/rest/{{mpgs_api_version}}/merchant/{{mpgs_merchant_id}}/order/{{order_ref}}/transaction/refund-{{order_ref}}-1",
              "protocol": "https",
              "host": [
                "{{mpgs_host}}"
              ],
              "path": [
                "api",
                "rest",
                "{{mpgs_api_version}}",
                "merchant",
                "{{mpgs_merchant_id}}",
                "order",
                "{{order_ref}}",
                "transaction",
                "refund-{{order_ref}}-1"
              ]
            },
            "description": "Moves money back to the payer. **Live effect** — do not run against a production merchant casually.\n\nOmit `transaction.amount` for a full refund. Prefer `Payments.refund/2` in application code, which also records the refund as a child row in pg_transactions."
          },
          "response": []
        },
        {
          "name": "Capture (after AUTHORIZE)",
          "request": {
            "auth": {
              "type": "basic",
              "basic": [
                {
                  "key": "username",
                  "value": "merchant.{{mpgs_merchant_id}}"
                },
                {
                  "key": "password",
                  "value": "{{mpgs_api_password}}"
                }
              ]
            },
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json;charset=UTF-8"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"apiOperation\": \"CAPTURE\",\n  \"partnerSolutionId\": \"CLOUDLAYER_0.1.0\",\n  \"transaction\": {\n    \"amount\": \"10.00\",\n    \"currency\": \"AED\",\n    \"reference\": \"capture-{{order_ref}}-1\"\n  },\n  \"order\": {\n    \"notificationUrl\": \"https://shukriapg.ariticapp.com/mastercard/callback\",\n    \"reference\": \"{{order_ref}}\"\n  }\n}"
            },
            "url": {
              "raw": "https://{{mpgs_host}}/api/rest/{{mpgs_api_version}}/merchant/{{mpgs_merchant_id}}/order/{{order_ref}}/transaction/capture-{{order_ref}}-1",
              "protocol": "https",
              "host": [
                "{{mpgs_host}}"
              ],
              "path": [
                "api",
                "rest",
                "{{mpgs_api_version}}",
                "merchant",
                "{{mpgs_merchant_id}}",
                "order",
                "{{order_ref}}",
                "transaction",
                "capture-{{order_ref}}-1"
              ]
            },
            "description": "Takes funds reserved by an earlier AUTHORIZE. Only relevant when the merchant's `txn_mode` is `authorize`."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Notification simulation",
      "description": "Reproduces what the acquirer POSTs to our webhook. Internal because the payload is acquirer-shaped and merchants never send these.",
      "item": [
        {
          "name": "Simulate gateway notification",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "X-Notification-Secret",
                "value": "{{webhook_secret}}",
                "description": "Must match the merchant's stored webhook_secret, or the request answers 401"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"gatewayEntryPoint\": \"CHECKOUT_VIA_WEBSITE\",\n  \"result\": \"SUCCESS\",\n  \"status\": \"CAPTURED\",\n  \"order\": {\n    \"id\": \"{{order_ref}}\",\n    \"amount\": \"6297.90\",\n    \"currency\": \"AED\",\n    \"status\": \"CAPTURED\"\n  },\n  \"transaction\": {\n    \"id\": \"{{order_ref}}-1\",\n    \"type\": \"PAYMENT\",\n    \"reference\": \"{{order_ref}}-1\",\n    \"authorizationCode\": \"OK1234\"\n  },\n  \"response\": {\n    \"gatewayCode\": \"APPROVED\"\n  },\n  \"sourceOfFunds\": {\n    \"provided\": {\n      \"card\": {\n        \"number\": \"512345xxxxxx0008\",\n        \"brand\": \"MASTERCARD\"\n      }\n    }\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}{{pg_prefix}}/pay/callback",
              "host": [
                "{{base_url}}{{pg_prefix}}"
              ],
              "path": [
                "mastercard",
                "callback"
              ]
            },
            "description": "Reproduces what the gateway posts asynchronously.\n\n**Responses**\n- `200` empty — applied, or ignored as a duplicate\n- `401` empty — wrong/missing secret, or unknown order. Deliberately indistinguishable so the endpoint cannot be used to probe for order references.\n- `400` empty — malformed\n\n`order.id` must match a real gateway_order_ref, and the merchant must have a webhook_secret stored, or this returns 401."
          },
          "response": []
        },
        {
          "name": "Simulate gateway notification — declined",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "X-Notification-Secret",
                "value": "{{webhook_secret}}"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"gatewayEntryPoint\": \"CHECKOUT_VIA_WEBSITE\",\n  \"result\": \"FAILURE\",\n  \"status\": \"FAILED\",\n  \"order\": {\n    \"id\": \"{{order_ref}}\",\n    \"amount\": \"6297.90\",\n    \"currency\": \"AED\"\n  },\n  \"transaction\": {\n    \"id\": \"{{order_ref}}-1\",\n    \"type\": \"PAYMENT\"\n  },\n  \"response\": {\n    \"gatewayCode\": \"DECLINED\"\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}{{pg_prefix}}/pay/callback",
              "host": [
                "{{base_url}}{{pg_prefix}}"
              ],
              "path": [
                "mastercard",
                "callback"
              ]
            },
            "description": "The decline path. Note that a payment already settled as CLOSED will **not** be reopened by this — the webhook refuses to move a settled payment backwards."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Gateway internals (YSP path)",
      "description": "The current widget-based integration, as used by demo_checkout_page.html.",
      "item": [
        {
          "name": "Prepare redirect (UAT / production)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"merchantId\": \"{{ysp_merchant_id}}\",\n  \"merchantKey\": \"{{ysp_merchant_key}}\",\n  \"amount\": 6297.90,\n  \"orderId\": \"ORD_{{$timestamp}}\",\n  \"customerName\": \"Meghana Rao\",\n  \"customerEmail\": \"meghana@example.com\",\n  \"customerPhone\": \"+971501234567\",\n  \"customerId\": \"CUST_001\",\n  \"currency\": \"AED\",\n  \"returnUrl\": \"https://example.com/thanks\",\n  \"environment\": \"uat\"\n}"
            },
            "url": {
              "raw": "{{base_url}}{{pg_prefix}}/prepare-redirect",
              "host": [
                "{{base_url}}{{pg_prefix}}"
              ],
              "path": [
                "prepare-redirect"
              ]
            },
            "description": "Creates a transaction record and returns the NAR fields plus the YSP URL for the browser to POST.\n\nCalled by the widget when `environment` is `production`. Returns `{ success, nar_fields, ysp_url }`."
          },
          "response": []
        },
        {
          "name": "Compute NAR checksum",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"merchant_key\": \"{{ysp_merchant_key}}\",\n  \"nar_msgType\": \"AR\",\n  \"nar_merTxnTime\": \"20260816120000\",\n  \"nar_merBankCode\": \"01\",\n  \"nar_orderNo\": \"ORD_TEST_1\",\n  \"nar_merId\": \"{{ysp_merchant_id}}\",\n  \"nar_txnCurrency\": \"AED\",\n  \"nar_txnAmount\": \"6297.90\",\n  \"nar_remitterEmail\": \"meghana@example.com\",\n  \"nar_remitterMobile\": \"+971501234567\",\n  \"nar_cardType\": \"MC\",\n  \"nar_cardNo\": \"\",\n  \"nar_expirydate\": \"\",\n  \"nar_cvv\": \"\",\n  \"nar_nameoncard\": \"Meghana Rao\",\n  \"nar_browserlanguage\": \"en-US\",\n  \"nar_browserscreenheight\": \"1080\",\n  \"nar_browserscreenwidth\": \"1920\",\n  \"nar_browseruseragent\": \"Mozilla/5.0\",\n  \"nar_paymentDesc\": \"Card Payment\",\n  \"nar_version\": \"1.0\",\n  \"nar_mcccode\": \"\",\n  \"nar_returnUrl\": \"https://example.com/thanks\",\n  \"nar_Secure\": \"MERSECURE\"\n}"
            },
            "url": {
              "raw": "{{base_url}}{{pg_prefix}}/api/checksum",
              "host": [
                "{{base_url}}{{pg_prefix}}"
              ],
              "path": [
                "api",
                "checksum"
              ]
            },
            "description": "HMAC-SHA256 over the 23 NAR fields in the order defined by YSP spec §3.1, joined with `|`.\n\nReturns `{ \"checksum\": \"<lowercase hex>\" }`.\n\nUseful for verifying a checksum mismatch is a field-order problem rather than a key problem."
          },
          "response": []
        },
        {
          "name": "Payment callback (simulate Narada AC response)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/x-www-form-urlencoded"
              }
            ],
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                {
                  "key": "nar_msgType",
                  "value": "AC"
                },
                {
                  "key": "nar_orderNo",
                  "value": "ORD_TEST_1"
                },
                {
                  "key": "Response_Code",
                  "value": "00"
                },
                {
                  "key": "txnrefno",
                  "value": "NAR123456789"
                },
                {
                  "key": "nar_maskcardno",
                  "value": "512345XXXXXX0008"
                },
                {
                  "key": "nar_remarks",
                  "value": "Transaction Successful"
                },
                {
                  "key": "txn_id",
                  "value": "{{txn_id}}",
                  "description": "pg_transactions.id, added by our middleware"
                }
              ]
            },
            "url": {
              "raw": "{{base_url}}{{pg_prefix}}/callback",
              "host": [
                "{{base_url}}{{pg_prefix}}"
              ],
              "path": [
                "callback"
              ]
            },
            "description": "Reproduces the form POST Narada makes after a payment.\n\n`nar_msgType=AC` is an approval; `DC` is a decline. Note this endpoint is unauthenticated and the checksum is not yet verified (see the TODO in page_controller.ex)."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Legacy API (mock — do not build against)",
      "description": "The original generic endpoints from the app scaffold.\n\n**These are backed by MockProvider and return fabricated data.** They do not query pg_transactions. Use `/pay/status/:id` for real the gateway payment state.",
      "item": [
        {
          "name": "Initiate payment (mock)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"amount\": 50.0,\n  \"currency\": \"AED\",\n  \"user_id\": \"{{user_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}{{pg_prefix}}/api/initiate",
              "host": [
                "{{base_url}}{{pg_prefix}}"
              ],
              "path": [
                "api",
                "initiate"
              ]
            },
            "description": "Creates a mock payment via the configured `api_provider`, which is `MockProvider`. Returns a generated id; **no gateway is contacted and nothing is written to pg_transactions.**"
          },
          "response": []
        },
        {
          "name": "Payment status (mock — returns random status)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}{{pg_prefix}}/api/status/pay_abc123",
              "host": [
                "{{base_url}}{{pg_prefix}}"
              ],
              "path": [
                "api",
                "status",
                "pay_abc123"
              ]
            },
            "description": "⚠️ **Returns a random status.**\n\nThis routes to `MockProvider.get_payment_status/1`, which is literally `Enum.random([\"pending\", \"completed\", \"failed\"])`. It does not read the database.\n\nKept here so nobody mistakes it for a working status API. Use **the gateway (the gateway) → Payment status** instead."
          },
          "response": []
        }
      ]
    }
  ]
}