@rovela-ai/sdk 0.19.8 → 0.21.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 +4 -2
- package/dist/admin/api/index.d.ts.map +1 -1
- package/dist/admin/api/index.js +4 -2
- package/dist/admin/api/index.js.map +1 -1
- package/dist/admin/api/integrations.d.ts +60 -0
- package/dist/admin/api/integrations.d.ts.map +1 -0
- package/dist/admin/api/integrations.js +337 -0
- package/dist/admin/api/integrations.js.map +1 -0
- 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/AdminNav.d.ts.map +1 -1
- package/dist/admin/components/AdminNav.js +3 -1
- package/dist/admin/components/AdminNav.js.map +1 -1
- package/dist/admin/components/AdminSelect.d.ts.map +1 -1
- package/dist/admin/components/AdminSelect.js +2 -0
- package/dist/admin/components/AdminSelect.js.map +1 -1
- package/dist/admin/components/DiscountsManager.d.ts.map +1 -1
- package/dist/admin/components/DiscountsManager.js +2 -2
- package/dist/admin/components/DiscountsManager.js.map +1 -1
- package/dist/admin/components/IntegrationsManager.d.ts +5 -0
- package/dist/admin/components/IntegrationsManager.d.ts.map +1 -0
- package/dist/admin/components/IntegrationsManager.js +138 -0
- package/dist/admin/components/IntegrationsManager.js.map +1 -0
- package/dist/admin/components/OrderDetails.d.ts.map +1 -1
- package/dist/admin/components/OrderDetails.js +75 -3
- 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 +316 -137
- 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 +2 -0
- package/dist/admin/components/UsersTable.js.map +1 -1
- package/dist/admin/components/VariantManager.d.ts.map +1 -1
- package/dist/admin/components/VariantManager.js +8 -2
- package/dist/admin/components/VariantManager.js.map +1 -1
- package/dist/admin/components/index.d.ts +2 -0
- package/dist/admin/components/index.d.ts.map +1 -1
- package/dist/admin/components/index.js +1 -0
- package/dist/admin/components/index.js.map +1 -1
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +2 -0
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/integrations/brevo-logo.d.ts +11 -0
- package/dist/admin/integrations/brevo-logo.d.ts.map +1 -0
- package/dist/admin/integrations/brevo-logo.js +11 -0
- package/dist/admin/integrations/brevo-logo.js.map +1 -0
- package/dist/admin/integrations/registry.d.ts +116 -0
- package/dist/admin/integrations/registry.d.ts.map +1 -0
- package/dist/admin/integrations/registry.js +389 -0
- package/dist/admin/integrations/registry.js.map +1 -0
- 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 +141 -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 +143 -21
- package/dist/core/db/queries.d.ts.map +1 -1
- package/dist/core/db/queries.js +228 -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 +132 -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 +132 -0
- package/dist/core/i18n/messages/fr.js.map +1 -1
- package/dist/products/api/products.d.ts +3 -1
- package/dist/products/api/products.d.ts.map +1 -1
- package/dist/products/api/products.js +29 -3
- package/dist/products/api/products.js.map +1 -1
- package/dist/products/components/VariantSelector.d.ts.map +1 -1
- package/dist/products/components/VariantSelector.js +4 -1
- package/dist/products/components/VariantSelector.js.map +1 -1
- package/dist/products/hooks/useProducts.d.ts +3 -1
- package/dist/products/hooks/useProducts.d.ts.map +1 -1
- package/dist/products/hooks/useProducts.js +4 -2
- package/dist/products/hooks/useProducts.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-07-shipping-carriers.md +125 -0
- package/docs/changelog/epoch-08-integrations.md +125 -0
- package/package.json +6 -1
- package/templates/store-template/.claude/skills/build-feature/SKILL.md +19 -0
- package/templates/store-template/.claude/skills/manage-products/SKILL.md +1 -1
- package/templates/store-template/app/admin/(dashboard)/integrations/page.tsx +17 -0
- package/templates/store-template/app/api/admin/integrations/route.ts +9 -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/app/faq/page.tsx +42 -56
- package/templates/store-template/app/privacy/page.tsx +65 -105
- package/templates/store-template/app/products/[slug]/ProductDetailClient.tsx +6 -3
- package/templates/store-template/app/products/page.tsx +63 -1
- package/templates/store-template/app/terms/page.tsx +70 -198
- 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"}
|
|
@@ -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.
|