Data Retrieval
Common Patterns
For each data type (Customers, Subscriptions, Prices, and optionally Coupons), you'll need to implement two types of functions:
retrieve
: Fetch a single item by IDlist
: 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
- Use a consistent error handling across all functions.
- Implement proper logging for easier debugging.
- Use type annotations if your language supports them (e.g., TypeScript) for better type safety.
- 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.
Data Structures
When integrating directly with Churnkey, you'll need to define data structures that map your payment provider's data to Churnkey's expected format. Here are the key data structures to implement
Action endpoints
Action functions are responsible for performing specific operations on subscriptions, such as applying discounts, canceling subscriptions, or changing prices. These functions will interact with your payment provider's API to perform the requested actions and return the updated data in the format expected by Churnkey.