Skip to main content
All CollectionsGetting started
Integration instructions
Integration instructions

Quick start guide for how to integrate the cancellation flow modal into your website.

David Wibergh avatar
Written by David Wibergh
Updated over a month ago

This guide walks you through the process of integrating Refloat's cancellation flow modal into your website. The modal helps retain customers by offering personalized offers during subscription cancellations.

1. Place the Script Element

The code snippet below imports the Refloat client-side module and assigns it to the window.refloat namespace. This allows you to initialize the Refloat Cancel Flow for your customers later on. Please place this code in the HTML <head> element.

To ensure Failed Payment Recovery works properly upon release, it's recommended to place the snippet on all pages, rather than just the subscription cancellation page.

<!-- Include the Refloat SDK on your page -->
<script>
!function(){
const a = document.createElement('script');
a.src = 'https://assets.refloat.com/snippet.js';
a.async = true;
const b = document.getElementsByTagName('script')[0];
b.parentNode.insertBefore(a, b);
}();
</script>

2. Generate Secure HMAC Hash

To ensure secure access, requests need to be authenticated by generating an HMAC hash using the Stripe Customer ID and your Secret Key.

Below are examples of how to generate an HMAC hash in various backend languages.

NodeJS

import { createHmac } from "node:crypto";
const user_hash = crypto.createHmac(
"sha256",
SECRET_KEY // Replace with Secret Key (keep safe)
)
.update(STRIPE_CUSTOMER_ID) // Replace with actual Stripe Customer ID
.digest("hex"); // Send to front-end

Next.js

import crypto from "node:crypto";
import type { NextApiRequest, NextApiResponse } from "next";

type Data = {
readonly userHash?: string;
readonly error?: string;
};

export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
// Replace with actual Stripe Customer ID
const { STRIPE_CUSTOMER_ID } = req.body;

const authHash = crypto
// Your Secret Key (keep safe)
.createHmac("sha256", SECRET_KEY) // Replace with Secret Key (keep safe)
.update(STRIPE_CUSTOMER_ID)
.digest("hex");

// Send computed auth hash to front-end application
return res.status(200).json({ authHash });
}

Python (Django)

import hmac
import hashlib

auth_hash = hmac.new(
"SECRET_KEY".encode('utf-8'), # Replace SECRET_KEY with Secret Key (keep safe)
"STRIPE_CUSTOMER_ID".encode('utf-8'), # Replace STRIPE_CUSTOMER_ID with actual Stripe Customer ID
digestmod=hashlib.sha256
).hexdigest() # Send to front-end

Ruby (Rails)

OpenSSL::HMAC.hexdigest(
"sha256",
SECRET_KEY, # Replace with Secret Key (keep safe)
STRIPE_CUSTOMER_ID # Replace with actual Stripe Customer ID
) # Send to front-end

PHP

<?php
echo hash_hmac(
'sha256',
STRIPE_CUSTOMER_ID, // Replace with actual Stripe Customer ID
SECRET_KEY // Replace with Secret Key (keep safe)
);
?>

Go

package main

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)

func main() {
hash := hmac.New(
sha256.New,
SECRET_KEY // Replace with Secret Key (keep safe)
)
hash.Write(STRIPE_CUSTOMER_ID) // Replace with actual Stripe Customer ID
hex.EncodeToString(hash.Sum(nil))
}

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class Test {
public static void main(String[] args) {
try {
String clientSecret = SECRET_KEY // Replace with Secret Key (keep safe)
String stripeCustomerId = STRIPE_CUSTOMER_ID; // Replace with actual Stripe Customer ID

Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(clientSecret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);

byte[] hash = (sha256_HMAC.doFinal(stripeCustomerId.getBytes()));
StringBuffer result = new StringBuffer();
for (byte b : hash) {
result.append(String.format("%02x", b));
}

// Send to front-end
System.out.println(result.toString());
} catch (Exception e) {
System.out.println("Error");
}
}
}


3. Trigger the Cancellation Modal (from your web page)

Attach a listener to a button that will trigger the Refloat cancellation flow modal. Below is an example of how to attach a click event listener to a button.

<body>

<!-- ... -->

<button id="refloat-cancel-button">
Cancel subscription
</button>

<!-- ... -->

<script>
document.addEventListener("DOMContentLoaded", () => {
function spawnModal() {
window.refloat?.init && window.refloat.init({
tenantId: "", // Replace with Refloat Tenant ID
apiKey: "", // Replace with Refloat API Key
mode: "live", // Or "test". If test mode is used, then no actions will be performed to customer's subscriptions.
stripeCustomerId: "", // Replace with actual Stripe Customer ID
stripeSubscriptionId: "", // Optional, otherwise omit. If customers may have multiple subscriptions, use this parameter to choose which subscription to launch the flow for
authHash: "", // Replace with calculated HMAC hash
})
}

document
.getElementById('refloat-cancel-button')
.addEventListener('click', spawnModal);
})
</script>
</body

4. Done

After adding the necessary script and handlers, when a user clicks the cancellation button, the Refloat SDK will open a modal that presents personalized offers, captures cancellation reasons, and provides insights for reducing churn.


Additional information

Live Mode vs. Test Mode

Both modes use Stripe production data. When mode is set to live, then actions such as pause, discount & cancel will be performed based on what occurs during the cancel flow.

When mode is set to test, then no actions will be performed on customer subscriptions. For example, if offers are accepted in the cancel flow, then no adjustments to the Stripe subscription will be performed.

Subscription ID

The stripeSubscriptionId parameter is optional and should only be used in case your customers may have multiple active subscriptions. If your customers only have one active subscription at any given time, then it can be omitted.

If your customers have multiple subscriptions, and they can cancel each one individually, then supplying the Stripe Subscription ID as parameter determines which subscription that the cancellation flow is launched for.


Don’t hesitate to reach out if you have any questions, need assistance, or have any feedback.

Did this answer your question?