@rovela-ai/sdk 0.19.8 → 0.20.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.
Files changed (89) hide show
  1. package/dist/admin/api/index.d.ts +2 -2
  2. package/dist/admin/api/index.d.ts.map +1 -1
  3. package/dist/admin/api/index.js +2 -2
  4. package/dist/admin/api/index.js.map +1 -1
  5. package/dist/admin/api/orders.d.ts +49 -0
  6. package/dist/admin/api/orders.d.ts.map +1 -1
  7. package/dist/admin/api/orders.js +293 -30
  8. package/dist/admin/api/orders.js.map +1 -1
  9. package/dist/admin/api/settings.d.ts.map +1 -1
  10. package/dist/admin/api/settings.js +6 -1
  11. package/dist/admin/api/settings.js.map +1 -1
  12. package/dist/admin/api/shipping.d.ts +49 -0
  13. package/dist/admin/api/shipping.d.ts.map +1 -1
  14. package/dist/admin/api/shipping.js +144 -0
  15. package/dist/admin/api/shipping.js.map +1 -1
  16. package/dist/admin/components/OrderDetails.d.ts.map +1 -1
  17. package/dist/admin/components/OrderDetails.js +69 -1
  18. package/dist/admin/components/OrderDetails.js.map +1 -1
  19. package/dist/admin/components/ShippingSettings.d.ts.map +1 -1
  20. package/dist/admin/components/ShippingSettings.js +313 -134
  21. package/dist/admin/components/ShippingSettings.js.map +1 -1
  22. package/dist/admin/types.d.ts +13 -0
  23. package/dist/admin/types.d.ts.map +1 -1
  24. package/dist/checkout/api/colissimo-relays.d.ts +24 -0
  25. package/dist/checkout/api/colissimo-relays.d.ts.map +1 -0
  26. package/dist/checkout/api/colissimo-relays.js +60 -0
  27. package/dist/checkout/api/colissimo-relays.js.map +1 -0
  28. package/dist/checkout/api/manual.d.ts.map +1 -1
  29. package/dist/checkout/api/manual.js +21 -5
  30. package/dist/checkout/api/manual.js.map +1 -1
  31. package/dist/checkout/api/shipping.d.ts.map +1 -1
  32. package/dist/checkout/api/shipping.js +60 -82
  33. package/dist/checkout/api/shipping.js.map +1 -1
  34. package/dist/checkout/components/CheckoutFlow.d.ts.map +1 -1
  35. package/dist/checkout/components/CheckoutFlow.js +142 -3
  36. package/dist/checkout/components/CheckoutFlow.js.map +1 -1
  37. package/dist/checkout/components/ShippingOptions.d.ts +8 -1
  38. package/dist/checkout/components/ShippingOptions.d.ts.map +1 -1
  39. package/dist/checkout/components/ShippingOptions.js +40 -11
  40. package/dist/checkout/components/ShippingOptions.js.map +1 -1
  41. package/dist/checkout/components/colissimo-logo.d.ts +12 -0
  42. package/dist/checkout/components/colissimo-logo.d.ts.map +1 -0
  43. package/dist/checkout/components/colissimo-logo.js +12 -0
  44. package/dist/checkout/components/colissimo-logo.js.map +1 -0
  45. package/dist/checkout/server/create-checkout-session.d.ts.map +1 -1
  46. package/dist/checkout/server/create-checkout-session.js +55 -13
  47. package/dist/checkout/server/create-checkout-session.js.map +1 -1
  48. package/dist/checkout/server/handle-webhook.js +7 -3
  49. package/dist/checkout/server/handle-webhook.js.map +1 -1
  50. package/dist/checkout/server/order-service.d.ts.map +1 -1
  51. package/dist/checkout/server/order-service.js +25 -0
  52. package/dist/checkout/server/order-service.js.map +1 -1
  53. package/dist/checkout/server/pricing.d.ts +20 -0
  54. package/dist/checkout/server/pricing.d.ts.map +1 -1
  55. package/dist/checkout/server/pricing.js +51 -5
  56. package/dist/checkout/server/pricing.js.map +1 -1
  57. package/dist/checkout/types.d.ts +57 -1
  58. package/dist/checkout/types.d.ts.map +1 -1
  59. package/dist/core/db/queries.d.ts +157 -35
  60. package/dist/core/db/queries.d.ts.map +1 -1
  61. package/dist/core/db/queries.js +223 -1
  62. package/dist/core/db/queries.js.map +1 -1
  63. package/dist/core/db/schema.d.ts +323 -5
  64. package/dist/core/db/schema.d.ts.map +1 -1
  65. package/dist/core/db/schema.js +70 -0
  66. package/dist/core/db/schema.js.map +1 -1
  67. package/dist/core/i18n/messages/en.d.ts.map +1 -1
  68. package/dist/core/i18n/messages/en.js +9 -0
  69. package/dist/core/i18n/messages/en.js.map +1 -1
  70. package/dist/core/i18n/messages/fr.d.ts.map +1 -1
  71. package/dist/core/i18n/messages/fr.js +9 -0
  72. package/dist/core/i18n/messages/fr.js.map +1 -1
  73. package/dist/shipping/colissimo.d.ts +166 -0
  74. package/dist/shipping/colissimo.d.ts.map +1 -0
  75. package/dist/shipping/colissimo.js +424 -0
  76. package/dist/shipping/colissimo.js.map +1 -0
  77. package/dist/shipping/index.d.ts +4 -1
  78. package/dist/shipping/index.d.ts.map +1 -1
  79. package/dist/shipping/index.js +3 -1
  80. package/dist/shipping/index.js.map +1 -1
  81. package/docs/changelog/epoch-07-shipping-carriers.md +125 -0
  82. package/package.json +6 -1
  83. package/templates/store-template/.claude/skills/manage-products/SKILL.md +1 -1
  84. package/templates/store-template/app/api/admin/orders/[id]/colissimo-defaults/route.ts +6 -0
  85. package/templates/store-template/app/api/admin/orders/[id]/label/route.ts +6 -0
  86. package/templates/store-template/app/api/admin/orders/[id]/ship-with-colissimo/route.ts +6 -0
  87. package/templates/store-template/app/api/admin/shipping/colissimo/route.ts +10 -0
  88. package/templates/store-template/app/api/checkout/colissimo-relays/route.ts +4 -0
  89. package/templates/store-template/package.json +1 -1
@@ -0,0 +1,424 @@
1
+ /**
2
+ * @rovela/sdk/shipping/colissimo
3
+ *
4
+ * La Poste Colissimo integration (France-first carrier):
5
+ * - SLS (Simple Label Solution) label generation — home delivery (DOM/DOS)
6
+ * and pickup-point relay delivery (A2P/BPR).
7
+ * - Point Retrait pickup-point search (findRDVPointRetraitAcheminement).
8
+ *
9
+ * Uses direct fetch calls — no SDK dependency. Credentials (contract number +
10
+ * password) are store-level secrets read server-side from store_settings;
11
+ * they are BODY-borne on Colissimo's APIs and must never reach the client.
12
+ *
13
+ * Load-bearing protocol notes (verified against the official SLS spec):
14
+ * - generateLabel responds with `multipart/mixed` (MTOM-style): a JSON part
15
+ * (Content-ID <jsonInfos>) + the raw label bytes (Content-ID <label>).
16
+ * The body MUST be read as a Buffer (label bytes are binary) and the JSON
17
+ * part must be parsed EVEN ON HTTP 4xx — Colissimo returns its error
18
+ * catalog inside the same multipart wrapper.
19
+ * - Colissimo has NO rate-quoting API: checkout prices stay on the store's
20
+ * manual zone rates; this module only does labels + pickup points.
21
+ * - There is no sandbox: `checkGenerateLabel` validates without creating a
22
+ * parcel, and real labels are free until physically scanned.
23
+ * - Parcel-number ranges recycle after ~18 months — the order reference is
24
+ * always stamped on the label (senderParcelRef/orderNumber) and the order
25
+ * row remains the durable key.
26
+ * - Point Retrait: empty-valued params break the call — omit unused params
27
+ * entirely. `shippingDate` is dd/MM/yyyy there while SLS `depositDate` is
28
+ * yyyy-MM-dd.
29
+ *
30
+ * API docs: https://www.colissimo.entreprise.laposte.fr/en/system-integration
31
+ */
32
+ import { findSettings } from '../core/db/queries';
33
+ const SLS_BASE_URL = 'https://ws.colissimo.fr/sls-ws/SlsServiceWSRest/2.0';
34
+ const PDR_BASE_URL = 'https://ws.colissimo.fr/pointretrait-ws-cxf/rest/v2/pointretrait';
35
+ /** Thrown when Colissimo returns an ERROR message or the call fails. */
36
+ export class ColissimoError extends Error {
37
+ messages;
38
+ constructor(message, messages = []) {
39
+ super(message);
40
+ this.name = 'ColissimoError';
41
+ this.messages = messages;
42
+ }
43
+ }
44
+ // =============================================================================
45
+ // Credentials
46
+ // =============================================================================
47
+ /**
48
+ * Get Colissimo credentials from store settings (DB only — the contract is
49
+ * merchant-specific, there is no platform fallback).
50
+ */
51
+ export async function getColissimoCredentials() {
52
+ const settings = await findSettings().catch(() => null);
53
+ const contractNumber = settings?.colissimoContractNumber;
54
+ const password = settings?.colissimoPassword;
55
+ if (!contractNumber || !password) {
56
+ throw new ColissimoError('Colissimo is not configured. Add your contract number and password in Settings > Shipping.');
57
+ }
58
+ return { contractNumber, password };
59
+ }
60
+ // =============================================================================
61
+ // Error catalog — friendly messages for the codes merchants actually hit
62
+ // =============================================================================
63
+ const FRIENDLY_ERRORS = {
64
+ '30000': 'Invalid Colissimo contract number or password.',
65
+ '30001': 'Invalid Colissimo contract number or password.',
66
+ '30007': 'This Colissimo account is inactive. Contact Colissimo support.',
67
+ '30015': 'This shipping product is not valid for this parcel.',
68
+ '30109': 'The recipient mobile number is missing or invalid (required for pickup-point delivery).',
69
+ '30220': 'The recipient phone number is required for this destination.',
70
+ '30300': 'The parcel weight is missing.',
71
+ '30301': 'The parcel weight is invalid (0.01–30 kg, 2 decimals).',
72
+ '30700': 'This shipping product is not enabled on your Colissimo contract.',
73
+ '30008': 'This shipping product is not enabled on your Colissimo contract.',
74
+ '30009': 'This shipping product is not enabled on your Colissimo contract.',
75
+ '30002': 'The deposit date must be today or later.',
76
+ };
77
+ function friendlyError(messages) {
78
+ const firstError = messages.find((m) => m.type === 'ERROR');
79
+ if (!firstError)
80
+ return 'Colissimo request failed';
81
+ const friendly = FRIENDLY_ERRORS[firstError.id];
82
+ return friendly
83
+ ? `${friendly} (Colissimo ${firstError.id})`
84
+ : `Colissimo error ${firstError.id}: ${firstError.messageContent}`;
85
+ }
86
+ /**
87
+ * Parse an SLS response body. Handles both `multipart/mixed` (success — JSON
88
+ * infos part + binary label part) and plain JSON (checkGenerateLabel, some
89
+ * errors). Exported for tests.
90
+ *
91
+ * Buffer↔latin1 round-trips are byte-exact (1 char = 1 byte), so the parts
92
+ * are located on a latin1 view and the label bytes recovered losslessly.
93
+ */
94
+ export function parseSlsResponse(body, contentType) {
95
+ const result = { messages: [], parcelNumber: null, labelBytes: null };
96
+ const readJsonPart = (jsonText) => {
97
+ try {
98
+ const parsed = JSON.parse(jsonText);
99
+ result.messages = parsed.messages ?? [];
100
+ result.parcelNumber = parsed.labelV2Response?.parcelNumber ?? null;
101
+ }
102
+ catch {
103
+ // Leave messages empty — the caller surfaces a generic error.
104
+ }
105
+ };
106
+ const boundaryMatch = /boundary="?([^";]+)"?/i.exec(contentType);
107
+ if (!boundaryMatch) {
108
+ readJsonPart(body.toString('utf8'));
109
+ return result;
110
+ }
111
+ const text = body.toString('latin1');
112
+ const parts = text.split(`--${boundaryMatch[1]}`);
113
+ for (const part of parts) {
114
+ const headerEnd = part.indexOf('\r\n\r\n');
115
+ if (headerEnd === -1)
116
+ continue;
117
+ const headers = part.slice(0, headerEnd).toLowerCase();
118
+ let content = part.slice(headerEnd + 4);
119
+ // Strip the trailing CRLF that precedes the next boundary marker.
120
+ if (content.endsWith('\r\n'))
121
+ content = content.slice(0, -2);
122
+ if (headers.includes('application/json') || headers.includes('<jsoninfos>')) {
123
+ // Reinterpret as UTF-8 (French accented messages).
124
+ readJsonPart(Buffer.from(content, 'latin1').toString('utf8'));
125
+ }
126
+ else if (headers.includes('<label>') || headers.includes('octet-stream')) {
127
+ result.labelBytes = Buffer.from(content, 'latin1');
128
+ }
129
+ }
130
+ return result;
131
+ }
132
+ // =============================================================================
133
+ // SLS letter construction
134
+ // =============================================================================
135
+ function buildLetter(params) {
136
+ const { credentials, productCode, weightKg, orderRef, commercialName, sender, addressee, pickupLocationId, format, } = params;
137
+ const isRelay = productCode === 'A2P' || productCode === 'BPR';
138
+ const today = new Date();
139
+ const depositDate = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
140
+ // Omit optional fields entirely rather than sending them empty.
141
+ const compact = (obj) => {
142
+ const out = {};
143
+ for (const [k, v] of Object.entries(obj)) {
144
+ if (v !== undefined && v !== null && v !== '')
145
+ out[k] = v;
146
+ }
147
+ return out;
148
+ };
149
+ return {
150
+ contractNumber: credentials.contractNumber,
151
+ password: credentials.password,
152
+ outputFormat: { x: 0, y: 0, outputPrintingType: format },
153
+ letter: {
154
+ service: compact({
155
+ productCode,
156
+ depositDate,
157
+ orderNumber: orderRef,
158
+ // Shown in customer SMS/email notifications — mandatory for relay.
159
+ commercialName,
160
+ }),
161
+ parcel: compact({
162
+ // 2 decimals max per the SLS spec.
163
+ weight: Math.round(weightKg * 100) / 100,
164
+ // Must be present for relay products and ABSENT for home delivery.
165
+ pickupLocationId: isRelay ? pickupLocationId : undefined,
166
+ }),
167
+ sender: {
168
+ senderParcelRef: orderRef,
169
+ address: compact({
170
+ companyName: sender.companyName,
171
+ line2: sender.line2,
172
+ line3: sender.line3,
173
+ zipCode: sender.zipCode,
174
+ city: sender.city,
175
+ countryCode: sender.countryCode,
176
+ email: sender.email,
177
+ phoneNumber: sender.phoneNumber,
178
+ }),
179
+ },
180
+ addressee: {
181
+ addresseeParcelRef: orderRef,
182
+ address: compact({
183
+ firstName: addressee.firstName,
184
+ lastName: addressee.lastName,
185
+ line2: addressee.line2,
186
+ line3: addressee.line3,
187
+ zipCode: addressee.zipCode,
188
+ city: addressee.city,
189
+ countryCode: addressee.countryCode,
190
+ phoneNumber: addressee.phoneNumber,
191
+ mobileNumber: addressee.mobileNumber,
192
+ email: addressee.email,
193
+ }),
194
+ },
195
+ },
196
+ };
197
+ }
198
+ // =============================================================================
199
+ // Public API — validation
200
+ // =============================================================================
201
+ /**
202
+ * Validate Colissimo credentials via `checkGenerateLabel` (pure validation —
203
+ * no parcel is created, nothing is billed). Never throws.
204
+ */
205
+ export async function validateColissimoCredentials(contractNumber, password) {
206
+ try {
207
+ const body = buildLetter({
208
+ credentials: { contractNumber, password },
209
+ check: true,
210
+ productCode: 'DOM',
211
+ weightKg: 1,
212
+ orderRef: 'ROVELA-VALIDATE',
213
+ commercialName: 'Validation',
214
+ sender: {
215
+ companyName: 'Validation', line2: '10 rue de Rivoli',
216
+ zipCode: '75004', city: 'Paris', countryCode: 'FR',
217
+ },
218
+ addressee: {
219
+ firstName: 'Jean', lastName: 'Dupont', line2: '5 avenue Jean Jaures',
220
+ zipCode: '69007', city: 'Lyon', countryCode: 'FR', mobileNumber: '0612345678',
221
+ },
222
+ format: 'PDF_A4_300dpi',
223
+ });
224
+ const response = await fetch(`${SLS_BASE_URL}/checkGenerateLabel`, {
225
+ method: 'POST',
226
+ headers: { 'Content-Type': 'application/json' },
227
+ body: JSON.stringify(body),
228
+ });
229
+ const parsed = parseSlsResponse(Buffer.from(await response.arrayBuffer()), response.headers.get('content-type') || '');
230
+ const hasError = parsed.messages.some((m) => m.type === 'ERROR');
231
+ if (!hasError && response.ok)
232
+ return { valid: true };
233
+ return { valid: false, error: friendlyError(parsed.messages) };
234
+ }
235
+ catch (error) {
236
+ return {
237
+ valid: false,
238
+ error: error instanceof Error ? error.message : 'Network error contacting Colissimo',
239
+ };
240
+ }
241
+ }
242
+ // =============================================================================
243
+ // Public API — label generation
244
+ // =============================================================================
245
+ /**
246
+ * Generate a Colissimo shipping label (SLS `generateLabel`).
247
+ *
248
+ * IMPORTANT: every successful call creates a REAL parcel number on the
249
+ * merchant's contract (free until physically scanned). Labels cannot be
250
+ * re-downloaded from Colissimo — the caller MUST persist `labelBytes`.
251
+ */
252
+ export async function generateColissimoLabel(params) {
253
+ const credentials = await getColissimoCredentials();
254
+ const format = params.format || 'PDF_A4_300dpi';
255
+ const isRelay = params.productCode === 'A2P' || params.productCode === 'BPR';
256
+ if (isRelay && !params.pickupLocationId) {
257
+ throw new ColissimoError('A pickup point is required for relay delivery.');
258
+ }
259
+ if (isRelay && !params.addressee.mobileNumber) {
260
+ throw new ColissimoError('A French mobile number is required for pickup-point delivery (Colissimo sends the customer an SMS).');
261
+ }
262
+ if (!(params.weightKg >= 0.01 && params.weightKg <= 30)) {
263
+ throw new ColissimoError(`Parcel weight must be between 0.01 and 30 kg (got ${params.weightKg} kg). Split heavier orders into multiple parcels.`);
264
+ }
265
+ const body = buildLetter({ credentials, check: false, format, ...params });
266
+ const response = await fetch(`${SLS_BASE_URL}/generateLabel`, {
267
+ method: 'POST',
268
+ headers: { 'Content-Type': 'application/json' },
269
+ body: JSON.stringify(body),
270
+ });
271
+ // Parse the body even on 4xx — the error catalog rides the same wrapper.
272
+ const parsed = parseSlsResponse(Buffer.from(await response.arrayBuffer()), response.headers.get('content-type') || '');
273
+ const hasError = parsed.messages.some((m) => m.type === 'ERROR');
274
+ if (hasError || !response.ok) {
275
+ throw new ColissimoError(friendlyError(parsed.messages), parsed.messages);
276
+ }
277
+ if (!parsed.parcelNumber || !parsed.labelBytes || parsed.labelBytes.length === 0) {
278
+ throw new ColissimoError('Colissimo returned no label. Please try again.', parsed.messages);
279
+ }
280
+ return {
281
+ parcelNumber: parsed.parcelNumber,
282
+ labelBytes: parsed.labelBytes,
283
+ format,
284
+ warnings: parsed.messages.filter((m) => m.type !== 'ERROR'),
285
+ };
286
+ }
287
+ /** Public tracking page for a Colissimo parcel (no API key needed). */
288
+ export function colissimoTrackingUrl(parcelNumber) {
289
+ return `https://www.laposte.fr/outils/suivre-vos-envois?code=${encodeURIComponent(parcelNumber)}`;
290
+ }
291
+ function toRelayPoint(raw) {
292
+ const closed = (h) => !h || h === '00:00-00:00';
293
+ const hours = [
294
+ raw.horairesOuvertureLundi,
295
+ raw.horairesOuvertureMardi,
296
+ raw.horairesOuvertureMercredi,
297
+ raw.horairesOuvertureJeudi,
298
+ raw.horairesOuvertureVendredi,
299
+ raw.horairesOuvertureSamedi,
300
+ raw.horairesOuvertureDimanche,
301
+ ].map((h) => (closed(h) ? null : h));
302
+ return {
303
+ id: raw.identifiant,
304
+ name: raw.nom,
305
+ type: raw.typeDePoint,
306
+ line1: raw.adresse1 || '',
307
+ line2: [raw.adresse2, raw.adresse3].filter(Boolean).join(' ') || null,
308
+ postalCode: raw.codePostal || '',
309
+ city: raw.localite || '',
310
+ country: (raw.codePays || 'FR').toUpperCase(),
311
+ distanceMeters: typeof raw.distanceEnMetre === 'number' ? raw.distanceEnMetre : null,
312
+ latitude: raw.coordGeolocalisationLatitude || null,
313
+ longitude: raw.coordGeolocalisationLongitude || null,
314
+ maxWeightGrams: typeof raw.poidsMaxi === 'number' ? raw.poidsMaxi : null,
315
+ /** Mon..Sun; null = closed that day. */
316
+ openingHours: hours,
317
+ };
318
+ }
319
+ /** Relay point types we support in v1 (map cleanly to A2P/BPR products). */
320
+ const SUPPORTED_RELAY_TYPES = new Set(['A2P', 'BPR', 'CDI', 'ACP', 'CMT', 'PCS', 'BDP']);
321
+ /** Map a relay point's type to the SLS product code for label generation. */
322
+ export function relayTypeToProductCode(type) {
323
+ // Post-office family (bureau de poste / centre de distribution / agence).
324
+ if (type === 'BPR' || type === 'CDI' || type === 'ACP')
325
+ return 'BPR';
326
+ // Merchant relays + lockers.
327
+ return 'A2P';
328
+ }
329
+ /**
330
+ * Search pickup points around an address (findRDVPointRetraitAcheminement).
331
+ * Weight is passed in GRAMS (points enforce per-point maximums).
332
+ */
333
+ export async function searchRelayPoints(params) {
334
+ const credentials = await getColissimoCredentials();
335
+ const d = new Date(Date.now() + 24 * 3600 * 1000);
336
+ const shippingDate = `${String(d.getDate()).padStart(2, '0')}/${String(d.getMonth() + 1).padStart(2, '0')}/${d.getFullYear()}`;
337
+ // Empty-valued params break the call — build the body without them.
338
+ const body = {
339
+ accountNumber: credentials.contractNumber,
340
+ password: credentials.password,
341
+ zipCode: params.zipCode,
342
+ city: params.city,
343
+ countryCode: (params.countryCode || 'FR').toUpperCase(),
344
+ weight: String(Math.max(1, Math.round(params.weightGrams ?? 1000))),
345
+ shippingDate,
346
+ filterRelay: '1',
347
+ requestId: `rovela-${Date.now()}`,
348
+ lang: 'FR',
349
+ };
350
+ if (params.address)
351
+ body.address = params.address;
352
+ const response = await fetch(`${PDR_BASE_URL}/findRDVPointRetraitAcheminement`, {
353
+ method: 'POST',
354
+ headers: { 'Content-Type': 'application/json' },
355
+ body: JSON.stringify(body),
356
+ });
357
+ if (!response.ok) {
358
+ throw new ColissimoError(`Colissimo pickup-point search failed (${response.status})`);
359
+ }
360
+ const data = (await response.json());
361
+ if (data.errorCode && data.errorCode !== 0) {
362
+ throw new ColissimoError(`Colissimo pickup-point search failed: ${data.errorMessage || data.errorCode}`);
363
+ }
364
+ return (data.listePointRetraitAcheminement ?? [])
365
+ .filter((p) => p?.identifiant && SUPPORTED_RELAY_TYPES.has(p.typeDePoint))
366
+ .map(toRelayPoint);
367
+ }
368
+ /**
369
+ * Fetch one pickup point by id (findPointRetraitAcheminementByID) — the
370
+ * server-side re-verification used by the pricing core at quote time.
371
+ * Returns null when the point is unknown/unavailable.
372
+ */
373
+ export async function getRelayPointById(id) {
374
+ try {
375
+ const credentials = await getColissimoCredentials();
376
+ const d = new Date(Date.now() + 24 * 3600 * 1000);
377
+ const date = `${String(d.getDate()).padStart(2, '0')}/${String(d.getMonth() + 1).padStart(2, '0')}/${d.getFullYear()}`;
378
+ // NOTE: this endpoint REQUIRES `date` (error 106 without it) and takes
379
+ // `langue` — its sibling search endpoint takes `lang`; `lang` here → 500.
380
+ const body = {
381
+ accountNumber: credentials.contractNumber,
382
+ password: credentials.password,
383
+ id,
384
+ date,
385
+ langue: 'FR',
386
+ };
387
+ const response = await fetch(`${PDR_BASE_URL}/findPointRetraitAcheminementByID`, {
388
+ method: 'POST',
389
+ headers: { 'Content-Type': 'application/json' },
390
+ body: JSON.stringify(body),
391
+ });
392
+ if (!response.ok)
393
+ return null;
394
+ const data = (await response.json());
395
+ if (data.errorCode && data.errorCode !== 0)
396
+ return null;
397
+ const raw = data.pointRetraitAcheminement;
398
+ if (!raw?.identifiant)
399
+ return null;
400
+ return toRelayPoint(raw);
401
+ }
402
+ catch {
403
+ return null;
404
+ }
405
+ }
406
+ // =============================================================================
407
+ // Helpers shared with the admin fulfillment surface
408
+ // =============================================================================
409
+ /** French mobile check (Colissimo requires 06/07 mobiles for relay SMS). */
410
+ export function isFrenchMobile(phone) {
411
+ if (!phone)
412
+ return false;
413
+ const digits = phone.replace(/[\s.\-()]/g, '');
414
+ return /^(?:\+33|0033|0)[67]\d{8}$/.test(digits);
415
+ }
416
+ /** Normalize a French mobile to the 10-digit national form Colissimo expects. */
417
+ export function normalizeFrenchMobile(phone) {
418
+ const digits = phone.replace(/[\s.\-()]/g, '');
419
+ const m = /^(?:\+33|0033)([67]\d{8})$/.exec(digits);
420
+ if (m)
421
+ return `0${m[1]}`;
422
+ return digits;
423
+ }
424
+ //# sourceMappingURL=colissimo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colissimo.js","sourceRoot":"","sources":["../../src/shipping/colissimo.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,10 @@
2
2
  * @rovela/sdk/shipping
3
3
  *
4
4
  * Shipping module for carrier integrations.
5
- * Currently supports Shippo for real-time carrier rates and label purchases.
5
+ * - Shippo: real-time carrier rates and label purchases.
6
+ * - Colissimo (La Poste): label generation + pickup-point (relay) delivery.
6
7
  */
7
8
  export { validateShippoKey, getShippoRates, purchaseShippoLabel, shippoRatesToStripeOptions, } from './shippo';
9
+ export { validateColissimoCredentials, generateColissimoLabel, searchRelayPoints, getRelayPointById, relayTypeToProductCode, colissimoTrackingUrl, isFrenchMobile, normalizeFrenchMobile, parseSlsResponse, ColissimoError, } from './colissimo';
10
+ export type { ColissimoCredentials, ColissimoSenderAddress, ColissimoAddressee, ColissimoProductCode, ColissimoLabelFormat, ColissimoLabelResult, ColissimoValidationResult, ColissimoMessage, } from './colissimo';
8
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shipping/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,UAAU,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shipping/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,UAAU,CAAA;AAEjB,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,GACf,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,aAAa,CAAA"}
@@ -2,7 +2,9 @@
2
2
  * @rovela/sdk/shipping
3
3
  *
4
4
  * Shipping module for carrier integrations.
5
- * Currently supports Shippo for real-time carrier rates and label purchases.
5
+ * - Shippo: real-time carrier rates and label purchases.
6
+ * - Colissimo (La Poste): label generation + pickup-point (relay) delivery.
6
7
  */
7
8
  export { validateShippoKey, getShippoRates, purchaseShippoLabel, shippoRatesToStripeOptions, } from './shippo';
9
+ export { validateColissimoCredentials, generateColissimoLabel, searchRelayPoints, getRelayPointById, relayTypeToProductCode, colissimoTrackingUrl, isFrenchMobile, normalizeFrenchMobile, parseSlsResponse, ColissimoError, } from './colissimo';
8
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shipping/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,UAAU,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shipping/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,UAAU,CAAA;AAEjB,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,GACf,MAAM,aAAa,CAAA"}
@@ -0,0 +1,125 @@
1
+ # E7 Shipping carriers — 0.20.0..0.20.0
2
+
3
+ ## What landed
4
+ Shipping became a carrier REGISTRY (the payments-registry model): multiple
5
+ carriers coexist, each with its own zones + rates, and the global
6
+ `store_settings.shipping_mode` either/or is retired (column kept, ignored).
7
+ Colissimo (La Poste) is the first integrated carrier: label generation from
8
+ the admin order page (home + point relais products), pickup-point (relay)
9
+ delivery at checkout with a nested point picker, tracking links, and the
10
+ order-shipped email carrying the tracking number. Relay offers are ordinary
11
+ `shipping_rates` rows (`fulfillment='relay'`) — zones, cart thresholds and
12
+ customer-group gating apply to every carrier through the one rate engine.
13
+
14
+ ## SDK-internal (free via pin bump)
15
+ - Checkout `ShippingOptions` renders carrier groups as contained parent
16
+ cards (identity header w/ provider badge or merchant `logo_url`, child
17
+ option cards at full width) and sorts groups + options by price
18
+ descending. `renderOptionDetail` slot nests content under an option.
19
+ - Quote API degrades gracefully: a Shippo outage drops its live quotes but
20
+ no longer blanks the other carriers' options.
21
+ - `sendStatusNotification` logs shipped-email failures honestly (was an
22
+ unconditional "sent" log).
23
+ - Order confirmation + shipped emails label relay orders' address block
24
+ "Point relais — {name}" instead of presenting the relay street as the
25
+ customer's home address.
26
+
27
+ ## Template contract
28
+ - `app/api/checkout/colissimo-relays/route.ts` (NEW) — re-export of
29
+ `@rovela-ai/sdk/checkout/api/colissimo-relays`; the checkout relay picker
30
+ POSTs here. Without it, selecting a relay rate shows a permanent
31
+ "search failed" state.
32
+ - `app/api/admin/shipping/colissimo/route.ts` (NEW) — GET/POST/DELETE
33
+ re-exports (`getColissimoConfig`/`saveColissimoSettings`/
34
+ `deleteColissimoSettings`); the ShippingSettings Colissimo panel reads and
35
+ writes through it.
36
+ - `app/api/admin/orders/[id]/ship-with-colissimo/route.ts` (NEW) — POST
37
+ `shipWithColissimo`; `app/api/admin/orders/[id]/colissimo-defaults/route.ts`
38
+ (NEW) — GET `getColissimoDefaults`; `app/api/admin/orders/[id]/label/route.ts`
39
+ (NEW) — GET `downloadOrderLabel` (admin-gated label bytes; `orders.label_url`
40
+ points at this path for Colissimo labels).
41
+
42
+ ## Behavior contracts
43
+ - `checkout/api/shipping` (quote) now UNIONS all active carriers' DB rates
44
+ with Shippo live quotes when a `provider='shippo'` carrier is active —
45
+ `store_settings.shipping_mode` no longer selects a path. A legacy
46
+ `shipping_mode='shippo'` store self-heals a shippo carrier row
47
+ (`ensureLegacyShippoCarrier`) on the first quote/admin-carriers read.
48
+ Response `mode` field is now the constant `'manual'` (legacy shape only).
49
+ - `resolveShippingSelection` manual branch: rates from provider-bound
50
+ carriers stamp `orders.carrier` with the PROVIDER (`'colissimo'`), not the
51
+ carrier's slug. Rates with `fulfillment='relay'` REQUIRE
52
+ `selection.relayPointId`; the point is re-verified with the carrier and a
53
+ French mobile is required on the shipping address (PricingError
54
+ otherwise). The relay's address becomes the order's `shippingAddress`
55
+ snapshot ("Point relais — {name}" convention, mirroring §59 pickup); the
56
+ customer's own entered address is preserved as `billingAddress` when no
57
+ separate billing was given.
58
+ - `ShippingOption.source` union is unchanged (`manual|shippo|pickup`) but
59
+ options gained `provider?`/`fulfillment?`/`relayPointId?` — custom
60
+ checkout code switching on `source` keeps working.
61
+ - Admin `ShippingSettings` is carriers-first: the manual/shippo mode toggle
62
+ is GONE; Shippo config renders inside its carrier card (own transactional
63
+ Save), the AdminSaveBar document now tracks only `shippingCountries`.
64
+
65
+ ## Custom-code hazards
66
+ - grep `shippingMode` in store code: any UI/logic branching on
67
+ `'manual' | 'shippo'` is dead — the quote response's `mode` is now a
68
+ constant. Replace with capability checks on the options themselves.
69
+ - grep `source === 'relay'` (stores that adopted 0.20.0 pre-releases only):
70
+ the relay discriminator is `fulfillment === 'relay'`; `'relay'` was never
71
+ a published `source` value.
72
+ - Stores with a custom checkout that forked `ShippingOptions`: the fork
73
+ misses the carrier-group containers and the `renderOptionDetail` slot —
74
+ relay rates will render without a point picker. Replace the fork or port
75
+ the slot.
76
+
77
+ ## DB
78
+ - `shipping_carriers.provider varchar(20) NOT NULL DEFAULT 'manual'`,
79
+ `shipping_carriers.logo_url varchar(500)`,
80
+ `shipping_rates.fulfillment varchar(20) NOT NULL DEFAULT 'home'` — all
81
+ self-heal via `ensureShippingCarrierSchema()` at every carrier/zone/rate
82
+ chokepoint. Defaults make existing data behave identically.
83
+ - `orders.relay jsonb` (`{provider,id,type,name}`) — rides
84
+ `ensurePaymentMethodsSchema()`.
85
+ - `shipping_labels` (NEW table: order_id FK cascade, carrier, format,
86
+ data base64) — self-heals whole via `ensureShippingLabelsSchema()`.
87
+ Deliberately not a column on orders (drizzle full-row selects).
88
+ - `store_settings`: `colissimo_contract_number varchar(20)`,
89
+ `colissimo_password varchar(64)` (SERVER-ONLY secret — stripped from the
90
+ admin settings GET; never `NEXT_PUBLIC_`, never client-side),
91
+ `colissimo_enabled boolean`, `colissimo_sender_address jsonb`,
92
+ `colissimo_default_weight_kg decimal` — ride `ensureStoreSettingsSchema()`.
93
+
94
+ ## New surfaces
95
+ - The five routes in Template contract above. No new admin PAGES — the
96
+ Colissimo panel lives inside `/admin/shipping`'s carrier card and the
97
+ "Ship with Colissimo" card inside the existing order detail page.
98
+
99
+ ## MERCHANT-DATA HAZARD
100
+ - A store that ran `shipping_mode='shippo'`: its Shippo config keeps working
101
+ through the self-healed carrier row, but the row is created ACTIVE — if
102
+ the merchant had abandoned Shippo (mode set but key removed), the quote
103
+ path silently skips it (no key → degraded, no options added). Audit: does
104
+ the store have `shippo_api_key` set AND `shipping_mode='shippo'`? If mode
105
+ is shippo but the key is gone, deactivate the self-healed carrier row.
106
+ - Parcel weight for Colissimo labels reads the product `weight` METAFIELD
107
+ (kg, PrestaShop-import convention) with `colissimo_default_weight_kg` as
108
+ the per-item fallback — stores without weight metafields ship at the
109
+ default weight unless the admin overrides per label.
110
+
111
+ ## Verify
112
+ - `/admin/shipping`: carriers list renders (no mode toggle); "Add carrier"
113
+ offers Manual/Colissimo/Shippo with duplicates disabled; a Colissimo
114
+ carrier card shows the Connection panel and its zones/rates; the rate
115
+ editor on a Colissimo carrier shows "Delivery type: Home / Pickup point".
116
+ - Checkout with a FR address: carrier groups render with badges, sorted by
117
+ price desc; selecting a relay rate opens the nested point picker; continue
118
+ is blocked until a point is picked and the address has a FR mobile.
119
+ - Place a relay order (manual payment path is fine): `orders.relay` set,
120
+ `shipping_address` = the relay's address, `carrier='colissimo'`.
121
+ - Admin: mark paid → "Ship with Colissimo" (weight prefilled from
122
+ metafields) → Generate Label & Ship → tracking number appears (`6M`/`6H`
123
+ prefix for relay, `6A`/`6C` for home), label downloads from
124
+ `/api/admin/orders/{id}/label`, shipped email sent with tracking.
125
+ - `npm run build` green.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rovela-ai/sdk",
3
- "version": "0.19.8",
3
+ "version": "0.20.0",
4
4
  "description": "Rovela SDK - Pre-built e-commerce components for AI-powered store generation",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -90,6 +90,10 @@
90
90
  "types": "./dist/checkout/api/pickup-locations.d.ts",
91
91
  "import": "./dist/checkout/api/pickup-locations.js"
92
92
  },
93
+ "./checkout/api/colissimo-relays": {
94
+ "types": "./dist/checkout/api/colissimo-relays.d.ts",
95
+ "import": "./dist/checkout/api/colissimo-relays.js"
96
+ },
93
97
  "./checkout/server": {
94
98
  "types": "./dist/checkout/server/index.d.ts",
95
99
  "import": "./dist/checkout/server/index.js"
@@ -292,6 +296,7 @@
292
296
  },
293
297
  "scripts": {
294
298
  "build": "tsc && cp -r src/admin/styles dist/admin/",
299
+ "prepublishOnly": "npm run build && cd ../.. && bash scripts/check-prompts.sh",
295
300
  "dev": "tsc --watch",
296
301
  "clean": "rm -rf dist tsconfig.tsbuildinfo",
297
302
  "db:push": "drizzle-kit push",
@@ -33,7 +33,7 @@ Usage: `nextjs_call` with `port="3000"` and `toolName="execute_sql"`
33
33
  | `orders` | id, customerId, email, status, total, currency (ISO snapshot — display orders in THIS currency), refundAmount, returnReason, shippingAddress (JSONB) |
34
34
  | `order_items` | id, orderId (CASCADE), productId, variantId, name, price, quantity |
35
35
  | `customers` | id, email (unique), name, passwordHash, phone, groupId, locale, stripeCustomerId, emailVerified |
36
- | `store_settings` | id, storeName, storeEmail, storeCurrency, storeTimezone, storeCountry, defaultLocale, enabledLocales, taxIncludedInPrices, shippingMode |
36
+ | `store_settings` | id, storeName, storeEmail, storeCurrency, storeTimezone, storeCountry, defaultLocale, enabledLocales, taxIncludedInPrices, shippingMode (retired — the shipping_carriers registry decides; carriers have provider manual/shippo/colissimo, rates have fulfillment home/relay) |
37
37
 
38
38
  **Money contract (load-bearing)**: `price`, `total` etc. are DECIMAL CURRENCY UNITS — `29.99`, NEVER cents (`2999`). No ×100/÷100 anywhere except the Stripe API boundary. On multilingual stores (`enabled_locales`), catalog content can carry per-locale `translations` — when editing names/descriptions, keep every enabled locale in sync (CLAUDE.md §5.5).
39
39