npm package: @pulseroute/sdk
Description: PulseRoute SDK — intelligent payment routing with automatic failover
Author: PulseRoute
Latest version: 0.1.0

# PulseRoute Node.js SDK

Intelligent payment routing with automatic failover. Route transactions to the healthiest processor in real time.

## Install

```bash
npm install @pulseroute/sdk
```

## Quick Start (5 lines)

```javascript
const PulseRoute = require('@pulseroute/sdk');

const client = PulseRoute.init({ apiKey: 'your-api-key' });

// Before payment: where should I route?
const decision = await client.getRoute({ country: 'US', currency: 'USD', amount: 99.99 });

// After payment: report what happened
client.reportOutcome({ ruleId: 'us_usd_card', processorId: decision.processorId, success: true, latencyMs: 120 });
```

## Integration with Stripe + Adyen

```javascript
const PulseRoute = require('@pulseroute/sdk');
const Stripe = require('stripe');
const { Client: AdyenClient } = require('@adyen/api-library');

const pulse = PulseRoute.init({
  apiKey: process.env.PULSEROUTE_API_KEY,
  baseUrl: process.env.PULSEROUTE_URL || 'http://localhost:8080',
});

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const adyen = new AdyenClient({ apiKey: process.env.ADYEN_API_KEY });

async function processPayment(order) {
  // 1. Ask PulseRoute where to route
  const decision = await pulse.getRoute({
    country: order.country,
    currency: order.currency,
    paymentMethod: 'card',
    amount: order.amount,
  });

  const start = Date.now();
  let success = false;
  let errorCode = null;

  try {
    // 2. Route to the recommended processor
    if (decision.processorId === 'stripe') {
      const intent = await stripe.paymentIntents.create({
        amount: Math.round(order.amount * 100),
        currency: order.currency.toLowerCase(),
        payment_method: order.paymentMethodId,
        confirm: true,
      });
      success = intent.status === 'succeeded';
    } else if (decision.processorId === 'adyen') {
      const result = await adyen.checkout.payments({
        amount: { value: Math.round(order.amount * 100), currency: order.currency },
        reference: order.id,
        paymentMethod: order.adyenPaymentMethod,
        merchantAccount: process.env.ADYEN_MERCHANT,
      });
      success = result.resultCode === 'Authorised';
      if (!success) errorCode = result.resultCode;
    }
  } catch (err) {
    errorCode = err.code || err.message;
  }

  // 3. Report the outcome (fire-and-forget, non-blocking)
  pulse.reportOutcome({
    ruleId: `${order.country.toLowerCase()}_${order.currency.toLowerCase()}_card`,
    processorId: decision.processorId,
    success,
    latencyMs: Date.now() - start,
    errorCode,
  });

  return { success, processor: decision.processorId };
}
```

## Programmatic Onboarding

Skip the dashboard wizard — set up routing rules from code:

```javascript
const client = PulseRoute.init({ apiKey: 'your-api-key' });

const result = await client.onboard({
  processors: [
    {
      processorId: 'stripe',
      name: 'Stripe',
      countries: ['US', 'GB', 'DE'],
      currencies: ['USD', 'GBP', 'EUR'],
      paymentMethods: ['card', 'wallet'],
      priority: 1,
    },
    {
      processorId: 'adyen',
      name: 'Adyen',
      countries: ['US', 'GB', 'DE', 'FR', 'NL'],
      currencies: ['USD', 'GBP', 'EUR'],
      paymentMethods: ['card'],
      priority: 2,
    },
  ],
  primaryWeight: 0.9,
});

console.log(`Created ${result.rulesCreated} routing rules`);
// Created 9 routing rules
```

## Express Middleware

```javascript
const express = require('express');
const PulseRoute = require('@pulseroute/sdk');

const app = express();
const pulse = PulseRoute.init({ apiKey: process.env.PULSEROUTE_API_KEY });

app.post('/pay', express.json(), async (req, res) => {
  const { country, currency, amount, paymentMethodId } = req.body;

  const decision = await pulse.getRoute({ country, currency, amount });

  // ... process with decision.processorId ...

  res.json({ processor: decision.processorId, fallback: decision.fallbackProcessorId });
});

// Graceful shutdown
process.on('SIGTERM', async () => {
  await pulse.shutdown();
  process.exit(0);
});
```

## API Reference

### `PulseRoute.init(options)`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `apiKey` | string | required | API key |
| `baseUrl` | string | `http://localhost:8080` | PulseRoute API URL |
| `timeout` | number | `3000` | Request timeout (ms) |
| `flushInterval` | number | `1000` | Outcome flush interval (ms) |
| `maxBufferSize` | number | `500` | Max buffered outcomes |
| `enableCache` | boolean | `true` | Cache decisions locally |

### `client.getRoute(transaction)` → `Promise<RoutingDecision>`

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `country` | string | yes | ISO country code |
| `currency` | string | yes | ISO currency code |
| `paymentMethod` | string | no | `card`, `bank_transfer`, `wallet`, `bnpl` |
| `cardType` | string | no | `visa`, `mastercard`, `amex`, etc. |
| `amount` | number | no | Transaction amount |

Returns: `{ processorId, weight, fallbackProcessorId, decisionSource, failureProbability }`

### `client.reportOutcome(outcome)` → `void`

Fire-and-forget. Buffers outcomes and flushes periodically.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `ruleId` | string | yes | Rule that matched |
| `processorId` | string | yes | Processor used |
| `success` | boolean | yes | Transaction succeeded? |
| `latencyMs` | number | yes | Response time (ms) |
| `errorCode` | string | no | Error code if failed |

### `client.onboard(config)` → `Promise<OnboardResult>`

Bulk setup. See [Programmatic Onboarding](#programmatic-onboarding).

### `client.flush()` → `Promise<void>`

Flush buffered outcomes immediately.

### `client.shutdown()` → `Promise<void>`

Flush and stop background timers. Call before process exit.