# 🔒 Paylock Node SDK

The official Node.js SDK for **Paylock** - designed to enforce license validation, secure your SaaS projects, and manage injectables, domains, and subscriptions effortlessly. Built for modern JavaScript and TypeScript environments.

---

## 🌟 Why Paylock?

Paylock provides the infrastructure to protect your intellectual property and enforce subscription compliance without the complexity of building it yourself.

- **✅ Bulletproof Validation** - Real-time license key verification.
- **🔐 Dynamic Injectables** - Securely deliver encrypted assets (configs, binaries, tokens) only to authorized users.
- **🌐 Domain Locking** - Prevent unauthorized distribution by restricting usage to specific domains.
- **📡 Service-First** - Modular API access for projects, licenses, and payments.
- **🧩 Ultra Flexible** - Framework-agnostic and fully customizable failure behaviors.

---

## 📦 Installation

Get started in seconds with your favorite package manager.

**Using npm:**
```bash
npm install @paylock/node
```

**Using yarn:**
```bash
yarn add @paylock/node
```

> **Note:** Requires Node.js v14+ and works seamlessly with TypeScript.

---

## 🚀 Quick Start

Initialize Paylock at the entry point of your application to start enforcing security immediately.

```ts
import { Paylock } from '@paylock/node';

Paylock.bootstrap({
  license: 'your-license-key-here',
  secret: 'your-secret-key', // for injectables
  base_url: 'https://api.paylock.ng',
  action: 'boot',
  injectables: true,
  onReady: (response) => {
    console.log('✅ License Valid:', response);
  },
  invalidBehavior: 'modal', // or 'redirect', 'silent', 'log'
});
```

If a license is invalid or unpaid, Paylock takes over based on your `invalidBehavior` setting - keeping your application secure by default.

---

## ⚙️ Configuration Options

Tailor the SDK to your specific needs.

### Core Settings

| Key        | Type     | Description                                           |
|------------|----------|-------------------------------------------------------|
| `base_url` | `string` | Your API base URL (e.g., `https://api.paylock.ng`)    |
| `secret`   | `string` | AES-256-CBC key for decrypting injectables            |

### Behavioral Settings

| Key                      | Type       | Description                                                                            |
|--------------------------|------------|----------------------------------------------------------------------------------------|
| `license`                | `string`   | The license key to validate.                                                           |
| `api_key`                | `string`   | API key for administrative or project-scoped access.                                   |
| `recheck`                | `boolean`  | Force revalidation instead of using local cache. (`true` by default)                   |
| `action`                 | `string`   | Tracking tag for the validation request (e.g. `'boot'`).                              |
| `timeout`                | `number`   | HTTP request timeout in ms (`1000` default).                                           |
| `per_page`               | `number`   | Items per page for paginated API results.                                              |
| `injectables`            | `boolean`  | Enable/disable injectable fetching (`true` by default).                                |
| `injectablesVerify`      | `boolean`  | Enable HMAC integrity checks (`true` by default).                                      |
| `injectablesPath`        | `string`   | Local directory to store processed injectables.                                        |
| `handleInjectables`      | `boolean`  | Auto-process and store injectables upon validation.                                    |
| `injectablesProcessor`   | `function` | Your custom logic for handling injectables.                                            |
| `invalidBehavior`        | `string`   | `'modal'` (default), `'redirect'`, `'log'`, or `'silent'`.                             |
| `redirectUrl`            | `string`   | Target URL for the `'redirect'` behavior.                                              |
| `customInvalidMessage`   | `string`   | A custom message to show when validation fails.                                        |
| `customInvalidView`      | `string`   | Path to a custom HTML template for the blocking view.                                  |
| `onReady`                | `function` | Success callback after validation.                                                     |

---

## 🛠 Service API Access

Need more control? Access the Paylock core services directly once bootstrapped.

```ts
import { Paylock } from '@paylock/node';

const projectService    = Paylock.projects();
const licenseService    = Paylock.licenses();
const domainService     = Paylock.domains();
const injectableService = Paylock.injectables();
const paymentService    = Paylock.payments();
```

### Available Methods

| Service          | Methods                                                              |
|------------------|----------------------------------------------------------------------|
| `projects()`     | `list()`, `create()`, `show()`, `update()`, `delete()`              |
| `licenses()`     | `list()`, `show()`, `create()`, `revoke()`, `reactivate()`, `delete()` |
| `domains()`      | `list()`, `create()`, `show()`, `update()`, `delete()`              |
| `injectables()`  | `list()`, `create()`, `show()`, `update()`, `delete()`, `stream()`  |
| `payments()`     | `checkWithLicenseKey()`, `checkWithApiKey()`                        |

---

## 📦 Handling Injectables

Injectables are encrypted assets (scripts, blobs, configs) tied to a license. They are securely streamed and can be automatically processed.

### Custom Processing
Override the default behavior to handle assets your way:

```ts
Paylock.bootstrap({
  license: 'your-license-key',
  secret: 'your-encryption-secret',
  injectables: true,
  handleInjectables: true,
  injectablesProcessor: (injectable, secret, basePath, verify) => {
    // Decrypt, verify, and save or handle it however you want
    return `/custom/path/${injectable.name}`;
  },
});
```

### Signature Verification
For maximum security, always keep verification enabled.

```ts
Paylock.bootstrap({
  // ...
  injectablesVerify: true // Ensure integrity check is enabled
});
```
> ⚠️ **Warning:** Disabling verification (`injectablesVerify: false`) exposes your app to tampered assets.

---

## 🔐 Security & Crypto Utilities

Access the underlying cryptographic engine for your own custom security workflows.

### CryptoHelper
High-level encryption/decryption using AES-256-CBC.

**Decrypting:**
```ts
import { CryptoHelper } from '@paylock/node';

const decrypted = CryptoHelper.decrypt(encryptedString, secretKey);
```

**Encrypting:**
```ts
const encrypted = CryptoHelper.encrypt('Hello World', secretKey);
```

### HashHelper
Secure hashing and HMAC signatures.

**Generate:**
```ts
const hash      = HashHelper.hash('some content');
const signature = HashHelper.signature('content', secret);
```

**Verify:**
```ts
HashHelper.verifyHash(content, expectedHash);        // returns true/false
HashHelper.verifySignature(content, secret, sig);    // returns true/false
```

---

## 🚨 Error Handling & Failure Modes

Paylock gives you full control over what happens when things go wrong.

### Failure Behaviors

| Mode | Action |
|---|---|
| `modal` | (Default) Shows a beautiful, non-bypassable HTML modal. |
| `redirect` | Forces a browser redirect to your upgrade page. |
| `log` | Simply logs the failure to the console. |
| `silent` | Keeps quiet-allowing you to handle the logic manually. |

```ts
invalidBehavior: 'redirect',
redirectUrl: 'https://yourdomain.com/upgrade',
customInvalidMessage: 'Your license is no longer valid.',
customInvalidView: '/views/custom-unlicensed.html'
```

### Catching Exceptions
For fine-grained control, use standard try/catch blocks.

```ts
try {
  await Paylock.bootstrap({ ... });
} catch (error) {
  if (error instanceof LicenseValidationException) {
    console.warn('Invalid license:', error.message);
  } else if (error instanceof HttpRequestException) {
    console.error('API error:', error.statusCode, error.responseBody);
  } else {
    console.error('Unknown Paylock error:', error.message);
  }
}
```

---

## 🧪 Real-World Examples

We've included a variety of ready-to-use examples in the `src/examples/` folder.

### Basic Validation (`validateLicense.ts`)

```ts
import { Paylock } from '../core/Paylock';

Paylock.bootstrap({
  license: 'your-license-key-here',
  base_url: 'https://api.paylock.ng',
  injectables: true,
  debug: true,
  secret: '',
  timeout: 5000,
  invalidBehavior: 'log',
  onReady: (data: any) => {
    console.log('✅ License validated successfully:', data.data);
  }
});
```

Run it locally:
```bash
npm run example
```

---

## 🧠 Advanced Concepts

### Direct Service Access
If you don't need runtime validation, you can use services as a standalone API client.

```ts
import { LicenseService, Config } from '@paylock/node';

const config = new Config({
  api_key: 'your-api-key-here',
  base_url: 'https://api.paylock.ng',
});

const licenses = new LicenseService(config);

licenses.list(1234).then(result => {
  console.log('Licenses:', result);
});
```

### Headless/CLI Environments
For non-browser contexts, use the silent behavior:

```ts
invalidBehavior: 'silent'
```

---

### Custom Injectable Processors

Define how your application consumes delivered assets.

**Type Definitions:**
```ts
type InjectableProcessor = (injectable: InjectablePayload) => void | Promise<void>;
```

**Interface Structure:**
```ts
interface InjectableProcessor {
  handle(
    injectable: InjectablePayload,
    secret: string,
    basePath: string,
    verifySignature?: boolean
  ): string | Promise<string>;
}
```

**Payload Data:**
```ts
interface InjectablePayload {
  name: string;                             // Unique name for the blob
  mode: 'append' | 'prepend' | 'replace';  // How to apply the content
  content: string;                          // Decrypted content
  signature: string;                        // HMAC-SHA256 signature
  targetPath?: string;                      // Optional target path from backend
  [key: string]: any;                       // Additional metadata
}
```

**Implementation Example:**
```ts
import { InjectablePayload, InjectableProcessor } from '@paylock/node';
import { promises as fs } from 'fs';
import { join } from 'path';

export class MyInjectableSaver implements InjectableProcessor {
  async handle(
    injectable: InjectablePayload,
    secret: string,
    basePath: string,
    verifySignature = true
  ): Promise<string> {
    const filePath = join(basePath, injectable.name + '.txt');
    await fs.writeFile(filePath, injectable.content, 'utf8');
    return filePath;
  }
}
```

**Enabling Your Processor:**
```ts
import { Paylock } from '@paylock/node';
import { MyInjectableSaver } from './MyInjectableSaver';

Paylock.bootstrap({
  license: 'your-license-key',
  secret: 'your-shared-secret',
  handleInjectables: true,
  injectablesProcessor: new MyInjectableSaver()
});
```

**Manual Verification & Decryption:**
```ts
import { InjectablePayload, CryptoHelper, HashHelper } from '@paylock/node';

export async function customInjectableProcessor(
  injectable: InjectablePayload,
  secret: string,
  basePath: string,
  verifySignature = true
): Promise<string> {
  // 🔏 Verify signature
  if (verifySignature && !HashHelper.verifySignature(injectable.content, secret, injectable.signature)) {
    throw new Error('Invalid HMAC signature - injectable may have been tampered with.');
  }

  // 🔐 Decrypt content
  const decrypted = CryptoHelper.decrypt(injectable.content, secret);

  console.log(`Injectable ${injectable.name} verified and decrypted:`, decrypted);
  return injectable.name;
}
```

---

## ⚡ TypeScript First

Paylock is built with TypeScript from the ground up, providing a first-class developer experience.

```json
"types": "dist/index.d.ts"
```

**Typed Response Example:**
```ts
import { Paylock, LicenseService } from '@paylock/node';

Paylock.bootstrap({
  license: 'your-license',
  secret: 'your-secret',
  injectables: true,
  onReady: (response) => {
    // Fully typed response structure
    console.log('Validated license data:', response.data);
  }
});

const licenseService: LicenseService = Paylock.licenses();
```

---

## 📄 License

MIT © [paylockHQ](https://paylock.ng)
