Personalizing the user journey in Affinity

Use custom flow extensions to intercept regular customer actions (ie. a customer skips or reschedules an order) and provide tailored experiences

🗒️

Note

Custom flow extensions are supported in both the original Affinity experience (Affinity 1.0) and the new Affinity experience (Affinity 2.0). Where event behavior differs between versions, it's called out below.

Custom flow extensions intercept the initial click before the action occurs. Recharge no longer owns the interaction after the click is intercepted, and gives you full control over the flow. In addition to click events, Affinity emits action events after an action completes and navigation events when a customer moves between pages, which you can use to track customer behavior or trigger custom experiences.

📘

Platform:

  • Shopify Checkout Integration
  • Migrated Shopify Checkout Integration

Before you start


Event types

The following event types are available when customizing the Affinity customer portals:

Event typeWhen it occursUse it forAvailable on
Click When a customer begins an actionMeasuring intent or replacing the default Affinity flow
  • Affinity 1.0
  • Affinity 2.0
ActionAfter an action completes successfullyMeasuring completed outcomes
  • Affinity 1.0
  • Affinity 2.0
NavigationWhen the customer moves to another Affinity pageJourney analytics or page-specific extension behavior
  • Affinity 2.0
DirectiveSent by your code when you want Affinity to perform a supported behaviorRefreshing Affinity after external data changes
  • Affinity 2.0

Available click and action events

Click and action events describe different points in the same journey. For example, Recharge::click::skip is emitted when the customer clicks Skip, while Recharge::action::skip is emitted only after the order has been skipped. Comparing the two can show where customers abandon a flow.

Implement custom flow extensions to respond when a user clicks one of the following:

ActionTriggerClick eventCompletion event
SkipThe user clicks on the "Skip" button from an order or the subscription management page.Recharge::click::skipRecharge::action::skip
UnskipThe user clicks on the "Unskip" button from an order or subscription page.Recharge::click::unskipRecharge::action::unskip
Manage subscriptionThe user clicks on the "Manage subscription" button.Recharge::click::manageSubscription
Order NowThe user clicks on the "Send now" button on the next order page.Recharge::click::orderNowRecharge::action::orderNow
RescheduleThe user clicks on the "Reschedule" button from an order or the subscription management page.Recharge::click::rescheduleRecharge::action::reschedule
CancelThe user clicks on the "Cancel this subscription" link on the Overview page (Affinity 2.0) or the Subscription details page.Recharge::click::cancel
ReactivateThe user clicks on the "Reactivate" link on the subscription management page, or the Affinity overview page if the customer doesn't have active subscriptions.Recharge::click::reactivateRecharge::action::reactivate
Changes to an upcoming orderAny change affecting an upcoming orderRecharge::action::orderChanged
⚠️

Warning

Recharge::click::cancel is gradually replacing Recharge::extension::cancellation_flow. If your existing extension listens to Recharge::extension::cancellation_flow, it continues to work, but use Recharge::click::cancel for new implementations.


Navigation events

🗒️

Note

Navigation events can only be used in Affinity 2.0

Listen for Recharge::location::change to detect when a customer moves to another page in Affinity. The event detail includes the new path, which can be used for journey analytics:

document.addEventListener('Recharge::location::change', event => {
  analytics.track('Affinity page viewed', {
    path: event.detail?.pathname,
  });
});

Directives

🗒️

Note

Directives can only be used in Affinity 2.0

Directives work in the opposite direction from events; your code dispatches a supported event for Affinity to receive and act on. Use directives to open common actions or refresh portal data after external changes:

What you want Affinity to doDirectiveOptional details
Open RescheduleRecharge::order::openRescheduleModalOmit detail to reschedule the complete upcoming charge, or provide subscriptionId to reschedule one active subscription.
Open SkipRecharge::order::openSkipModalOmit detail to skip the complete upcoming charge, or provide subscriptionId to skip one active subscription.
Open Order nowRecharge::order::openOrderNowModalNone
Open UnskipRecharge::order::unskipNone
Open Add productRecharge::order::openAddProductModalproductId opens a product directly; collectionIds opens a filtered product list. When both are provided, productId takes priority.
Refresh Affinity dataAffinity:refreshNone

Dispatch a directive as a CustomEvent on document. Unlike events emitted by Affinity, directive details are placed directly on event.detail rather than inside event.detail.payload. For example, to open Reschedule for the complete upcoming charge:

document.dispatchEvent(new CustomEvent('Recharge::order::openRescheduleModal'));

To open Reschedule for one specific subscription:

document.dispatchEvent(
  new CustomEvent('Recharge::order::openRescheduleModal', {
    detail: { subscriptionId: 12345 },
  })
);

To open the Add product experience for a specific product:

document.dispatchEvent(
  new CustomEvent('Recharge::order::openAddProductModal', {
    detail: { productId: '9876543210' },
  })
);

If custom code changes data outside the standard Affinity flow, dispatch the refresh directive so the page requests the latest data:

document.dispatchEvent(new CustomEvent('Affinity:refresh'));
📘

Note:

Directives only work while the part of Affinity that handles them is available. Order directives are intended for the Next order experience, and the Add product directive requires the Add product carousel to be available. A targeted Reschedule or Skip directive is ignored when subscriptionId doesn't identify an eligible active subscription.


Track customer behavior

Listen for events with document.addEventListener. The following example records when a customer starts and successfully completes the Skip flow:

document.addEventListener('Recharge::click::skip', event => {
  analytics.track('Affinity skip started', event.detail?.payload);
});
document.addEventListener('Recharge::action::skip', event => {
  analytics.track('Affinity skip completed', event.detail?.payload);
});

Replace a standard flow with a custom flow extension

Click events occur before Affinity continues with its default behavior. A custom flow extension intercepts the click, blocks the standard flow, and displays your own experience instead.

To create a custom flow extension:

  1. Listen for the specific click event.
  2. Call event.preventDefault() to block the standard flow.
  3. Retrieve the relevant subscription IDs. All click events include a list of relevant subscription IDs in the payload under event.detail.payload.subscriptionIds.
  4. Use the storefront SDK to access additional context about the subscription, address, or customer.
  5. Display your custom flow based on the gained context.
⚠️

Warning

Calling preventDefault() transfers full responsibility for that interaction to your code. Affinity won't continue with its standard flow, so your implementation must provide the complete experience, including validation, error handling, confirmation, accessibility, and any required Recharge API calls.

Custom flow example

The following example interrupts the cancelation process for specific products. If the subscription being canceled matches a list of restricted product IDs, a message urges the customer to contact support instead. The standard cancelation flow applies to all other products:

<script>
// Define the list of product IDs that are not eligible for direct cancellation
const uncancellableProductIds = [PRODUCTID];

// Customize the message to guide customers to contact you
const alertMessage = "To cancel this product, please reach out to us at [email protected]";

// Listen for the cancel click event
// Note: Recharge::click::cancel replaces Recharge::extension::cancellation_flow
document.addEventListener(
  "Recharge::click::cancel",
  (event) => {
    const { subscription } = event.detail;
    const subscriptionProductId = subscription.shopify_product_id;

    // Check if the product is in the list of uncancellable products
    if (uncancellableProductIds.includes(subscriptionProductId)) {
      // Prevent the default cancellation flow
      event.preventDefault();

      // Display an alert to guide customers
      window.alert(alertMessage);
    }
  }
);
</script>
❗️

Warning

It is important to consider and incorporate Automatic Renewal Laws (ARL) into any custom cancelation process to ensure legal compliance. See Automatic Renewal Law (ARL) and Recharge to learn more about ARLs and how Recharge remains compliant.