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
/en ese dominio.account/ callback - Un servidor que guarde la sesión del cliente.
Configura el acceso del cliente
POST / 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"{
"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}/. loginMode es el modo
elegido en Configuración → Compra → Cuentas de clientes → Inicio de sesión:
loginMode | Formulario |
|---|---|
hosted | Página de Auth con el nombre y logo de la tienda. |
storefront | Dentro 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:
| Ruta | Uso |
|---|---|
POST / | Verificar correo y contraseña y continuar el desafío OAuth. |
POST / | Enviar el código para crear o recuperar la contraseña. |
POST / | Verificar ese código antes de permitir una nueva contraseña. |
PUT / | Guardar 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 /, compara state con el de la cookie que guardaste al enviarlo a iniciar sesión. Luego
canjea code con POST /. 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>"
}'{
"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 / devuelve el cliente actual, o 401 si la
sesión venció.
| Respuesta | Causa |
|---|---|
401 | El código venció o ya se usó, codeVerifier no coincide o la cuenta no está activa. |
409 | Customer.AccountConflict: el correo pertenece a otra cuenta de cliente de la tienda. |
Completa el registro
GET / 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 /.
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" }'{
"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),optionalorequired. - Los campos
hiddense ignoran aunque los envíes. - Si no falta ningún campo
required,{}completa el registro. dateOfBirthva comoYYYY-MM-DDy no puede ser posterior a hoy.- Con el registro pendiente, las llamadas de cuenta responden
403conCustomer.RegistrationRequired.
Muestra los pedidos
GET / 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 /
recibe el orderId, no el número. Suma payments, shipments y
statusTimeline.
Administra las direcciones
POST / 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"
}'| Acción | Operación |
|---|---|
| Listar | GET / |
| Reemplazar | PUT / |
| Marcar predeterminada | POST / |
| Eliminar | DELETE / |
PUT reemplaza la dirección completa: envía todos sus campos. PUT, DELETE
y default responden 204.
Edita el perfil
GET / devuelve el perfil: correo, nombre, teléfono, documento y
direcciones. PATCH / 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 / 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/. El SDK guarda
la sesión en una cookie segura y usa las rutas /,
/ y /.
| Paso | SDK |
|---|---|
| Inicio de sesión | startCustomerLogin y submitCustomerLogin en /, con CustomerLoginForm. |
| Sesión | completeCustomerLogin en /; customer y requireCustomer la leen. |
| Registro | loadCustomerRegistration y submitCustomerRegistration, con CustomerRegistrationForm. |
| Pedidos | loadOrders y loadOrder. |
| Direcciones | loadAddresses y addressAction. |
| Perfil | loadProfile y profileAction. |
| Cierre de sesión | logout en la acción de /. |
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.
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
- Carrito: crea el carrito con la sesión del cliente para usar sus precios y códigos.
- Autenticación: modos de inicio de sesión y datos del registro.
- Cuenta del cliente: perfil, direcciones, registro y sesiones.
- Pedidos del cliente: lista, detalle y estado.
Última actualización: