ChurnCut/Docs

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-key

Important: 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