npm package: @playcamp/node-sdk
Description: TypeScript-first Node.js SDK for PlayCamp API
Author: PlayCamp
Latest version: 0.0.4

# PlayCamp SDK

TypeScript-first Node.js SDK for the PlayCamp API.

## Installation

```bash
npm install @playcamp/node-sdk
```

**Requirements:** Node.js >= 18.0.0

## Quick Start

### Client SDK (Read-only)

Use `PlayCampClient` with a CLIENT API key for read-only operations:

```typescript
import { PlayCampClient } from '@playcamp/node-sdk';

const client = new PlayCampClient('your_client_key:your_secret');

// List campaigns
const { data: campaigns, pagination } = await client.campaigns.listCampaigns();

// Get a specific campaign
const campaign = await client.campaigns.getCampaign('campaign_id');

// Get campaign creators
const creators = await client.campaigns.getCreators('campaign_id');

// Search creators
const results = await client.creators.search({ keyword: 'gaming' });

// Get a creator by key
const creator = await client.creators.getCreator('ABCDE');

// Validate a coupon
const validation = await client.coupons.validate({ couponCode: 'CODE123' });

// Check sponsor status
const sponsor = await client.sponsors.getSponsor({ userId: 'user_123' });
```

### Server SDK (Read/Write)

Use `PlayCampServer` with a SERVER API key for full read/write access:

```typescript
import { PlayCampServer } from '@playcamp/node-sdk';

const server = new PlayCampServer('your_server_key:your_secret');

// Redeem a coupon
const result = await server.coupons.redeem({
  couponCode: 'CODE123',
  userId: 'user_abc',
});

// Create a sponsor relationship
const sponsor = await server.sponsors.create({
  userId: 'user_abc',
  creatorKey: 'ABCDE',
});

// Record a payment
const payment = await server.payments.create({
  userId: 'user_abc',
  transactionId: 'txn_123',
  productId: 'product_abc',
  amount: 9.99,
  currency: 'USD',
  platform: 'iOS',
  purchasedAt: new Date().toISOString(),
});

// Configure webhooks
const webhook = await server.webhooks.create({
  eventType: 'coupon.redeemed',
  url: 'https://your-server.com/webhooks/playcamp',
});
```

## Configuration

```typescript
const client = new PlayCampClient('key:secret', {
  environment: 'sandbox',  // 'sandbox' | 'live' (default: 'live')
  timeout: 30000,          // Request timeout in ms (default: 30000)
  isTest: false,           // Enable test mode (default: false)
  maxRetries: 3,           // Max retry attempts (default: 3)
  debug: true,             // Enable debug logging (default: false)
});
```

### Environments

| Environment | URL | Purpose |
|-------------|-----|---------|
| `sandbox` | https://sandbox-sdk-api.playcamp.io | Development/Testing |
| `live` | https://sdk-api.playcamp.io | Production (default) |

You can also specify a custom URL directly:

```typescript
const client = new PlayCampClient('key:secret', {
  baseUrl: 'http://localhost:3003',  // Overrides environment setting
});
```

## Pagination

List endpoints return paginated results:

```typescript
// Get a single page
const { data, pagination, hasNextPage } = await client.campaigns.listCampaigns({
  page: 1,
  limit: 20,
});

// Iterate through all items
for await (const campaign of client.campaigns.listAllCampaigns()) {
  console.log(campaign.campaignId);
}
```

## Error Handling

The SDK throws typed errors for different scenarios:

```typescript
import {
  PlayCampApiError,
  PlayCampAuthError,
  PlayCampNotFoundError,
  PlayCampRateLimitError,
  PlayCampNetworkError,
} from '@playcamp/node-sdk';

try {
  await client.campaigns.getCampaign('invalid_id');
} catch (error) {
  if (error instanceof PlayCampNotFoundError) {
    console.log('Campaign not found');
  } else if (error instanceof PlayCampAuthError) {
    console.log('Invalid API key');
  } else if (error instanceof PlayCampRateLimitError) {
    console.log('Rate limited, retry after:', error.retryAfter);
  } else if (error instanceof PlayCampNetworkError) {
    console.log('Network error:', error.message);
  } else if (error instanceof PlayCampApiError) {
    console.log('API error:', error.code, error.message);
  }
}
```

## Webhook Verification

Verify incoming webhook signatures:

```typescript
import { verifyWebhook } from '@playcamp/node-sdk';

// In your webhook handler
app.post('/webhooks/playcamp', (req, res) => {
  const result = verifyWebhook({
    payload: req.rawBody,  // Raw request body (string or Buffer)
    signature: req.headers['x-webhook-signature'],
    secret: 'your_webhook_secret',
    tolerance: 300,  // Optional: max age in seconds (default: 300)
  });

  if (!result.valid) {
    return res.status(401).json({ error: result.error });
  }

  // Webhooks contain batched events
  for (const event of result.payload.events) {
    switch (event.event) {
      case 'coupon.redeemed':
        console.log('Coupon redeemed:', event.data.couponCode);
        break;
      case 'payment.created':
        console.log('Payment created:', event.data.transactionId);
        break;
      case 'sponsor.created':
        console.log('Sponsor created:', event.data.userId);
        break;
    }
  }

  res.status(200).json({ received: true });
});
```

### Testing Webhooks

Generate test signatures for local development:

```typescript
import { constructWebhookSignature } from '@playcamp/node-sdk';

const payload = JSON.stringify({
  events: [{
    event: 'coupon.redeemed',
    timestamp: new Date().toISOString(),
    data: { couponCode: 'TEST', userId: 'user_123', usageId: 1, reward: [] },
  }]
});

const signature = constructWebhookSignature(payload, 'your_webhook_secret');
```

## API Reference

### Client Resources

| Resource | Methods |
|----------|---------|
| `campaigns` | `listCampaigns()`, `listAllCampaigns()`, `getCampaign()`, `getCreators()`, `getPackages()` |
| `creators` | `getCreator()`, `search()` |
| `coupons` | `validate()` |
| `sponsors` | `getSponsor()` |

### Server Resources

| Resource | Methods |
|----------|---------|
| `campaigns` | `listCampaigns()`, `listAllCampaigns()`, `getCampaign()`, `getCreators()` |
| `creators` | `getCreator()`, `search()`, `getCoupons()` |
| `coupons` | `validate()`, `redeem()`, `getUserHistory()`, `listAllUserHistory()` |
| `sponsors` | `create()`, `getByUser()`, `update()`, `remove()`, `getHistory()`, `listAllHistory()` |
| `payments` | `create()`, `getByTransactionId()`, `listByUser()`, `listAllByUser()`, `refund()` |
| `webhooks` | `listWebhooks()`, `create()`, `update()`, `remove()`, `getLogs()`, `test()` |

## TypeScript

The SDK is written in TypeScript and exports all types:

```typescript
import type {
  Campaign,
  Creator,
  CouponValidation,
  RedeemResult,
  Sponsor,
  Payment,
  Webhook,
  WebhookPayload,
} from '@playcamp/node-sdk';
```

## Development

```bash
npm run build       # Build SDK
npm run dev         # Watch mode build
npm test            # Run tests
npm run lint        # Run ESLint
npm run typecheck   # TypeScript check
```

## License

MIT