npm package: @kerembay9/horizon-pay
Description: TypeScript client for Horizon Zeta Payment API — create sessions, checkout URLs, complete payments, PayTR iframe
Latest version: 1.0.11

# horizon-pay

TypeScript/JavaScript client for the **Horizon Zeta Payment** API. Create sessions, get payment info, build checkout URLs, and complete payments with a simple, typed API.

**Runtime:** Node 18+ (native `fetch`) or any browser with `fetch`.  
**Package:** ESM + CJS, TypeScript types included.

---

## Quick start

```bash
npm install horizon-pay
```

```ts
import { PaymentClient } from "horizon-pay";

const client = new PaymentClient({
  baseUrl: "https://pay.horizonzeta.com",
  apiKey: "sk_your_api_key_here",
});

// Backend: create session and get checkout URL
const { token } = await client.createSession({
  amount: 99.99,
  payerEmail: "user@example.com",
  currency: "TL",
  successRedirectUrl: "https://yoursite.com/success",
  failRedirectUrl: "https://yoursite.com/failed",
});

const checkoutUrl = client.getCheckoutUrl(token, "tr");
// Redirect the user to checkoutUrl.
```

---

## Package summary (for developers and AI)

- **Package name:** `horizon-pay`
- **Exports:** `PaymentClient` (class), `PaymentClientError` (class), and types: `PaymentClientConfig`, `CreateSessionParams`, `CreateSessionResponse`, `PaymentInfo`, `CompletePaymentParams`, `CompletePaymentResponse`.
- **Flow:** (1) Backend creates a session with `createSession` (requires API key) and gets `token`. (2) Backend builds checkout URL with `getCheckoutUrl(token, locale)` and redirects the user. (3) On the checkout page, frontend calls `getPaymentInfo(token)` then `completePayment({ token, userName, userAddress, userPhone })` to get `iframeToken` for the PayTR iframe.
- **Auth:** API key is only required for `createSession`; it can be set in the client config or passed in `createSession(..., { apiKey })`. Other methods use the session `token` only.
- **Errors:** All failed requests throw `PaymentClientError` with `message`, optional `status`, and optional `body`.

---

## Setup

```ts
import { PaymentClient } from "horizon-pay";

const client = new PaymentClient({
  baseUrl: "https://pay.horizonzeta.com",
  apiKey: "sk_your_api_key_here", // required for createSession; optional here if passed per-call
});
```

| Config field  | Type     | Description                                      |
|---------------|----------|--------------------------------------------------|
| `baseUrl`     | `string` | Base URL of the payment service (no trailing `/`) |
| `apiKey`      | `string` | Optional; required when calling `createSession`   |

---

## Usage

### Backend: create session and return checkout URL

```ts
const { token } = await client.createSession({
  amount: 99.99,
  payerEmail: "user@example.com",
  currency: "TL",
  successRedirectUrl: "https://yoursite.com/success",
  failRedirectUrl: "https://yoursite.com/failed",
  webhookUrl: "https://yoursite.com/webhooks/payment", // optional
  metadata: { plan: "pro", userId: "123" },            // optional
});

const checkoutUrl = client.getCheckoutUrl(token, "tr"); // or "en"
// Redirect the user to checkoutUrl.
```

### Frontend (or Node): get payment info and complete payment

```ts
// Get session info (e.g. token from URL query)
const info = await client.getPaymentInfo(token);
console.log(info.totalPayment, info.currency, info.payment_type);

// When user submits the form, complete payment and show PayTR iframe
const { iframeToken } = await client.completePayment({
  token,
  userName: "John Doe",
  userAddress: "Istanbul, Turkey",
  userPhone: "+905551234567",
});
// Use iframeToken in the PayTR iframe as per PayTR docs.
```

### Checkout URL helper

```ts
client.getCheckoutUrl(token);        // Turkish checkout: .../tr?token=...
client.getCheckoutUrl(token, "en");  // English: .../en?token=...
```

---

## API reference

### `PaymentClient`

| Method | Description |
|--------|-------------|
| `createSession(params, options?)` | Create a payment session. Requires API key (in config or `options.apiKey`). Returns `{ token }`. |
| `getPaymentInfo(token)` | Get payment/session info by token. Returns `PaymentInfo`. |
| `getCheckoutUrl(token, locale?)` | Build checkout page URL. `locale`: `"tr"` \| `"en"`, default `"tr"`. |
| `completePayment(params)` | Submit customer details. Returns `{ iframeToken }` for PayTR iframe. |

### Types

#### `PaymentClientConfig`

| Field     | Type     | Required | Description |
|-----------|----------|----------|-------------|
| `baseUrl` | `string` | Yes      | Base URL of the payment service. |
| `apiKey`  | `string` | No       | API key for `createSession`. |

#### `CreateSessionParams`

| Field                | Type     | Required | Description |
|----------------------|----------|----------|-------------|
| `amount`             | `number` | Yes      | Payment amount (e.g. in TL). |
| `payerEmail`         | `string` | Yes      | Payer email address. |
| `currency`           | `string` | No       | Currency code; default `"TL"`. |
| `paymentType`        | `"one_time"` \| `"subscription"` | No | Default `"one_time"`. |
| `metadata`           | `Record<string, unknown>` | No | Optional metadata (discounts, plan info, IDs, etc.). |
| `productDetailTextTr` | `string` | No | Convenience field; localized product name (Turkish). Written into `metadata.product_detail_text_tr`. |
| `productDetailTextEn` | `string` | No | Convenience field; localized product name (English). Written into `metadata.product_detail_text_en`. |
| `successRedirectUrl` | `string` | No       | Redirect after successful payment. |
| `failRedirectUrl`    | `string` | No       | Redirect after failed payment. |
| `webhookUrl`         | `string` | No       | Webhook for payment completion. |

#### `CreateSessionResponse`

| Field   | Type     | Description |
|---------|----------|-------------|
| `token` | `string` | Session token for checkout URL and later calls. |

#### `PaymentInfo`

Returned by `getPaymentInfo(token)`.

| Field             | Type     | Description |
|-------------------|----------|-------------|
| `totalPayment`    | `number` | Total amount. |
| `discount`        | `number` | Discount applied. |
| `upgradeDisc`     | `number` | Upgrade discount. |
| `users`           | `unknown[]` | User-related data. |
| `plan`            | `unknown \| null` | Plan info. |
| `productDetailText` | `unknown \| null` | Product text (legacy/general field). |
| `productDetailTextTr` | `unknown \| null` | Localized product name (Turkish), from `metadata.product_detail_text_tr`. |
| `productDetailTextEn` | `unknown \| null` | Localized product name (English), from `metadata.product_detail_text_en`. |
| `currency`        | `string` | Currency code. |
| `payment_type`    | `string` | e.g. `"one_time"`. |

#### `CompletePaymentParams`

| Field         | Type     | Required | Description |
|---------------|----------|----------|-------------|
| `token`       | `string` | Yes      | Session token from `createSession`. |
| `userName`    | `string` | Yes      | Customer full name. |
| `userAddress`| `string` | Yes      | Customer address. |
| `userPhone`  | `string` | Yes      | Customer phone. |

#### `CompletePaymentResponse`

| Field         | Type     | Description |
|---------------|----------|-------------|
| `iframeToken` | `string` | Token for the PayTR iframe. |

### `PaymentClientError`

Thrown on 4xx/5xx or network failure.

| Property  | Type     | Description |
|-----------|----------|-------------|
| `message` | `string` | Human-readable message (or server `error` when JSON). |
| `status`  | `number` | HTTP status code (optional). |
| `body`    | `unknown`| Raw response body (optional). |

```ts
import { PaymentClient, PaymentClientError } from "horizon-pay";

try {
  await client.createSession({ ... });
} catch (err) {
  if (err instanceof PaymentClientError) {
    console.error(err.status, err.message, err.body);
  }
  throw err;
}
```

---

## End-to-end flow (summary)

1. **Backend:** `createSession(...)` → get `token` → `getCheckoutUrl(token, "tr")` → redirect user to this URL.
2. **Checkout page:** Read `token` from URL → `getPaymentInfo(token)` → show amount/details → user fills form → `completePayment({ token, userName, userAddress, userPhone })` → get `iframeToken` → embed PayTR iframe with `iframeToken`.
3. User pays in iframe; then redirect to `successRedirectUrl` or `failRedirectUrl`.

---

## License

MIT