Skip to main content
Glama
seyithanalkan

iyzico MCP Server

README.md
# iyzico MCP Server

A [Model Context Protocol](https://modelcontextprotocol.io) server that gives AI
agents a typed, safe interface to the [iyzico](https://www.iyzico.com) payments
API. Look up card BINs and installment plans, spin up hosted checkout forms,
inspect payments, issue refunds and cancellations, and query recurring
subscriptions — all from Claude Desktop, Claude Code, Cursor, or any MCP client.

iyzico is one of the most widely used payment gateways in Turkey. It exposes a
rich REST API but signs every request with a non-trivial `IYZWSv2` HMAC-SHA256
scheme; this server implements that correctly so you don't have to, and wraps
each operation in a well-described, zod-validated tool.

> **Status:** unofficial, community-built. Not affiliated with or endorsed by
> iyzico. Defaults to the **sandbox** environment so you can't accidentally move
> real money while experimenting.

---

## Features

- **10 well-scoped tools** covering BIN/installment lookups, checkout forms,
  payment inspection, refunds/cancellations, and subscription queries.
- **Correct `IYZWSv2` request signing** (HMAC-SHA256), verified by a unit test
  against an independent reference implementation.
- **Input validation with [zod](https://zod.dev)** — malformed arguments are
  rejected before they ever reach iyzico.
- **Graceful, informative errors.** iyzico reports business failures as HTTP 200
  with a `status: "failure"` body; this server surfaces those as real errors
  with the iyzico `errorCode`, so agents can react instead of proceeding on a
  silent failure.
- **Sandbox-first & safe by default.** Write operations (`cancel_payment`,
  `refund_payment`) are annotated as destructive so clients can warn the user.
- **Runs over stdio** — the standard transport for local MCP servers.

---

## Requirements

- Node.js **18+** (uses the built-in global `fetch`).
- iyzico API credentials:
  - Sandbox: create a free merchant account at
    <https://sandbox-merchant.iyzipay.com> → **Settings → API Keys**.
  - Production: <https://merchant.iyzipay.com> → **Settings → API Keys**.

---

## Installation

### From source (current)

```bash
git clone <your-fork-url> iyzico-mcp
cd iyzico-mcp
npm install
npm run build
```

The built entrypoint is `dist/index.js`. You can run it directly:

```bash
IYZICO_API_KEY=sandbox-xxxx \
IYZICO_SECRET_KEY=sandbox-yyyy \
IYZICO_ENVIRONMENT=sandbox \
node dist/index.js
```

### Via npx (once published)

If/after this package is published to npm, it can be launched without a local
checkout:

```bash
npx -y iyzico-mcp
```

The config snippets below show both forms.

---

## Configuration

All configuration is via environment variables:

| Variable             | Required | Default    | Description                                   |
| -------------------- | -------- | ---------- | --------------------------------------------- |
| `IYZICO_API_KEY`     | yes      | —          | Your iyzico API key.                          |
| `IYZICO_SECRET_KEY`  | yes      | —          | Your iyzico secret key.                       |
| `IYZICO_ENVIRONMENT` | no       | `sandbox`  | `sandbox` (safe) or `production` (live money).|
| `IYZICO_TIMEOUT_MS`  | no       | `30000`    | Per-request timeout in milliseconds.          |

Copy `.env.example` to `.env` for local reference. Credentials are read lazily,
so the server will still start and list its tools without them — you'll only get
an error (naming the missing variable) when you actually call a tool.

### Verify your setup

```bash
# List the tools without any credentials (offline):
node dist/index.js --list-tools

# Confirm your credentials authenticate against iyzico:
#   then call the `ping` tool from your MCP client.
```

---

## Client setup

### Claude Desktop

Edit the config file:

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

**Using a local build:**

```json
{
  "mcpServers": {
    "iyzico": {
      "command": "node",
      "args": ["/absolute/path/to/iyzico-mcp/dist/index.js"],
      "env": {
        "IYZICO_API_KEY": "sandbox-xxxx",
        "IYZICO_SECRET_KEY": "sandbox-yyyy",
        "IYZICO_ENVIRONMENT": "sandbox"
      }
    }
  }
}
```

**Using npx (once published):**

```json
{
  "mcpServers": {
    "iyzico": {
      "command": "npx",
      "args": ["-y", "iyzico-mcp"],
      "env": {
        "IYZICO_API_KEY": "sandbox-xxxx",
        "IYZICO_SECRET_KEY": "sandbox-yyyy",
        "IYZICO_ENVIRONMENT": "sandbox"
      }
    }
  }
}
```

Restart Claude Desktop after editing.

### Claude Code

Add the server with the `claude mcp add` command:

```bash
# Local build:
claude mcp add iyzico \
  --env IYZICO_API_KEY=sandbox-xxxx \
  --env IYZICO_SECRET_KEY=sandbox-yyyy \
  --env IYZICO_ENVIRONMENT=sandbox \
  -- node /absolute/path/to/iyzico-mcp/dist/index.js

# npx (once published):
claude mcp add iyzico \
  --env IYZICO_API_KEY=sandbox-xxxx \
  --env IYZICO_SECRET_KEY=sandbox-yyyy \
  --env IYZICO_ENVIRONMENT=sandbox \
  -- npx -y iyzico-mcp
```

Or add it to `.mcp.json` in your project root:

```json
{
  "mcpServers": {
    "iyzico": {
      "command": "node",
      "args": ["/absolute/path/to/iyzico-mcp/dist/index.js"],
      "env": {
        "IYZICO_API_KEY": "sandbox-xxxx",
        "IYZICO_SECRET_KEY": "sandbox-yyyy",
        "IYZICO_ENVIRONMENT": "sandbox"
      }
    }
  }
}
```

### Cursor

Add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per-project) using
the same JSON shape as the Claude Desktop example above.

---

## Tools

| Tool                            | Type       | What it does                                                        |
| ------------------------------- | ---------- | ------------------------------------------------------------------- |
| `ping`                          | read-only  | Verify connectivity and that your credentials authenticate.         |
| `check_bin`                     | read-only  | Look up bank/scheme/type metadata for a card BIN (first 6-8 digits). |
| `retrieve_installment_info`     | read-only  | Get installment plans and per-installment pricing for a BIN + amount.|
| `initialize_checkout_form`      | **write**  | Start a hosted checkout form; returns a payment page URL and token.  |
| `retrieve_checkout_form_result` | read-only  | Fetch the outcome of a checkout form by its token.                   |
| `retrieve_payment`              | read-only  | Get full detail of a payment (status, amounts, item transactions).   |
| `cancel_payment`                | **write**  | Void a payment in full (same-day, before settlement).                |
| `refund_payment`                | **write**  | Refund a payment transaction, in full or in part.                    |
| `search_subscriptions`          | read-only  | List/filter recurring subscriptions with paging.                     |
| `retrieve_subscription`         | read-only  | Get detail of one subscription by its reference code.                |

Write tools are annotated as destructive (`readOnlyHint: false`,
`destructiveHint: true`) so MCP clients can prompt for confirmation.

### Example prompts

Once connected, you can ask your agent things like:

- *"Check the BIN 554960 and tell me which bank issued it and whether it's a
  credit or debit card."* → `check_bin`
- *"For a 1,200 TRY purchase on BIN 552879, what installment options are
  available and what's the total cost at 6 installments?"* →
  `retrieve_installment_info`
- *"Create a checkout form for a 249.90 TRY order for buyer Ada Lovelace, one
  virtual item, callback to https://example.com/iyzico/callback."* →
  `initialize_checkout_form`
- *"Look up payment 12345678 and refund the first basket item in full."* →
  `retrieve_payment` then `refund_payment`
- *"How many ACTIVE subscriptions do we have, and show me the ones for customer
  CUST-42."* → `search_subscriptions`

### Example tool call

Arguments passed to `check_bin`:

```json
{
  "binNumber": "554960",
  "locale": "en"
}
```

Returns iyzico's response as pretty-printed JSON, e.g.:

```json
{
  "status": "success",
  "binNumber": "554960",
  "cardType": "CREDIT_CARD",
  "cardAssociation": "MASTER_CARD",
  "cardFamily": "Bonus",
  "bankName": "Garanti Bankası",
  "bankCode": 62,
  "commercial": 0
}
```

---

## How authentication works

Every request is signed with iyzico's `IYZWSv2` scheme:

1. Generate a random per-request key (sent in the `x-iyzi-rnd` header).
2. Compute `HMAC-SHA256(randomKey + uriPath + JSON(body), secretKey)` as
   lowercase hex. For `GET` endpoints the body is `{}` and parameters travel in
   the query string.
3. Build `apiKey:<key>&randomKey:<rnd>&signature:<hex>`, base64-encode it, and
   send it as `Authorization: IYZWSv2 <base64>`.

This is implemented in [`src/auth.ts`](src/auth.ts) and covered by
[`test/auth.test.mjs`](test/auth.test.mjs).

---

## Development

```bash
npm install       # install dependencies
npm run build     # compile TypeScript to dist/
npm test          # build, run the auth unit test, and the stdio smoke test
npm run list-tools # print the tool catalog as JSON
```

The smoke test launches the built server over stdio **without** any API key and
verifies the MCP handshake, the full tool catalog, tool annotations, and that a
credential-less call returns a clean, actionable error — so it runs safely in CI
with no secrets.

### Project layout

```
src/
  index.ts            CLI entrypoint (stdio transport, --list-tools/--help)
  server.ts           Builds the McpServer and registers all tools
  client.ts           HTTP client: signing, query strings, timeouts, error mapping
  auth.ts             IYZWSv2 HMAC-SHA256 request signing
  config.ts           Env-var configuration + validation
  tools/
    shared.ts         Common request fields, result/error helpers, tool type
    index.ts          Tool registry
    ping.ts, checkBin.ts, installmentInfo.ts, checkoutForm.ts,
    retrievePayment.ts, cancelPayment.ts, refundPayment.ts, subscriptions.ts
test/
  auth.test.mjs       Signature/authorization-header unit test
  smoke.mjs           End-to-end stdio handshake + tool-catalog test
```

---

## Safety notes

- The server **defaults to sandbox**. Set `IYZICO_ENVIRONMENT=production` only
  when you intend to move real money.
- `cancel_payment` and `refund_payment` are irreversible. They are flagged
  destructive for MCP clients, but you remain responsible for confirming intent.
- Never pass full card numbers to `check_bin` — only the 6-8 digit BIN.
- Keep your secret key out of source control. Use environment variables or your
  MCP client's secret storage.

---

## License

[MIT](LICENSE) © Seyithan Alkan