Skip to main content
Use @shoppexio/storefront/customer when your storefront runs on your own domain and you want to build the customer portal yourself. Shoppex handles email OTP login, tenant isolation, customer data, and buyer actions. Your frontend owns the UI.
Headless customer accounts are available on every plan as part of the Storefront SDK. They reuse the same publishable key and allowed origins as Headless Checkout, while creating Headless Checkout sessions still requires Business.

Setup

Open Dashboard -> Settings -> Developer -> Headless. Generate a publishable key and add the exact origin that hosts the customer page, for example https://yourstore.com.
The publishable key is browser-safe. Never use a secret shx_ Developer API key in this client.

What you can build

The client covers the buyer-safe parts of the Shoppex customer portal: dashboard() is the starting payload for an account home page. It includes recent orders and tickets, licenses, subscriptions, deliverables, wallet state, rewards, affiliate state, and summary counts. Use the focused methods when the buyer opens or changes one area.

OTP login

Normal login is stored in sessionStorage and is valid for up to 12 hours. rememberMe: true stores it in localStorage and keeps the customer signed in for up to 30 days. Both are capped by the underlying Shoppex buyer session. A logout or an unauthorized response removes the saved session. Use rememberMe only after the buyer explicitly selects a Keep me signed in option on a private device. OTP requests use a success-like response for known and unknown email addresses. Do not use the response to decide if a customer account exists. Check the saved session when your customer page starts:

Customer and orders

The client validates the me, order-list, and order-detail response contracts before returning them. Prices remain decimal strings so your UI does not introduce floating-point rounding.

Order reviews

Load review state when the buyer opens a completed order. Shoppex decides if that signed-in customer may review the purchase, so render the action from can_review instead of deriving eligibility from the order status yourself.
orderReview() also returns an existing review. already_submitted, score, message, and submitted_at let the customer dashboard show what was already sent. Reviews are Shoppex shop reviews tied to a verified order, not separate reviews for every line item. A foreign order ID returns 404. Shoppex stores review sentiment in three levels. Send 1 for negative, 3 for neutral, or 5 for positive. Review messages may contain up to 256 visible Unicode characters; joined emoji count as one character.

Account home and settings

Use dashboard() for the first account screen. The returned IDs are the inputs for the focused actions below.
File deliverables contain invoice_uniqid and downloadIndex. Pass both values to download() instead of following a hosted customer-portal URL:
Profile, avatar, marketing preferences, and transactional email preferences are separate calls:
Only send preference fields that the buyer changed. Show the selected file name and a preview before you call updateAvatar().

Support tickets

The dashboard response contains the buyer’s recent ticket list. Use the ticket ID to open the full conversation and send replies.
invoiceId is optional. When you include it, Shoppex verifies that the order belongs to the signed-in buyer.

Licenses and subscriptions

Use the license and subscription IDs returned by dashboard().
Render actions only when the matching actions.can_* field is true. Ask for buyer confirmation immediately before a destructive action such as cancellation or hardware reset. Wallet and license renewal methods create a normal Shoppex checkout. They do not charge the buyer inside the SDK:

Replacements, loyalty, warranties, and favorites

Generate one idempotency key per buyer action and reuse that key when retrying the same action. A double-click must not create a second redemption or replacement request.

Affiliate and reseller views

affiliate() returns the existing affiliate links, balances, commissions, and payout state. createAffiliateLink() creates or requests a custom link. It does not move funds.
The reseller methods support enrollment, catalog browsing, server-priced quotes, order history, wallet display, wallet top-up checkout, and API-key summaries. Shoppex keeps reseller order placement and API-key creation or revocation behind protected flows.
Use the quote response as the price source. Do not calculate reseller prices from catalog values in the browser. orders() returns summary rows for an order list. Each summary contains at most three line items. Do not use the summary as a full order. Call order() when the buyer opens one order. The detail response contains pricing, payment, line-item, and delivery data. Use pagination.has_more to decide if your UI must show a Load more button. The API returns at most 50 orders per page.

Order detail data

Use decimal strings from the response. Do not convert money to JavaScript floating-point values before calculations.
Use displayName before apmMethod. The cryptoGateway field contains the exact coin when both labels are null. Format that coin with your buyer-facing labels. Do not show the raw gateway value because it can identify an internal payment rail. The order detail contains these main groups: Use currentPayment for the active payment method. payments contains the same public payment snapshot in array form for existing consumers.

Delivery and service instructions

Call order() before you build the delivery section. The order-list response does not contain delivery data. Each item in order.lineItems contains these delivery fields: Shoppex returns deliveryText and product.serviceText only after the item is delivered. This rule prevents delivery content from appearing before payment and fulfillment. Use the order-specific deliveryText first. Use the product serviceText when the order has no specific instructions.
deliveryText and serviceText can contain rich-text HTML. Sanitize this HTML before you use dangerouslySetInnerHTML or a similar API. Open an external delivery link only when href is not null. Use rel="noopener noreferrer" when the link opens a new browser tab. Each serial has these fields:
The Shoppex editor does not edit a self-hosted customer panel. Your site must render these fields in its own order-detail component.

Delivery status behavior

This delivery status belongs to one line item. It is separate from the payment status of the complete order.

Downloads

Shoppex checks that the invoice belongs to the signed-in buyer before it returns the file. Always pass the downloadIndex returned by order() or dashboard(). The SDK removes hosted-portal-only download URLs so a self-hosted site never follows a broken /api/customer/* link or receives an unsafe aggregate HTML launcher.

Logout and session state

logout() revokes only this external customer session. It does not sign the buyer out of a separate Shoppex-hosted customer portal tab. If the network request fails, the SDK keeps the local token so your UI can retry the revocation; a successful logout or an already-unauthorized response clears it. Use clearSession() only when you must remove local state without a server request. For normal logout, call logout() by itself.

Error handling

Use HeadlessCustomerError to handle expected API errors. A 401 response clears an expired or rejected session.

Implementation checklist

  • Build the OTP request and OTP verification forms.
  • Add a buyer-controlled Keep me signed in option.
  • Use me() for the signed-in buyer and shop branding, then dashboard() for the account home.
  • Use orders() for the order list and pagination.
  • Use order() for pricing, payment, and delivery details.
  • Render delivery state for every line item.
  • Use download() only with a returned downloadIndex.
  • Add account settings, support, license, subscription, and benefits screens from the focused methods above.
  • Confirm destructive buyer actions in your UI immediately before calling the SDK.
  • Handle 401 by returning the buyer to the login form.
  • Add a logout action that calls logout().

Security boundary

Every customer request must pass all of these checks:
  • valid pk_live_ or pk_test_ publishable key
  • exact browser Origin in the shop’s allowed-origin list
  • active Shoppex shop
  • valid hcs_ customer session bound to the same origin and shop
  • invoice ownership for order detail and downloads
The SDK exposes buyer-safe reads and mutations. A merchant controls the JavaScript on an allowed origin, so Shoppex does not expose actions that could silently move buyer money, reveal a one-time provider URL, or create a durable credential. Keep these actions behind a Shoppex-controlled page or explicit hosted customer-portal handoff: For example, send the buyer to https://your-shop.myshoppex.io/dashboard?tab=subscriptions for a protected subscription payment-method change. The hosted portal may ask the buyer to sign in again because the headless session is intentionally not transferred to another origin. Wallet top-ups and license renewals are available because they return a plain Shoppex checkout URL. Your UI redirects there and the buyer still chooses whether and how to pay. An unsupported headless route returns 404. Do not work around this by calling /v1/customer/* with a secret Developer API key or by proxying the buyer’s hcs_ token through your own backend.