> ## Documentation Index
> Fetch the complete documentation index at: https://docs.elasticfunnels.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Customer Care Portal

> Let customers manage their subscriptions and request refunds directly from the members area.

The Customer Care portal adds two self-service features to the members area:

* **Manage** — Customers can pause, skip, or cancel a subscription (with optional retention offers). The manage page also shows a list of orders eligible for a refund.
* **Self-refund** — Customers can submit a refund request for eligible orders (one-time purchases or subscription first charges).

Both features are configured per merchant in **Merchant settings → Customer care** and are completely optional.

***

## Brand-level settings

Before configuring individual merchants, set the brand-wide customer support details in **Brand Settings → General** under the **Customer support** section:

| Setting                         | Purpose                                                                                                                                                                       |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Support email**               | Displayed to customers in error messages (e.g. "Please contact us at [support@yourbrand.com](mailto:support@yourbrand.com)").                                                 |
| **Support phone**               | Optional phone number displayed alongside the support email.                                                                                                                  |
| **Money-back guarantee (days)** | A brand-wide refund window. Orders older than this many days are automatically ineligible for a refund, regardless of per-merchant rules. Set to 0 or leave blank to disable. |

These values are stored as brand variables and are accessible in templates as `{{ brand.support_email }}`, `{{ brand.support_phone }}`, and `{{ brand.mbg_days }}`.

<Tip>
  When a refund request is denied, the error message automatically includes your support email and phone so customers know how to reach you. Make sure to fill these in.
</Tip>

***

## Enabling Customer Care

1. Navigate to **Merchants** and open a merchant configured with Stripe or NMI.
2. Click the **Customer care** tab.
3. Enable the features you want:
   * **Subscription management** — turns on the Manage page for active subscriptions.
   * **Self-refund** — allows customers to request a refund on eligible orders.
4. Save the merchant.

### Subscription management

When enabled, an active subscription card on `/members/subscription` will show a **Manage** button. Clicking it opens `/members/manage?tx=<transactionId>`.

<Note>
  Configure the exit survey reasons and retention offers in the **Retention offers** tab. The Customer care tab controls the feature toggle; the Retention offers tab controls which offers are shown.
</Note>

### Self-refund — eligibility rules

Eligibility is checked at two levels:

**Brand level** (checked first):

| Rule                            | What it checks                                                                 |
| ------------------------------- | ------------------------------------------------------------------------------ |
| **Money-back guarantee (days)** | If set and the order is older than this window, it's automatically ineligible. |

**Merchant level** (checked second):

| Rule                        | What it checks                                                               |
| --------------------------- | ---------------------------------------------------------------------------- |
| **Max days since purchase** | The order must have been placed within N days.                               |
| **Require not fulfilled**   | Orders that have already been shipped, fulfilled, or delivered are excluded. |
| **Max order amount**        | Orders above this dollar threshold are excluded.                             |

If an order passes both levels, a **Request refund** link appears on its card in `/members/orders` and in the refund candidates section on `/members/manage`.

### Self-refund — on-request action

When a customer submits a refund request, one of three things happens (your choice):

| Action                           | What happens                                                                                                                                                                 |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Process refund automatically** | The refund is applied immediately via the payment gateway. The customer sees a confirmation that the refund will appear in 3–5 business days.                                |
| **Send email notification**      | An email is sent to the configured **Notification email** address with order and customer details. The customer sees a "request received" message.                           |
| **Create support ticket**        | A support ticket is created on behalf of the customer. The customer receives the ticket number. This option is only available when the brand has the Tickets module enabled. |

### Error messages

All customer-facing error messages are friendly and non-technical. When a refund is denied, the message includes your brand's support contact information automatically:

> "The refund window for this order has closed. Please contact us at [support@yourbrand.com](mailto:support@yourbrand.com) or call +1 800 555 0100."

If no support email or phone is configured, it falls back to:

> "Please contact our support team."

***

## The Manage page (`/members/manage`)

The manage page is a multi-step flow. Customers land here from the **Manage** button on their subscription card.

**Step 1 — Subscription summary + refund candidates**\
Shows the product name, current status, and next charge date with a "I want to cancel" action. Below the subscription card, any orders eligible for a refund are displayed with a "Request refund" link.

**Step 2 — Exit survey**\
Displays the exit survey reasons configured in the Retention offers tab. The customer selects the reason that applies.

**Step 3 — Retention offer** *(optional)*\
If a retention offer is configured for the selected reason, the customer is shown the offer (discount, pause, or downgrade). They can accept it or proceed to cancel.

**Step 4 — Outcome**\
Shows the result: cancelled, offer accepted, or an error.

### Refund candidates on the manage page

The manage page automatically shows refund-eligible orders below the subscription card. An order appears in this list when:

* `refund_eligible` is `true` (merchant rules pass)
* The order is not fully refunded
* The order is within the brand's money-back guarantee window (if configured)

Each candidate card displays the order number, date, how many days ago the order was placed, the total amount, and a link to the refund page.

### Customising the manage page

The built-in `members/manage` page is a starting point. You can build a fully custom version using the `getManageData()` backend function, `getOrders()`, and the `window.ef.customerCare` JavaScript API.

```liquid theme={null}
<script scope="backend">
  var data = getManageData(query.tx);
  if (!data) { redirect('/members/subscription'); }
  setVariable('manage', data);

  // Refund candidates
  var orders = getOrders('newest', 50);
  var mbgDays = brand.mbg_days || 0;
  var refundOrders = orders.filter(function(o) {
    if (!o.refund_eligible || o.refund.fully_refunded) return false;
    if (mbgDays > 0 && o.days_since_order != null && o.days_since_order >= mbgDays) return false;
    return true;
  });
  setVariable('refund_orders', refundOrders);
</script>
```

The `getManageData()` function returns `null` if the subscription doesn't exist, doesn't belong to the current customer, or manage is not enabled on the merchant. Use this to redirect before rendering the page.

***

## The Refund page (`/members/refund`)

The refund page lets a customer submit a refund request for a specific order. They arrive here from the **Request refund** link on an order card or from the manage page.

**What the customer sees:**

* A summary of the order (order number, date, amount).
* The refund amount (the full remaining balance — read-only).
* A text area for a reason.
* A submit button.

**Outcome states after submitting:**

| Outcome           | Message shown                                                              |
| ----------------- | -------------------------------------------------------------------------- |
| Auto refunded     | "Your refund has been processed and will appear within 3–5 business days." |
| Queued for review | "Your refund request has been received. Our team will review it shortly."  |
| Ticket created    | "A support ticket has been created (#XXXXXXXX). We'll be in touch soon."   |

### Customising the refund page

Build a custom page using `getRefundEligibility()` and `window.ef.customerCare.requestRefund()`:

```liquid theme={null}
<script scope="backend">
  var elig = getRefundEligibility(query.order);
  if (!elig or !elig.eligible) { redirect('/members/orders'); }
  setVariable('elig', elig);
</script>
```

The `getRefundEligibility()` function returns `null` if the order is not found, doesn't belong to the current customer, or is not eligible for a refund. The `eligible` flag is `false` when rules are not met (the `reasons` array describes why).

***

## Brand variables (`brand.*`)

The following brand-level variables are available in all templates:

| Variable              | Type   | Description                                        |
| --------------------- | ------ | -------------------------------------------------- |
| `brand.name`          | string | Brand display name                                 |
| `brand.code`          | string | Brand code                                         |
| `brand.currency`      | string | Default currency code (e.g. `USD`)                 |
| `brand.timezone`      | string | Brand timezone (e.g. `America/New_York`)           |
| `brand.support_email` | string | Customer support email (from Brand Settings)       |
| `brand.support_phone` | string | Customer support phone (from Brand Settings)       |
| `brand.mbg_days`      | number | Money-back guarantee window in days (0 = disabled) |

Use these in your templates:

```liquid theme={null}
@if(brand.support_email)
  <p>Need help? Email us at {{ brand.support_email }}</p>
@endif
```

***

## Template variables reference

### `getManageData(transactionId)`

Returns an object (or `null`):

| Field                      | Type      | Description                                                |
| -------------------------- | --------- | ---------------------------------------------------------- |
| `manage_enabled`           | boolean   | Always `true` when returned (null if disabled)             |
| `retention_offers_enabled` | boolean   | Whether retention offers are configured                    |
| `exit_survey_reasons`      | string\[] | Exit survey reason keys                                    |
| `offers`                   | object\[] | Retention offer rules                                      |
| `subscription`             | object    | Subscription details (id, status, amount, frequency, etc.) |

### `getRefundEligibility(orderCode)`

Returns an object (or `null`):

| Field                   | Type      | Description                                            |
| ----------------------- | --------- | ------------------------------------------------------ |
| `eligible`              | boolean   | Whether the order can be refunded                      |
| `reasons`               | string\[] | Why the order is ineligible (empty when eligible)      |
| `on_request_action`     | string    | `auto_refund`, `send_email`, or `create_ticket`        |
| `order.conversion_code` | string    | Conversion code (use as `order_id` in `requestRefund`) |
| `order.order_number`    | string    | Display order number                                   |
| `order.total`           | number    | Order total                                            |
| `order.remaining`       | number    | Remaining refundable amount                            |
| `order.currency`        | string    | Currency code                                          |
| `order.created_at`      | string    | ISO date of purchase                                   |

### Order fields (from `getOrders()`)

| Field                      | Type           | Description                                                           |
| -------------------------- | -------------- | --------------------------------------------------------------------- |
| `order.refund_eligible`    | boolean        | Whether the order passes merchant-level refund rules                  |
| `order.conversion_code`    | string         | Conversion code (use as the order identifier for refund links)        |
| `order.days_since_order`   | number \| null | Number of full days since the order was placed                        |
| `order.fulfillment_status` | string \| null | Current fulfillment status (e.g. `shipped`, `fulfilled`, `delivered`) |

### `sub.manage_enabled` (subscriptions list)

The `getSubscriptions()` function includes `manage_enabled` on each subscription object. Use this to conditionally show the Manage button:

```liquid theme={null}
@if(sub.manage_enabled and sub.status eq 'active')
  <a href="/members/manage?tx={{ sub.original_transaction_id }}">Manage</a>
@endif
```

### Showing refund-eligible orders

Use `refund_eligible`, `days_since_order`, and `brand.mbg_days` together for the most accurate display:

```liquid theme={null}
@foreach(order in orders)
  @if(order.refund_eligible and not order.refund.fully_refunded)
    @if(brand.mbg_days lte 0 or order.days_since_order lt brand.mbg_days)
      <a href="/members/refund?order={{ order.conversion_code }}">Request refund</a>
    @endif
  @endif
@endforeach
```
