npm package: @subscriptionsapp/sdk
Description: Official JavaScript/TypeScript SDK for Subscriptions App API
Author: SubscriptionsApp
Latest version: 1.0.0

# Subscriptions App SDK for JavaScript/TypeScript

Official JavaScript/TypeScript SDK for the Subscriptions App API. Easily integrate subscription management into your Node.js or browser applications.

## Installation

```bash
npm install @subscriptionsapp/sdk
# or
pnpm add @subscriptionsapp/sdk
# or
yarn add @subscriptionsapp/sdk
```

## Quick Start

```typescript
import { SubscriptionsClient } from '@subscriptionsapp/sdk';

// Initialize the client
const client = new SubscriptionsClient('your-api-key');

// Create a checkout session
const session = await client.createCheckout(
  'plan_123',
  'user@example.com',
  'https://yourapp.com/success',
  'https://yourapp.com/cancel'
);

// Redirect user to checkout
window.location.href = session.url;
```

## Features

- ✅ **Full TypeScript Support** - Complete type definitions for all API resources
- ✅ **Promise-based API** - Modern async/await syntax
- ✅ **Automatic Retries** - Built-in error handling and retries
- ✅ **Webhook Verification** - Secure HMAC signature validation
- ✅ **Tree-shakeable** - Only bundle what you use
- ✅ **Zero Dependencies** - Lightweight and fast

## Authentication

Get your API key from the [Subscriptions App Dashboard](https://yourapp.com/apps):

```typescript
const client = new SubscriptionsClient({
  apiKey: 'your-api-key',
  baseURL: 'https://api.yourapp.com', // optional, defaults to production
  timeout: 30000 // optional, request timeout in ms
});

// Or simply pass the API key
const client = new SubscriptionsClient('your-api-key');
```

## Usage Examples

### Apps Management

```typescript
// List all apps
const apps = await client.apps.list();

// Create a new app
const app = await client.apps.create({
  name: 'My Application',
  description: 'My awesome app',
  webhook_url: 'https://myapp.com/webhooks'
});

// Get app details
const app = await client.apps.get('app_123');

// Update an app
const updated = await client.apps.update('app_123', {
  name: 'Updated Name',
  webhook_url: 'https://myapp.com/new-webhook'
});

// Rotate API keys
const rotated = await client.apps.rotateKeys('app_123');
console.log('New keys:', rotated.public_key);

// Delete an app
await client.apps.delete('app_123');
```

### Plans Management

```typescript
// List plans for an app
const plans = await client.plans.list('app_123');

// Create a subscription plan
const plan = await client.plans.create({
  app_id: 'app_123',
  name: 'Pro Plan',
  description: 'Full access to all features',
  price: 2999, // $29.99 in cents
  currency: 'usd',
  billing_interval: 'month',
  trial_days: 14
});

// Get plan details
const plan = await client.plans.get('plan_123');

// Update a plan
const updated = await client.plans.update('plan_123', {
  price: 3999, // $39.99
  trial_days: 30
});

// Delete a plan
await client.plans.delete('plan_123');
```

### Subscriptions

```typescript
// Create a checkout session
const session = await client.subscriptions.createCheckoutSession({
  plan_id: 'plan_123',
  user_email: 'customer@example.com',
  user_metadata: {
    user_id: '12345',
    company: 'Acme Inc'
  },
  success_url: 'https://myapp.com/success',
  cancel_url: 'https://myapp.com/cancel'
});

// Redirect to checkout
console.log('Checkout URL:', session.url);

// Validate a subscription (check if user has access)
const validation = await client.subscriptions.validate({
  subscription_id: 'sub_123'
});

if (validation.valid) {
  console.log('User has active subscription');
  console.log('Expires:', validation.subscription.current_period_end);
} else {
  console.log('No active subscription:', validation.message);
}

// List subscriptions for a plan
const subscriptions = await client.subscriptions.list('plan_123');

// Get subscription by ID
const subscription = await client.subscriptions.get('sub_123');

// Get subscription by user email
const userSub = await client.subscriptions.getByEmail('plan_123', 'user@example.com');

// Cancel a subscription
const canceled = await client.subscriptions.cancel('sub_123', false);
console.log('Subscription ends at:', canceled.current_period_end);

// Cancel immediately (no refund grace period)
await client.subscriptions.cancel('sub_123', true);
```

### Webhooks

```typescript
import express from 'express';

const app = express();

// Use raw body for webhook verification
app.post('/webhooks', express.raw({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['x-signature'] as string;
  const timestamp = req.headers['x-timestamp'] as string;
  const payload = req.body.toString('utf8');

  try {
    // Verify and parse webhook event
    const event = await client.webhooks.constructEvent(
      payload,
      signature,
      timestamp,
      'your-webhook-secret' // From app settings
    );

    console.log('Received event:', event.type);

    // Handle different event types
    switch (event.type) {
      case 'subscription.created':
        console.log('New subscription:', event.data.subscription);
        // Grant access to user
        break;

      case 'subscription.canceled':
        console.log('Subscription canceled:', event.data.subscription);
        // Revoke access
        break;

      case 'subscription.updated':
        console.log('Subscription updated:', event.data.subscription);
        break;

      case 'payment.succeeded':
        console.log('Payment successful:', event.data.payment);
        break;

      case 'payment.failed':
        console.log('Payment failed:', event.data.payment);
        // Notify user
        break;
    }

    res.json({ received: true });
  } catch (err) {
    console.error('Webhook error:', err);
    res.status(400).json({ error: 'Invalid webhook signature' });
  }
});
```

### Manual Signature Verification

```typescript
const isValid = await client.webhooks.verifySignature(
  payload,
  signature,
  timestamp,
  webhookSecret
);

if (isValid) {
  const event = JSON.parse(payload);
  // Process event
}
```

## Framework Examples

### Next.js App Router

```typescript
// app/api/checkout/route.ts
import { SubscriptionsClient } from '@subscriptionsapp/sdk';
import { NextResponse } from 'next/server';

const client = new SubscriptionsClient(process.env.SUBSCRIPTIONS_API_KEY!);

export async function POST(request: Request) {
  const { planId, email } = await request.json();

  const session = await client.createCheckout(
    planId,
    email,
    `${process.env.NEXT_PUBLIC_URL}/success`,
    `${process.env.NEXT_PUBLIC_URL}/cancel`
  );

  return NextResponse.json({ url: session.url });
}
```

### Express.js

```typescript
import express from 'express';
import { SubscriptionsClient } from '@subscriptionsapp/sdk';

const app = express();
const client = new SubscriptionsClient(process.env.SUBSCRIPTIONS_API_KEY);

app.use(express.json());

app.post('/api/checkout', async (req, res) => {
  const { planId, email } = req.body;

  const session = await client.createCheckout(
    planId,
    email,
    'http://localhost:3000/success',
    'http://localhost:3000/cancel'
  );

  res.json({ url: session.url });
});

app.listen(3000, () => console.log('Server running on port 3000'));
```

### React Hook

```typescript
import { useState } from 'react';
import { SubscriptionsClient } from '@subscriptionsapp/sdk';

const client = new SubscriptionsClient(import.meta.env.VITE_API_KEY);

export function useCheckout() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const createCheckout = async (planId: string, email: string) => {
    setLoading(true);
    setError(null);

    try {
      const session = await client.createCheckout(
        planId,
        email,
        window.location.origin + '/success',
        window.location.origin + '/cancel'
      );

      window.location.href = session.url;
    } catch (err: any) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return { createCheckout, loading, error };
}
```

## Error Handling

```typescript
import {
  SubscriptionsClient,
  APIRequestError,
  AuthenticationError,
  ValidationError,
  NetworkError,
  TimeoutError
} from '@subscriptionsapp/sdk';

const client = new SubscriptionsClient('api-key');

try {
  const app = await client.apps.create({ name: 'Test' });
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Invalid API key');
  } else if (error instanceof ValidationError) {
    console.error('Invalid request data:', error.message);
  } else if (error instanceof APIRequestError) {
    console.error('API error:', error.statusCode, error.detail);
  } else if (error instanceof TimeoutError) {
    console.error('Request timed out');
  } else if (error instanceof NetworkError) {
    console.error('Network error:', error.message);
  }
}
```

## TypeScript Support

The SDK is written in TypeScript and provides full type definitions:

```typescript
import type {
  App,
  Plan,
  Subscription,
  WebhookEvent,
  SubscriptionStatus
} from '@subscriptionsapp/sdk';

// All types are exported and available
const handleSubscription = (sub: Subscription) => {
  const status: SubscriptionStatus = sub.status;
  // TypeScript knows all properties and validates them
};
```

## API Reference

### SubscriptionsClient

Main client class for interacting with the API.

#### Constructor

```typescript
new SubscriptionsClient(config: SDKConfig | string)
```

#### Resources

- `client.apps` - Apps management
- `client.plans` - Subscription plans
- `client.subscriptions` - User subscriptions
- `client.webhooks` - Webhook utilities

### Apps Resource

- `apps.list(params?)` - List all apps
- `apps.get(appId)` - Get app by ID
- `apps.create(data)` - Create new app
- `apps.update(appId, data)` - Update app
- `apps.delete(appId)` - Delete app
- `apps.rotateKeys(appId)` - Rotate API keys
- `apps.rotateWebhookSecret(appId)` - Rotate webhook secret

### Plans Resource

- `plans.list(appId, params?)` - List plans for app
- `plans.get(planId)` - Get plan by ID
- `plans.create(data)` - Create new plan
- `plans.update(planId, data)` - Update plan
- `plans.delete(planId)` - Delete plan

### Subscriptions Resource

- `subscriptions.list(planId, params?)` - List subscriptions
- `subscriptions.get(subscriptionId)` - Get subscription
- `subscriptions.createCheckoutSession(data)` - Create checkout
- `subscriptions.validate(data)` - Validate subscription
- `subscriptions.cancel(subscriptionId, immediate)` - Cancel subscription
- `subscriptions.getByEmail(planId, email)` - Get user's subscription

### Webhooks Resource

- `webhooks.verifySignature(payload, signature, timestamp, secret)` - Verify webhook
- `webhooks.parseEvent(payload, signature, timestamp, secret)` - Parse event
- `webhooks.constructEvent(payload, signature, timestamp, secret)` - Construct and verify

## Contributing

Contributions are welcome! Please open an issue or submit a pull request on [GitHub](https://github.com/ajvaldesg/SubscriptionsApp).

## License

MIT License - see [LICENSE](LICENSE) file for details.

## Support

- Documentation: [https://yourapp.com/docs](https://yourapp.com/docs)
- Email: support@yourapp.com
- GitHub Issues: [https://github.com/ajvaldesg/SubscriptionsApp/issues](https://github.com/ajvaldesg/SubscriptionsApp/issues)