Staysignal Docs
  • Welcome
  • Getting Started
    • Quickstart
      • Connecting Stripe
      • How to Customize Widget Follow-Ups
      • How to Connect Slack
    • Publish your Staysignal widget
  • Basics
    • Managing your Staysignal subscription
    • Integration Guides
      • Simple HTML/JS integration
      • NextJS integration (App Router)
      • React integration
      • WordPress/WooCommerce integration
Powered by GitBook
On this page
  • Simple HTML/JavaScript Integration
  • Error Handling
  • Testing
  • Finding Stripe Subscription IDs
  • Need Help?
  1. Basics
  2. Integration Guides

Simple HTML/JS integration

PreviousIntegration GuidesNextNextJS integration (App Router)

Last updated 1 day ago

Simple HTML/JavaScript Integration

Follow these steps to integrate the Staysignal widget into your website:

1. Include the Script

Add the Staysignal script to your website, replacing YOUR_SITE_ID_HERE with your actual Site ID:

<script src="https://app.staysignal.com/api/widget/script/YOUR_SITE_ID_HERE"></script>

Activate your 14-day free trial to get Site ID

Please to get your unique Site ID and fully integrate the widget. This code example uses a placeholder.

2. Initialize the Widget

Initialize the widget with callback handlers. The Site ID is automatically included from the script URL.

<script>
StaySignal.init({
  // site_id is embedded in the script loaded via /api/widget/script/[siteId]
  onComplete: (payload) => {
    console.log('Widget flow complete:', payload);
    if (payload.resolution === 'cancelled') {
      // Handle successful cancellation
    } else {
      // Handle when user decides to stay
    }
  },
  onError: (error) => {
    console.error('Widget error:', error);
  }
});
</script>

3. Add Cancel Buttons

You have two options to add cancel buttons to your site, and you can also opt out of automatic cancellation if you have a custom process:

Opt-Out of Automatic Cancellation

To prevent Staysignal from cancelling the subscription automatically, use either of these methods:

  • Config option: Pass cancelOptOut: true to the widget init config.

  • Button attribute: Add data-ss-cancel-optout="true" to your cancel button.

Precedence: If either method is set, cancellation is skipped and a message is logged to the console.

Option 1: Class-based Approach (Recommended)

Add the staysignal-cancel class and data-subscription_id attribute to any button. To opt out, add data-ss-cancel-optout="true":

<button class="staysignal-cancel" data-subscription_id="sub_123456" data-ss-cancel-optout="true">
    Cancel Subscription
</button>

Option 2: JavaScript Approach

Manually trigger the widget using JavaScript. To opt out globally, pass cancelOptOut: true to the config:

<script>
StaySignal.init({
    siteId: 'YOUR_SITE_ID',
    cancelOptOut: true // Opt out of automatic cancellation
});
</script>

Complete Example

<!DOCTYPE html>
<html>
<head>
    <title>StaySignal Integration Example</title>
</head>
<body>
    {/* Example Button: */} 
    <button class="staysignal-cancel" data-subscription_id="sub_123456">Cancel Subscription</button>

    <!-- Include the StaySignal Widget -->
    <script>
        const script = document.createElement('script');
        script.src = 'https://app.staysignal.com/api/widget/script/YOUR_SITE_ID_HERE';
        script.onload = function() {
            StaySignal.init({
                // site_id is embedded in the script loaded via /api/widget/script/[siteId]
                onComplete: (payload) => {
                    console.log('Widget flow complete:', payload);
                    if (payload.resolution === 'cancelled') {
                        alert('Subscription cancelled successfully.');
                    } else {
                        alert('Great! You decided to stay with us.');
                    }
                },
                onError: (error) => {
                    console.error('Widget error:', error);
                }
            }); // Escape backticks in the initLogic string itself
            StaySignal.onReady(() => {
                console.log('StaySignal Ready!');
                // Optional: Add JS click handlers if not using class-based approach
            });
        };
        document.body.appendChild(script);
    </script>
</body>
</html>

Error Handling

It's recommended to implement proper error handling:

<script>
// Global error handler for widget loading issues
window.onerror = function(msg, url, line, col, error) {
    console.error('Widget error:', { msg, url, line, col, error });
    return false;
};

// Widget-specific error handling
StaySignal.init({
    // site_id is no longer needed here, it's included in the script URL
    onError: (error) => {
        console.error('Widget error:', error);
        // Handle the error appropriately
    }
});
</script>

Testing

To test your integration:

  1. Ensure the widget script loads successfully

  2. Check browser console for any errors

  3. Test both class-based and JavaScript-based cancel buttons

  4. Verify the onComplete callback is triggered

  5. Test error scenarios by providing invalid subscription IDs

Finding Stripe Subscription IDs

Staysignal requires a subscription_id to identify which subscription a user is trying to cancel. Here are ways to find and use a user's Stripe subscription ID:

From Your Database

Most applications store subscription IDs in their database after a successful Stripe subscription creation. This is the recommended approach for performance and reliability.

<script>
// Example using Node.js with Express and MongoDB
app.get('/account', async (req, res) => {
  try {
    // Get user ID from session/JWT
    const userId = req.user.id;
    
    // Fetch subscription from your database
    const user = await User.findById(userId).populate('subscription');
    const subscriptionId = user.subscription?.stripeSubscriptionId;
    
    res.render('account', { 
      user, 
      subscriptionId  // Pass to your view
    });
  } catch (error) {
    console.error('Error fetching subscription:', error);
    res.status(500).send('Server error');
  }
});
</script>
<?php
// Example using PHP with Laravel
public function showAccount()
{
    $user = Auth::user();
    $subscription = $user->subscription; // Assuming relationship is set up
    $subscriptionId = $subscription ? $subscription->stripe_subscription_id : null;
    
    return view('account', [
        'user' => $user,
        'subscriptionId' => $subscriptionId
    ]);
}
# Example using Ruby on Rails
class AccountController < ApplicationController
  def show
    @user = current_user
    @subscription = @user.subscription
    @subscription_id = @subscription&.stripe_subscription_id
  end
end

# In your view: account.html.erb
<button class="staysignal-cancel" data-subscription_id="<%= @subscription_id %>">
  Cancel Subscription
</button>
# Example using Django
def account_view(request):
    user = request.user
    subscription = Subscription.objects.filter(user=user).first()
    subscription_id = subscription.stripe_subscription_id if subscription else None
    
    return render(request, 'account.html', {
        'user': user,
        'subscription_id': subscription_id
    })

Fetching from Stripe API

If you need to fetch the subscription ID directly from Stripe (not recommended for production):

<script>
// Using Stripe Node.js SDK
const stripe = require('stripe')('sk_test_your_stripe_secret_key');

async function getCustomerSubscriptions(customerId) {
  try {
    const subscriptions = await stripe.subscriptions.list({
      customer: customerId,
      status: 'active',
      limit: 1
    });
    
    if (subscriptions.data.length > 0) {
      return subscriptions.data[0].id; // Returns the subscription ID
    }
    return null;
  } catch (error) {
    console.error('Error fetching from Stripe:', error);
    throw error;
  }
}
</script>
<?php
// Using Stripe PHP SDK
require_once 'vendor/autoload.php';
StripeStripe::setApiKey('sk_test_your_stripe_secret_key');

function getCustomerSubscriptions($customerId) {
    try {
        $subscriptions = StripeSubscription::all([
            'customer' => $customerId,
            'status' => 'active',
            'limit' => 1
        ]);
        
        if (count($subscriptions->data) > 0) {
            return $subscriptions->data[0]->id;
        }
        return null;
    } catch (Exception $e) {
        error_log('Stripe API Error: ' . $e->getMessage());
        return null;
    }
}
# Using Stripe Ruby SDK
require 'stripe'
Stripe.api_key = 'sk_test_your_stripe_secret_key'

def get_customer_subscriptions(customer_id)
  begin
    subscriptions = Stripe::Subscription.list(
      customer: customer_id,
      status: 'active',
      limit: 1
    )
    
    return subscriptions.data.first&.id
  rescue Stripe::StripeError => e
    Rails.logger.error("Stripe API Error: #{e.message}")
    return nil
  end
end
# Using Stripe Python SDK
import stripe
stripe.api_key = "sk_test_your_stripe_secret_key"

def get_customer_subscriptions(customer_id):
    try:
        subscriptions = stripe.Subscription.list(
            customer=customer_id,
            status='active',
            limit=1
        )
        
        if subscriptions.data:
            return subscriptions.data[0].id
        return None
    except stripe.error.StripeError as e:
        print(f"Stripe API Error: {e}")
        return None

Security Considerations

Always follow these security best practices:

  • Never expose your Stripe secret key in client-side code

  • Implement proper authentication before showing/using subscription IDs

  • If using the Stripe API directly, cache the results to minimize API calls

Need Help?

If you encounter any issues with the integration, please contact our support team.

Consider using to keep your database in sync with Stripe

activate your subscription
Need help finding the subscription ID?
Stripe Webhooks