Integrations

Integration API Setup

This section demonstrates how to set up an Express API that integrates all components of your Churnkey integration. It provides a unified interface for Churnkey to interact with your payment provider. While we use Express in this example, you can use any language or framework as long as you properly expose the required routes.

API Structure

The API consists of two main types of routes:

  1. Retrieval Routes: GET endpoints for fetching customer, subscription, and price data.
  2. Action Routes: POST endpoints for performing operations on subscriptions.

Prerequisites

  • Node.js and npm installed
  • Express.js
  • body-parser for parsing JSON request bodies
  • dotenv for managing environment variables

Implementation

Setup and Configuration

First, set up your project and install dependencies:

npm init -y
npm install express body-parser dotenv

Create a .env file in your project root:

PORT=3000
CHURNKEY_API_KEY=your_secret_api_key_here

Main Application File (app.js)

const express = require('express');
const bodyParser = require('body-parser');
const {
  retrieveCustomer,
  listCustomers,
  findCustomerByEmail,
  findCustomerByPhone,
  retrieveSubscription,
  listSubscriptions,
  retrievePrice,
  listPrices,
  retrieveCoupon,
  listCoupons,
  cancelSubscription,
  applyCoupon,
  extendTrial,
  pauseSubscription,
  changePrice,
} = require('./churnkeyIntegration');

const app = express();
const port = process.env.PORT || 3000;

app.use(bodyParser.json());

// Middleware for API key authentication
const authenticateApiKey = (req, res, next) => {
  const apiKey = req.headers['x-api-key'];
  if (apiKey !== process.env.CHURNKEY_API_KEY) {
    return res.status(401).json({ error: 'Invalid API key' });
  }
  next();
};

app.use(authenticateApiKey);

// Error handling middleware
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'An unexpected error occurred' });
});

// Retrieval Routes
app.get('/customers/:id', async (req, res, next) => {
  try {
    const customer = await retrieveCustomer(req.params.id);
    res.json(customer);
  } catch (error) {
    next(error);
  }
});

app.get('/customers', async (req, res, next) => {
  try {
    const { limit, cursor } = req.query;
    const customers = await listCustomers({ limit, cursor });
    res.json(customers);
  } catch (error) {
    next(error);
  }
});

app.get('/customers/find-by-email/:email', async (req, res, next) => {
  try {
    const customer = await findCustomerByEmail(req.params.email);
    res.json(customer);
  } catch (error) {
    next(error);
  }
});

app.get('/customers/find-by-phone/:phone', async (req, res, next) => {
  try {
    const customer = await findCustomerByPhone(req.params.phone);
    res.json(customer);
  } catch (error) {
    next(error);
  }
});

app.get('/subscriptions/:id', async (req, res, next) => {
  try {
    const subscription = await retrieveSubscription(req.params.id);
    res.json(subscription);
  } catch (error) {
    next(error);
  }
});

app.get('/subscriptions', async (req, res, next) => {
  try {
    const { limit, cursor, customerId } = req.query;
    const subscriptions = await listSubscriptions({ limit, cursor, customerId });
    res.json(subscriptions);
  } catch (error) {
    next(error);
  }
});

app.get('/prices/:id', async (req, res, next) => {
  try {
    const price = await retrievePrice(req.params.id);
    res.json(price);
  } catch (error) {
    next(error);
  }
});

app.get('/prices', async (req, res, next) => {
  try {
    const { limit, cursor, productId } = req.query;
    const prices = await listPrices({ limit, cursor, productId });
    res.json(prices);
  } catch (error) {
    next(error);
  }
});

app.get('/coupons/:id', async (req, res, next) => {
  try {
    const coupon = await retrieveCoupon(req.params.id);
    res.json(coupon);
  } catch (error) {
    next(error);
  }
});

app.get('/coupons', async (req, res, next) => {
  try {
    const { limit, cursor } = req.query;
    const coupons = await listCoupons({ limit, cursor });
    res.json(coupons);
  } catch (error) {
    next(error);
  }
});

// Action Routes
app.post('/subscriptions/cancel', async (req, res, next) => {
  try {
    const { customerId, subscriptionIds, scheduledAt, unpause } = req.body;
    const result = await cancelSubscription({ customerId, subscriptionIds, scheduledAt, unpause });
    res.json(result);
  } catch (error) {
    next(error);
  }
});

app.post('/subscriptions/apply-coupon', async (req, res, next) => {
  try {
    const { customerId, subscriptionIds, id } = req.body;
    const result = await applyCoupon({ customerId, subscriptionIds, id });
    res.json(result);
  } catch (error) {
    next(error);
  }
});

app.post('/subscriptions/extend-trial', async (req, res, next) => {
  try {
    const { customerId, subscriptionIds, duration } = req.body;
    const result = await extendTrial({ customerId, subscriptionIds, duration });
    res.json(result);
  } catch (error) {
    next(error);
  }
});

app.post('/subscriptions/pause', async (req, res, next) => {
  try {
    const { customerId, subscriptionIds, duration, startDate, allowAnnual } = req.body;
    const result = await pauseSubscription({ customerId, subscriptionIds, duration, startDate, allowAnnual });
    res.json(result);
  } catch (error) {
    next(error);
  }
});

app.post('/subscriptions/change-price', async (req, res, next) => {
  try {
    const { customerId, subscriptionIds, newPriceId, prorate } = req.body;
    const result = await changePrice({ customerId, subscriptionIds, newPriceId, prorate });
    res.json(result);
  } catch (error) {
    next(error);
  }
});

app.listen(port, () => {
  console.log(`Churnkey integration API listening at http://localhost:${port}`);
});

This Express API sets up routes for all the retrieval and action functions we've discussed. Here are some key points about this implementation:

  1. We import all the functions from a churnkeyIntegration.js file, which would contain all the retrieval and action functions we've previously discussed.
  2. We use middleware for API key authentication. This ensures that only requests with a valid API key can access our endpoints.
  3. We've set up error handling middleware to catch any unexpected errors and return a 500 status code.
  4. For retrieval functions, we use GET routes. For action functions, we use POST routes.
  5. We pass query parameters for list functions and use route parameters for individual item retrieval.
  6. Action functions receive their parameters through the request body.
  7. All route handlers are wrapped in try/catch blocks to handle errors gracefully.

To use this API, you would need to:

  1. Set up your environment variables (like CHURNKEY_API_KEY and PORT).
  2. Implement all the imported functions in churnkeyIntegration.js.
  3. Set up your payment provider's API client and any necessary configuration.

This Express API provides a clean, RESTful interface for Churnkey to interact with your payment provider. It encapsulates all the complexity of your payment provider's API behind a standardized interface that Churnkey expects.