@pcreative/commerce-contract 1.0.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.d.ts CHANGED
@@ -32,6 +32,7 @@ export interface OptionValue {
32
32
  /** Nombre de la opción: "Talla", "Color". */
33
33
  name: string
34
34
  value: string
35
+ color?: string | null
35
36
  }
36
37
 
37
38
  export interface Variant {
@@ -48,6 +49,84 @@ export interface Variant {
48
49
  options: OptionValue[]
49
50
  weight?: number | null
50
51
  image?: Image | null
52
+ images?: Image[]
53
+ tiers?: PriceTier[]
54
+ digital?: boolean
55
+ requiresShipping?: boolean
56
+ bundle?: BundleComponent[] | null
57
+ bundleGroups?: BundleGroup[] | null
58
+ measure?: { quantity: number; unit: string } | null
59
+ unitPrice?: { amount: number; currency: string; unit: string } | null
60
+ }
61
+
62
+ export interface BundleGroup {
63
+ id: string
64
+ title: string
65
+ min: number
66
+ max: number
67
+ options: { variantId: string; title: string; price: number | null; image?: string | null; available: boolean }[]
68
+ }
69
+
70
+ export interface WithdrawalState {
71
+ disponible: boolean
72
+ pais: string
73
+ dias: number | null
74
+ diasHabiles: boolean
75
+ caduca: string | null
76
+ dentroDePlazo: boolean
77
+ lineas: { id: string; titulo: string; cantidad: number; excluida: string | null }[]
78
+ solicitudes: { id: string; recibidoEn: string; acuseEn: string | null; atendidoEn: string | null }[]
79
+ }
80
+
81
+ export interface BundleComponent {
82
+ variantId: string
83
+ title: string
84
+ quantity: number
85
+ handle?: string | null
86
+ image?: string | null
87
+ }
88
+
89
+ export interface Download {
90
+ id: string
91
+ lineId: string
92
+ name: string
93
+ bytes: number
94
+ version: number
95
+ status: "bloqueada" | "activa" | "suspendida" | "revocada"
96
+ remaining: number | null
97
+ expiresAt: string | null
98
+ downloaded: number
99
+ firstDownloadAt: string | null
100
+ url: string | null
101
+ }
102
+
103
+ export interface PriceTier {
104
+ minQuantity: number
105
+ maxQuantity?: number | null
106
+ price: Money
107
+ savePercent: number
108
+ unitPrice?: { amount: number; currency: string; unit: string } | null
109
+ }
110
+
111
+ export interface CustomField {
112
+ key: string
113
+ label: string
114
+ type: "texto" | "texto_largo" | "numero" | "lista" | "casilla" | "fecha" | "fichero"
115
+ required: boolean
116
+ help?: string | null
117
+ config: Record<string, unknown>
118
+ variantIds: string[]
119
+ condition?: { field: string; equals: string } | null
120
+ personalizes: boolean
121
+ surcharge: { mode: string; amount: number; freeChars: number }
122
+ values: { id: string; label: string; surcharge: { mode: string; amount: number }; variantIds: string[] }[]
123
+ }
124
+
125
+ export interface QuantityRules {
126
+ scope: "variant" | "product"
127
+ min: number
128
+ max?: number | null
129
+ increment: number
51
130
  }
52
131
 
53
132
  export interface Product {
@@ -75,6 +154,8 @@ export interface Product {
75
154
  rating?: number | null
76
155
  reviewCount?: number
77
156
  available: boolean
157
+ quantityRules?: QuantityRules | null
158
+ customization?: CustomField[]
78
159
  metadata?: Record<string, unknown>
79
160
  }
80
161
 
@@ -122,6 +203,14 @@ export interface LineItem {
122
203
  * subtotal, o lo cuenta dos veces.
123
204
  */
124
205
  isFee?: boolean
206
+ quantityRule?: (QuantityRules & { counted: number }) | null
207
+ tier?: { from: number; regularUnitPrice: Money } | null
208
+ nextTier?: { from: number; missing: number; unitPrice: Money } | null
209
+ problem?: { code: string; message: string } | null
210
+ customization?: { label: string; value: string; surcharge: number }[] | null
211
+ personalized?: boolean
212
+ digital?: boolean
213
+ bundle?: { title: string; quantity: number }[] | null
125
214
  }
126
215
 
127
216
  /** Un sitio del que sale mercancía: un paquete, un porte. */
@@ -147,6 +236,35 @@ export interface ShippingGroup {
147
236
  option_id?: string | null
148
237
  }
149
238
 
239
+ export interface StoreInfo {
240
+ name: string | null
241
+ legalName: string | null
242
+ taxId: string | null
243
+ address: string | null
244
+ email: string | null
245
+ phone: string | null
246
+ country?: string | null
247
+ legal?: {
248
+ digital?: {
249
+ pais: string
250
+ exigir: boolean
251
+ texto: string
252
+ version: number
253
+ }
254
+ personalizados: {
255
+ pais: string
256
+ excluye: boolean
257
+ puedeExcluir: boolean
258
+ porLey: boolean
259
+ dias: number | null
260
+ diasHabiles: boolean
261
+ prohibeNoDevoluciones: boolean
262
+ texto: string
263
+ garantias: string | null
264
+ }
265
+ } | null
266
+ }
267
+
150
268
  export interface ShippingGroups {
151
269
  grupos: ShippingGroup[]
152
270
  moneda: string
@@ -164,6 +282,10 @@ export interface ShippingOption {
164
282
  /** El backend calcula el precio al elegirla (envío por peso, por ejemplo). */
165
283
  priceCalculated?: boolean
166
284
  description?: string | null
285
+ /** Plazo de entrega tal y como lo escribió la tienda: "24-48 h". */
286
+ deliveryTime?: string | null
287
+ /** El cliente recoge el pedido: no hay porte ni reparto. */
288
+ pickup?: boolean
167
289
  }
168
290
 
169
291
  export interface PaymentMethod {
@@ -171,6 +293,10 @@ export interface PaymentMethod {
171
293
  /** Identificador del proveedor: "transfer", "cod", "bizum", "card"… */
172
294
  provider: string
173
295
  name?: string
296
+ /** Texto corto que la tienda escribe para explicarla en la caja. */
297
+ description?: string | null
298
+ /** Lo que el comprador tiene que hacer para pagar (datos de una transferencia). */
299
+ instructions?: string | null
174
300
  /**
175
301
  * Recargo de esta forma de pago, en unidades mayores. Sin definir = ninguno.
176
302
  *
@@ -214,6 +340,10 @@ export interface Cart {
214
340
  shippingMethods: { id: string; name: string; price: Money }[]
215
341
  regionId?: string
216
342
  promoCodes?: string[]
343
+ hasDigital?: boolean
344
+ digitalConsent?: { at: string; version: number } | null
345
+ vatNumber?: { number: string; valid: boolean; checked: boolean; name: string | null } | null
346
+ taxExempt?: { reason: "intra_eu"; articles: string[] } | null
217
347
  /**
218
348
  * El paso que falta para pagar, si el método elegido lo pide. `null` con los
219
349
  * métodos sin pasarela: ahí pagar es confirmar el pedido y ya está.
@@ -313,18 +443,36 @@ export interface CommerceClient {
313
443
  search(q: string, limit?: number): Promise<Product[]>
314
444
  /** Pedido por id, para la página de confirmación tras el checkout. */
315
445
  getOrder(id: string): Promise<Order | null>
446
+ /** Sube un fichero que el cliente adjunta a una personalización. */
447
+ subirFichero?(archivo: File): Promise<{ id: string; nombre: string; tipo: string; bytes: number; firma: string }>
448
+ /** Opcional: las descargas de un pedido con contenido digital. */
449
+ getDownloads?(orderId: string): Promise<Download[]>
450
+ /** Opcional: estado del derecho de desistimiento de un pedido. */
451
+ getWithdrawal?(orderId: string): Promise<WithdrawalState | null>
452
+ /** Opcional: solicita el desistimiento de un pedido (botón obligatorio en la UE). */
453
+ requestWithdrawal?(orderId: string, datos?: { lineas?: string[]; motivo?: string }): Promise<{ solicitud: string; aceptadas: number }>
316
454
 
317
455
  cart: {
318
456
  get(id: string): Promise<Cart | null>
319
457
  create(): Promise<Cart>
320
- addItem(cartId: string, variantId: string, quantity: number): Promise<Cart>
458
+ addItem(
459
+ cartId: string,
460
+ variantId: string,
461
+ quantity: number,
462
+ personalizacion?: Record<string, unknown>,
463
+ pack?: Record<string, string[]>
464
+ ): Promise<Cart>
321
465
  updateItem(cartId: string, lineId: string, quantity: number): Promise<Cart>
322
466
  removeItem(cartId: string, lineId: string): Promise<Cart>
467
+ /** Consentimiento previo para entregar contenido digital ya (UE, Reino Unido). */
468
+ consentirDigital?(cartId: string, aceptado: boolean): Promise<Cart>
323
469
  applyPromo(cartId: string, code: string): Promise<Cart>
324
470
  removePromo(cartId: string, code: string): Promise<Cart>
325
471
  }
326
472
 
327
473
  checkout: {
474
+ listCountries?(): Promise<string[]>
475
+ setVatNumber?(cartId: string, vatNumber: string | null): Promise<Cart>
328
476
  setEmail(cartId: string, email: string): Promise<Cart>
329
477
  setAddresses(cartId: string, shipping: Address, billing?: Address): Promise<Cart>
330
478
  listShippingOptions(cartId: string): Promise<ShippingOption[]>
@@ -360,12 +508,23 @@ export interface CommerceClient {
360
508
 
361
509
  /** Opcional: un adaptador puede no soportar cuentas de cliente. */
362
510
  account?: {
363
- login(email: string, password: string): Promise<{ token: string }>
511
+ login(email: string, password: string, cartId?: string): Promise<{ token: string }>
364
512
  register(datos: { email: string; password: string; firstName?: string; lastName?: string }): Promise<Customer>
365
513
  me(token: string): Promise<Customer | null>
366
514
  orders(token: string): Promise<Order[]>
515
+ confirmEmail?(sessionToken: string, token: string): Promise<{ adopted: number } | null>
367
516
  }
368
517
 
518
+ /** Opcional: nombre y datos legales de la tienda, tal y como los tiene el panel. */
519
+ getStore?(): Promise<StoreInfo | null>
520
+
521
+ /**
522
+ * Opcional: manda a la tienda un mensaje del formulario de contacto. Rechaza
523
+ * con `status` 400 si faltan datos, 429 si hay demasiados envíos y 503 si la
524
+ * tienda no tiene a dónde mandarlo.
525
+ */
526
+ sendContact?(mensaje: { name?: string; email: string; subject?: string; message: string }): Promise<void>
527
+
369
528
  /** Opcional: contenido editorial servido por el backend. */
370
529
  content?: {
371
530
  listPosts(limit?: number): Promise<Post[]>
package/src/index.js CHANGED
@@ -1,26 +1,12 @@
1
- /**
2
- * @pcreative/commerce-contract — el contrato de datos que consume un tema.
3
- *
4
- * Aquí solo hay las utilidades comunes y el comprobador de conformidad: las
5
- * entidades viven en `index.d.ts` (son forma, no código) y cada adaptador
6
- * traduce lo suyo. Sin dependencias, para que valga igual en Next, Astro, Nuxt
7
- * o un runtime edge.
8
- */
9
1
 
10
2
  export const CONTRACT_VERSION = "1.0"
11
3
 
12
- /** Construye un importe. Los nulos del backend se vuelven 0, no NaN. */
13
4
  export function money(amount, currency, taxIncluded) {
14
5
  const m = { amount: Number(amount ?? 0) || 0, currency: String(currency ?? "eur").toLowerCase() }
15
6
  if (taxIncluded !== undefined) m.taxIncluded = taxIncluded
16
7
  return m
17
8
  }
18
9
 
19
- /**
20
- * Formatea con `Intl`. Los importes del contrato son unidades mayores, así que
21
- * no hay que dividir por 100 en ningún sitio — que es de donde salen la mitad
22
- * de los errores de precio en las tiendas.
23
- */
24
10
  export function formatMoney(m, locale = "es-ES") {
25
11
  if (!m) return ""
26
12
  try {
@@ -33,7 +19,6 @@ export function formatMoney(m, locale = "es-ES") {
33
19
  }
34
20
  }
35
21
 
36
- /** Suma importes. Mezclar monedas es un error, no algo que redondear. */
37
22
  export function sumMoney(...importes) {
38
23
  const validos = importes.filter(Boolean)
39
24
  if (validos.length === 0) return money(0, "eur")
@@ -56,11 +41,6 @@ const REQUERIDO = {
56
41
  checkout: ["setEmail", "setAddresses", "listShippingOptions", "setShippingMethod", "listPaymentMethods", "selectPaymentMethod", "complete"],
57
42
  }
58
43
 
59
- /**
60
- * Comprueba que un adaptador implementa el contrato. Devuelve la lista de lo
61
- * que falta (vacía si cumple). Un tema puede llamarlo en desarrollo para
62
- * enterarse al arrancar y no en mitad del checkout.
63
- */
64
44
  export function assertCommerceClient(cliente) {
65
45
  const faltan = []
66
46
  if (!cliente || typeof cliente !== "object") return ["el cliente no es un objeto"]