{
  "info": {
    "_postman_id": "97bf3e89-dafd-4f61-b56d-531164c48696",
    "name": "MintCash API — Clients",
    "description": "Client-facing MintCash endpoints only — the routes a merchant integrates against.\n\nIncludes the **Merchant** self-service endpoints (payments, refunds, subscriptions, API key listing, provider credentials) plus the public **Health check**. Admin-only routes and internal endpoints (Inngest, the Corefy inbound webhook) are intentionally excluded — use the full `MintCash API` collection for those.\n\n## Setup\n\n1. Import this collection into Postman.\n\n2. Create an environment with these variables:\n\n    - `base_url` — e.g. `https://sandbox.mintcash.me` (sandbox) or your production URL.\n\n    - `merchant_public_key` — your merchant-role public key (e.g. `pk_test_xxx`).\n\n    - `merchant_secret_key` — your merchant-role secret key. Store as a **secret** variable.\n\n## Auth model\n\n- **Public** folder (health check) → no auth.\n\n- **Merchant** folder → Basic Auth with `merchant_public_key` / `merchant_secret_key`, configured at the folder level.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "Public",
      "item": [
        {
          "name": "Health check",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "pm.test('Status code is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "",
                  "pm.test('Response is valid JSON', function () {",
                  "    pm.response.to.be.json;",
                  "});",
                  "",
                  "pm.test('Body has status, timestamp, database', function () {",
                  "    const body = pm.response.json();",
                  "    pm.expect(body).to.have.property('status');",
                  "    pm.expect(body).to.have.property('timestamp');",
                  "    pm.expect(body).to.have.property('database');",
                  "});"
                ],
                "type": "text/javascript",
                "packages": {},
                "requests": {}
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/health",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "health"
              ]
            },
            "description": "Returns the current status of the API and its database connection. Used by uptime monitors (e.g. Betterstack).\n\n**Auth:** none."
          },
          "response": []
        }
      ],
      "description": "Unauthenticated endpoints intended for uptime monitoring and infrastructure."
    },
    {
      "name": "Merchant",
      "item": [
        {
          "name": "Create payment",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status code is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "",
                  "pm.test('Response is valid JSON', function () {",
                  "    pm.response.to.be.json;",
                  "});",
                  "",
                  "pm.test('Response has required payment fields', function () {",
                  "    const body = pm.response.json();",
                  "    pm.expect(body).to.have.property('id');",
                  "    pm.expect(body).to.have.property('status');",
                  "    pm.expect(body).to.have.property('amount');",
                  "    pm.expect(body).to.have.property('currency');",
                  "    pm.expect(body).to.have.property('externalId');",
                  "    pm.expect(body).to.have.property('createdAt');",
                  "});",
                  "",
                  "// Persist the payment id for chained requests",
                  "if (pm.response.code === 200) {",
                  "    const body = pm.response.json();",
                  "    pm.collectionVariables.set('paymentId', body.id);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"amount\": 1.99,\n  \"currency\": \"USD\",\n  \"externalId\": \"order_001\",\n  \"description\": \"Test payment\",\n  \"returnUrl\": \"https://example.com/return\",\n  \"callbackUrl\": \"https://example.com/callback\",\n  \"customer\": {\n    \"email\": \"customer@example.com\",\n    \"name\": \"Jane Doe\",\n    \"externalId\": \"cust_001\"\n  },\n  \"billing\": {\n    \"country\": \"US\",\n    \"postalCode\": \"10001\",\n    \"line1\": \"123 Main St\",\n    \"city\": \"New York\",\n    \"state\": \"NY\"\n  },\n  \"metadata\": {\n    \"orderId\": \"12345\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/payments",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "payments"
              ]
            },
            "description": "Create a payment. Two flows:\n- **HPP** (no `cardToken`): creates a hosted payment page invoice and returns a `redirectUrl` the merchant must send the customer to.\n- **Token** (with `cardToken`): charges a stored card token directly server-to-server.\n\n`externalId` is unique per merchant — repeated requests with the same `externalId` return the original payment without re-charging.\n\n**Auth:** Basic Auth (merchant role).\n\n**Errors:** `400` validation failure, `401` bad credentials, `403` not merchant / merchant not active, `500` no active provider, `502` bad provider response, `503` provider unreachable."
          },
          "response": []
        },
        {
          "name": "Get payment by ID",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status code is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "",
                  "pm.test('Response is valid JSON', function () {",
                  "    pm.response.to.be.json;",
                  "});",
                  "",
                  "pm.test('Response has required payment fields', function () {",
                  "    const body = pm.response.json();",
                  "    pm.expect(body).to.have.property('id');",
                  "    pm.expect(body).to.have.property('status');",
                  "    pm.expect(body).to.have.property('amount');",
                  "    pm.expect(body).to.have.property('currency');",
                  "    pm.expect(body).to.have.property('externalId');",
                  "    pm.expect(body).to.have.property('amountRefunded');",
                  "    pm.expect(body).to.have.property('amountRefundable');",
                  "    pm.expect(body).to.have.property('createdAt');",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/payments/{{paymentId}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "payments",
                "{{paymentId}}"
              ],
              "variable": [
                {
                  "key": "paymentId",
                  "value": "{{paymentId}}",
                  "description": "ID of the payment to retrieve."
                }
              ]
            },
            "description": "Returns a payment by its ID. Only returns payments owned by the authenticated merchant.\n\nResponse includes `amountRefunded` (successfully refunded) and `amountRefundable` (still available, with in-flight refunds reserved).\n\n**Auth:** Basic Auth (merchant role).\n\n**Errors:** `401` bad credentials, `403` not merchant / merchant not active, `404` payment not found or not owned by this merchant."
          },
          "response": []
        },
        {
          "name": "Refund payment",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status code is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "",
                  "pm.test('Response is valid JSON', function () {",
                  "    pm.response.to.be.json;",
                  "});",
                  "",
                  "pm.test('Response has required refund fields', function () {",
                  "    const body = pm.response.json();",
                  "    pm.expect(body).to.have.property('id');",
                  "    pm.expect(body).to.have.property('paymentId');",
                  "    pm.expect(body).to.have.property('status');",
                  "    pm.expect(body).to.have.property('amount');",
                  "    pm.expect(body).to.have.property('currency');",
                  "    pm.expect(body).to.have.property('externalId');",
                  "    pm.expect(body).to.have.property('createdAt');",
                  "});",
                  "",
                  "// Persist the refund id for chained requests",
                  "if (pm.response.code === 200) {",
                  "    const body = pm.response.json();",
                  "    pm.collectionVariables.set('refundId', body.id);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"externalId\": \"refund_001\",\n  \"amount\": 1.99,\n  \"reason\": \"customer_requested\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/payments/{{paymentId}}/refund",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "payments",
                "{{paymentId}}",
                "refund"
              ],
              "variable": [
                {
                  "key": "paymentId",
                  "value": "{{paymentId}}",
                  "description": "ID of the payment to refund."
                }
              ]
            },
            "description": "Refund a payment.\n\nCalls the provider inline and returns whatever status the provider reports — possibly still `processing` if the provider hasn't finished, in which case the inbound Corefy refund webhook will reconcile the refund to a terminal status.\n\nMultiple partial refunds are allowed up to the original payment amount; only one in-flight refund per payment at a time. Omit `amount` to refund the remaining refundable balance.\n\n`externalId` is unique per merchant — repeated requests return the original refund without re-charging the provider.\n\n**Auth:** Basic Auth (merchant role).\n\n**Errors:** `400` validation failure, `401` bad credentials, `403` not merchant / merchant not active, `404` payment not found, `409` payment not refundable or refund already in flight, `422` amount exceeds remaining balance, `502` bad provider response, `503` provider unreachable."
          },
          "response": []
        },
        {
          "name": "List refunds for a payment",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status code is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "",
                  "pm.test('Response is valid JSON', function () {",
                  "    pm.response.to.be.json;",
                  "});",
                  "",
                  "pm.test('Response has refunds array, newest first', function () {",
                  "    const body = pm.response.json();",
                  "    pm.expect(body).to.have.property('refunds');",
                  "    pm.expect(body.refunds).to.be.an('array');",
                  "    body.refunds.forEach((r) => {",
                  "        pm.expect(r).to.have.property('id');",
                  "        pm.expect(r).to.have.property('paymentId');",
                  "        pm.expect(r).to.have.property('status');",
                  "        pm.expect(r).to.have.property('amount');",
                  "        pm.expect(r).to.have.property('currency');",
                  "        pm.expect(r).to.have.property('externalId');",
                  "        pm.expect(r).to.have.property('createdAt');",
                  "    });",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/payments/{{paymentId}}/refunds",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "payments",
                "{{paymentId}}",
                "refunds"
              ],
              "variable": [
                {
                  "key": "paymentId",
                  "value": "{{paymentId}}",
                  "description": "ID of the payment to list refunds for."
                }
              ]
            },
            "description": "Returns all refunds for a payment, newest first. Only returns refunds owned by the authenticated merchant. Returns an empty array if the payment has no refunds.\n\n**Auth:** Basic Auth (merchant role).\n\n**Errors:** `401` bad credentials, `403` not merchant / merchant not active, `404` payment not found or not owned by this merchant."
          },
          "response": []
        },
        {
          "name": "Get refund by ID",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status code is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "",
                  "pm.test('Response is valid JSON', function () {",
                  "    pm.response.to.be.json;",
                  "});",
                  "",
                  "pm.test('Response has required refund fields', function () {",
                  "    const body = pm.response.json();",
                  "    pm.expect(body).to.have.property('id');",
                  "    pm.expect(body).to.have.property('paymentId');",
                  "    pm.expect(body).to.have.property('status');",
                  "    pm.expect(body).to.have.property('amount');",
                  "    pm.expect(body).to.have.property('currency');",
                  "    pm.expect(body).to.have.property('externalId');",
                  "    pm.expect(body).to.have.property('createdAt');",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/refunds/{{refundId}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "refunds",
                "{{refundId}}"
              ],
              "variable": [
                {
                  "key": "refundId",
                  "value": "{{refundId}}",
                  "description": "ID of the refund to retrieve."
                }
              ]
            },
            "description": "Returns a refund by its ID. Only returns refunds owned by the authenticated merchant.\n\n**Auth:** Basic Auth (merchant role).\n\n**Errors:** `401` bad credentials, `403` not merchant / merchant not active, `404` refund not found or not owned by this merchant."
          },
          "response": []
        },
        {
          "name": "Subscriptions",
          "item": [
            {
              "name": "Create subscription",
              "event": [
                {
                  "listen": "test",
                  "script": {
                    "type": "text/javascript",
                    "exec": [
                      "pm.test('Status code is 200', function () {",
                      "    pm.response.to.have.status(200);",
                      "});",
                      "",
                      "pm.test('Response is valid JSON', function () {",
                      "    pm.response.to.be.json;",
                      "});",
                      "",
                      "pm.test('Response has required subscription fields', function () {",
                      "    const body = pm.response.json();",
                      "    pm.expect(body).to.have.property('id');",
                      "    pm.expect(body).to.have.property('status');",
                      "    pm.expect(body).to.have.property('amount');",
                      "    pm.expect(body).to.have.property('currency');",
                      "    pm.expect(body).to.have.property('billingIntervalDays');",
                      "    pm.expect(body).to.have.property('externalId');",
                      "    pm.expect(body).to.have.property('currentPeriodStart');",
                      "    pm.expect(body).to.have.property('currentPeriodEnd');",
                      "    pm.expect(body).to.have.property('createdAt');",
                      "});",
                      "",
                      "pm.test('Response has the full phases schedule', function () {",
                      "    const body = pm.response.json();",
                      "    pm.expect(body).to.have.property('phases');",
                      "    pm.expect(body.phases).to.be.an('array').that.is.not.empty;",
                      "    body.phases.forEach(function (p) {",
                      "        pm.expect(p).to.have.property('id');",
                      "        pm.expect(p).to.have.property('amount');",
                      "        pm.expect(p).to.have.property('billingIntervalDays');",
                      "        pm.expect(p).to.have.property('iterations');",
                      "        pm.expect(p).to.have.property('order');",
                      "    });",
                      "});",
                      "",
                      "pm.test('Schedule ends with a recurring phase (iterations null)', function () {",
                      "    const body = pm.response.json();",
                      "    pm.expect(body.phases.some(function (p) { return p.iterations === null; })).to.be.true;",
                      "});",
                      "",
                      "// Persist the subscription id for chained requests",
                      "if (pm.response.code === 200) {",
                      "    const body = pm.response.json();",
                      "    pm.collectionVariables.set('subscriptionId', body.id);",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"amount\": 9.99,\n  \"currency\": \"USD\",\n  \"billingIntervalDays\": 30,\n  \"externalId\": \"sub_001\",\n  \"phases\": [\n    {\n      \"name\": \"intro\",\n      \"amount\": 4.99,\n      \"billingIntervalDays\": 30,\n      \"iterations\": 3\n    }\n  ],\n  \"description\": \"Monthly subscription\",\n  \"returnUrl\": \"https://example.com/return\",\n  \"callbackUrl\": \"https://example.com/callback\",\n  \"customer\": {\n    \"email\": \"customer@example.com\",\n    \"name\": \"Jane Doe\",\n    \"externalId\": \"cust_001\"\n  },\n  \"billing\": {\n    \"country\": \"US\",\n    \"postalCode\": \"10001\",\n    \"line1\": \"123 Main St\",\n    \"city\": \"New York\",\n    \"state\": \"NY\"\n  },\n  \"metadata\": {\n    \"plan\": \"premium\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/subscriptions",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "subscriptions"
                  ]
                },
                "description": "Create a subscription. Two flows:\n- **HPP** (no `cardToken`): creates a hosted payment page invoice and returns a `redirectUrl` the merchant must send the customer to. Once the customer completes payment, the card is tokenized and the subscription activates.\n- **Token** (with `cardToken`): charges a stored card token directly server-to-server.\n\nIn both cases the subscription starts in `pending` status and transitions to `active` when the Corefy webhook confirms payment success.\n\n`externalId` is unique per merchant — repeated requests with the same `externalId` return the original subscription without re-charging.\n\n**Intro pricing (`phases`, optional):** pass an ordered `phases` array to bill introductory/ramp pricing before the recurring terms. Each phase has `amount`, `billingIntervalDays`, `iterations` (cycles to run) and an optional `name`, and the phases run first in array order. The top-level `amount` / `billingIntervalDays` stay the recurring terms — the server appends the recurring phase automatically. Omit `phases` for a plain recurring subscription. The response's `phases` array is always the full ordered schedule (any intro phases followed by the final recurring phase, which has `iterations: null`).\n\n**Auth:** Basic Auth (merchant role)."
              },
              "response": []
            },
            {
              "name": "Get subscription by ID",
              "event": [
                {
                  "listen": "test",
                  "script": {
                    "type": "text/javascript",
                    "exec": [
                      "pm.test('Status code is 200', function () {",
                      "    pm.response.to.have.status(200);",
                      "});",
                      "",
                      "pm.test('Response is valid JSON', function () {",
                      "    pm.response.to.be.json;",
                      "});",
                      "",
                      "pm.test('Response has required subscription fields', function () {",
                      "    const body = pm.response.json();",
                      "    pm.expect(body).to.have.property('id');",
                      "    pm.expect(body).to.have.property('status');",
                      "    pm.expect(body).to.have.property('amount');",
                      "    pm.expect(body).to.have.property('currency');",
                      "    pm.expect(body).to.have.property('billingIntervalDays');",
                      "    pm.expect(body).to.have.property('externalId');",
                      "    pm.expect(body).to.have.property('currentPeriodStart');",
                      "    pm.expect(body).to.have.property('currentPeriodEnd');",
                      "    pm.expect(body).to.have.property('createdAt');",
                      "});",
                      "",
                      "pm.test('Response has the full phases schedule', function () {",
                      "    const body = pm.response.json();",
                      "    pm.expect(body).to.have.property('phases');",
                      "    pm.expect(body.phases).to.be.an('array').that.is.not.empty;",
                      "    pm.expect(body.phases.some(function (p) { return p.iterations === null; })).to.be.true;",
                      "});"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/subscriptions/{{subscriptionId}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "subscriptions",
                    "{{subscriptionId}}"
                  ],
                  "variable": [
                    {
                      "key": "subscriptionId",
                      "value": "{{subscriptionId}}",
                      "description": "ID of the subscription to retrieve."
                    }
                  ]
                },
                "description": "Returns a subscription by its ID. Only returns subscriptions owned by the authenticated merchant.\n\n**Auth:** Basic Auth (merchant role).\n\n**Errors:** `401` bad credentials, `403` not merchant / merchant not active, `404` subscription not found or not owned by this merchant."
              },
              "response": []
            },
            {
              "name": "Cancel subscription",
              "event": [
                {
                  "listen": "test",
                  "script": {
                    "type": "text/javascript",
                    "exec": [
                      "pm.test('Status code is 200', function () {",
                      "    pm.response.to.have.status(200);",
                      "});",
                      "",
                      "pm.test('Response is valid JSON', function () {",
                      "    pm.response.to.be.json;",
                      "});",
                      "",
                      "pm.test('Response has required subscription fields', function () {",
                      "    const body = pm.response.json();",
                      "    pm.expect(body).to.have.property('id');",
                      "    pm.expect(body).to.have.property('status');",
                      "    pm.expect(body.status).to.equal('cancelled');",
                      "    pm.expect(body).to.have.property('phases');",
                      "    pm.expect(body.phases).to.be.an('array').that.is.not.empty;",
                      "});"
                    ]
                  }
                }
              ],
              "request": {
                "method": "DELETE",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/subscriptions/{{subscriptionId}}?cancellationReason=merchant_cancelled",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "subscriptions",
                    "{{subscriptionId}}"
                  ],
                  "query": [
                    {
                      "key": "cancellationReason",
                      "value": "merchant_cancelled",
                      "description": "Optional reason for cancellation."
                    }
                  ],
                  "variable": [
                    {
                      "key": "subscriptionId",
                      "value": "{{subscriptionId}}",
                      "description": "ID of the subscription to cancel."
                    }
                  ]
                },
                "description": "Cancel an active or past_due subscription. Cancellation is immediate — the subscription will not renew. The current period remains valid (no prorated refund at MVP).\n\n**Auth:** Basic Auth (merchant role).\n\n**Errors:** `401` bad credentials, `403` not merchant / merchant not active, `404` subscription not found, `400` subscription not in cancellable state."
              },
              "response": []
            }
          ],
          "description": "Subscription management endpoints for creating, retrieving, and cancelling subscriptions."
        },
        {
          "name": "List API keys",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status code is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "",
                  "pm.test('Response is valid JSON', function () {",
                  "    pm.response.to.be.json;",
                  "});",
                  "",
                  "pm.test('Body has apiKeys array', function () {",
                  "    const body = pm.response.json();",
                  "    pm.expect(body).to.have.property('apiKeys');",
                  "    pm.expect(body.apiKeys).to.be.an('array');",
                  "});",
                  "",
                  "pm.test('No secretKey is leaked', function () {",
                  "    const body = pm.response.json();",
                  "    (body.apiKeys || []).forEach(function (k) {",
                  "        pm.expect(k).to.not.have.property('secretKey');",
                  "    });",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/merchant/api-keys",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "merchant",
                "api-keys"
              ]
            },
            "description": "Returns all API keys (active and revoked) belonging to the currently authenticated merchant. Secret keys are never included.\n\n**Auth:** Basic Auth (merchant role)."
          },
          "response": []
        },
        {
          "name": "Set provider credentials",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "pm.test('Status code is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "",
                  "pm.test('Response is valid JSON', function () {",
                  "    pm.response.to.be.json;",
                  "});",
                  "",
                  "pm.test('Response has required fields', function () {",
                  "    const body = pm.response.json();",
                  "    pm.expect(body).to.have.property('id');",
                  "    pm.expect(body).to.have.property('merchantId');",
                  "    pm.expect(body).to.have.property('provider');",
                  "    pm.expect(body).to.have.property('createdAt');",
                  "});"
                ],
                "type": "text/javascript",
                "packages": {},
                "requests": {}
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"provider\": \"corefy\",\n  \"environment\": \"live\",\n  \"publicKey\": \"your_corefy_public_key\",\n  \"secretKey\": \"your_corefy_secret_key\",\n  \"webhookSecret\": \"your_corefy_webhook_secret\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/merchant/provider-credentials",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "merchant",
                "provider-credentials"
              ]
            },
            "description": "Set (or rotate) the authenticated merchant's provider credentials.\n\nIf the merchant already has active credentials for the given provider and environment, the old row is deactivated and a new one is created — making rotation a single atomic operation.\n\nCredentials are stored in the database. The response returns only the row ID and metadata — never the credential values. Use `GET /merchant/provider-credentials` afterwards to see masked previews.\n\n**Auth:** Basic Auth (merchant role).\n\n**Errors:** `400` missing required fields, `401` bad credentials, `403` not merchant / merchant not active, `404` provider not found."
          },
          "response": []
        }
      ],
      "description": "Self-service endpoints for an authenticated merchant to manage their own data.",
      "auth": {
        "type": "basic",
        "basic": [
          {
            "key": "username",
            "value": "{{merchant_public_key}}",
            "type": "string"
          },
          {
            "key": "password",
            "value": "{{merchant_secret_key}}",
            "type": "string"
          }
        ]
      }
    }
  ],
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "packages": {},
        "requests": {},
        "exec": [
          ""
        ]
      }
    },
    {
      "listen": "test",
      "script": {
        "type": "text/javascript",
        "requests": {},
        "exec": [
          "// Collection-level default tests — applied to every request unless overridden.",
          "pm.test('Status code is successful (2xx)', function () {",
          "    pm.expect(pm.response.code).to.be.within(200, 299);",
          "});",
          "",
          "pm.test('Response time is under 2000ms', function () {",
          "    pm.expect(pm.response.responseTime).to.be.below(2000);",
          "});"
        ]
      }
    }
  ],
  "variable": [
    {
      "key": "base_url",
      "value": "https://sandbox.mintcash.me",
      "description": "Fallback if no environment is selected. Override via the environment."
    },
    {
      "key": "merchant_public_key",
      "value": "",
      "description": "Merchant-role public key (e.g. `pk_test_xxx`). Used by the `Merchant / *` folder."
    },
    {
      "key": "merchant_secret_key",
      "value": "",
      "description": "Merchant-role secret key (e.g. `sk_test_xxx`). Set via the environment as a **secret** variable — never commit."
    },
    {
      "key": "paymentId",
      "value": "pay_123",
      "description": "Payment ID used in the get-payment and refund endpoints. Auto-set by 'Create payment'."
    },
    {
      "key": "refundId",
      "value": "ref_123",
      "description": "Refund ID used in the get-refund endpoint. Auto-set by 'Refund payment'."
    },
    {
      "key": "subscriptionId",
      "value": "sub_123",
      "description": "Subscription ID used in get/cancel endpoints. Auto-set by 'Create subscription'."
    }
  ]
}
