Powered by Coinflow
Payments · Documentation
Operational

Enabling Customers to Cancel Subscriptions

Overview

Allowing customers to cancel their subscriptions is essential for a good user experience and helps reduce chargebacks. Coinflow provides simple APIs to enable subscription cancellation in two ways:

Cancellation Methods

Two Approaches to Cancellation

  1. Customer-Initiated Cancellation - Customers cancel through your application using their session key
  2. Merchant-Initiated Cancellation - Merchants cancel on behalf of customers using the merchant API

Customer-Initiated Cancellation

Step 1: Get Customer’s Subscriptions

First, retrieve all active subscriptions for the customer:

// API Reference: /api-reference/api-reference/customers/get-customersubscriptions

const response = await fetch(
  'https://api-sandbox.coinflow.cash/api/subscription/{merchantId}/subscribers',
  {
    method: 'GET',
    headers: {
      'accept': 'application/json',
      'x-coinflow-auth-session-key': sessionKey
    }
  }
);

const subscriptions = await response.json();

Example Response:

[
  {
    "id": "6851d8c378269da7d5a535d6",
    "customerId": "customer123",
    "merchantId": "your-merchant-id",
    "email": "customer@example.com",
    "plan": "Premium Plan",
    "planCode": "premium-monthly",
    "status": "Active",
    "createdAt": "2024-01-15T10:30:00Z",
    "nextBillingDate": "2024-02-15T10:30:00Z"
  }
]

Step 2: Cancel the Subscription

Use the subscription ID to cancel:

// API Reference: /api-reference/api-reference/subscription/cancel-customer-subscription

const response = await fetch(
  `https://api-sandbox.coinflow.cash/api/subscription/{merchantId}/subscribers/{subscriptionId}`,
  {
    method: 'PATCH',
    headers: {
      'x-coinflow-auth-session-key': sessionKey
    }
  }
);

if (response.ok) {
  console.log('Subscription cancelled successfully');
}

Building a Cancellation UI

Here’s an example React component for subscription cancellation:

import { useState, useEffect } from 'react';

function SubscriptionManager({ sessionKey, merchantId }) {
  const [subscriptions, setSubscriptions] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchSubscriptions();
  }, []);

  const fetchSubscriptions = async () => {
    try {
      const response = await fetch(
        `https://api-sandbox.coinflow.cash/api/subscription/${merchantId}/subscribers`,
        {
          headers: {
            'x-coinflow-auth-session-key': sessionKey
          }
        }
      );
      const data = await response.json();
      setSubscriptions(data);
    } catch (error) {
      console.error('Error fetching subscriptions:', error);
    } finally {
      setLoading(false);
    }
  };

  const cancelSubscription = async (subscriptionId) => {
    if (!confirm('Are you sure you want to cancel this subscription?')) {
      return;
    }

    try {
      const response = await fetch(
        `https://api-sandbox.coinflow.cash/api/subscription/${merchantId}/subscribers/${subscriptionId}`,
        {
          method: 'PATCH',
          headers: {
            'x-coinflow-auth-session-key': sessionKey
          }
        }
      );

      if (response.ok) {
        alert('Subscription cancelled successfully');
        fetchSubscriptions(); // Refresh the list
      }
    } catch (error) {
      console.error('Error cancelling subscription:', error);
      alert('Failed to cancel subscription');
    }
  };

  if (loading) {
    return <div>Loading subscriptions...</div>;
  }

  return (
    <div>
      <h2>Your Subscriptions</h2>
      {subscriptions.map(sub => (
        <div key={sub.id} className="subscription-card">
          <h3>{sub.plan}</h3>
          <p>Status: {sub.status}</p>
          <p>Next billing: {new Date(sub.nextBillingDate).toLocaleDateString()}</p>
          {sub.status === 'Active' && (
            <button onClick={() => cancelSubscription(sub.id)}>
              Cancel Subscription
            </button>
          )}
        </div>
      ))}
    </div>
  );
}

Merchant-Initiated Cancellation

Merchants can cancel subscriptions on behalf of customers using the merchant API:

// API Reference: /api/cancelsubscription

const response = await fetch(
  `https://api-sandbox.coinflow.cash/api/merchant/subscription/subscribers/{subscriptionId}`,
  {
    method: 'PATCH',
    headers: {
      'Authorization': 'YOUR_API_KEY',
      'Content-Type': 'application/json'
    }
  }
);

This is useful for:

  • Customer support scenarios
  • Policy violations
  • Refund situations
  • Account closures

Cancellation via Merchant Dashboard

Merchants can also cancel subscriptions through the Coinflow merchant dashboard:

  1. Log in to merchant dashboard
  2. Navigate to Subscriptions
  3. Click on the subscription plan
  4. Find the subscriber in the list
  5. Click Cancel Subscription

Cancellation Behavior

What Happens When a Subscription is Cancelled

When a subscription is cancelled:

  • Status Update: Changes to Canceled
  • Billing: No future payments will be processed
  • Access: Customer typically retains access until end of current billing period
  • Notifications: Cancellation webhook is triggered

Configuring Access After Cancellation

You can implement different access policies:

  1. Immediate Termination - Access ends immediately upon cancellation
  2. End of Period - Access continues until the end of the current billing period (recommended)
  3. Grace Period - Custom grace period after cancellation

Implement your preferred policy in your application logic using the cancellation timestamp and next billing date from the subscription data.

Handling Cancellation Webhooks

Set up webhooks to receive cancellation notifications:

// Webhook payload for subscription cancellation
{
  "event": "subscription.cancelled",
  "data": {
    "subscriptionId": "6851d8c378269da7d5a535d6",
    "customerId": "customer123",
    "planCode": "premium-monthly",
    "cancelledAt": "2024-01-20T15:45:00Z",
    "reason": "customer_requested"
  }
}

Handle the webhook in your backend:

app.post('/webhooks/coinflow', (req, res) => {
  const event = req.body;

  if (event.event === 'subscription.cancelled') {
    const { subscriptionId, customerId } = event.data;

    // Update your database
    await db.subscriptions.update({
      where: { id: subscriptionId },
      data: { status: 'cancelled', cancelledAt: new Date() }
    });

    // Send cancellation email to customer
    await sendCancellationEmail(customerId);

    // Log for analytics
    analytics.track('Subscription Cancelled', {
      subscriptionId,
      customerId
    });
  }

  res.sendStatus(200);
});

Best Practices

Clear Communication

  • Explain what happens when they cancel
  • Show when access will end
  • Offer alternatives (pause, downgrade)
  • Request feedback on why they’re cancelling

Retention Strategies

Before cancelling, consider:

  • Offering a discount or special offer
  • Suggesting a pause instead of cancel
  • Downgrading to a lower tier
  • Providing a feedback form

User Experience

  • Make cancellation easy to find (don’t hide it)
  • Require confirmation but don’t make it difficult
  • Send confirmation email after cancellation
  • Allow easy reactivation

Example Cancellation Flow

function CancellationFlow({ subscription, onCancel }) {
  const [step, setStep] = useState('confirm');
  const [feedback, setFeedback] = useState('');

  const handleConfirm = () => {
    setStep('feedback');
  };

  const handleSubmit = async () => {
    // Submit feedback (optional)
    if (feedback) {
      await submitCancellationFeedback(subscription.id, feedback);
    }

    // Proceed with cancellation
    await onCancel(subscription.id);
    setStep('complete');
  };

  if (step === 'confirm') {
    return (
      <div>
        <h3>Cancel {subscription.plan}?</h3>
        <p>Your subscription will remain active until {subscription.nextBillingDate}</p>
        <button onClick={handleConfirm}>Yes, Cancel</button>
        <button onClick={() => window.history.back()}>Keep Subscription</button>
      </div>
    );
  }

  if (step === 'feedback') {
    return (
      <div>
        <h3>Help us improve</h3>
        <p>Why are you cancelling? (optional)</p>
        <textarea
          value={feedback}
          onChange={(e) => setFeedback(e.target.value)}
          placeholder="Your feedback..."
        />
        <button onClick={handleSubmit}>Complete Cancellation</button>
      </div>
    );
  }

  return (
    <div>
      <h3>Subscription Cancelled</h3>
      <p>You'll have access until {subscription.nextBillingDate}</p>
      <p>You can reactivate anytime from your account settings.</p>
    </div>
  );
}

Reactivating Cancelled Subscriptions

Customers may want to reactivate a cancelled subscription. You’ll need to create a new subscription using the same plan code:

// Create a new subscription with the same plan
const response = await fetch(
  'https://api-sandbox.coinflow.cash/api/subscription/{merchantId}/subscribers/card',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-coinflow-auth-session-key': sessionKey
    },
    body: JSON.stringify({
      planCode: previousPlanCode,
      card: savedCardToken,
      // ... other required fields
    })
  }
);