@pcreative/commerce-contract 1.0.0 → 1.2.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
@@ -171,6 +289,10 @@ export interface PaymentMethod {
171
289
  /** Identificador del proveedor: "transfer", "cod", "bizum", "card"… */
172
290
  provider: string
173
291
  name?: string
292
+ /** Texto corto que la tienda escribe para explicarla en la caja. */
293
+ description?: string | null
294
+ /** Lo que el comprador tiene que hacer para pagar (datos de una transferencia). */
295
+ instructions?: string | null
174
296
  /**
175
297
  * Recargo de esta forma de pago, en unidades mayores. Sin definir = ninguno.
176
298
  *
@@ -214,6 +336,10 @@ export interface Cart {
214
336
  shippingMethods: { id: string; name: string; price: Money }[]
215
337
  regionId?: string
216
338
  promoCodes?: string[]
339
+ hasDigital?: boolean
340
+ digitalConsent?: { at: string; version: number } | null
341
+ vatNumber?: { number: string; valid: boolean; checked: boolean; name: string | null } | null
342
+ taxExempt?: { reason: "intra_eu"; articles: string[] } | null
217
343
  /**
218
344
  * El paso que falta para pagar, si el método elegido lo pide. `null` con los
219
345
  * métodos sin pasarela: ahí pagar es confirmar el pedido y ya está.
@@ -313,18 +439,36 @@ export interface CommerceClient {
313
439
  search(q: string, limit?: number): Promise<Product[]>
314
440
  /** Pedido por id, para la página de confirmación tras el checkout. */
315
441
  getOrder(id: string): Promise<Order | null>
442
+ /** Sube un fichero que el cliente adjunta a una personalización. */
443
+ subirFichero?(archivo: File): Promise<{ id: string; nombre: string; tipo: string; bytes: number; firma: string }>
444
+ /** Opcional: las descargas de un pedido con contenido digital. */
445
+ getDownloads?(orderId: string): Promise<Download[]>
446
+ /** Opcional: estado del derecho de desistimiento de un pedido. */
447
+ getWithdrawal?(orderId: string): Promise<WithdrawalState | null>
448
+ /** Opcional: solicita el desistimiento de un pedido (botón obligatorio en la UE). */
449
+ requestWithdrawal?(orderId: string, datos?: { lineas?: string[]; motivo?: string }): Promise<{ solicitud: string; aceptadas: number }>
316
450
 
317
451
  cart: {
318
452
  get(id: string): Promise<Cart | null>
319
453
  create(): Promise<Cart>
320
- addItem(cartId: string, variantId: string, quantity: number): Promise<Cart>
454
+ addItem(
455
+ cartId: string,
456
+ variantId: string,
457
+ quantity: number,
458
+ personalizacion?: Record<string, unknown>,
459
+ pack?: Record<string, string[]>
460
+ ): Promise<Cart>
321
461
  updateItem(cartId: string, lineId: string, quantity: number): Promise<Cart>
322
462
  removeItem(cartId: string, lineId: string): Promise<Cart>
463
+ /** Consentimiento previo para entregar contenido digital ya (UE, Reino Unido). */
464
+ consentirDigital?(cartId: string, aceptado: boolean): Promise<Cart>
323
465
  applyPromo(cartId: string, code: string): Promise<Cart>
324
466
  removePromo(cartId: string, code: string): Promise<Cart>
325
467
  }
326
468
 
327
469
  checkout: {
470
+ listCountries?(): Promise<string[]>
471
+ setVatNumber?(cartId: string, vatNumber: string | null): Promise<Cart>
328
472
  setEmail(cartId: string, email: string): Promise<Cart>
329
473
  setAddresses(cartId: string, shipping: Address, billing?: Address): Promise<Cart>
330
474
  listShippingOptions(cartId: string): Promise<ShippingOption[]>
@@ -360,12 +504,23 @@ export interface CommerceClient {
360
504
 
361
505
  /** Opcional: un adaptador puede no soportar cuentas de cliente. */
362
506
  account?: {
363
- login(email: string, password: string): Promise<{ token: string }>
507
+ login(email: string, password: string, cartId?: string): Promise<{ token: string }>
364
508
  register(datos: { email: string; password: string; firstName?: string; lastName?: string }): Promise<Customer>
365
509
  me(token: string): Promise<Customer | null>
366
510
  orders(token: string): Promise<Order[]>
511
+ confirmEmail?(sessionToken: string, token: string): Promise<{ adopted: number } | null>
367
512
  }
368
513
 
514
+ /** Opcional: nombre y datos legales de la tienda, tal y como los tiene el panel. */
515
+ getStore?(): Promise<StoreInfo | null>
516
+
517
+ /**
518
+ * Opcional: manda a la tienda un mensaje del formulario de contacto. Rechaza
519
+ * con `status` 400 si faltan datos, 429 si hay demasiados envíos y 503 si la
520
+ * tienda no tiene a dónde mandarlo.
521
+ */
522
+ sendContact?(mensaje: { name?: string; email: string; subject?: string; message: string }): Promise<void>
523
+
369
524
  /** Opcional: contenido editorial servido por el backend. */
370
525
  content?: {
371
526
  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"]