npm package: @montarist/pos
Description: Turkish Virtual POS Integration Library for Node.js/NestJS - Supporting all major Turkish banks and payment institutions
Author: Vignetim
Latest version: 1.0.0

# @vignetim/pos

A comprehensive TypeScript/Node.js library for Turkish Virtual POS (Payment Gateway) integrations. Supports all major Turkish banks and payment institutions with unified API, 3D Secure authentication, and NestJS integration.

[![npm version](https://badge.fury.io/js/%40vignetim%2Fpos.svg)](https://badge.fury.io/js/%40vignetim%2Fpos)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.3-blue.svg)](https://www.typescriptlang.org/)

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Bank Support Matrix](#bank-support-matrix)
- [API Reference](#api-reference)
  - [Authentication](#authentication)
  - [Sale Transaction](#sale-transaction)
  - [3D Secure Flow](#3d-secure-flow)
  - [Cancel (Void)](#cancel-void)
  - [Refund](#refund)
  - [Transaction Query](#transaction-query)
  - [Installment Query](#installment-query)
- [Bank-Specific Configuration](#bank-specific-configuration)
- [NestJS Integration](#nestjs-integration)
- [BIN (Card Number) Lookup](#bin-card-number-lookup)
- [Error Handling](#error-handling)
- [Test Cards](#test-cards)
- [Migration from Other Libraries](#migration-from-other-libraries)
- [Contributing](#contributing)
- [License](#license)

---

## Features

- **Unified API**: Single interface for all banks and payment institutions
- **Type-Safe**: Full TypeScript support with comprehensive type definitions
- **3D Secure**: Complete 3D Secure 1.0/2.0 payment flow support
- **Multi-Currency**: Support for TRY, USD, EUR, GBP
- **Installment Support**: Up to 36 months installment payments
- **Validation**: Built-in request validation with detailed error messages
- **BIN Database**: Credit card BIN lookup for card identification
- **NestJS Ready**: First-class NestJS module support
- **ESM & CJS**: Dual module format support
- **Test Cards**: Built-in test card data for development

---

## Installation

```bash
# Using npm
npm install @vignetim/pos

# Using yarn
yarn add @vignetim/pos

# Using pnpm
pnpm add @vignetim/pos
```

### Requirements

- Node.js >= 18.0.0
- TypeScript >= 5.0 (for TypeScript users)

---

## Quick Start

```typescript
import { VPOSClient, BankCode, Currency, SaleRequest, VirtualPOSAuth } from '@vignetim/pos';

// 1. Configure authentication
const auth = new VirtualPOSAuth({
  bankCode: BankCode.GarantiBBVA,
  merchantId: '7000679',
  merchantUser: 'PROVAUT',
  merchantPassword: '123qweASD/',
  merchantStoreKey: '12345678',
  terminalId: '30691298',
  testPlatform: true, // Set to false for production
});

// 2. Create sale request
const request = new SaleRequest({
  orderNumber: 'ORDER-2024-001',
  customerIpAddress: '192.168.1.100',
  amount: 150.00,
  currency: Currency.TRY,
  installment: 1, // Single payment
  card: {
    holderName: 'John Doe',
    cardNumber: '4155650100416111',
    expiryMonth: 12,
    expiryYear: 2028,
    cvv: '123',
  },
});

// 3. Process payment
const response = await VPOSClient.sale(request, auth);

if (response.isSuccess()) {
  console.log('Payment successful!');
  console.log('Transaction ID:', response.transactionId);
  console.log('Auth Code:', response.authCode);
} else {
  console.error('Payment failed:', response.message);
  console.error('Error Code:', response.errorCode);
}
```

---

## Bank Support Matrix

### Traditional Banks

| Bank | Code | Sale | 3D Secure | Cancel | Refund | Query | Protocol |
|------|------|:----:|:---------:|:------:|:------:|:-----:|----------|
| Garanti BBVA | `0062` | ✅ | ✅ | ✅ | ✅ | ✅ | GVP (XML) |
| Yapi Kredi | `0067` | ✅ | ✅ | ✅ | ✅ | ✅ | Posnet (XML) |
| Akbank (Direct) | `0046` | ✅ | ✅ | ✅ | ✅ | ❌ | JSON API + HMAC-SHA512 |
| Vakifbank | `0015` | ✅ | ✅ | ❌ | ❌ | ❌ | VPos XML |
| Denizbank | `0134` | ✅ | ✅ | ✅ | ✅ | ❌ | InterPOS |
| QNB Finansbank (Direct) | `0111` | ✅ | ✅ | ✅ | ✅ | ❌ | Form POST |

### NestPay (EST) Banks

These banks use the NestPay/EST payment gateway:

| Bank | Bank Code | Sale | 3D Secure | Cancel | Refund | Query |
|------|-----------|:----:|:---------:|:------:|:------:|:-----:|
| Is Bankasi | `0064` | ✅ | ✅ | ✅ | ✅ | ✅ |
| Akbank | `9046` | ✅ | ✅ | ✅ | ✅ | ✅ |
| QNB Finansbank | `9111` | ✅ | ✅ | ✅ | ✅ | ✅ |
| Halkbank | `0012` | ✅ | ✅ | ✅ | ✅ | ✅ |
| Ziraat Bankasi | `0010` | ✅ | ✅ | ✅ | ✅ | ✅ |
| TEB | `0032` | ✅ | ✅ | ✅ | ✅ | ✅ |
| Sekerbank | `0059` | ✅ | ✅ | ✅ | ✅ | ✅ |
| Anadolubank | `0135` | ✅ | ✅ | ✅ | ✅ | ✅ |
| Alternatif Bank | `0124` | ✅ | ✅ | ✅ | ✅ | ✅ |
| ING Bank | `0099` | ✅ | ✅ | ✅ | ✅ | ✅ |
| Turkiye Finans | `0206` | ✅ | ✅ | ✅ | ✅ | ✅ |
| Cardplus | `9998` | ✅ | ✅ | ✅ | ✅ | ✅ |

### Participation Banks (Katilim Bankalari)

| Bank | Code | Sale | 3D Secure | Cancel | Refund | Query | Protocol |
|------|------|:----:|:---------:|:------:|:------:|:-----:|----------|
| Kuveyt Turk | `0205` | ✅ | ✅ | ✅ | ✅ | ✅ | XML + SHA256 |
| Albaraka Turk | `0203` | ✅ | ✅ | ✅ | ✅ | ✅ | JSON |

### Payment Institutions

| Institution | Code | Sale | 3D Secure | Cancel | Refund | Query | Notes |
|-------------|------|:----:|:---------:|:------:|:------:|:-----:|-------|
| Iyzico | `9997` | ✅ | ✅ | ✅ | ✅ | ❌ | Marketplace support |
| Moka | `9984` | ✅ | ✅ | ✅ | ✅ | ❌ | SHA256 CheckKey auth |
| PayTR | `9994` | ✅ | ✅ | ✅ | ✅ | ✅ | iFrame integration |
| iPara | `9995` | ✅ | ✅ | ✅ | ✅ | ✅ | JSON API |
| Hepsipay | `9992` | ✅ | ✅ | ✅ | ✅ | ✅ | JSON API |
| Tosla (AKOde) | `9970` | ✅ | ✅ | ✅ | ✅ | ✅ | JSON API |
| Lidio | `9971` | ✅ | ✅ | ✅ | ✅ | ✅ | JSON API |
| PayU | `9996` | ✅ | ✅ | ✅ | ✅ | ✅ | OAuth 2.0 REST |
| Paratika | `9999` | ✅ | ✅ | ✅ | ✅ | ✅ | Payten MSU |
| Payten | `9993` | ✅ | ✅ | ✅ | ✅ | ✅ | MSU API |
| ZiraatPay | `9982` | ✅ | ✅ | ✅ | ✅ | ✅ | Payten-based |
| VakifPayS | `9981` | ✅ | ✅ | ✅ | ✅ | ✅ | Payten-based |
| Sipay | `9991` | ✅ | ✅ | ✅ | ✅ | ✅ | CCPayment |
| QNBpay | `9990` | ✅ | ✅ | ✅ | ✅ | ✅ | CCPayment |
| PayBull | `9988` | ✅ | ✅ | ✅ | ✅ | ✅ | CCPayment |
| Parolapara | `9987` | ✅ | ✅ | ✅ | ✅ | ✅ | CCPayment |
| IQmoney | `9986` | ✅ | ✅ | ✅ | ✅ | ✅ | CCPayment |
| Vepara | `9983` | ✅ | ✅ | ✅ | ✅ | ✅ | CCPayment |
| ParamPos | `9989` | ✅ | ✅ | ✅ | ✅ | ✅ | SOAP API |
| Tami | `9980` | ✅ | ✅ | ✅ | ✅ | ✅ | JSON API |
| Ahlpay | `9985` | ✅ | ✅ | ✅ | ✅ | ✅ | JSON API |

### Legend

- ✅ Fully supported
- ❌ Not supported / Not implemented
- Bank Codes starting with `9xxx` are virtual codes for payment institutions

---

## API Reference

### Authentication

All operations require a `VirtualPOSAuth` object:

```typescript
import { VirtualPOSAuth, BankCode } from '@vignetim/pos';

const auth = new VirtualPOSAuth({
  // Required fields
  bankCode: BankCode.GarantiBBVA,    // Bank identifier
  merchantId: '7000679',              // Merchant ID from bank
  merchantUser: 'PROVAUT',            // API username
  merchantPassword: '123qweASD/',     // API password

  // Optional fields
  merchantStoreKey: '12345678',       // Required for 3D Secure
  terminalId: '30691298',             // Required by some banks
  testPlatform: true,                 // Use test environment
  options: {},                        // Bank-specific options
});
```

### Sale Transaction

#### 2D (Non-3D Secure) Payment

```typescript
import { VPOSClient, SaleRequest, Currency } from '@vignetim/pos';

const request = new SaleRequest({
  orderNumber: 'ORDER-001',
  customerIpAddress: '192.168.1.100',
  amount: 100.00,
  currency: Currency.TRY,
  installment: 1,
  card: {
    holderName: 'John Doe',
    cardNumber: '4111111111111111',
    expiryMonth: 12,
    expiryYear: 2028,
    cvv: '123',
  },
  // Optional: Billing information
  billingInfo: {
    firstName: 'John',
    lastName: 'Doe',
    email: 'john@example.com',
    phone: '+905551234567',
    address: '123 Main Street',
    city: 'Istanbul',
    country: 'TR',
    postalCode: '34000',
  },
});

const response = await VPOSClient.sale(request, auth);
```

#### Installment Payment

```typescript
const request = new SaleRequest({
  orderNumber: 'ORDER-002',
  customerIpAddress: '192.168.1.100',
  amount: 1200.00,
  currency: Currency.TRY,
  installment: 12, // 12-month installment
  card: {
    holderName: 'John Doe',
    cardNumber: '4111111111111111',
    expiryMonth: 12,
    expiryYear: 2028,
    cvv: '123',
  },
});
```

### 3D Secure Flow

3D Secure payments require a two-step process:

#### Step 1: Initiate 3D Payment

```typescript
import { VPOSClient, SaleRequest, Payment3DConfig, SaleResponseStatus } from '@vignetim/pos';

const request = new SaleRequest({
  orderNumber: 'ORDER-3D-001',
  customerIpAddress: '192.168.1.100',
  amount: 250.00,
  currency: Currency.TRY,
  installment: 1,
  card: {
    holderName: 'John Doe',
    cardNumber: '4111111111111111',
    expiryMonth: 12,
    expiryYear: 2028,
    cvv: '123',
  },
  // Enable 3D Secure
  payment3D: new Payment3DConfig({
    enabled: true,
    returnUrl: 'https://yoursite.com/payment/callback',
    callbackUrl: 'https://yoursite.com/payment/notify', // Optional
  }),
});

const response = await VPOSClient.sale(request, auth);

// Handle 3D redirect
if (response.status === SaleResponseStatus.RedirectUrl) {
  // Redirect customer to the URL
  console.log('Redirect URL:', response.message);
  // res.redirect(response.message);
} else if (response.status === SaleResponseStatus.RedirectHtml) {
  // Render the HTML form that auto-submits
  console.log('HTML Form:', response.secure3D?.htmlContent);
  // res.send(response.secure3D.htmlContent);
}
```

#### Step 2: Handle 3D Callback

```typescript
import { VPOSClient, Sale3DResponseRequest } from '@vignetim/pos';

// In your callback handler (Express example)
app.post('/payment/callback', async (req, res) => {
  const callbackRequest = new Sale3DResponseRequest({
    orderNumber: 'ORDER-3D-001',
    rawData: req.body, // POST data from bank
  });

  const response = await VPOSClient.sale3DResponse(callbackRequest, auth);

  if (response.isSuccess()) {
    // Payment completed successfully
    res.redirect('/payment/success');
  } else {
    // Payment failed
    res.redirect('/payment/failed?error=' + response.message);
  }
});
```

### Cancel (Void)

Cancel a same-day transaction (before settlement):

```typescript
import { VPOSClient, CancelRequest } from '@vignetim/pos';

const cancelRequest = new CancelRequest({
  orderNumber: 'ORDER-001',         // Original order number
  transactionId: 'TXN123456',       // Transaction ID from sale response
});

const response = await VPOSClient.cancel(cancelRequest, auth);

if (response.status === ResponseStatus.Success) {
  console.log('Transaction cancelled successfully');
}
```

### Refund

Refund a transaction (full or partial):

```typescript
import { VPOSClient, RefundRequest, Currency } from '@vignetim/pos';

// Full refund
const fullRefund = new RefundRequest({
  orderNumber: 'ORDER-001',
  transactionId: 'TXN123456',
  amount: 100.00,
  currency: Currency.TRY,
});

// Partial refund
const partialRefund = new RefundRequest({
  orderNumber: 'ORDER-001',
  transactionId: 'TXN123456',
  amount: 50.00, // Refund only 50 TRY
  currency: Currency.TRY,
});

const response = await VPOSClient.refund(fullRefund, auth);

if (response.status === ResponseStatus.Success) {
  console.log('Refunded amount:', response.refundedAmount);
}
```

### Transaction Query

Query the status of a transaction:

```typescript
import { VPOSClient, SaleQueryRequest } from '@vignetim/pos';

const queryRequest = new SaleQueryRequest({
  orderNumber: 'ORDER-001',
  // OR
  transactionId: 'TXN123456',
});

const response = await VPOSClient.saleQuery(queryRequest, auth);

console.log('Transaction Status:', response.transactionStatus);
console.log('Amount:', response.amount);
console.log('Date:', response.transactionDate);
```

### Installment Query

Query available installment options:

```typescript
import { VPOSClient, BINInstallmentQueryRequest } from '@vignetim/pos';

// Query by card BIN
const request = new BINInstallmentQueryRequest({
  bin: '411111',        // First 6 digits of card
  amount: 1000.00,
  currency: Currency.TRY,
});

const response = await VPOSClient.binInstallmentQuery(request, auth);

for (const option of response.installments) {
  console.log(`${option.count} months: ${option.monthlyAmount} TRY/month`);
}
```

---

## Bank-Specific Configuration

### Garanti BBVA

```typescript
const auth = new VirtualPOSAuth({
  bankCode: BankCode.GarantiBBVA,
  merchantId: '7000679',           // Merchant ID (Uye Is Yeri No)
  merchantUser: 'PROVAUT',         // Provision Username
  merchantPassword: '123qweASD/',  // Provision Password
  merchantStoreKey: '12345678',    // 3D Secure Store Key
  terminalId: '30691298',          // Terminal ID
  testPlatform: true,
});
```

### Yapi Kredi (Posnet)

```typescript
const auth = new VirtualPOSAuth({
  bankCode: BankCode.YapiKrediBankasi,
  merchantId: '6706598320',        // Merchant ID
  merchantUser: '67005551',        // Terminal ID (used as user)
  merchantPassword: '',            // Not used for Posnet
  merchantStoreKey: '10,10,10,10,10,10,10,10', // Encryption Key
  terminalId: '67005551',
  testPlatform: true,
});
```

### NestPay Banks (Akbank, Is Bankasi, etc.)

```typescript
const auth = new VirtualPOSAuth({
  bankCode: BankCode.IsBankasi,    // Or any NestPay bank
  merchantId: '100100000',         // Client ID
  merchantUser: 'ISBANK',          // Username
  merchantPassword: 'ISBANK123',   // Password
  merchantStoreKey: 'TRPS1234',    // 3D Secure Store Key
  testPlatform: true,
});
```

### Kuveyt Turk

```typescript
const auth = new VirtualPOSAuth({
  bankCode: BankCode.KuveytTurk,
  merchantId: '496',               // Customer ID (Musteri No)
  merchantUser: '496',             // Merchant ID (Magaza Kodu)
  merchantPassword: 'apiuser',     // API Username
  merchantStoreKey: 'Api123',      // API Password
  testPlatform: true,
});
```

### Albaraka Turk

```typescript
const auth = new VirtualPOSAuth({
  bankCode: BankCode.AlbarakaTurk,
  merchantId: 'merchant123',       // Uye Isyeri No
  terminalId: 'terminal123',       // Terminal No
  merchantUser: 'username',        // API Username
  merchantPassword: 'password',    // API Password
  testPlatform: true,
});
```

### Iyzico

```typescript
const auth = new VirtualPOSAuth({
  bankCode: BankCode.Iyzico,
  merchantId: 'sandbox-xxxxx',     // API Key
  merchantUser: '',                // Not used
  merchantPassword: 'sandbox-secret-xxxxx', // Secret Key
  testPlatform: true,
});
```

### PayTR

```typescript
const auth = new VirtualPOSAuth({
  bankCode: BankCode.PayTR,
  merchantId: 'merchant_id',       // Merchant ID
  merchantUser: 'merchant_key',    // Merchant Key
  merchantPassword: 'merchant_salt', // Merchant Salt
  testPlatform: true,
});
```

### iPara

```typescript
const auth = new VirtualPOSAuth({
  bankCode: BankCode.IPara,
  merchantId: 'public_key',        // Public Key
  merchantUser: '',                // Not used
  merchantPassword: 'private_key', // Private Key
  testPlatform: true,
});
```

### PayU

```typescript
const auth = new VirtualPOSAuth({
  bankCode: BankCode.PayU,
  merchantId: 'pos_id',            // POS ID (merchant_pos_id)
  merchantUser: 'client_id',       // OAuth Client ID
  merchantPassword: 'client_secret', // OAuth Client Secret
  testPlatform: true,
});
```

### CCPayment Banks (Sipay, QNBpay, PayBull, etc.)

```typescript
const auth = new VirtualPOSAuth({
  bankCode: BankCode.Sipay,        // Or QNBpay, PayBull, Parolapara, IQmoney, Vepara
  merchantId: 'merchant_id',       // Uye Isyeri ID
  merchantUser: 'app_key',         // Uygulama Anahtari
  merchantPassword: 'app_secret',  // Uygulama Parolasi
  merchantStoreKey: 'merchant_key', // Uye Isyeri Anahtari
  testPlatform: true,
});
```

---

## NestJS Integration

### Module Setup

```typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { PosModule } from '@vignetim/pos/nestjs';

@Module({
  imports: [
    PosModule.forRoot({
      timeout: 30000,
      debug: process.env.NODE_ENV === 'development',
    }),
  ],
})
export class AppModule {}
```

### Service Usage

```typescript
// payment.service.ts
import { Injectable } from '@nestjs/common';
import { PosService } from '@vignetim/pos/nestjs';
import { SaleRequest, VirtualPOSAuth, Currency, BankCode } from '@vignetim/pos';

@Injectable()
export class PaymentService {
  constructor(private readonly posService: PosService) {}

  async processPayment(orderData: any) {
    const auth = new VirtualPOSAuth({
      bankCode: BankCode.GarantiBBVA,
      merchantId: process.env.POS_MERCHANT_ID,
      merchantUser: process.env.POS_USER,
      merchantPassword: process.env.POS_PASSWORD,
      merchantStoreKey: process.env.POS_STORE_KEY,
      terminalId: process.env.POS_TERMINAL_ID,
      testPlatform: process.env.NODE_ENV !== 'production',
    });

    const request = new SaleRequest({
      orderNumber: orderData.orderId,
      customerIpAddress: orderData.clientIp,
      amount: orderData.amount,
      currency: Currency.TRY,
      installment: orderData.installment || 1,
      card: orderData.card,
    });

    return this.posService.sale(request, auth);
  }
}
```

---

## BIN (Card Number) Lookup

Identify card type and issuing bank from card number:

```typescript
import { VPOSClient } from '@vignetim/pos';

// Query by card number
const result = VPOSClient.queryBIN('411111');

console.log('Bank:', result.bankName);
console.log('Card Brand:', result.cardBrand);    // Visa, Mastercard, etc.
console.log('Card Type:', result.cardType);      // Credit, Debit
console.log('Is Commercial:', result.isCommercial);

// Check card type
const isDebit = VPOSClient.isDebitCard('411111');
const isCommercial = VPOSClient.isCommercialCard('411111');

// Get issuing bank
const bankName = VPOSClient.getCardBank('411111');
```

---

## Error Handling

### Response Status Codes

```typescript
import { SaleResponseStatus, ResponseStatus, SaleQueryResponseStatus } from '@vignetim/pos';

// Sale Response Statuses
SaleResponseStatus.Success       // Payment completed
SaleResponseStatus.Error         // Payment failed
SaleResponseStatus.RedirectUrl   // Redirect to 3D URL
SaleResponseStatus.RedirectHtml  // Render 3D HTML form

// General Response Statuses (Cancel, Refund)
ResponseStatus.Success           // Operation successful
ResponseStatus.Error             // Operation failed

// Query Response Statuses
SaleQueryResponseStatus.Success  // Transaction found
SaleQueryResponseStatus.Error    // Query failed
SaleQueryResponseStatus.NotFound // Transaction not found
```

### Error Response

```typescript
const response = await VPOSClient.sale(request, auth);

if (response.isError()) {
  console.error('Error Message:', response.message);
  console.error('Error Code:', response.errorCode);
  console.error('Raw Response:', response.rawResponse);
}
```

### Common Error Codes

| Bank | Code | Description |
|------|------|-------------|
| All | `05` | Transaction not approved |
| All | `12` | Invalid transaction |
| All | `13` | Invalid amount |
| All | `14` | Invalid card number |
| All | `51` | Insufficient funds |
| All | `54` | Expired card |
| All | `57` | Transaction not permitted |
| All | `91` | Issuer unavailable |
| Garanti | `0054` | Card expired |
| NestPay | `99` | General error |
| Vakifbank | `0057` | Card restricted |

---

## Test Cards

The library includes comprehensive test card data for development and testing.

### Using Built-in Test Cards

```typescript
import {
  NestPayTestCards,
  GarantiTestCards,
  IyzicoTestCards,
  TestCredentials,
  getTestCardsByBank,
  getRandomTestCard,
} from '@vignetim/pos';

// Get a specific bank's test cards
const garantiCards = GarantiTestCards;
const isbankCards = getTestCardsByBank('Is Bankasi');

// Get a random test card
const randomCard = getRandomTestCard();

// Use test credentials
const auth = new VirtualPOSAuth({
  bankCode: BankCode.GarantiBBVA,
  ...TestCredentials.GarantiBBVA,
  testPlatform: true,
});
```

### Test Cards Reference

#### NestPay Banks (Is Bankasi, Akbank, Halkbank, Ziraat, etc.)

| Bank | Card Number | Expiry | CVV | 3D Password |
|------|-------------|--------|-----|-------------|
| Is Bankasi | `4508034508034509` | 12/26 | 000 | a |
| Is Bankasi | `5406675406675403` | 12/26 | 000 | a |
| Akbank | `4355084355084358` | 12/30 | 000 | a |
| Akbank | `5571135571135575` | 12/30 | 000 | a |
| Ziraat | `4546711234567894` | 12/26 | 000 | a |
| Ziraat | `5401341234567891` | 12/26 | 000 | a |
| Halkbank | `5818775818772285` | 12/26 | 001 | a |
| Halkbank | `4531444531442283` | 12/26 | 001 | a |

#### Garanti BBVA

| Card Number | Expiry | CVV | Type |
|-------------|--------|-----|------|
| `5289394722895016` | 01/25 | 030 | Mastercard |
| `5406697543211173` | 04/27 | 423 | Mastercard |
| `5170410000000004` | 12/30 | 123 | Debit |
| `5400360000000003` | 12/30 | 123 | Credit |

#### Yapi Kredi

| Card Number | Expiry | CVV | 3D Password |
|-------------|--------|-----|-------------|
| `4506347027911094` | 03/25 | 000 | 34020 |
| `5400619360964581` | 03/25 | 000 | 34020 |

#### QNB Finansbank

| Card Number | Expiry | CVV | Type |
|-------------|--------|-----|------|
| `4022774022774026` | 12/26 | 000 | Credit |
| `4155650100416111` | 01/25 | 123 | Credit |
| `5311570000000005` | 12/30 | 123 | Credit |

#### Kuveyt Turk

| Card Number | Expiry | CVV | 3D Password |
|-------------|--------|-----|-------------|
| `5188961939192544` | 06/25 | 929 | 123456 |

#### Iyzico (Sandbox)

For Iyzico sandbox, expiry and CVV can be any valid future date and 3-digit number.

| Card Number | Bank | Type | Notes |
|-------------|------|------|-------|
| `5890040000000016` | Akbank | Debit | Success |
| `5526080000000006` | Akbank | Credit | Success |
| `4766620000000001` | Denizbank | Debit | Success |
| `4603450000000000` | Denizbank | Credit | Success |
| `9792072000017956` | Akbank | Troy | Success |
| `374427000000003` | Garanti | Amex | CVV: 1234 |

**Iyzico Error Cards:**

| Card Number | Error Type |
|-------------|------------|
| `4111111111111129` | Insufficient funds |
| `4129111111111111` | Do not honor |
| `4125111111111115` | Expired card |
| `4124111111111116` | Invalid CVC |

#### CCPayment (Sipay, QNBpay, PayBull, etc.)

| Card Number | Expiry | CVV | Notes |
|-------------|--------|-----|-------|
| `4022780520669303` | 01/50 | 988 | Works with all CCPayment providers |

#### Foreign/International Cards

| Card Number | Type | Notes |
|-------------|------|-------|
| `5400010000000004` | Credit | Non-Turkish |
| `4054180000000007` | Debit | Non-Turkish |

### Test Credentials

```typescript
// Garanti BBVA
{
  merchantId: '7000679',
  merchantUser: '30691297',
  merchantPassword: '123qweASD/',
  merchantStoreKey: '12345678',
  terminalId: '30691298',
}

// Is Bankasi
{
  merchantId: '700655000200',
  merchantUser: 'ISBANKAPI',
  merchantPassword: 'ISBANK07',
  merchantStoreKey: 'TRPS0200',
}

// QNBpay
{
  merchantId: '20158',
  merchantUser: '07fb70f9d8de575f32baa6518e38c5d6',
  merchantPassword: '61d97b2cac247069495be4b16f8604db',
  merchantStoreKey: '$2y$10$N9IJkgazXMUwCzpn7NJrZePy3v.dIFOQUyW4yGfT3eWry6m.KxanK',
}
```

---

## Migration from Other Libraries

### From CP.VPOS (C#)

This library is a TypeScript port of CP.VPOS with an identical API structure:

| CP.VPOS (C#) | @vignetim/pos (TypeScript) |
|--------------|---------------------------|
| `VirtualPOSFactory.CreateVirtualPOSService()` | `VPOSClient.sale()` |
| `SaleRequest` | `SaleRequest` |
| `SaleResponse` | `SaleResponse` |
| `VirtualPOSAuth` | `VirtualPOSAuth` |
| `BankCode.GarantiBBVA` | `BankCode.GarantiBBVA` |

### From nestpay-js

```typescript
// Before (nestpay-js)
const nestpay = new NestPay({ clientId: '...', storeKey: '...' });
await nestpay.pay({ amount: 100, ... });

// After (@vignetim/pos)
const auth = new VirtualPOSAuth({ bankCode: BankCode.IsBankasi, merchantId: '...', ... });
const request = new SaleRequest({ amount: 100, ... });
await VPOSClient.sale(request, auth);
```

---

## Project Structure

```
src/
├── enums/                    # Enumerations (BankCode, Currency, etc.)
├── models/                   # Data models (Request/Response classes)
├── interfaces/               # TypeScript interfaces
├── helpers/                  # Utility functions (HTTP, XML, Crypto)
├── services/                 # Core services (Bank, BIN)
├── data/                     # Test cards and credentials
├── integrations/
│   ├── banks/
│   │   ├── garanti/         # Garanti BBVA (GVP Protocol)
│   │   ├── yapikredi/       # Yapi Kredi (Posnet Protocol)
│   │   ├── akbank/          # Akbank Direct (JSON API)
│   │   ├── vakifbank/       # Vakifbank (XML VPos)
│   │   ├── denizbank/       # Denizbank (InterPOS)
│   │   ├── qnb-finansbank/  # QNB Finansbank Direct
│   │   ├── kuveytturk/      # Kuveyt Turk (XML + SHA256)
│   │   ├── albaraka/        # Albaraka Turk (JSON)
│   │   └── nestpay/         # NestPay banks (EST Protocol)
│   └── payment-institutions/
│       ├── iyzico/          # Iyzico
│       ├── moka/            # Moka
│       ├── paytr/           # PayTR
│       ├── ipara/           # iPara
│       ├── hepsipay/        # Hepsipay
│       ├── tosla/           # Tosla (AKOde)
│       ├── lidio/           # Lidio
│       ├── payu/            # PayU
│       ├── payten/          # Payten, Paratika, ZiraatPay, VakifPayS
│       ├── ccpayment/       # Sipay, QNBpay, PayBull, Parolapara, IQmoney, Vepara
│       ├── parampos/        # ParamPos
│       ├── tami/            # Tami
│       └── ahlpay/          # Ahlpay
├── nestjs/                   # NestJS module integration
├── vpos-client.ts           # Main client facade
└── index.ts                 # Public exports
```

---

## Adding Custom Bank Integration

```typescript
import { BaseVirtualPOSService, IVirtualPOSService, VPOSClient, VirtualPOSAuth, SaleRequest, SaleResponse } from '@vignetim/pos';

class CustomBankService extends BaseVirtualPOSService implements IVirtualPOSService {
  readonly bankCode = 'CUSTOM';
  readonly bankName = 'Custom Bank';

  protected getApiUrl(auth: VirtualPOSAuth): string {
    return auth.testPlatform ? 'https://test-api.bank.com' : 'https://api.bank.com';
  }

  protected get3DUrl(auth: VirtualPOSAuth): string {
    return this.getApiUrl(auth) + '/3d';
  }

  async sale(request: SaleRequest, auth: VirtualPOSAuth): Promise<SaleResponse> {
    // Implement bank-specific logic
    request.validateOrThrow();

    // Make API call...
    // Parse response...
    // Return SaleResponse
  }

  async sale3DResponse(request: Sale3DResponseRequest, auth: VirtualPOSAuth): Promise<SaleResponse> {
    // Handle 3D callback
  }

  async cancel(request: CancelRequest, auth: VirtualPOSAuth): Promise<CancelResponse> {
    // Implement cancel logic
  }

  async refund(request: RefundRequest, auth: VirtualPOSAuth): Promise<RefundResponse> {
    // Implement refund logic
  }

  async saleQuery(request: SaleQueryRequest, auth: VirtualPOSAuth): Promise<SaleQueryResponse> {
    // Implement query logic
  }
}

// Register the custom service
VPOSClient.registerService('CUSTOM', CustomBankService);
```

---

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

### Development Setup

```bash
# Clone the repository
git clone https://github.com/vignetim/pos.git
cd pos

# Install dependencies
npm install

# Run in development mode
npm run dev

# Run tests
npm test

# Build
npm run build

# Lint
npm run lint

# Format
npm run format
```

---

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

---

## Support

- **Issues**: [GitHub Issues](https://github.com/vignetim/pos/issues)
- **Documentation**: [Wiki](https://github.com/vignetim/pos/wiki)
- **Email**: support@vignetim.com

---

Made with by [Vignetim](https://vignetim.com)