{
  "info": {
    "_postman_id": "c3e64ff5-9ba4-4a67-a211-3f879a9ac27c",
    "name": "MEMEH RTPO Fintech and Payment Facilitator API",
    "description": "Executable MEMEH RTPO Request-to-Pay integration journey for approved fintech and payment facilitator backends. The fintech supplies the payee participant BIC in both write flows. Core uses its trusted participant directory to reach that participant adapter for alias resolution and initiation; there is no merchant-to-participant routing map. The adapter calls the participant one-alias API separately for payer and payee and combines only two successful results. Run the requests in order.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "noauth"
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "const path = pm.variables.replaceIn(pm.request.url.getPath());",
          "if (path === '/api/v1/aliases/resolve') {",
          "  const runId = Date.now().toString();",
          "  pm.collectionVariables.set('reference', `POSTMAN-${runId}`);",
          "  pm.collectionVariables.set('idempotency_key', `postman-${runId}-attempt-1`);",
          "  ['resolution_id', 'payer_finaddress_id', 'payee_finaddress_id', 'request_id'].forEach(function (name) { pm.collectionVariables.unset(name); });",
          "}",
          "if (path !== '/health') {",
          "  const clientId = pm.variables.get('memeh_client_id');",
          "  const secret = pm.variables.get('memeh_hmac_secret');",
          "  if (!clientId || !secret) throw new Error('memeh_client_id and memeh_hmac_secret are required');",
          "  const timestamp = Math.floor(Date.now() / 1000).toString();",
          "  const nonce = pm.variables.replaceIn('{{$guid}}');",
          "  const body = pm.request.body && pm.request.body.mode === 'raw' ? pm.variables.replaceIn(pm.request.body.raw || '') : '';",
          "  const canonical = [pm.request.method.toUpperCase(), path, timestamp, nonce, body].join('\\n');",
          "  const signature = CryptoJS.HmacSHA256(canonical, secret).toString(CryptoJS.enc.Hex);",
          "  pm.request.headers.upsert({ key: 'X-MEMEH-CLIENT-ID', value: clientId });",
          "  pm.request.headers.upsert({ key: 'X-MEMEH-TIMESTAMP', value: timestamp });",
          "  pm.request.headers.upsert({ key: 'X-MEMEH-NONCE', value: nonce });",
          "  pm.request.headers.upsert({ key: 'X-MEMEH-SIGNATURE', value: signature });",
          "}"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "00 - Connectivity",
      "description": "Confirm that the configured MEMEH RTPO endpoint is reachable before running the protected flow.",
      "item": [
        {
          "name": "Health Check",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{memeh_base_url}}/health",
            "description": "Public liveness endpoint. A successful response confirms HTTP connectivity only; it does not prove CAS, routing, or participant-adapter readiness."
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Health endpoint returns HTTP 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "",
                  "pm.test(\"Health response is JSON\", function () {",
                  "  pm.expect(pm.response.headers.get(\"Content-Type\") || \"\").to.include(\"application/json\");",
                  "});"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "01 - Required Two-Flow Request-to-Pay",
      "description": "Run in order. Resolve both parties first, choose active address options, then create the Request-to-Pay with the captured opaque IDs.",
      "item": [
        {
          "name": "Flow 1 - Resolve Payer and Payee",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              },
              {
                "key": "Accept",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payer_alias\": \"{{payer_alias}}\",\n  \"payee_alias\": \"{{payee_alias}}\",\n  \"payee_bic\": \"{{payee_bic}}\",\n  \"amount\": \"{{amount}}\",\n  \"currency\": \"{{currency}}\",\n  \"reference\": \"{{reference}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": "{{memeh_base_url}}/api/v1/aliases/resolve",
            "description": "Required Flow 1. Core resolves payee_bic to a trusted participant adapter. The adapter calls the participant one-alias API once for payer and once for payee, then returns separate masked financial-address options only when both calls succeed. The returned payee servicer must equal payee_bic."
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Flow 1 returns HTTP 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "",
                  "const body = pm.response.json();",
                  "const payerOptions = body.payer && Array.isArray(body.payer.finaddresses) ? body.payer.finaddresses : [];",
                  "const payeeOptions = body.payee && Array.isArray(body.payee.finaddresses) ? body.payee.finaddresses : [];",
                  "const activePayerOptions = payerOptions.filter(function (option) { return option.status === \"ACTIVE\"; });",
                  "const activePayeeOptions = payeeOptions.filter(function (option) { return option.status === \"ACTIVE\"; });",
                  "",
                  "pm.test(\"Alias resolution is verified and identified\", function () {",
                  "  pm.expect(body.verified).to.eql(true);",
                  "  pm.expect(body.resolution_id).to.be.a(\"string\").and.not.empty;",
                  "  pm.expect(body.source).to.be.a(\"string\").and.not.empty;",
                  "});",
                  "",
                  "pm.test(\"Payer has at least one active participant/address option\", function () {",
                  "  pm.expect(activePayerOptions.length).to.be.greaterThan(0);",
                  "});",
                  "",
                  "pm.test(\"Payee has at least one active participant/address option\", function () {",
                  "  pm.expect(activePayeeOptions.length).to.be.greaterThan(0);",
                  "});",
                  "",
                  "pm.test(\"Payee servicer matches payee_bic\", function () {",
                  "  activePayeeOptions.forEach(function (option) { pm.expect(option.servicer_id).to.eql(pm.variables.get('payee_bic')); });",
                  "});",
                  "",
                  "pm.test(\"Public options contain opaque IDs and masked values\", function () {",
                  "  activePayerOptions.concat(activePayeeOptions).forEach(function (option) {",
                  "    pm.expect(option.id).to.match(/^fa_/);",
                  "    pm.expect(option.masked).to.be.a(\"string\").and.not.empty;",
                  "    pm.expect(option.servicer_id).to.be.a(\"string\").and.not.empty;",
                  "    pm.expect(option).not.to.have.property(\"fin_address\");",
                  "  });",
                  "});",
                  "",
                  "if (body.verified && body.resolution_id && activePayerOptions.length && activePayeeOptions.length) {",
                  "  const payerChoice = activePayerOptions.find(function (option) { return option.default; }) || activePayerOptions[0];",
                  "  const payeeChoice = activePayeeOptions.find(function (option) { return option.default; }) || activePayeeOptions[0];",
                  "  pm.collectionVariables.set(\"resolution_id\", body.resolution_id);",
                  "  pm.collectionVariables.set(\"payer_finaddress_id\", payerChoice.id);",
                  "  pm.collectionVariables.set(\"payee_finaddress_id\", payeeChoice.id);",
                  "  pm.collectionVariables.set(\"payer_choice_label\", payerChoice.label || payerChoice.servicer_id);",
                  "  pm.collectionVariables.set(\"payee_choice_label\", payeeChoice.label || payeeChoice.servicer_id);",
                  "  console.log(\"Selected payer option\", payerChoice.label, payerChoice.masked, payerChoice.servicer_id);",
                  "  console.log(\"Selected payee option\", payeeChoice.label, payeeChoice.masked, payeeChoice.servicer_id);",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Flow 2 - Create Request-to-Pay",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              },
              {
                "key": "Accept",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"idempotency_key\": \"{{idempotency_key}}\",\n  \"resolution_id\": \"{{resolution_id}}\",\n  \"payer_finaddress_id\": \"{{payer_finaddress_id}}\",\n  \"payee_finaddress_id\": \"{{payee_finaddress_id}}\",\n  \"payer_alias\": \"{{payer_alias}}\",\n  \"payee_alias\": \"{{payee_alias}}\",\n  \"payee_bic\": \"{{payee_bic}}\",\n  \"amount\": \"{{amount}}\",\n  \"currency\": \"{{currency}}\",\n  \"reference\": \"{{reference}}\",\n  \"description\": \"{{description}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": "{{memeh_base_url}}/api/v1/payment-requests",
            "description": "Required Flow 2. The Edge injects the governed merchant identity for audit. Core uses payee_bic to ask the same trusted participant adapter to revalidate both opaque selections through two participant one-alias calls, verifies the payee servicer, and dispatches the canonical instruction."
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "[\"resolution_id\", \"payer_finaddress_id\", \"payee_finaddress_id\"].forEach(function (name) {",
                  "  if (!pm.collectionVariables.get(name)) {",
                  "    throw new Error(`${name} is missing. Run Flow 1 - Resolve Payer and Payee first.`);",
                  "  }",
                  "});"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Flow 2 returns HTTP 202\", function () {",
                  "  pm.response.to.have.status(202);",
                  "});",
                  "",
                  "const body = pm.response.json();",
                  "pm.test(\"Accepted response contains request correlation\", function () {",
                  "  pm.expect(body.request_id).to.be.a(\"string\").and.not.empty;",
                  "  pm.expect(body.status).to.be.a(\"string\").and.not.empty;",
                  "  pm.expect(body.dispatch).to.be.an(\"object\");",
                  "});",
                  "",
                  "pm.test(\"Dispatch acceptance is not misreported as payment completion\", function () {",
                  "  pm.expect(body.status).not.to.eql(\"ACSC\");",
                  "});",
                  "",
                  "if (body.request_id) {",
                  "  pm.collectionVariables.set(\"request_id\", body.request_id);",
                  "}"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "02 - Status and Lifecycle",
      "description": "Read the current state and immutable event history using the request_id captured from Flow 2.",
      "item": [
        {
          "name": "Read Current Request Status",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": "{{memeh_base_url}}/api/v1/payment-requests/{{request_id}}",
            "description": "Returns the latest known request state. DISPATCHED, RCVD, PDNG, and ACSP remain non-final. ACSC is successful completion; RETURNED, RJCT, CANC, and FAILED are terminal outcomes."
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (!pm.collectionVariables.get(\"request_id\")) {",
                  "  throw new Error(\"request_id is missing. Run Flow 2 - Create Request-to-Pay first.\");",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status endpoint returns HTTP 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "",
                  "const body = pm.response.json();",
                  "const knownStatuses = [\"RECEIVED\", \"ROUTABLE\", \"DISPATCHED\", \"RCVD\", \"PDNG\", \"ACSP\", \"ACSC\", \"RETURNED\", \"RJCT\", \"CANC\", \"FAILED\"];",
                  "pm.test(\"Status response matches the captured request\", function () {",
                  "  pm.expect(body.request_id).to.eql(pm.collectionVariables.get(\"request_id\"));",
                  "  pm.expect(knownStatuses).to.include(body.status);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Read Immutable Lifecycle Events",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": "{{memeh_base_url}}/api/v1/payment-requests/{{request_id}}/events",
            "description": "Returns the append-only event history used for support, audit, lifecycle reconstruction, and reconciliation. Fintechs read this endpoint; participating-provider adapters write lifecycle updates to a separate protected Core endpoint."
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (!pm.collectionVariables.get(\"request_id\")) {",
                  "  throw new Error(\"request_id is missing. Run Flow 2 - Create Request-to-Pay first.\");",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Lifecycle endpoint returns HTTP 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "",
                  "const body = pm.response.json();",
                  "pm.test(\"Lifecycle response contains an event array\", function () {",
                  "  pm.expect(body.events).to.be.an(\"array\");",
                  "});"
                ]
              }
            }
          ]
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "memeh_base_url",
      "value": "http://127.0.0.1:8082",
      "type": "string"
    },
    {
      "key": "memeh_client_id",
      "value": "sample-client",
      "type": "string"
    },
    {
      "key": "memeh_hmac_secret",
      "value": "sample-secret",
      "type": "string"
    },
    {
      "key": "payer_alias",
      "value": "078657484",
      "type": "string"
    },
    {
      "key": "payee_alias",
      "value": "076000001",
      "type": "string"
    },
    {
      "key": "payee_bic",
      "value": "SLCBSLFR",
      "type": "string"
    },
    {
      "key": "amount",
      "value": "100.00",
      "type": "string"
    },
    {
      "key": "currency",
      "value": "SLE",
      "type": "string"
    },
    {
      "key": "description",
      "value": "GovPay service fee",
      "type": "string"
    },
    {
      "key": "reference",
      "value": "",
      "type": "string"
    },
    {
      "key": "idempotency_key",
      "value": "",
      "type": "string"
    },
    {
      "key": "resolution_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "payer_finaddress_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "payee_finaddress_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "payer_choice_label",
      "value": "",
      "type": "string"
    },
    {
      "key": "payee_choice_label",
      "value": "",
      "type": "string"
    },
    {
      "key": "request_id",
      "value": "",
      "type": "string"
    }
  ]
}
