@asteby/storefront 0.11.1 → 0.12.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/README.md +5 -0
- package/dist/cli.js +25 -2
- package/dist/fiscal-document.d.cts +2 -2
- package/dist/fiscal-document.d.ts +2 -2
- package/dist/index.cjs +59 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -3
- package/dist/index.d.ts +72 -3
- package/dist/index.js +57 -3
- package/dist/index.js.map +1 -1
- package/dist/mock.cjs +2 -2
- package/dist/mock.cjs.map +1 -1
- package/dist/mock.d.cts +1 -1
- package/dist/mock.d.ts +1 -1
- package/dist/mock.js +2 -2
- package/dist/mock.js.map +1 -1
- package/dist/mockup.d.cts +1 -1
- package/dist/mockup.d.ts +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.js +1 -1
- package/dist/{types-bsdlAumU.d.cts → types-BQ3S6VjF.d.cts} +19 -1
- package/dist/{types-B1E3GySa.d.ts → types-C_rsJRCl.d.ts} +19 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -263,6 +263,9 @@ const result = await sf.checkouts.pay(checkout.checkout.id, { method: 'card', pr
|
|
|
263
263
|
{ idempotencyKey: paymentIdempotencyKey(checkout.checkout.id, attemptId) }) // idempotencyKey is REQUIRED (throws before calling)
|
|
264
264
|
const outcome = paymentOutcome(result, 'es-MX') // approved | in_review | voucher | challenge | redirect | rejected | expired | cancelled
|
|
265
265
|
paymentErrorView(error) // network (retry same attempt) | price_mismatch | payment_in_progress | expired | too_many_attempts
|
|
266
|
+
classifyPaymentError(error) // { kind, retryable } WITHOUT texts; same rules in the server and the browser
|
|
267
|
+
// kinds: transient | in_progress | rate_limited | merchant_config | provider_declined | stale_checkout | expired | price_mismatch | store_closed | validation | other
|
|
268
|
+
// honours OPS `retryable:false` and the stable codes payment_provider_rejected / payment_provider_misconfigured / unavailable / invalid_state
|
|
266
269
|
```
|
|
267
270
|
|
|
268
271
|
- `declineMessage(code, locale)` / `paymentMessages(locale, overrides)`: actionable es/en texts; unknown codes become `declined`.
|
|
@@ -443,6 +446,8 @@ connectStore().keepFresh() // heartbeat; rotates (POST /api/conne
|
|
|
443
446
|
missing and where to repair them (`OPS → Conexiones y API → <conexión> → Reparar permisos`), showing `sk_…a91f` and never
|
|
444
447
|
the key. Repairing permissions does not change the key.
|
|
445
448
|
|
|
449
|
+
`StorefrontError` also keeps what OPS sends in `error.*`: `retryable`, `retryAfter`, `checkoutId`, `cost` (of a `price_mismatch`) and `messageEn`.
|
|
450
|
+
|
|
446
451
|
Errors: a `403 insufficient_scope` is a `StorefrontError` with `code: 'insufficient_scope'`, `status: 403` and
|
|
447
452
|
`requiredScopes: ['customer:write']`. `customer.capabilities()` reports `password: false`, `unavailable_reason:
|
|
448
453
|
'missing_scope'` and `missing_scopes` when the key cannot create accounts. The mock rehearses all of it with
|
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,16 @@ var StorefrontError = class extends Error {
|
|
|
15
15
|
url;
|
|
16
16
|
/** Scopes a `403 insufficient_scope` says the route needs (empty for any other error). */
|
|
17
17
|
requiredScopes;
|
|
18
|
+
/** `true`: repeating the request (same idempotency key) can succeed. `false`: not until someone fixes something. `undefined`: OPS did not say. */
|
|
19
|
+
retryable;
|
|
20
|
+
/** Seconds OPS asks to wait before retrying. */
|
|
21
|
+
retryAfter;
|
|
22
|
+
/** Checkout that a refused cart write can resume. */
|
|
23
|
+
checkoutId;
|
|
24
|
+
/** Current cart cost sent with a `price_mismatch` (untrusted shape). */
|
|
25
|
+
cost;
|
|
26
|
+
/** English version of `message`, when OPS sent one. */
|
|
27
|
+
messageEn;
|
|
18
28
|
constructor(init) {
|
|
19
29
|
super(init.message, init.cause === void 0 ? void 0 : { cause: init.cause });
|
|
20
30
|
this.status = init.status;
|
|
@@ -23,6 +33,11 @@ var StorefrontError = class extends Error {
|
|
|
23
33
|
this.details = init.details;
|
|
24
34
|
this.url = init.url;
|
|
25
35
|
this.requiredScopes = init.requiredScopes ?? [];
|
|
36
|
+
this.retryable = init.retryable;
|
|
37
|
+
this.retryAfter = init.retryAfter;
|
|
38
|
+
this.checkoutId = init.checkoutId;
|
|
39
|
+
this.cost = init.cost;
|
|
40
|
+
this.messageEn = init.messageEn;
|
|
26
41
|
}
|
|
27
42
|
/**
|
|
28
43
|
* Affected lines of a `409 insufficient_stock` (empty for any other error or a
|
|
@@ -41,7 +56,10 @@ var StorefrontError = class extends Error {
|
|
|
41
56
|
code: this.code,
|
|
42
57
|
message: this.message,
|
|
43
58
|
field: this.field,
|
|
44
|
-
...this.requiredScopes.length ? { requiredScopes: this.requiredScopes } : {}
|
|
59
|
+
...this.requiredScopes.length ? { requiredScopes: this.requiredScopes } : {},
|
|
60
|
+
...this.retryable !== void 0 ? { retryable: this.retryable } : {},
|
|
61
|
+
...this.retryAfter !== void 0 ? { retryAfter: this.retryAfter } : {},
|
|
62
|
+
...this.checkoutId ? { checkoutId: this.checkoutId } : {}
|
|
45
63
|
};
|
|
46
64
|
}
|
|
47
65
|
};
|
|
@@ -169,6 +187,11 @@ async function readError(res, url) {
|
|
|
169
187
|
message: e?.message ?? `Storefront API responded ${res.status}${res.statusText ? " " + res.statusText : ""}`,
|
|
170
188
|
...e?.field ? { field: e.field } : {},
|
|
171
189
|
...e?.details !== void 0 ? { details: e.details } : {},
|
|
190
|
+
...typeof e?.retryable === "boolean" ? { retryable: e.retryable } : {},
|
|
191
|
+
...typeof e?.retry_after === "number" && e.retry_after > 0 ? { retryAfter: e.retry_after } : {},
|
|
192
|
+
...typeof e?.checkout_id === "string" && e.checkout_id ? { checkoutId: e.checkout_id } : {},
|
|
193
|
+
...e?.cost !== void 0 ? { cost: e.cost } : {},
|
|
194
|
+
...typeof e?.message_en === "string" && e.message_en ? { messageEn: e.message_en } : {},
|
|
172
195
|
url
|
|
173
196
|
});
|
|
174
197
|
}
|
|
@@ -464,7 +487,7 @@ function repairPath(connectionName) {
|
|
|
464
487
|
import { chmodSync, closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeSync } from "fs";
|
|
465
488
|
import { dirname, join, resolve } from "path";
|
|
466
489
|
import { hostname } from "os";
|
|
467
|
-
var SDK_VERSION = true ? "0.
|
|
490
|
+
var SDK_VERSION = true ? "0.12.0" : "dev";
|
|
468
491
|
function writeFileAtomic(file, content, mode = 384) {
|
|
469
492
|
const dir = dirname(file);
|
|
470
493
|
mkdirSync(dir, { recursive: true });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { U as FiscalDocumentSource, aR as RegionFiscalEntry, Z as IdentityDocumentSpec, T as FiscalDocumentEvidence, aS as RegionFiscalId, $ as IdentityRulesV1 } from './types-
|
|
2
|
-
export { Y as IdentityCapture, _ as IdentityFieldRule, a0 as IdentityStrategy } from './types-
|
|
1
|
+
import { U as FiscalDocumentSource, aR as RegionFiscalEntry, Z as IdentityDocumentSpec, T as FiscalDocumentEvidence, aS as RegionFiscalId, $ as IdentityRulesV1 } from './types-BQ3S6VjF.cjs';
|
|
2
|
+
export { Y as IdentityCapture, _ as IdentityFieldRule, a0 as IdentityStrategy } from './types-BQ3S6VjF.cjs';
|
|
3
3
|
import './geo.cjs';
|
|
4
4
|
|
|
5
5
|
/** A value read from a document: text, or the codes of a catalog (`multiple` fields). */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { U as FiscalDocumentSource, aR as RegionFiscalEntry, Z as IdentityDocumentSpec, T as FiscalDocumentEvidence, aS as RegionFiscalId, $ as IdentityRulesV1 } from './types-
|
|
2
|
-
export { Y as IdentityCapture, _ as IdentityFieldRule, a0 as IdentityStrategy } from './types-
|
|
1
|
+
import { U as FiscalDocumentSource, aR as RegionFiscalEntry, Z as IdentityDocumentSpec, T as FiscalDocumentEvidence, aS as RegionFiscalId, $ as IdentityRulesV1 } from './types-C_rsJRCl.js';
|
|
2
|
+
export { Y as IdentityCapture, _ as IdentityFieldRule, a0 as IdentityStrategy } from './types-C_rsJRCl.js';
|
|
3
3
|
import './geo.js';
|
|
4
4
|
|
|
5
5
|
/** A value read from a document: text, or the codes of a catalog (`multiple` fields). */
|
package/dist/index.cjs
CHANGED
|
@@ -10,6 +10,16 @@ var StorefrontError = class extends Error {
|
|
|
10
10
|
url;
|
|
11
11
|
/** Scopes a `403 insufficient_scope` says the route needs (empty for any other error). */
|
|
12
12
|
requiredScopes;
|
|
13
|
+
/** `true`: repeating the request (same idempotency key) can succeed. `false`: not until someone fixes something. `undefined`: OPS did not say. */
|
|
14
|
+
retryable;
|
|
15
|
+
/** Seconds OPS asks to wait before retrying. */
|
|
16
|
+
retryAfter;
|
|
17
|
+
/** Checkout that a refused cart write can resume. */
|
|
18
|
+
checkoutId;
|
|
19
|
+
/** Current cart cost sent with a `price_mismatch` (untrusted shape). */
|
|
20
|
+
cost;
|
|
21
|
+
/** English version of `message`, when OPS sent one. */
|
|
22
|
+
messageEn;
|
|
13
23
|
constructor(init) {
|
|
14
24
|
super(init.message, init.cause === void 0 ? void 0 : { cause: init.cause });
|
|
15
25
|
this.status = init.status;
|
|
@@ -18,6 +28,11 @@ var StorefrontError = class extends Error {
|
|
|
18
28
|
this.details = init.details;
|
|
19
29
|
this.url = init.url;
|
|
20
30
|
this.requiredScopes = init.requiredScopes ?? [];
|
|
31
|
+
this.retryable = init.retryable;
|
|
32
|
+
this.retryAfter = init.retryAfter;
|
|
33
|
+
this.checkoutId = init.checkoutId;
|
|
34
|
+
this.cost = init.cost;
|
|
35
|
+
this.messageEn = init.messageEn;
|
|
21
36
|
}
|
|
22
37
|
/**
|
|
23
38
|
* Affected lines of a `409 insufficient_stock` (empty for any other error or a
|
|
@@ -36,7 +51,10 @@ var StorefrontError = class extends Error {
|
|
|
36
51
|
code: this.code,
|
|
37
52
|
message: this.message,
|
|
38
53
|
field: this.field,
|
|
39
|
-
...this.requiredScopes.length ? { requiredScopes: this.requiredScopes } : {}
|
|
54
|
+
...this.requiredScopes.length ? { requiredScopes: this.requiredScopes } : {},
|
|
55
|
+
...this.retryable !== void 0 ? { retryable: this.retryable } : {},
|
|
56
|
+
...this.retryAfter !== void 0 ? { retryAfter: this.retryAfter } : {},
|
|
57
|
+
...this.checkoutId ? { checkoutId: this.checkoutId } : {}
|
|
40
58
|
};
|
|
41
59
|
}
|
|
42
60
|
};
|
|
@@ -201,6 +219,11 @@ async function readError(res, url) {
|
|
|
201
219
|
message: e?.message ?? `Storefront API responded ${res.status}${res.statusText ? " " + res.statusText : ""}`,
|
|
202
220
|
...e?.field ? { field: e.field } : {},
|
|
203
221
|
...e?.details !== void 0 ? { details: e.details } : {},
|
|
222
|
+
...typeof e?.retryable === "boolean" ? { retryable: e.retryable } : {},
|
|
223
|
+
...typeof e?.retry_after === "number" && e.retry_after > 0 ? { retryAfter: e.retry_after } : {},
|
|
224
|
+
...typeof e?.checkout_id === "string" && e.checkout_id ? { checkoutId: e.checkout_id } : {},
|
|
225
|
+
...e?.cost !== void 0 ? { cost: e.cost } : {},
|
|
226
|
+
...typeof e?.message_en === "string" && e.message_en ? { messageEn: e.message_en } : {},
|
|
204
227
|
url
|
|
205
228
|
});
|
|
206
229
|
}
|
|
@@ -482,6 +505,35 @@ function storefrontDomainHeader(domain, market) {
|
|
|
482
505
|
return code && /^[a-z0-9_-]{1,32}$/.test(code) ? `${host}#${code}` : host;
|
|
483
506
|
}
|
|
484
507
|
|
|
508
|
+
// src/payment-errors.ts
|
|
509
|
+
function opsSaysNotRetryable(err) {
|
|
510
|
+
if (err.retryable === false) return true;
|
|
511
|
+
const d = err.details;
|
|
512
|
+
return typeof d === "object" && d !== null && d.retryable === false;
|
|
513
|
+
}
|
|
514
|
+
var PAYMENT_CHOICE_REFUSED_FIELD = "payment_choice";
|
|
515
|
+
var MERCHANT_CONFIG_CODE = /credential|not_configured|unconfigured|invalid_access|access_token|provider_config|merchant|invalid_public_key|unauthorized/i;
|
|
516
|
+
function classifyPaymentError(err) {
|
|
517
|
+
const status = typeof err.status === "number" ? err.status : 0;
|
|
518
|
+
const code = typeof err.code === "string" ? err.code : "";
|
|
519
|
+
if (code === "store_not_ready") return { kind: "store_closed", retryable: false };
|
|
520
|
+
if (code === "price_mismatch" || status === 422 && err.field === "expected_total") return { kind: "price_mismatch", retryable: false };
|
|
521
|
+
if (status === 409 && (code === "payment_in_progress" || err.field === "payment_in_progress")) return { kind: "in_progress", retryable: true };
|
|
522
|
+
if (status === 409 && code === "expired") return { kind: "expired", retryable: false };
|
|
523
|
+
if (status === 429 || code === "too_many_attempts" || code === "rate_limited") return { kind: "rate_limited", retryable: true };
|
|
524
|
+
if (status === 422 && code === "invalid_state") {
|
|
525
|
+
if (err.field === PAYMENT_CHOICE_REFUSED_FIELD) return { kind: "other", retryable: false };
|
|
526
|
+
return { kind: "stale_checkout", retryable: false };
|
|
527
|
+
}
|
|
528
|
+
if (code === "payment_provider_misconfigured") return { kind: "merchant_config", retryable: false };
|
|
529
|
+
if (code === "payment_provider_rejected") return { kind: "provider_declined", retryable: false };
|
|
530
|
+
if (MERCHANT_CONFIG_CODE.test(code) || status === 401 || status === 403) return { kind: "merchant_config", retryable: false };
|
|
531
|
+
if (status >= 500 && opsSaysNotRetryable(err)) return { kind: "other", retryable: false };
|
|
532
|
+
if (status === 0 || status >= 500 || code === "network" || code === "timeout" || code === "unavailable") return { kind: "transient", retryable: true };
|
|
533
|
+
if (status === 400 || code === "validation") return { kind: "validation", retryable: false };
|
|
534
|
+
return { kind: "other", retryable: false };
|
|
535
|
+
}
|
|
536
|
+
|
|
485
537
|
// src/diagnose.ts
|
|
486
538
|
function maskToken(token) {
|
|
487
539
|
if (!token) return "(sin token)";
|
|
@@ -944,7 +996,9 @@ function paymentErrorView(err, locale, overrides) {
|
|
|
944
996
|
if (status === 429 || code === "too_many_attempts") {
|
|
945
997
|
return { kind: "too_many_attempts", message: m["error.too_many_attempts"], retrySameAttempt: false, canRetry: false };
|
|
946
998
|
}
|
|
947
|
-
|
|
999
|
+
const declared = e.retryable;
|
|
1000
|
+
if (status >= 500 && declared === false) return { kind: "generic", message: m["error.generic"], retrySameAttempt: false, canRetry: true };
|
|
1001
|
+
if (status === 0 || status === 502 || status === 503 || status === 504 || code === "network" || code === "timeout" || status >= 500) {
|
|
948
1002
|
return { kind: "network", message: m["error.network"], retrySameAttempt: true, canRetry: true };
|
|
949
1003
|
}
|
|
950
1004
|
return { kind: "generic", message: m["error.generic"], retrySameAttempt: false, canRetry: true };
|
|
@@ -1247,6 +1301,7 @@ exports.CLIENT_IP_HEADER = CLIENT_IP_HEADER;
|
|
|
1247
1301
|
exports.COUNTRY_HEADERS = COUNTRY_HEADERS;
|
|
1248
1302
|
exports.MISSING_WEIGHT = MISSING_WEIGHT;
|
|
1249
1303
|
exports.NOTE_MAX_LENGTH = NOTE_MAX_LENGTH;
|
|
1304
|
+
exports.PAYMENT_CHOICE_REFUSED_FIELD = PAYMENT_CHOICE_REFUSED_FIELD;
|
|
1250
1305
|
exports.StorefrontError = StorefrontError;
|
|
1251
1306
|
exports.appendParams = appendParams;
|
|
1252
1307
|
exports.availabilityLocale = availabilityLocale;
|
|
@@ -1254,6 +1309,7 @@ exports.availabilityMessages = availabilityMessages;
|
|
|
1254
1309
|
exports.availabilityView = availabilityView;
|
|
1255
1310
|
exports.cartLineProblemView = cartLineProblemView;
|
|
1256
1311
|
exports.clampPercent = clampPercent;
|
|
1312
|
+
exports.classifyPaymentError = classifyPaymentError;
|
|
1257
1313
|
exports.createStorefrontClient = createStorefrontClient;
|
|
1258
1314
|
exports.declineMessage = declineMessage;
|
|
1259
1315
|
exports.declineMessageKey = declineMessageKey;
|
|
@@ -1271,6 +1327,7 @@ exports.maskToken = maskToken;
|
|
|
1271
1327
|
exports.negotiateLocale = negotiateLocale;
|
|
1272
1328
|
exports.normalizeClientIp = normalizeClientIp;
|
|
1273
1329
|
exports.normalizeNote = normalizeNote;
|
|
1330
|
+
exports.opsSaysNotRetryable = opsSaysNotRetryable;
|
|
1274
1331
|
exports.parseDelivery = parseDelivery;
|
|
1275
1332
|
exports.parseMoney = parseMoney;
|
|
1276
1333
|
exports.parseProductFilters = parseProductFilters;
|