@scaletrail/connector 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +17 -0
- package/examples/event-add-to-cart.json +3 -1
- package/examples/event-login-identity.json +1 -1
- package/package.json +1 -1
- package/src/schema.js +22 -5
- package/src/types.d.ts +3 -1
- package/src/validate.js +7 -4
- package/src/version.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 (12.11.2026)
|
|
4
|
+
|
|
5
|
+
- `validateMessage('redact', …)`: `phone` nur noch in E.164 (`^\+[1-9][0-9]{6,14}$`). `email` zählt nur mit `@`
|
|
6
|
+
als Merkmal; ohne gültiges Merkmal braucht die Anfrage `order_ids` (eingeführt mit Schema `1.0.0-draft.8`;
|
|
7
|
+
ausgeliefert wird `draft.9`, siehe unten).
|
|
8
|
+
- Jeder Betrag (`*_cents`) höchstens `10000000000`, sonst ungültig.
|
|
9
|
+
- README: Löschanfrage mit allen Bestellungen der Kundin, Telefon in E.164, Beispiel.
|
|
10
|
+
- `add_to_cart` nimmt zwei optionale Felder: `dedup_id` (UUID aus dem Browser, dieselbe ID wie das Browser-Tag
|
|
11
|
+
desselben Klicks) und `consent` (Form wie an der Bestellung). `quantity` höchstens `10000`. Schema-Version
|
|
12
|
+
`1.0.0-draft.9`.
|
|
13
|
+
- Beispiele: `event-add-to-cart.json` mit `dedup_id` und `consent`, `event-login-identity.json` mit einer UUID als
|
|
14
|
+
`event_id` (je Login neu).
|
|
15
|
+
- README: Ereignisse sofort senden, `occurred_at` höchstens 300 s in der Zukunft, älter als 24 Stunden nicht mehr
|
|
16
|
+
berücksichtigt, `event_id` je Ereignis neu.
|
|
17
|
+
|
|
3
18
|
## 0.2.0 (11.11.2026)
|
|
4
19
|
|
|
5
20
|
- **Signatur `v2`, nicht kompatibel mit 0.1.0.** Signiert werden Zweck, Zeitstempel, Pfad, Testmodus und Body.
|
package/README.md
CHANGED
|
@@ -62,6 +62,23 @@ ScaleTrail warten.
|
|
|
62
62
|
Ebenso: `sendEvent(event)` für `add_to_cart` und `login_identity`, `sendRedact(redact)`, wenn eine Kundin im Shop
|
|
63
63
|
anonymisiert wird.
|
|
64
64
|
|
|
65
|
+
Ereignisse (`sendEvent`): sofort senden, `occurred_at` ist die Zeit des Ereignisses (höchstens 300 s nach der Uhr von
|
|
66
|
+
ScaleTrail, sonst `rejected`). Was älter als 24 Stunden ist, wird angenommen und nicht mehr berücksichtigt. `event_id`
|
|
67
|
+
ist je Ereignis eine neue `uuidV7()`, beim Wiederholen desselben Aufrufs dieselbe. Ohne `sa_uid` (Warenkorb und
|
|
68
|
+
Anmeldung) oder ohne gültige `email` (Anmeldung) wird das Ereignis angenommen und nicht berücksichtigt. Beim Warenkorb
|
|
69
|
+
optional `dedup_id` (Browser-ID aus dem Cookie `_sa_eid`) und `consent` (Stand der Einwilligung, Form wie an der
|
|
70
|
+
Bestellung): `statistics: false` oder `marketing: false` gelten für dieses Ereignis.
|
|
71
|
+
|
|
72
|
+
Löschanfrage (`sendRedact`): senden, **bevor** der Shop die Werte anonymisiert, und die Werte halten, bis `accepted`
|
|
73
|
+
zurückkommt. `order_ids` enthält **alle** Bestellungen der Kundin, die ihre Daten tragen, auch offene und unbezahlte.
|
|
74
|
+
`phone` nur in E.164 (`+41791234567`), sonst `rejected`. Mindestens eines von `email` (mit `@`), `phone` oder
|
|
75
|
+
`order_ids` muss gesetzt sein. `retry` (auch bei sehr vielen Bestellungen) heisst: dieselbe Anfrage wiederholen.
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
await client.sendRedact({ email: 'kundin@example.ch', phone: '+41791234567', order_ids: ['200123', '200456'],
|
|
79
|
+
requested_at: new Date().toISOString() });
|
|
80
|
+
```
|
|
81
|
+
|
|
65
82
|
## Cookies am Server des Shops
|
|
66
83
|
|
|
67
84
|
Der Server des Shops setzt bei **jeder** Antwort den Spiegel der Besucher-ID `_sa_uid_shop` und bei einer Landung mit
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
"unit_price_cents": 4990,
|
|
13
13
|
"currency": "CHF",
|
|
14
14
|
"page_url": "https://www.example.ch/produkt/beispiel-serum/",
|
|
15
|
-
"client": { "ip": "203.0.113.24", "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 26_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.1 Mobile/15E148 Safari/604.1" }
|
|
15
|
+
"client": { "ip": "203.0.113.24", "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 26_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.1 Mobile/15E148 Safari/604.1" },
|
|
16
|
+
"dedup_id": "0192f3b0-19f8-7a21-9c3e-2b4d6f8a0c1e",
|
|
17
|
+
"consent": { "source": "custom", "statistics": true, "marketing": true, "captured_at": "2025-12-04T09:31:15.000+01:00" }
|
|
16
18
|
}
|
|
17
19
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"event": {
|
|
3
3
|
"type": "login_identity",
|
|
4
|
-
"event_id": "
|
|
4
|
+
"event_id": "0192f3ad-77c2-7e10-8b5a-4c6d8e0f1a23",
|
|
5
5
|
"occurred_at": "2025-12-04T09:36:02.000+01:00",
|
|
6
6
|
"sa_uid": "0192f3a4-5b6c-7d8e-9f01-23456789abcd",
|
|
7
7
|
"customer_id": "c-58210",
|
package/package.json
CHANGED
package/src/schema.js
CHANGED
|
@@ -3,7 +3,7 @@ export const SCHEMA = Object.freeze({
|
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"$id": "https://api.scaletrail.io/connect/v1/schema.json",
|
|
5
5
|
"title": "ScaleTrail Connector API v1 (Eigenbau-Shops)",
|
|
6
|
-
"version": "1.0.0-draft.
|
|
6
|
+
"version": "1.0.0-draft.9",
|
|
7
7
|
"oneOf": [
|
|
8
8
|
{
|
|
9
9
|
"$ref": "#/definitions/order_message"
|
|
@@ -18,14 +18,16 @@ export const SCHEMA = Object.freeze({
|
|
|
18
18
|
"definitions": {
|
|
19
19
|
"cents": {
|
|
20
20
|
"type": "integer",
|
|
21
|
-
"minimum": 0
|
|
21
|
+
"minimum": 0,
|
|
22
|
+
"maximum": 10000000000
|
|
22
23
|
},
|
|
23
24
|
"cents_or_null": {
|
|
24
25
|
"type": [
|
|
25
26
|
"integer",
|
|
26
27
|
"null"
|
|
27
28
|
],
|
|
28
|
-
"minimum": 0
|
|
29
|
+
"minimum": 0,
|
|
30
|
+
"maximum": 10000000000
|
|
29
31
|
},
|
|
30
32
|
"datetime": {
|
|
31
33
|
"type": "string",
|
|
@@ -991,7 +993,8 @@ export const SCHEMA = Object.freeze({
|
|
|
991
993
|
},
|
|
992
994
|
"quantity": {
|
|
993
995
|
"type": "integer",
|
|
994
|
-
"minimum": 1
|
|
996
|
+
"minimum": 1,
|
|
997
|
+
"maximum": 10000
|
|
995
998
|
},
|
|
996
999
|
"unit_price_cents": {
|
|
997
1000
|
"$ref": "#/definitions/cents"
|
|
@@ -1016,6 +1019,19 @@ export const SCHEMA = Object.freeze({
|
|
|
1016
1019
|
"$ref": "#/definitions/client"
|
|
1017
1020
|
}
|
|
1018
1021
|
]
|
|
1022
|
+
},
|
|
1023
|
+
"dedup_id": {
|
|
1024
|
+
"$ref": "#/definitions/uuid"
|
|
1025
|
+
},
|
|
1026
|
+
"consent": {
|
|
1027
|
+
"oneOf": [
|
|
1028
|
+
{
|
|
1029
|
+
"type": "null"
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
"$ref": "#/definitions/consent"
|
|
1033
|
+
}
|
|
1034
|
+
]
|
|
1019
1035
|
}
|
|
1020
1036
|
}
|
|
1021
1037
|
},
|
|
@@ -1091,7 +1107,8 @@ export const SCHEMA = Object.freeze({
|
|
|
1091
1107
|
"string",
|
|
1092
1108
|
"null"
|
|
1093
1109
|
],
|
|
1094
|
-
"maxLength": 32
|
|
1110
|
+
"maxLength": 32,
|
|
1111
|
+
"pattern": "^\\+[1-9][0-9]{6,14}$"
|
|
1095
1112
|
},
|
|
1096
1113
|
"order_ids": {
|
|
1097
1114
|
"type": "array",
|
package/src/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated from the ScaleTrail Connector API v1 JSON Schema. Do not edit by hand.
|
|
2
|
-
// Schema version 1.0.0-draft.
|
|
2
|
+
// Schema version 1.0.0-draft.9.
|
|
3
3
|
|
|
4
4
|
export type Cents = number;
|
|
5
5
|
|
|
@@ -231,6 +231,8 @@ export interface EventAddToCart {
|
|
|
231
231
|
currency: Currency;
|
|
232
232
|
page_url?: string | null;
|
|
233
233
|
client?: null | Client;
|
|
234
|
+
dedup_id?: Uuid;
|
|
235
|
+
consent?: null | Consent;
|
|
234
236
|
}
|
|
235
237
|
|
|
236
238
|
export interface EventLoginIdentity {
|
package/src/validate.js
CHANGED
|
@@ -146,12 +146,15 @@ export function validateMessage(route, message, { strict = true } = {}) {
|
|
|
146
146
|
return [...new Set(errors)].slice(0, MAX_PATHS);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
// A redact request needs at least one
|
|
150
|
-
//
|
|
149
|
+
// A redact request needs at least one usable identifier: an email address (with "@"), a phone number in E.164
|
|
150
|
+
// (the schema checks the format), or a non-empty order_ids list. ScaleTrail finds the customer by these, not by
|
|
151
|
+
// the shop's customer ID. The schema cannot express "at least one", the API checks it. The API also rejects a
|
|
152
|
+
// phone number that is not valid for its country code.
|
|
151
153
|
function redactHasIdentifier(redact) {
|
|
152
154
|
if (!redact || typeof redact !== 'object') return false;
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
+
const email = typeof redact.email === 'string' && redact.email.trim().includes('@');
|
|
156
|
+
const phone = typeof redact.phone === 'string' && redact.phone.trim() !== '';
|
|
157
|
+
return email || phone || (Array.isArray(redact.order_ids) && redact.order_ids.length > 0);
|
|
155
158
|
}
|
|
156
159
|
|
|
157
160
|
/** @param {unknown} message @param {{strict?: boolean}} [opts] */
|
package/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Kept in sync with package.json by a test.
|
|
2
|
-
export const VERSION = '0.
|
|
2
|
+
export const VERSION = '0.3.0';
|