Installation
Complete guide to integrate ChurnCut into your application.
Architecture
ChurnCut works with two components:
- Backend - Generates HMAC tokens to authenticate users
- Frontend - JavaScript widget that displays the cancellation flow
text
Your Frontend --> Your Backend --> Churncut API
(JS Widget) (HMAC Token)Step 1: Get credentials
In your ChurnCut dashboard, go to Settings and copy:
- App ID: Unique identifier for your application
- Secret Key: Secret key for generating tokens (never expose on client)
Step 2: Implement token endpoint
Create an endpoint on your backend that generates the HMAC token when a user wants to cancel.
Express.js
javascript
const express = require('express');
const crypto = require('crypto');
const app = express();
const CHURNCUT_SECRET = process.env.CHURNCUT_SECRET_KEY;
const CHURNCUT_APP_ID = process.env.CHURNCUT_APP_ID;
app.get('/api/churncut-token', (req, res) => {
const { customerId, subscriptionId } = req.query;
// Token expires in 1 hour
const expiration = Math.floor(Date.now() / 1000) + 3600;
const message = customerId + CHURNCUT_APP_ID + expiration;
const token = crypto
.createHmac('sha256', CHURNCUT_SECRET)
.update(message)
.digest('hex');
res.json({
token,
expiration: expiration.toString(),
appId: CHURNCUT_APP_ID
});
});FastAPI (Python)
python
from fastapi import FastAPI
import hmac
import hashlib
import os
import time
app = FastAPI()
CHURNCUT_SECRET = os.environ["CHURNCUT_SECRET_KEY"]
CHURNCUT_APP_ID = os.environ["CHURNCUT_APP_ID"]
@app.get("/api/churncut-token")
def get_churncut_token(customer_id: str, subscription_id: str):
# Token expires in 1 hour
expiration = str(int(time.time()) + 3600)
message = (customer_id + CHURNCUT_APP_ID + expiration).encode('utf-8')
token = hmac.new(
CHURNCUT_SECRET.encode('utf-8'),
message,
digestmod=hashlib.sha256
).hexdigest()
return {
"token": token,
"expiration": expiration,
"appId": CHURNCUT_APP_ID
}Step 3: Integrate the widget
Add the ChurnCut script and connect it to your cancel button.
html
<script src="https://app.churncut.com/static/js/churncut.js"></script>React
jsx
import { useCallback } from 'react';
function CancelButton({ customerId, subscriptionId }) {
const handleCancel = useCallback(async () => {
// Get token from backend
const res = await fetch(
`/api/churncut-token?customerId=${customerId}&subscriptionId=${subscriptionId}`
);
const { token, expiration, appId } = await res.json();
// Start cancellation flow
window.startCancellationFlow(
customerId,
subscriptionId,
token,
expiration,
appId
);
}, [customerId, subscriptionId]);
return (
<button onClick={handleCancel}>
Cancel subscription
</button>
);
}Vue
vue
<template>
<button @click="handleCancel">Cancel subscription</button>
</template>
<script setup>
const props = defineProps(['customerId', 'subscriptionId']);
async function handleCancel() {
const res = await fetch(
`/api/churncut-token?customerId=${props.customerId}&subscriptionId=${props.subscriptionId}`
);
const { token, expiration, appId } = await res.json();
window.startCancellationFlow(
props.customerId,
props.subscriptionId,
token,
expiration,
appId
);
}
</script>Environment variables
Configure these variables on your server:
bash
CHURNCUT_APP_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
CHURNCUT_SECRET_KEY=your-secret-keyImportant: Never expose your Secret Key on the client. Always generate tokens on the backend.
Test the integration
- Use test credentials in your development environment
- Start a cancellation flow with a Stripe test customer
- Verify the widget appears correctly
- Check events in your ChurnCut dashboard
