@rovela-ai/sdk 0.19.7 → 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.
- package/dist/admin/api/index.d.ts +2 -2
- package/dist/admin/api/index.d.ts.map +1 -1
- package/dist/admin/api/index.js +2 -2
- package/dist/admin/api/index.js.map +1 -1
- package/dist/admin/api/orders.d.ts +49 -0
- package/dist/admin/api/orders.d.ts.map +1 -1
- package/dist/admin/api/orders.js +293 -30
- package/dist/admin/api/orders.js.map +1 -1
- package/dist/admin/api/settings.d.ts.map +1 -1
- package/dist/admin/api/settings.js +6 -1
- package/dist/admin/api/settings.js.map +1 -1
- package/dist/admin/api/shipping.d.ts +49 -0
- package/dist/admin/api/shipping.d.ts.map +1 -1
- package/dist/admin/api/shipping.js +144 -0
- package/dist/admin/api/shipping.js.map +1 -1
- package/dist/admin/components/OrderDetails.d.ts.map +1 -1
- package/dist/admin/components/OrderDetails.js +69 -1
- package/dist/admin/components/OrderDetails.js.map +1 -1
- package/dist/admin/components/ShippingSettings.d.ts.map +1 -1
- package/dist/admin/components/ShippingSettings.js +313 -134
- package/dist/admin/components/ShippingSettings.js.map +1 -1
- package/dist/admin/components/UsersTable.d.ts.map +1 -1
- package/dist/admin/components/UsersTable.js +65 -8
- package/dist/admin/components/UsersTable.js.map +1 -1
- package/dist/admin/types.d.ts +13 -0
- package/dist/admin/types.d.ts.map +1 -1
- package/dist/checkout/api/colissimo-relays.d.ts +24 -0
- package/dist/checkout/api/colissimo-relays.d.ts.map +1 -0
- package/dist/checkout/api/colissimo-relays.js +60 -0
- package/dist/checkout/api/colissimo-relays.js.map +1 -0
- package/dist/checkout/api/manual.d.ts.map +1 -1
- package/dist/checkout/api/manual.js +21 -5
- package/dist/checkout/api/manual.js.map +1 -1
- package/dist/checkout/api/shipping.d.ts.map +1 -1
- package/dist/checkout/api/shipping.js +60 -82
- package/dist/checkout/api/shipping.js.map +1 -1
- package/dist/checkout/components/CheckoutFlow.d.ts.map +1 -1
- package/dist/checkout/components/CheckoutFlow.js +142 -3
- package/dist/checkout/components/CheckoutFlow.js.map +1 -1
- package/dist/checkout/components/ShippingOptions.d.ts +8 -1
- package/dist/checkout/components/ShippingOptions.d.ts.map +1 -1
- package/dist/checkout/components/ShippingOptions.js +40 -11
- package/dist/checkout/components/ShippingOptions.js.map +1 -1
- package/dist/checkout/components/colissimo-logo.d.ts +12 -0
- package/dist/checkout/components/colissimo-logo.d.ts.map +1 -0
- package/dist/checkout/components/colissimo-logo.js +12 -0
- package/dist/checkout/components/colissimo-logo.js.map +1 -0
- package/dist/checkout/server/create-checkout-session.d.ts.map +1 -1
- package/dist/checkout/server/create-checkout-session.js +55 -13
- package/dist/checkout/server/create-checkout-session.js.map +1 -1
- package/dist/checkout/server/handle-webhook.js +7 -3
- package/dist/checkout/server/handle-webhook.js.map +1 -1
- package/dist/checkout/server/order-service.d.ts.map +1 -1
- package/dist/checkout/server/order-service.js +25 -0
- package/dist/checkout/server/order-service.js.map +1 -1
- package/dist/checkout/server/pricing.d.ts +20 -0
- package/dist/checkout/server/pricing.d.ts.map +1 -1
- package/dist/checkout/server/pricing.js +51 -5
- package/dist/checkout/server/pricing.js.map +1 -1
- package/dist/checkout/types.d.ts +57 -1
- package/dist/checkout/types.d.ts.map +1 -1
- package/dist/core/db/queries.d.ts +157 -35
- package/dist/core/db/queries.d.ts.map +1 -1
- package/dist/core/db/queries.js +223 -1
- package/dist/core/db/queries.js.map +1 -1
- package/dist/core/db/schema.d.ts +323 -5
- package/dist/core/db/schema.d.ts.map +1 -1
- package/dist/core/db/schema.js +70 -0
- package/dist/core/db/schema.js.map +1 -1
- package/dist/core/i18n/messages/en.d.ts.map +1 -1
- package/dist/core/i18n/messages/en.js +9 -0
- package/dist/core/i18n/messages/en.js.map +1 -1
- package/dist/core/i18n/messages/fr.d.ts.map +1 -1
- package/dist/core/i18n/messages/fr.js +9 -0
- package/dist/core/i18n/messages/fr.js.map +1 -1
- package/dist/shipping/colissimo.d.ts +166 -0
- package/dist/shipping/colissimo.d.ts.map +1 -0
- package/dist/shipping/colissimo.js +424 -0
- package/dist/shipping/colissimo.js.map +1 -0
- package/dist/shipping/index.d.ts +4 -1
- package/dist/shipping/index.d.ts.map +1 -1
- package/dist/shipping/index.js +3 -1
- package/dist/shipping/index.js.map +1 -1
- package/docs/changelog/epoch-02-platform-commerce.md +11 -0
- package/docs/changelog/epoch-06-mobile-integrity.md +29 -1
- package/docs/changelog/epoch-07-shipping-carriers.md +125 -0
- package/package.json +6 -1
- package/templates/store-template/.claude/skills/build-feature/SKILL.md +2 -0
- package/templates/store-template/.claude/skills/manage-products/SKILL.md +1 -1
- package/templates/store-template/.claude/skills/override-sdk-component/SKILL.md +7 -0
- package/templates/store-template/app/api/admin/orders/[id]/colissimo-defaults/route.ts +6 -0
- package/templates/store-template/app/api/admin/orders/[id]/label/route.ts +6 -0
- package/templates/store-template/app/api/admin/orders/[id]/ship-with-colissimo/route.ts +6 -0
- package/templates/store-template/app/api/admin/shipping/colissimo/route.ts +10 -0
- package/templates/store-template/app/api/checkout/colissimo-relays/route.ts +4 -0
- 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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"}
|
package/dist/shipping/index.d.ts
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
* @rovela/sdk/shipping
|
|
3
3
|
*
|
|
4
4
|
* Shipping module for carrier integrations.
|
|
5
|
-
*
|
|
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
|
|
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"}
|
package/dist/shipping/index.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* @rovela/sdk/shipping
|
|
3
3
|
*
|
|
4
4
|
* Shipping module for carrier integrations.
|
|
5
|
-
*
|
|
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
|
|
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"}
|
|
@@ -41,6 +41,17 @@ the NEW SURFACES below make it reachable.
|
|
|
41
41
|
2. **Native discounts replace Stripe coupons as the merchant surface.**
|
|
42
42
|
Legacy Stripe-coupon helpers still exist (feature-card usage) but the
|
|
43
43
|
admin Discounts manager + `/api/checkout/discount` are the system now.
|
|
44
|
+
MERCHANT-DATA HAZARD: codes issued as Stripe promotion codes (newsletter
|
|
45
|
+
welcome flows, coupon feature cards) do NOT validate in the native
|
|
46
|
+
discount box — customers holding them get "invalid code". Audit for a
|
|
47
|
+
Stripe-code issuer (grep for `createCouponWithCode` /
|
|
48
|
+
`createBulkPromotionCodes` / promotion-code inserts) and, if found, emit a
|
|
49
|
+
merchant follow-up: backfill outstanding codes into the `discounts` table
|
|
50
|
+
(idempotent INSERTs mirroring the issuer's terms) or keep
|
|
51
|
+
`store_settings.promotion_codes_enabled = true` so Stripe's own field
|
|
52
|
+
still accepts them during the transition. Rewire the issuer itself to the
|
|
53
|
+
native engine (`createDiscount` from `@rovela-ai/sdk/core/server`,
|
|
54
|
+
0.19.7+).
|
|
44
55
|
3. **`orders.payment_provider`** (default 'stripe') exists — custom order
|
|
45
56
|
readers must not assume every order is a Stripe order (manual methods
|
|
46
57
|
create `pending` orders with `manual_*` idempotency keys).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# E6 Mobile + integrity — 0.19.0..0.19.
|
|
1
|
+
# E6 Mobile + integrity — 0.19.0..0.19.8
|
|
2
2
|
|
|
3
3
|
## What landed
|
|
4
4
|
The save-grammar completion across every admin manager, full mobile
|
|
@@ -146,7 +146,35 @@ store code that mints codes programmatically (newsletter welcome flows).
|
|
|
146
146
|
`OrderTimeline` and `invoice.shipTo`.
|
|
147
147
|
- Cart/checkout pages follow the hydration contract above.
|
|
148
148
|
|
|
149
|
+
## 0.19.8 — Users row-actions menu portal
|
|
150
|
+
|
|
151
|
+
### SDK-internal (free via pin bump)
|
|
152
|
+
The `/admin/users` row-actions (⋯) menu portals to `<body>` — it was the one
|
|
153
|
+
menu still rendered in place inside the table's `overflow-x-auto` wrapper,
|
|
154
|
+
which clips vertically too (per CSS spec, auto on one axis forces auto on the
|
|
155
|
+
other), so the LAST rows' menus were cut off at the table edge. It now uses
|
|
156
|
+
the same mechanics AdminSelect adopted in 0.19.7 (`.admin-select-portal`
|
|
157
|
+
fixed positioning, flip-above near the viewport bottom, scroll/resize
|
|
158
|
+
re-anchoring, Escape closes). No API change, no new exports.
|
|
159
|
+
|
|
160
|
+
Same pass fixed the menu's dead hover states: items referenced
|
|
161
|
+
`--admin-background-hover`, a variable the theme never defined (invalid CSS,
|
|
162
|
+
so hovering did nothing except on destructive items). They now use
|
|
163
|
+
`--admin-background-subtle` — the item-hover token AdminSelect,
|
|
164
|
+
DiscountsManager and RecentOrders already use. There is no
|
|
165
|
+
`--admin-background-hover`; never reference it.
|
|
166
|
+
|
|
167
|
+
RULE for custom admin code: any menu or dropdown anchored inside a table
|
|
168
|
+
wrapper or any `overflow` ancestor MUST portal to `<body>` — AdminSelect and
|
|
169
|
+
the UsersTable RowActions menu are the two reference implementations. An
|
|
170
|
+
in-place `position: absolute` menu inside a scroller is always a latent
|
|
171
|
+
clipping bug.
|
|
172
|
+
|
|
149
173
|
## Verify
|
|
174
|
+
- `/admin/users` with enough rows to reach the viewport bottom: the LAST
|
|
175
|
+
row's ⋯ menu opens fully visible (flipped above the trigger), actions
|
|
176
|
+
fire, click-outside and Escape close it, and it tracks the row while
|
|
177
|
+
scrolling.
|
|
150
178
|
- Resize the admin to 375px: drawer opens from the hamburger, closes on
|
|
151
179
|
scrim/Escape; a delete dialog docks as a bottom sheet; Products table
|
|
152
180
|
scrolls itself (page never scrolls horizontally); Recent Orders shows
|