Saltar al contenido
Tienda online

Cuentas de clientes

Inicio de sesión, registro y cuenta del cliente en tu frontend con Storefront API.

Andrea inicia sesión en tu frontend con un código que le llega al correo o con su correo y contraseña, y ve sus pedidos, direcciones y datos. Esta guía arma esa cuenta con Storefront API. Tu servidor guarda la sesión; el navegador solo recibe una cookie.

Requisitos

  • El slug de la tienda o un dominio configurado, para x-Store-Domain.
  • Tu frontend con HTTPS en un dominio activo de la tienda.
  • La ruta /account/callback en ese dominio.
  • Un servidor que guarde la sesión del cliente.

Configura el acceso del cliente

POST /v1/customer/auth/config crea o actualiza el cliente OAuth para el origin de tu frontend. Devuelve los datos OAuth y el modo de inicio de sesión de la tienda. Responde 404 si el origen no es un dominio activo de la tienda.

curl -X POST "https://storefront.ecomiq.pe/v1/customer/auth/config?origin=https://mi-tienda.pe" \
  -H "x-Store-Domain: mi-tienda"
Respuesta
{
  "authority": "https://auth.ecomiq.pe",
  "clientId": "ecomiq-customer-7204558912004325377",
  "authorizationEndpoint": "https://auth.ecomiq.pe/connect/authorize",
  "tokenEndpoint": "https://auth.ecomiq.pe/connect/token",
  "revocationEndpoint": "https://auth.ecomiq.pe/connect/revoke",
  "scope": "openid profile email api_storefront offline_access",
  "redirectUri": "https://mi-tienda.pe/account/callback",
  "loginMode": "hosted",
  "captcha": { "provider": "off", "siteKey": null, "isConfigured": true }
}

redirectUri siempre es {origin}/account/callback. loginMode es el modo elegido en Configuración → Compra → Cuentas de clientes → Inicio de sesión:

loginModeFormulario
hostedPágina de Auth con el nombre y logo de la tienda.
storefrontDentro del tema de la tienda.

Los dos modos ofrecen código por correo y contraseña. Cambiar el modo mueve el formulario; no cambia la cuenta ni los métodos disponibles.

Modo storefront en tu frontend

El formulario dentro del tema solo existe con el SDK de temas. Tu frontend usa la página de Auth en los dos modos, y Auth muestra el CAPTCHA si la tienda lo activó.

Envía al cliente a iniciar sesión

Genera verifier, state y nonce aleatorios. Guárdalos en una cookie HttpOnly de corta duración y redirige el navegador a authorizationEndpoint. Usa clientId, redirectUri y scope tal como llegan.

import { createHash, randomBytes } from "node:crypto";

const verifier = randomBytes(32).toString("base64url");
const state = randomBytes(32).toString("base64url");
const nonce = randomBytes(32).toString("base64url");

const authorize = new URL(config.authorizationEndpoint);
authorize.search = new URLSearchParams({
  client_id: config.clientId,
  response_type: "code",
  redirect_uri: config.redirectUri,
  scope: config.scope,
  code_challenge: createHash("sha256").update(verifier).digest("base64url"),
  code_challenge_method: "S256",
  state,
  nonce,
}).toString();

Auth pide el correo. Enviar código envía un código de 6 dígitos que vence en 5 minutos; Entrar con contraseña permite usar la contraseña de esa cuenta. Si la cuenta tiene verificación en dos pasos, también pide el código de la aplicación de autenticación o uno de recuperación antes de iniciar la sesión.

Tras verificar al cliente, Auth vuelve a redirectUri con code y state. Si la URL trae error, empieza de nuevo. Los dos métodos completan el mismo flujo OAuth Authorization Code con PKCE; no uses grant_type=password.

Auth recuerda al cliente en ese navegador hasta 7 días. Agrega prompt=login a la URL para pedir que vuelva a identificarse.

Crear o recuperar la contraseña

Crear o recuperar contraseña verifica primero el correo con un código. Después, el cliente escribe la nueva contraseña y la repite. Si la cuenta tiene verificación en dos pasos, debe completarla también; el código de correo no reemplaza ese segundo factor. El cliente puede seguir entrando por código aunque haya creado una contraseña.

En hosted, Auth presenta estos pasos. En storefront, los presenta CustomerLoginForm dentro del tema. La integración conserva el mismo cliente OAuth, redirectUri, state, nonce y PKCE durante el flujo.

El SDK usa estas rutas de la autoridad de Auth para el formulario integrado:

RutaUso
POST /v1/customer-auth/password-challengesVerificar correo y contraseña y continuar el desafío OAuth.
POST /v1/customer-auth/password-setupsEnviar el código para crear o recuperar la contraseña.
POST /v1/customer-auth/password-setups/verifyVerificar ese código antes de permitir una nueva contraseña.
PUT /v1/customer-auth/passwordGuardar la nueva contraseña y completar las verificaciones pendientes.

Estas rutas no entregan tokens mediante un grant de contraseña. Deja que el SDK gestione los desafíos y completa el callback como se muestra a continuación.

Crea la sesión

En /account/callback, compara state con el de la cookie que guardaste al enviarlo a iniciar sesión. Luego canjea code con POST /v1/customer/sessions. Envía el mismo nonce: sin él, responde 401.

curl -X POST https://storefront.ecomiq.pe/v1/customer/sessions \
  -H "x-Store-Domain: mi-tienda" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "<code>",
    "codeVerifier": "<verifier>",
    "redirectUri": "https://mi-tienda.pe/account/callback",
    "nonce": "<nonce>"
  }'
Respuesta
{
  "sessionId": "kq8Vd2xN4mW7rT1yB6sF3hJ9pL0cZ5aE8uG2iO4nR7w",
  "expiresAt": "2026-11-03T15:30:00Z",
  "customer": {
    "id": "7204558912004325301",
    "firstName": null,
    "lastName": null,
    "email": "andrea.ramos@example.com",
    "status": "active"
  }
}

Si el correo no tenía cliente en la tienda, la sesión lo crea. Si ya compró como invitado con ese correo, lo vincula.

Guarda sessionId en una cookie HttpOnly y Secure que venza en expiresAt. La sesión también vence tras 7 días sin uso. Nunca la expongas al JavaScript del navegador.

En cada llamada de cuenta, envía sessionId en X-Customer-Session. No agregues Authorization: con ambos, responde 400. GET /v1/customer/sessions/current devuelve el cliente actual, o 401 si la sesión venció.

RespuestaCausa
401El código venció o ya se usó, codeVerifier no coincide o la cuenta no está activa.
409Customer.AccountConflict: el correo pertenece a otra cuenta de cliente de la tienda.

Completa el registro

GET /v1/customer/registration devuelve los campos que pide la tienda, sus valores y requiresCompletion. Si es true, muestra el formulario antes de la cuenta o el checkout. Envía los datos con POST /v1/customer/registration.

curl https://storefront.ecomiq.pe/v1/customer/registration \
  -H "x-Store-Domain: mi-tienda" \
  -H "X-Customer-Session: $SESSION_ID"

curl -X POST https://storefront.ecomiq.pe/v1/customer/registration \
  -H "x-Store-Domain: mi-tienda" \
  -H "X-Customer-Session: $SESSION_ID" \
  -H "Content-Type: application/json" \
  -d '{ "firstName": "Andrea", "lastName": "Ramos", "phone": "+51987654321" }'
Respuesta de GET
{
  "fields": {
    "firstName": "required",
    "lastName": "required",
    "phone": "optional",
    "dateOfBirth": "hidden"
  },
  "firstName": null,
  "lastName": null,
  "phone": null,
  "dateOfBirth": null,
  "requiresCompletion": true
}
  • Cada campo es hidden (no se pide), optional o required.
  • Los campos hidden se ignoran aunque los envíes.
  • Si no falta ningún campo required, {} completa el registro.
  • dateOfBirth va como YYYY-MM-DD y no puede ser posterior a hoy.
  • Con el registro pendiente, las llamadas de cuenta responden 403 con Customer.RegistrationRequired.

Muestra los pedidos

GET /v1/customer/orders lista las compras del cliente, de la más reciente a la más antigua. Incluye las compras como invitado hechas con el mismo correo.

curl "https://storefront.ecomiq.pe/v1/customer/orders?limit=20" \
  -H "x-Store-Domain: mi-tienda" \
  -H "X-Customer-Session: $SESSION_ID"

curl https://storefront.ecomiq.pe/v1/customer/orders/7204558912004325401 \
  -H "x-Store-Domain: mi-tienda" \
  -H "X-Customer-Session: $SESSION_ID"

Cada fila es una compra con orderId, orderNumber (como ORD-2026-0001), status, total e items. Si se dividió entre vendedores, orderIds trae cada pedido. Mientras hasNextPage sea true, envía nextCursor en cursor.

GET /v1/customer/orders/{orderId} recibe el orderId, no el número. Suma payments, shipments y statusTimeline.

Administra las direcciones

POST /v1/customer/addresses agrega una dirección y responde 201 con su id. addressKey, countryIsoCode y addressLine son obligatorios. La primera dirección queda como predeterminada.

curl -X POST https://storefront.ecomiq.pe/v1/customer/addresses \
  -H "x-Store-Domain: mi-tienda" \
  -H "X-Customer-Session: $SESSION_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "addressKey": "Casa",
    "countryIsoCode": "PE",
    "addressLine": "Av. José Larco 1150",
    "department": "Lima",
    "province": "Lima",
    "district": "Miraflores",
    "cellPhone": "+51987654321"
  }'

PUT reemplaza la dirección completa: envía todos sus campos. PUT, DELETE y default responden 204.

Edita el perfil

GET /v1/customer devuelve el perfil: correo, nombre, teléfono, documento y direcciones. PATCH /v1/customer lo actualiza y responde 204.

curl -X PATCH https://storefront.ecomiq.pe/v1/customer \
  -H "x-Store-Domain: mi-tienda" \
  -H "X-Customer-Session: $SESSION_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Andrea",
    "lastName": "Ramos",
    "phone": "+51987654321",
    "identificationType": "DNI",
    "identificationNumber": "45678912",
    "entityName": null,
    "entityIdentificationNumber": null
  }'

Reenvía el documento

Si omites identificationType, identificationNumber, entityName o entityIdentificationNumber, PATCH los vacía. Reenvía los valores que devolvió GET.

firstName, lastName y phone conservan su valor si no los envías. Cambiar phone desactiva smsConsent. El correo no se edita: es el del inicio de sesión.

Cierra la sesión

DELETE /v1/customer/sessions/current revoca la sesión y responde 204. Después, borra tu cookie.

curl -X DELETE https://storefront.ecomiq.pe/v1/customer/sessions/current \
  -H "x-Store-Domain: mi-tienda" \
  -H "X-Customer-Session: $SESSION_ID"

Auth sigue recordando al cliente en ese navegador. En el siguiente inicio de sesión, agrega prompt=login para pedir que vuelva a identificarse.

Con el SDK de temas

Los temas de Ecomiq hacen estos pasos con @ecomiq/storefront. El SDK guarda la sesión en una cookie segura y usa las rutas /account/login, /account/callback y /account/register.

PasoSDK
Inicio de sesiónstartCustomerLogin y submitCustomerLogin en /account/login, con CustomerLoginForm.
SesióncompleteCustomerLogin en /account/callback; customer y requireCustomer la leen.
RegistroloadCustomerRegistration y submitCustomerRegistration, con CustomerRegistrationForm.
PedidosloadOrders y loadOrder.
DireccionesloadAddresses y addressAction.
PerfilloadProfile y profileAction.
Cierre de sesiónlogout en la acción de /account/logout.

En modo storefront, CustomerLoginForm ofrece código por correo, contraseña y creación o recuperación de contraseña mediante un código. También presenta el CAPTCHA si la tienda lo activó y la verificación en dos pasos si la cuenta la requiere. requireCustomer envía al inicio de sesión o al registro pendiente.

app/routes/account/callback.ts
import { completeCustomerLogin } from "@ecomiq/storefront";
import type { Route } from "./+types/callback";

export async function loader({ request, context }: Route.LoaderArgs) {
  return completeCustomerLogin(request, context);
}

Siguientes pasos

Última actualización:

En esta página