Integrations

Data Retrieval

Retrieval endpoints are responsible for fetching individual items and listing multiple items for each data type. These functions will interact with your payment provider's API and return data in the format expected by Churnkey.

Common Patterns

For each data type (Customers, Subscriptions, Prices, and optionally Coupons), you'll need to implement two types of functions:

  1. retrieve: Fetch a single item by ID
  2. list: Fetch multiple items, supporting pagination

All functions should be asynchronous and handle errors appropriately.

Customers

The Customers controller is responsible for managing customers. It's one of the core modules required for the integration. All integrations should implement it because its functionality is essential for the rest of the modules.

Retrieve Customer

async function retrieveCustomer(id) {
  try {
    // Implement your own logic to fetch a customer by ID
    const customer = await fetchCustomerById(id);

    // Return the customer data in a standardized format as implemented in the Data Structures section.
    return mapCustomer(customer);
  } catch (error) {
    console.error('Failed to retrieve customer:', error);
    throw error;
  }
}

List Customers

async function listCustomers({ limit = 10, cursor } = {}) {
  try {
    // Implement your own logic to list customers with pagination
    const result = await fetchCustomers(limit, cursor);

    // Return the customer data in a standardized format as implemented in the Data Structures section.
    return {
      data: result.customers.map(mapCustomer),
      next: result.nextCursor || undefined,
    };
  } catch (error) {
    console.error('Failed to list customers:', error);
    throw error;
  }
}

Find Customer by Email

Implement this method to enable email-based Managed Flow for this integration.

async function findCustomerByEmail(email) {
  try {
    // Implement your own logic to find a customer by email
    const customer = await findCustomerByEmailAddress(email);

    // Return the customer data in a standardized format as implemented in the Data Structures section.
    return mapCustomer(customer);
  } catch (error) {
    console.error('Failed to find customer by email:', error);
    throw error;
  }
}

Find Customer by Phone

Implement this method to enable phone-based Managed Flow for this integration.

async function findCustomerByPhone(phone) {
  try {
    // Implement your own logic to find a customer by phone number
    const customer = await findCustomerByPhoneNumber(phone);

    // Return the customer data in a standardized format as implemented in the Data Structures section.
    return mapCustomer(customer);
  } catch (error) {
    console.error('Failed to find customer by phone:', error);
    throw error;
  }
}

Subscriptions

The Subscriptions controller is responsible for managing subscriptions. It's a core module required for integration, providing essential functionality for subscription-related operations.

Retrieve Subscription

async function retrieveSubscription(id) {
  try {
    // Implement your own logic to fetch a subscription by ID
    const subscription = await fetchSubscriptionById(id);

    // Return the subscription data in a standardized format as implemented in the Data Structures section.
    return mapSubscription(subscription);
  } catch (error) {
    console.error('Failed to retrieve subscription:', error);
    throw error;
  }
}

List Subscriptions

async function listSubscriptions({ limit = 10, cursor, customerId } = {}) {
  try {
    // Implement your own logic to list subscriptions with pagination
    const result = await fetchSubscriptions(limit, cursor, customerId);

    // Return the subscription data in a standardized format as implemented in the Data Structures section.
    return {
      data: result.subscriptions.map(mapSubscription),
      next: result.nextCursor || undefined,
    };
  } catch (error) {
    console.error('Failed to list subscriptions:', error);
    throw error;
  }
}

Prices

The Prices controller is responsible for managing price information. It's an essential module for integrations that deal with product pricing.

Retrieve Price

async function retrievePrice(id) {
  try {
    // Implement your own logic to fetch a price by ID
    const price = await fetchPriceById(id);

    // Return the price data in a standardized format as implemented in the Data Structures section.
    return mapPrice(price);
  } catch (error) {
    console.error('Failed to retrieve price:', error);
    throw error;
  }
}

List Prices

async function listPrices({ limit = 10, cursor, productId } = {}) {
  try {
    // Implement your own logic to list prices with pagination
    const result = await fetchPrices(limit, cursor, productId);

    // Return the price data in a standardized format as implemented in the Data Structures section.
    return {
      data: result.prices.map(mapPrice),
      next: result.nextCursor || undefined,
    };
  } catch (error) {
    console.error('Failed to list prices:', error);
    throw error;
  }
}

Coupons (Optional)

The Coupons controller is used to manage coupons in the system. If your integration supports coupons, you'll need to implement the following functions for retrieving and listing coupons.

Retrieve Coupon

async function retrieveCoupon(id) {
  try {
    // Implement your own logic to fetch a coupon by ID
    const coupon = await fetchCouponById(id);

    // Return the coupon data in a standardized format as implemented in the Data Structures section.
    return mapCoupon(coupon);
  } catch (error) {
    console.error('Failed to retrieve coupon:', error);
    throw error;
  }
}

List Coupons

async function listCoupons({ limit = 10, cursor } = {}) {
  try {
    // Implement your own logic to list coupons with pagination
    const result = await fetchCoupons(limit, cursor);

    // Return the coupons data in a standardized format
    return {
      data: result.coupons.map(mapCoupon),
      next: result.nextCursor || undefined,
    };
  } catch (error) {
    console.error('Failed to list coupons:', error);
    throw error;
  }
}

Note that the Coupons module is optional. If your integration doesn't support coupons, you can omit this module entirely.

Error Handling

It's crucial to implement proper error handling in your retrieval functions. Here's an example of a simple error handling function:

function handleError(error, message) {
  console.error(`${message}: ${error.message}`);
  if (error.response) {
    console.error('Response data:', error.response.data);
    console.error('Response status:', error.response.status);
  }
  throw new Error(message);
}

Pagination

The list functions implement cursor-based pagination. They accept a cursor parameter and return a next cursor in the response. This allows Churnkey to efficiently fetch large datasets in manageable chunks.

Best Practices

  1. Use a consistent error handling across all functions.
  2. Implement proper logging for easier debugging.
  3. Use type annotations if your language supports them (e.g., TypeScript) for better type safety.
  4. Consider implementing caching for frequently accessed data to improve performance.

After implementing Data Structures and Data Retrieval Endpoints, you'll have completed the read-only part of your Churnkey integration. This allows Churnkey to fetch and display data from your payment provider. In the Action Endpoints section, we'll explore how to implement action functions that enable Churnkey to perform operations on subscriptions.