Customer Portal

Let customers see and manage their subscriptions and billing history through a hosted portal.

Overview

The Customer Portal is a hosted page where your customers can self-manage their subscriptions without you building custom UI. Create a portal session to generate a short-lived URL, then redirect (or open) the customer to it.

Create a Portal Session

POST/api/v1/portal-sessions

Auth: Server key required

Request Body

{
  "customerId": "cus_xyz789",
  "returnUrl": "https://yoursite.com/account",
  "configuration": {
    "allowCancel": true,
    "allowPause": true,
    "allowPaymentMethodUpdate": true,
    "branding": {
      "primaryColor": "#4F46E5",
      "logoUrl": "https://yoursite.com/logo.png",
      "companyName": "Acme Inc."
    }
  }
}

Response

{
  "id": "ps_abc123",
  "object": "portal_session",
  "url": "https://portal.quickpay.com/session/ps_abc123_tok_xyz",
  "customerId": "cus_xyz789",
  "returnUrl": "https://yoursite.com/account",
  "expiresAt": "2025-01-15T11:30:00Z",
  "createdAt": "2025-01-15T10:30:00Z"
}

Example (Node.js)

portal-route.ts
import { QuickPay } from '@quickpay/node';

const qp = new QuickPay('sk_live_your_server_key');

app.post('/api/create-portal-session', async (req, res) => {
  const { customerId } = req.body;

  const session = await qp.portalSessions.create({
    customerId,
    returnUrl: 'https://yoursite.com/account',
    configuration: {
      allowCancel: true,
      allowPause: true,
      allowPaymentMethodUpdate: true,
      branding: {
        primaryColor: '#4F46E5',
        logoUrl: 'https://yoursite.com/logo.png',
        companyName: 'Acme Inc.',
      },
    },
  });

  res.json({ url: session.url });
});

Portal URL Format

Portal session URLs follow this format:

https://portal.quickpay.com/session/{session_id}_{token}

Each URL is single-use and expires after 1 hour. If the customer needs to access the portal again, create a new portal session.

Security

Portal session URLs grant full access to the customer's subscription management. Never expose the URL in publicly accessible logs or share it with unauthorized parties. Always generate a fresh session URL when the customer requests access.

Portal Capabilities

The portal provides the following self-service capabilities to your customers:

View Subscriptions

Customers can see all their active, paused, and cancelled subscriptions including plan details, billing amounts, and next billing date.

Update Payment Method — not available yet

The portal lists saved payment methods, but changing the card used for renewals is not available yet; the endpoint returns 501 not_implemented.

Cancel / Pause / Resume

Depending on your configuration, customers can cancel their subscription (immediately or at period end), pause it for a duration, or resume a paused subscription.

Billing History

Customers can view past invoices and payment receipts, including downloadable PDF receipts.

Merchant Configuration

Control what actions are available in the portal by passing a configuration object when creating the portal session.

OptionTypeDefaultDescription
allowCancelbooleantrueAllow customers to cancel their subscriptions
allowPausebooleanfalseAllow customers to pause their subscriptions
allowPaymentMethodUpdatebooleantrueReserved: card updates are not available yet
branding.primaryColorstring#4F46E5Primary color for buttons and highlights (hex)
branding.logoUrlstringnullURL to your company logo (displayed in portal header)
branding.companyNamestringnullYour company name (displayed in portal header)

Integration Examples

Web (redirect)

account-page.tsx
function AccountPage() {
  const handleManageSubscription = async () => {
    const res = await fetch('/api/create-portal-session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ customerId: currentUser.customerId }),
    });
    const { url } = await res.json();
    window.location.href = url;
  };

  return (
    <button onClick={handleManageSubscription}>
      Manage Subscription
    </button>
  );
}

Mobile (in-app browser)

import QuickPay

func openPortal() async {
    let session = try await api.createPortalSession(customerId: currentUser.customerId)
    QuickPay.presentPortal(url: session.url) { action in
        switch action {
        case .dismissed:
            refreshAccountData()
        default:
            break
        }
    }
}

Tip

After the customer returns from the portal, refresh your local subscription state by calling the Subscriptions API or relying on webhook events (e.g.subscription.cancelled,subscription.updated) to stay in sync.