
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@xultech/devpayr
Advanced tools
DevPayr Node.js SDK for secure license validation, injectable handling, and project access enforcement.
The official Node.js SDK for DevPayr — a secure license enforcement and access control system for SaaS platforms, downloadable software, and digital tools.
This SDK allows you to:
Whether you're building a backend API, SaaS platform, CLI tool, or desktop software, DevPayr gives you the infrastructure to protect your code, enforce payments, and control usage — all without locking you into a specific framework.
Using npm:
npm install @xultech/devpayr
Using yarn:
yarn add @xultech/devpayr
Requires Node.js v14+ and TypeScript (if using types).
Start by bootstrapping DevPayr with your license key and configuration options:
import { DevPayr } from '@xultech/devpayr';
DevPayr.bootstrap({
license: 'your-license-key-here',
secret: 'your secrete key', // for injectables
base_url: 'https://api.devpayr.com',
action: 'boot',
injectables: true,
onReady: (response) => {
console.log('✅ License Valid:', response);
},
invalidBehavior: 'modal', // or 'redirect', 'silent', 'log'
});
If the license is invalid or payment status of the project is unpaid, DevPayr will:
invalidBehavior🔒 Secure by Default – No valid license, no access.
You can customize DevPayr behavior by passing configuration options into the DevPayr.bootstrap() method. Some fields are required, while others have sensible defaults.
| Key | Type | Description |
|---|---|---|
base_url | string | API base URL (e.g., https://api.devpayr.com/api/v1/) |
secret | string | Secret used to decrypt injectables (AES-256-CBC) |
| Key | Type | Description |
|---|---|---|
license | string | License key for validation (optional if using API key only) |
api_key | string | API key for project-scoped or global API access |
recheck | boolean | Whether to skip local cache and revalidate license (true by default) |
action | string | Optional identifier to include in validation requests (e.g. 'boot', 'start') |
timeout | number | Timeout for HTTP requests in ms (1000 default) |
per_page | number | Used for paginated listing (e.g., licenses, domains) |
injectables | boolean | Whether to fetch injectables during validation (true by default) |
injectablesVerify | boolean | Verify HMAC signature of injectables (true by default) |
injectablesPath | string | Directory to write injectables to (optional, otherwise temp path) |
handleInjectables | boolean | If true, SDK will decrypt + store injectables automatically |
injectablesProcessor | function | Custom handler function/class for injectables |
invalidBehavior | string | 'modal' (default), 'redirect', 'log', or 'silent' |
redirectUrl | string | URL to redirect to if license is invalid (used if invalidBehavior = 'redirect') |
customInvalidMessage | string | Message to display or log if license is invalid |
customInvalidView | string | Custom HTML file to show for unlicensed copies |
onReady | function | Callback executed after successful validation |
You can override any of these when calling
DevPayr.bootstrap({ ... }).
DevPayr provides access to powerful core services through static methods. Once the SDK is bootstrapped, you can use the following service accessors:
import { DevPayr } from '@xultech/devpayr';
const projectService = DevPayr.projects();
const licenseService = DevPayr.licenses();
const domainService = DevPayr.domains();
const injectableService = DevPayr.injectables();
const paymentService = DevPayr.payments();
Each service exposes methods for interacting with your licensing and project environment:
| 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() |
Injectables are encrypted assets (scripts, config, JSON blobs, etc.) attached to your project via DevPayr. These are securely streamed at runtime and optionally auto-processed.
If enabled in the config (injectables: true), injectables are fetched during DevPayr.bootstrap() using the license key. The endpoint returns a list of encrypted injectables tied to that license.
Each injectable contains:
name: The file or block namecontent: AES-256-CBC encrypted and base64 encodedsignature: HMAC-SHA256 signature of the encrypted contentmode: append, prepend, replace, etc.You can define a custom processor to handle injectables however you like:
DevPayr.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}`;
},
});
Alternatively, implement the full InjectableProcessorContract for structure and consistency.
By default, DevPayr verifies each injectable’s HMAC signature using the license key or provided secret. You can disable this by setting:
injectablesVerify: false
⚠️ Disabling verification may expose your application to tampered injectables. Use with caution.
DevPayr exposes powerful cryptographic helpers to handle encryption, decryption, hashing, and signature verification. These can be used independently in your application for custom workflows.
CryptoHelperThis utility helps encrypt and decrypt strings using AES-256-CBC and is perfect for handling secure injectables.
import { CryptoHelper } from '@xultech/devpayr';
const decrypted = CryptoHelper.decrypt(encryptedString, secretKey);
iv::cipherTextconst encrypted = CryptoHelper.encrypt('Hello World', secretKey);
iv::cipherTextcrypto.randomBytesHandles SHA-256 hashing and HMAC signature generation/verification. Ideal for verifying injectables or signing internal payloads.
const hash = HashHelper.hash('some content');
const signature = HashHelper.signature('content', secret);
HashHelper.verifyHash(content, expectedHash); // returns true/false
HashHelper.verifySignature(content, secret, sig); // returns true/false
crypto.timingSafeEqualThese tools are used internally by DevPayr to handle decrypting injectables, verifying payloads, and ensuring content hasn’t been tampered with. You can also use them for your own custom secure workflows.
DevPayr handles errors and failed license checks gracefully. You can customize how the SDK behaves when validation fails or when an API call throws an error.
When a license is invalid, expired, or unauthorized, the SDK triggers the configured invalid behavior:
| Mode | Description |
|---|---|
modal | Displays a built-in HTML modal (default) with an error message. |
redirect | Redirects the user to a custom URL. |
log | Logs the error to the console using error_log (or console in JS). |
silent | Does nothing. Use when you want to handle errors manually. |
You can configure this using the invalidBehavior option:
invalidBehavior: 'redirect',
redirectUrl: 'https://yourdomain.com/upgrade',
customInvalidMessage: 'Your license is no longer valid.',
customInvalidView: '/path/to/custom.html'
By default, the SDK shows a built-in modal with a styled message. You can replace it with a fully customized HTML file:
customInvalidView: '/views/custom-unlicensed.html'
This file will be loaded and the
{{message}}placeholder will be replaced with the failure message.
The DevPayr SDK throws structured exceptions that help you understand what went wrong — whether it’s a failed license validation, a network error, or cryptographic issue. All custom errors extend the base DevPayrException.
You can import and catch any of these exceptions in your application:
import {
DevPayrException,
LicenseValidationException,
InjectableVerificationException,
HttpRequestException,
CryptoException
} from '@xultech/devpayr';
All errors thrown by the SDK extend from this class:
export class DevPayrException extends Error {
public readonly name: string = 'DevPayrException';
constructor(message: string) {
super(message);
Error.captureStackTrace?.(this, this.constructor);
}
}
Thrown when a license check fails or returns an invalid result.
throw new LicenseValidationException('License is expired or not found.');
Thrown when fetched injectables fail HMAC verification, are tampered with, or cannot be parsed.
throw new InjectableVerificationException('Invalid signature on downloaded injectable.');
Thrown when a DevPayr API call fails meaningfully (e.g., 401, 403, 422). This exception includes status code and response body:
throw new HttpRequestException('Unauthorized access', 401, response);
Thrown when encryption or decryption fails (e.g., wrong AES key, corrupt payload):
throw new CryptoException('Unable to decrypt payload');
try {
await sdk.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 DevPayr error:', error.message);
}
}
The SDK ships with a few ready-to-run examples inside the src/examples/ folder to help you quickly get started with common use cases.
validateLicense.tsThis example demonstrates how to bootstrap the SDK, validate a license key, and handle both success and failure cases:
import { DevPayr } from '../core/DevPayr';
// Bootstrap DevPayr SDK using a test license
DevPayr.bootstrap({
license: '01975a4e-bc1c-72fc-a1b5-b509d8f07c75', // Replace with a real key
base_url: 'https://api.devpayr.dev/api/v1/', // Or your DevPayr base URL
injectables: true, // Fetch injectables after validation
debug: true, // Enable detailed logs
secret: "", // AES secret for decryption
timeout: 5000, // Request timeout in ms
invalidBehavior: 'log', // Options: modal | redirect | log | silent
onReady: (data: any) => {
console.log('✅ License validated successfully:', data.data);
}
});
All examples live under: src/examples/ and you can run them using
npm run example✳️ More examples coming soon:
- Creating a new project
- Managing injectables manually
- Validating licenses inside CLI tools
- Using a custom injectable processor
While most users will use DevPayr.bootstrap() for automatic license validation and injectable handling, the SDK offers full flexibility and composability. All core classes, services, utilities, and exceptions are directly exported and can be used independently.
If you're only interested in calling API services without runtime validation, you can instantiate them manually:
import { LicenseService, Config } from '@xultech/devpayr';
const config = new Config({
api_key: 'your-api-key-here',
base_url: 'https://api.devpayr.com/api/v1/',
});
const licenses = new LicenseService(config);
licenses.list(1234).then(result => {
console.log('Licenses:', result);
});
You can safely use the SDK in headless or non-browser environments (e.g., CLI scripts) by setting:
invalidBehavior: 'silent'
This disables modal or redirect behavior for invalid licenses.
DevPayr supports encrypted injectables — small pieces of SDK-managed data (like scripts, configs, or logic) that are delivered after license validation. These are decrypted and can be automatically applied using a processor.
By default, if handleInjectables: true is set, DevPayr will attempt to apply injectables internally (e.g., print to console, save to memory). However, for full control over how these are handled — especially in environments like CLI tools, microservices, or embedded runtimes — you can override the injectablesProcessor option.
injectablesProcessor?It's a function or class that gets invoked for each decrypted injectable, giving you the power to decide how to apply, transform, or persist the content.
type InjectableProcessor = (injectable: InjectablePayload) => void | Promise<void>;
Or, for class-based control:
interface InjectableProcessor {
handle(
injectable: InjectablePayload,
secret: string,
basePath: string,
verifySignature?: boolean
): string | Promise<string>;
}
Every injectable is passed to your processor in the following format:
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
path?: string; // Optional target path or location
[key: string]: any; // Additional metadata (if any)
}
Here’s an example processor that saves injectables as text files under a custom directory:
import { InjectablePayload, InjectableProcessor } from '@xultech/devpayr';
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;
}
}
To use your custom processor, simply pass it during bootstrap:
import { DevPayr } from '@xultech/devpayr';
import { MyInjectableSaver } from './MyInjectableSaver';
DevPayr.bootstrap({
license: 'your-license-key',
secret: 'your-shared-secret',
handleInjectables: true,
injectablesProcessor: new MyInjectableSaver()
});
If you're processing any dynamic code or configuration, we strongly recommend enabling HMAC signature verification — which is enabled by default (injectablesVerify: true) — to protect against tampered or spoofed injectables.
When implementing a custom injectable handler, you are responsible for:
DevPayr provides internal utilities to simplify this securely:
CryptoHelper.decrypt(encryptedContent, secret) — Decrypts base64-encoded content in iv::cipherText format.HashHelper.verifySignature(content, secret, expectedSignature) — Verifies HMAC-SHA256 signatures in a timing-safe way.Example usage inside a custom processor:
import { InjectablePayload } from '@xultech/devpayr';
import { CryptoHelper, HashHelper } from '@xultech/devpayr';
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);
// ✅ Save, inject, or process as needed...
console.log(`Injectable ${injectable.name} verified and decrypted:`, decrypted);
return injectable.name;
}
DevPayr is written entirely in TypeScript, offering first-class developer experience out of the box.
There is no need to install any additional typings — type declarations are automatically included via:
"types": "dist/index.d.ts"
InjectableProcessor and InjectablePayloadDevPayrException, LicenseValidationException, etc.)import { DevPayr, LicenseService } from '@xultech/devpayr';
DevPayr.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 = DevPayr.licenses();
We welcome contributions from the community!
To get started, please see CONTRIBUTING.md for guidelines on:
If you discover any security vulnerability, please do not open a public issue.
Instead, report it responsibly by emailing us at:
We take security seriously and will respond promptly to any valid reports.
Thank you for helping keep DevPayr and its users safe.
Built and maintained by DevPayr, a product of XulTech Ltd.
Lead Engineer: Michael Erastus
Special thanks to our contributors, users, and early testers for feedback and support.
FAQs
DevPayr Node.js SDK for secure license validation, injectable handling, and project access enforcement.
We found that @xultech/devpayr demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.