npm package: @houlak/drynex-sdk
Description: TypeScript SDK for Drynex Payment Gateway
Latest version: 1.2.0

# Drynex SDK

[![npm version](https://img.shields.io/npm/v/@houlak/drynex-sdk.svg)](https://www.npmjs.com/package/@houlak/drynex-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)

A modern TypeScript SDK for integrating with the Drynex Payment Gateway. This SDK provides a type-safe and developer-friendly way to interact with Drynex's payment processing services.

⚠️ **This SDK is in ALPHA stage and is not intended for public use.**  
Access and usage are strictly limited to teams authorized by Drynex.

## 📋 Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [API Reference](#api-reference)
  - [Authentication](#authentication)
  - [Creating an DrynexClient instance](#creating-an-drynexclient-instance)
  - [Organizations](#organizations)
  - [Merchants](#merchants)
  - [Payment Instruments](#payment-instruments)
  - [Payments](#payments)
  - [Webhooks](#webhooks)
- [TypeScript Types](#typescript-types)
  - [Available Types](#available-types)
  - [Type Categories](#type-categories)
  - [Type Safety Example](#type-safety-example)
- [Error Handling](#error-handling)
- [License](#license)

<a id="features"></a>

## ✨ Features

- 🔒 Full TypeScript support with comprehensive type definitions
- 🚀 SDK functionalities:
  - Automatic token management (generation and refresh)
  - Organization Management
    - Create and manage organizations
    - Group related business entities
  - Merchant Management
    - Create, update, and delete merchants
    - Configure payment rails (VISA, Mastercard)
    - Set up merchant profiles and settings
  - Payment Instruments
    - Tokenize and store payment methods
    - Manage card information securely
    - Support for multiple payment methods
  - Payment Processing
    - Authorization with PAN or token
    - Automatic and manual capture modes
    - Payment cancellation
    - Refund processing
    - Payment verification
    - Installment support
  - Webhook Management
    - Configure webhook endpoints
    - Set up event notifications
    - Manage webhook secrets
- ⚡ Modern async/await API
- 🛡️ Built-in error handling
- 📚 Comprehensive documentation
- 🔐 Secure token management
- 🌐 Multi-currency support
- 🏢 Multi-tenant architecture

<a id="installation"></a>

## 📦 Installation

```bash
# Using npm
npm install @houlak/drynex-sdk

# Using yarn
yarn add @houlak/drynex-sdk

# Using pnpm
pnpm add @houlak/drynex-sdk
```

<a id="quick-start"></a>

## 🚀 Quick Start

```typescript
import { DrynexClient, DrynexTypes } from '@houlak/drynex-sdk';

// Create the DrynexClient instance with your credentials
// The client will automatically handle token management
const drynexClientConfig: DrynexTypes.ClientConfig = {
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  environment: DrynexTypes.Enums.Environment.SANDBOX, // or PRODUCTION
};

const drynexClient = new DrynexClient(drynexClientConfig);

// Now you can use the DrynexClient instance
const createOrganizationRequest: DrynexTypes.RequestTypes.CreateOrganizationRequest = {
  name: 'My Organization',
};

const createOrganizationResponse: DrynexTypes.ApiResponse<DrynexTypes.ResponseTypes.OrganizationDTO> =
  await drynexClient.organizations.create(createOrganizationRequest);

if (!createOrganizationResponse.success || !createOrganizationResponse.data) {
  throw new Error('Create organization failed');
}

const organization: DrynexTypes.ResponseTypes.OrganizationDTO = createOrganizationResponse.data;

const createMerchantRequest: DrynexTypes.RequestTypes.CreateMerchantRequest = {
  name: 'My Store',
  organization_id: organization.id,
  country: 'US',
  city: 'New York',
  state: 'NY',
  billing_address: {
    street: '123 Main St',
    city: 'New York',
    state: 'NY',
    country: 'US',
    postal_code: '10001',
  },
  location_address: {
    street: '123 Main St',
    city: 'New York',
    state: 'NY',
    country: 'US',
    postal_code: '10001',
  },
  classification_mode: 'retail',
  default_currency: 'USD',
  supported_currencies: ['USD', 'EUR'],
  activity: 'retail',
  alias: 'my-store-ny',
  email: 'store@example.com',
  external_id: 'ext_123',
  phone: '+1234567890',
  tax_id: '123456789',
  rails: {
    VISA: {
      category_code: '5411',
      payfac_id: 'payfac_123',
    },
    MASTERCARD: {
      category_code: '5411',
      payfac_id: 'payfac_123',
    },
  },
};

const createMerchantResponse: DrynexTypes.ApiResponse<DrynexTypes.ResponseTypes.MerchantDTO> =
  await drynexClient.merchants.create(createMerchantRequest);

if (!createMerchantResponse.success || !createMerchantResponse.data) {
  throw new Error('Create merchant failed');
}

const merchant: DrynexTypes.ResponseTypes.MerchantDTO = createMerchantResponse.data;

const createInstrumentRequest: DrynexTypes.RequestTypes.CreateInstrumentRequest = {
  type: 'CARD',
  card: {
    number: '4242424242424242',
    expiration_month: '12',
    expiration_year: '2030',
    first_name: 'John',
    last_name: 'Doe',
    cvv: '123',
  },
  user_data: {
    billing_address: {
      street: '123 Main St',
      number: 'Apt 4B',
      city: 'New York',
      state: 'NY',
      zip_code: '10001',
      country: 'US',
    },
  },
};

const createInstrumentResponse: DrynexTypes.ApiResponse<DrynexTypes.ResponseTypes.InstrumentDTO> =
  await drynexClient.instruments.create(createInstrumentRequest);

if (!createInstrumentResponse.success || !createInstrumentResponse.data) {
  throw new Error('Create instrument failed');
}

const instrument: DrynexTypes.ResponseTypes.InstrumentDTO = createInstrumentResponse.data;
```

<a id="api-reference"></a>

## 📋 API Reference

### Authentication

The SDK handles authentication automatically. When you create an `DrynexClient` instance with your client credentials, it will:

1. Generate an initial token using your credentials
2. Automatically refresh the token before it expires (with a 5-minute buffer)
3. Handle token reuse across all requests
4. Manage concurrent token refresh requests
5. Handle authentication errors gracefully

### Creating an DrynexClient instance

The `DrynexClient` is the main entry point for interacting with the Drynex API. It requires your client credentials and an environment configuration.

```typescript
import { DrynexClient, DrynexTypes } from '@houlak/drynex-sdk';

const clientConfig: DrynexTypes.ClientConfig = {
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  environment: DrynexTypes.Enums.Environment.SANDBOX, // or PRODUCTION
};

const drynexClient = new DrynexClient(clientConfig);
```

### Organizations

#### `organizations.create(data: CreateOrganizationRequest): Promise<ApiResponse<OrganizationDTO>>`

Creates a new organization in the Drynex system. Organizations can have multiple merchants and are used to group related business entities.

```typescript
const organization = await drynexClient.organizations.create({
  name: 'Acme Corp',
});
```

#### `organizations.get(): Promise<ApiResponse<OrganizationDTO[]>>`

Retrieves all organizations from the Drynex system.

```typescript
const organizations = await drynexClient.organizations.get();
```

### Merchants

#### `merchants.create(data: CreateMerchantRequest): Promise<ApiResponse<MerchantDTO>>`

Creates a new merchant in the Drynex system. This is the first step in setting up a new merchant account. The merchant will be associated with the specified organization and will be configured with the provided payment rails and settings.

```typescript
const merchant = await drynexClient.merchants.create({
  name: 'My Store',
  organization_id: 'org_123',
  country: DrynexTypes.Enums.Country.UNITED_STATES,
  city: 'New York',
  state: 'NY',
  billing_address: {
    street: '123 Main St',
    city: 'New York',
    state: 'NY',
    country: DrynexTypes.Enums.Country.UNITED_STATES,
    postal_code: '10001',
  },
  location_address: {
    street: '123 Main St',
    city: 'New York',
    state: 'NY',
    country: DrynexTypes.Enums.Country.UNITED_STATES,
    postal_code: '10001',
  },
  classification_mode: 'retail',
  default_currency: DrynexTypes.Enums.Currency.USD,
  supported_currencies: [DrynexTypes.Enums.Currency.USD, DrynexTypes.Enums.Currency.EUR],
  activity: 'retail',
  alias: 'my-store-ny',
  email: 'store@example.com',
  external_id: 'ext_123',
  phone: '+1234567890',
  tax_id: '123456789',
  rails: {
    [DrynexTypes.Enums.Rail.VISA]: {
      category_code: '5411',
      payfac_id: 'payfac_123',
    },
    [DrynexTypes.Enums.Rail.MASTERCARD]: {
      category_code: '5411',
      payfac_id: 'payfac_123',
    },
  },
});
```

#### `merchants.getById(merchantId: string): Promise<ApiResponse<MerchantDTO>>`

Retrieves a merchant's information by its unique identifier.

```typescript
const merchant = await drynexClient.merchants.getById('merch_123456789');
```

#### `merchants.get(request: GetMerchantsRequest): Promise<ApiResponse<MerchantDTO[]>>`

Lists all merchants for an organization with pagination support.

```typescript
const merchants = await drynexClient.merchants.get({
  organization_id: 'org-d0j35vc4t5u70r4eso70',
  page: 1,
  page_size: 20,
  sort: 'created_at:desc',
});
```

#### `merchants.update(merchantId: string, data: UpdateMerchantRequest): Promise<ApiResponse<MerchantDTO>>`

Updates an existing merchant's information.

```typescript
const updatedMerchant = await drynexClient.merchants.update('merch_123456789', {
  name: 'Updated Store Name',
  email: 'updated@example.com',
  phone: '+1987654321',
  billing_address: {
    street: '456 New St',
    city: 'Los Angeles',
    state: 'CA',
    country: DrynexTypes.Enums.Country.UNITED_STATES,
    postal_code: '90001',
  },
});
```

#### `merchants.updateRails(merchantId: string, data: UpdateMerchantRailsRequest): Promise<ApiResponse<MerchantDTO>>`

Updates the rails configuration for a merchant.

```typescript
const updatedMerchant = await drynexClient.merchants.updateRails('merch_123456789', {
  [DrynexTypes.Enums.Rail.VISA]: {
    category_code: '1234',
    payfac_id: 'pfc-123',
    external_id: 'mer-ext-123',
    classification: 'micro-merchant',
    annual_volume: [
      {
        currency: DrynexTypes.Enums.Currency.COP,
        value: 10000000,
      },
    ],
    products: {
      [DrynexTypes.Enums.Product.DEBIT]: {
        network_merchant_id: 'VISA-123',
        enabled: true,
      },
      [DrynexTypes.Enums.Product.CREDIT]: {
        network_merchant_id: 'VISA-123',
        enabled: true,
      },
    },
  },
});
```

#### `merchants.delete(merchantId: string): Promise<ApiResponse<void>>`

Deletes a merchant from the Drynex system.

```typescript
await drynexClient.merchants.delete('merch_123456789');
```

### Payment Instruments

#### `instruments.create(data: CreateInstrumentRequest): Promise<ApiResponse<InstrumentDTO>>`

Creates a new payment instrument. The instrument is tokenized and stored securely in the Drynex system.

```typescript
const card = await drynexClient.instruments.create({
  type: 'CARD',
  card: {
    number: '4242424242424242',
    expiration_month: '12',
    expiration_year: '2025',
    first_name: 'John',
    last_name: 'Doe',
    cvv: '123',
  },
  user_data: {
    billing_address: {
      street: '123 Main St',
      number: 'Apt 4B',
      city: 'New York',
      state: 'NY',
      zip_code: '10001',
      country: DrynexTypes.Enums.Country.UNITED_STATES,
    },
  },
});
```

#### `instruments.get(): Promise<ApiResponse<InstrumentDTO[]>>`

Retrieves all payment instruments associated with the merchant. This includes both active and inactive instruments, with their current status and details.

```typescript
const instruments = await drynexClient.instruments.get();
```

#### `instruments.getById(id: string): Promise<ApiResponse<InstrumentDTO>>`

Retrieves detailed information about a specific payment instrument, including its type and status.

```typescript
const instrument = await drynexClient.instruments.getById('instr_123');
```

#### `instruments.update(id: string, data: UpdateInstrumentRequest): Promise<ApiResponse<InstrumentDTO>>`

Updates an existing payment instrument's information. This can be used to update expiration dates, cardholder information, or other instrument details.

```typescript
const updatedInstrument = await drynexClient.instruments.update('instr_123', {
  card: {
    expiration_month: '12',
    expiration_year: '2026',
    first_name: 'John',
    last_name: 'Smith',
  },
});
```

#### `instruments.delete(id: string): Promise<ApiResponse<void>>`

Permanently deletes a payment instrument from the system. This operation cannot be undone and will invalidate the instrument for future use.

```typescript
await client.instruments.delete('instr_123');
```

### Payments

#### `payments.authorizeWithPan(data: AuthorizeWithPanRequest): Promise<ApiResponse<AuthorizePaymentDTO>>`

Authorizes a payment transaction using PAN (Primary Account Number). This is used for direct card payments.

```typescript
const payment = await drynexClient.payments.authorizeWithPan({
  amount: {
    value: 25.25,
    currency: DrynexTypes.Enums.Currency.USD,
  },
  intent: DrynexTypes.Enums.PaymentIntent.AUTHORIZATION,
  trace_id: 'any-format-34324366',
  entry_mode: DrynexTypes.Enums.PaymentEntryMode.MANUAL,
  capture: {
    mode: DrynexTypes.Enums.CaptureMode.AUTOMATIC,
  },
  merchant_id: 'mer-d0r0b17npsai542l2400',
  installments: {
    quantity: 2,
    type: '03',
  },
  instrument: {
    type: 'CARD',
    card: {
      number: '4024007108904086',
      expiration_month: '12',
      expiration_year: '25',
      holder_name: 'John Doe',
      cvv: '123',
    },
    user_data: {
      billing_address: {
        street: '123 Main St',
        number: 'Apt 4B',
        city: 'New York',
        state: 'NY',
        zip_code: '10001',
        country: DrynexTypes.Enums.Country.UNITED_STATES,
        phone_number: '+1234567890',
      },
    },
  },
});
```

#### `payments.authorizeWithToken(data: AuthorizeWithTokenRequest): Promise<ApiResponse<AuthorizePaymentDTO>>`

Authorizes a payment transaction using a previously tokenized instrument.

```typescript
const payment = await drynexClient.payments.authorizeWithToken({
  amount: {
    value: 25.25,
    currency: DrynexTypes.Enums.Currency.USD,
  },
  intent: DrynexTypes.Enums.PaymentIntent.AUTHORIZATION,
  trace_id: 'any-format-34324366',
  entry_mode: DrynexTypes.Enums.PaymentEntryMode.MANUAL,
  capture: {
    mode: DrynexTypes.Enums.CaptureMode.AUTOMATIC,
  },
  merchant_id: 'mer-d0r0b17npsai542l2400',
  installments: {
    quantity: 2,
    type: '03',
  },
  instrument: {
    id: 'instr_123456789',
  },
});
```

#### `payments.capture(paymentId: string, data: CapturePaymentRequest): Promise<ApiResponse<CapturePaymentDTO>>`

Captures a previously authorized payment. Only payments created with capture_manual mode can be captured.

```typescript
const capture = await drynexClient.payments.capture('pay_123', {
  amount: {
    value: 25.25,
    currency: DrynexTypes.Enums.Currency.USD,
  },
});
```

#### `payments.cancel(paymentId: string): Promise<ApiResponse<CancelPaymentDTO>>`

Cancels a payment transaction.

```typescript
const cancel = await drynexClient.payments.cancel('pay-d0v32ak2mts03heh7amg');
```

#### `payments.refund(paymentId: string, data: RefundPaymentRequest): Promise<ApiResponse<RefundPaymentDTO>>`

Refunds a payment transaction.

```typescript
const refund = await drynexClient.payments.refund('pay-d0v32ak2mts03heh7amg', {
  amount: {
    value: 25.25,
    currency: DrynexTypes.Enums.Currency.USD,
  },
});
```

#### `payments.verify(data: VerifyPaymentRequest): Promise<ApiResponse<AuthorizePaymentDTO>>`

Verifies a payment instrument (card) status.

```typescript
const verify = await drynexClient.payments.verify({
  intent: DrynexTypes.Enums.PaymentIntent.AUTHORIZATION,
  merchant_id: 'mer-d0r0b17npsai542l2400',
  trace_id: 'any-format-34324366',
  instrument: {
    type: 'CARD',
    card: {
      number: '4024007108904086',
      expiration_month: '12',
      expiration_year: '25',
      holder_name: 'John Doe',
      cvv: '123',
    },
    user_data: {
      billing_address: {
        street: '123 Main St',
        number: 'Apt 4B',
        city: 'New York',
        state: 'NY',
        zip_code: '10001',
        country: DrynexTypes.Enums.Country.UNITED_STATES,
        phone_number: '+1234567890',
      },
    },
  },
});
```

### Webhooks

#### `webhooks.create(data: CreateWebhookRequest): Promise<ApiResponse<WebhookDTO>>`

Creates a new webhook configuration.

```typescript
const webhook = await drynexClient.webhooks.create({
  url: 'https://example.com/webhook',
  events: [DrynexTypes.Enums.EventType.PAYMENT_AUTHORIZATION_PROCESSED],
  description: 'Webhook for payment events',
  rate_limit: 1,
});
```

#### `webhooks.get(): Promise<ApiResponse<WebhookDTO[]>>`

Retrieves all webhook configurations.

```typescript
const webhooks = await drynexClient.webhooks.get();
```

#### `webhooks.getSecret(webhookId: string): Promise<ApiResponse<WebhookSecretDTO>>`

Retrieves a webhook configuration's secret key by its ID.

```typescript
const webhookSecret = await drynexClient.webhooks.getSecret('webhook_123');
```

<a id="typescript-types"></a>

## 📘 TypeScript Types

The SDK provides comprehensive TypeScript types that can be imported from the `DrynexTypes` namespace. This allows you to have full type safety and autocompletion when working with the SDK.

### Available Types

```typescript
import { DrynexTypes } from '@houlak/drynex-sdk';

// Enums
const environment = DrynexTypes.Enums.Environment.SANDBOX;
const country = DrynexTypes.Enums.Country.USA;
const rail = DrynexTypes.Enums.Rail.VISA;

// Request Types
const createMerchantRequest: DrynexTypes.RequestTypes.CreateMerchantRequest = {
  name: 'My Store',
  organization_id: 'org_123',
  // ... other merchant fields
};

const createInstrumentRequest: DrynexTypes.RequestTypes.CreateInstrumentRequest = {
  type: 'CARD',
  card: {
    number: '4242424242424242',
    expiration_month: '12',
    expiration_year: '2025',
    first_name: 'John',
    last_name: 'Doe',
    cvv: '123',
  },
};

// Response Types
const merchantResponse: DrynexTypes.ResponseTypes.MerchantDTO = {
  id: 'merch_123',
  name: 'My Store',
  // ... other merchant fields
};

const instrumentResponse: DrynexTypes.ResponseTypes.InstrumentDTO = {
  id: 'instr_123',
  type: 'CARD',
  // ... other instrument fields
};
```

### Type Categories

The SDK's types are organized into several categories:

1. **Enums**: Constants and enumerations used throughout the SDK

2. **Request Types**: Types for API request payloads

3. **Response Types**: Types for API response data

4. **API Response**: Generic wrapper type for all API responses
   ```typescript
   interface ApiResponse<T> {
     data: T | null;
     success: boolean;
     error?: {
       code: string;
       message: string;
       details?: Record<string, unknown>;
     };
   }
   ```

### Type Safety Example

Here's an example of how the types help catch errors at compile time:

```typescript
import { DrynexClient, DrynexTypes } from '@houlak/drynex-sdk';

const client = new DrynexClient({
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  environment: DrynexTypes.Enums.Environment.SANDBOX,
});

// TypeScript will show an error if required fields are missing
const merchant = await client.merchants.create({
  name: 'My Store',
  // Error: missing required field 'organization_id'
});

// TypeScript will show an error if field types don't match
const instrument = await client.instruments.create({
  type: 'CARD',
  card: {
    number: 4242424242424242, // Error: number should be a string
    expiration_month: 12, // Error: should be a string
    expiration_year: 2025, // Error: should be a string
    first_name: 'John',
    last_name: 'Doe',
  },
});

// TypeScript will provide autocompletion for response fields
const response = await client.instruments.getById('ins_123');
if (response.success && response.data) {
  console.log(response.data.status); // Autocomplete available
  console.log(response.data.type); // Autocomplete available
  console.log(response.data.invalid_field); // Error: field doesn't exist
}
```

<a id="error-handling"></a>

## 🛡️ Error Handling

The SDK provides built-in error handling with detailed error messages. All API responses are wrapped in an `ApiResponse` type:

```typescript
interface ApiResponse<T> {
  data: T | null;
  success: boolean;
  error?: {
    code: string;
    message: string;
    details?: Record<string, unknown>;
  };
}
```

Example error handling:

```typescript
try {
  const result = await drynexClient.organizations.create({
    name: 'Acme Corp',
  });

  if (!result.success) {
    console.error(`Error ${result.error?.code}: ${result.error?.message}`);
    if (result.error?.details) {
      console.error('Details:', result.error.details);
    }
    return;
  }

  // Use result.data safely here
  console.log('Organization created:', result.data);
} catch (error) {
  // Handle unexpected errors
  console.error('Unexpected error:', error);
}
```

<a id="license"></a>

## 📄 License

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