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
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
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)
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)
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
<?php
echo hash_hmac(
'sha256',
STRIPE_CUSTOMER_ID, // Replace with actual Stripe Customer ID
SECRET_KEY // Replace with Secret Key (keep safe)
);
?>
Go
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
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.