@linkzly/affiliate-node
v1.1.0
Published
Node.js SDK for Linkzly affiliate S2S conversions, reversals, and webhook verification
Maintainers
Readme
@linkzly/affiliate-node
Server-side Node.js SDK for Linkzly affiliate S2S conversions, reversals, and webhook verification.
Installation
npm install @linkzly/affiliate-nodeRequires Node.js >= 18.
Quick Start
import { LinkzlyAffiliate } from '@linkzly/affiliate-node';
const client = new LinkzlyAffiliate({
apiSecret: process.env.LINKZLY_API_SECRET!,
programId: process.env.LINKZLY_PROGRAM_ID!,
});
// Track a conversion
const result = await client.trackConversion({
order_id: 'ORD-12345',
order_value: 99.99,
currency: 'USD',
click_id: req.body.lz_click_id, // From Web SDK
});
if (result.success) {
console.log('Commission:', result.data?.commission_amount);
}API Reference
new LinkzlyAffiliate(config)
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| apiSecret | string | Yes | — | Program API secret from Linkzly console |
| programId | string | Yes | — | Your program ID |
| baseUrl | string | No | Production URL | Affiliate service base URL |
| timeout | number | No | 10000 | Request timeout in ms |
| debug | boolean | No | false | Enable debug logging |
trackConversion(request)
Send a server-to-server conversion. Automatically signs with HMAC-SHA256.
const result = await client.trackConversion({
order_id: 'ORD-12345', // Required — unique order ID
order_value: 99.99, // Required — total value
currency: 'USD', // Required — ISO 4217
click_id: 'lz_click_id_here', // From URL param (primary attribution)
cookie_id: 'lz_aff_cookie', // From cookie (secondary attribution)
customer_id: 'cust_abc', // Your internal customer ID
coupon_code: 'SAVE20', // Coupon used (tertiary attribution)
line_items: [ // For category-based commission
{ product_id: 'SKU-1', amount: 49.99, category: 'electronics' },
{ product_id: 'SKU-2', amount: 50.00, category: 'clothing' },
],
});Idempotent: Sending the same order_id returns the existing conversion.
reverseConversion(request)
Reverse a conversion (refund/chargeback). Creates negative ledger entry.
const result = await client.reverseConversion({
order_id: 'ORD-12345',
reason: 'Customer refund',
});verifyWebhookSignature(payload, signature, timestamp)
Verify an incoming webhook signature:
const isValid = client.verifyWebhookSignature(
rawBody,
req.headers['x-linkzly-signature'],
req.headers['x-linkzly-timestamp']
);parseWebhookEvent(payload, signature, timestamp)
Verify and parse a webhook event in one call:
const event = client.parseWebhookEvent(rawBody, signature, timestamp);
if (event) {
switch (event.type) {
case 'conversion.created':
// Handle new conversion
break;
case 'conversion.reversed':
// Handle reversal
break;
case 'payout.created':
// Handle payout
break;
}
}sign(body)
Generate signature for custom requests:
const { signature, timestamp } = client.sign(JSON.stringify(data));Express Middleware
import { LinkzlyAffiliate, webhookMiddleware } from '@linkzly/affiliate-node';
const client = new LinkzlyAffiliate({
apiSecret: process.env.LINKZLY_API_SECRET!,
programId: process.env.LINKZLY_PROGRAM_ID!,
});
// Use raw body for signature verification
app.post('/webhooks/linkzly',
express.raw({ type: 'application/json' }),
webhookMiddleware(client),
(req, res) => {
const event = req.linkzlyEvent;
console.log('Webhook:', event.type, event.data);
res.json({ received: true });
}
);Integration Examples
Next.js API Route
// app/api/conversions/route.ts
import { LinkzlyAffiliate } from '@linkzly/affiliate-node';
const client = new LinkzlyAffiliate({
apiSecret: process.env.LINKZLY_API_SECRET!,
programId: process.env.LINKZLY_PROGRAM_ID!,
});
export async function POST(req: Request) {
const body = await req.json();
const result = await client.trackConversion({
order_id: body.orderId,
order_value: body.total,
currency: 'USD',
click_id: body.lz_click_id,
cookie_id: body.lz_cookie_id,
customer_id: body.customerId,
});
return Response.json(result);
}Express + Web SDK
// Frontend sends attribution from @linkzly/affiliate-web
// Backend receives and forwards to Linkzly via this SDK
app.post('/api/checkout', async (req, res) => {
// Process payment...
const order = await processPayment(req.body);
// Track affiliate conversion
const result = await client.trackConversion({
order_id: order.id,
order_value: order.total,
currency: order.currency,
click_id: req.body.click_id, // From Web SDK
cookie_id: req.body.cookie_id, // From Web SDK
customer_id: order.customerId,
});
res.json({ order, affiliate: result });
});HMAC Signature Format
The SDK signs requests as:
message = "{programId}.{timestamp}.{body}"
signature = HMAC-SHA256(message, apiSecret) → hexHeaders sent:
X-Linkzly-Signature: hex-encoded HMACX-Linkzly-Program-Id: your program IDX-Linkzly-Timestamp: Unix timestamp (seconds)
Timestamp must be within 5 minutes of server time.
License
MIT
