@01.software/sdk 0.49.0 → 0.56.1
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 +257 -1
- package/MIGRATION.md +56 -0
- package/README.md +269 -56
- package/dist/client.cjs +608 -190
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +6 -6
- package/dist/client.d.ts +6 -6
- package/dist/client.js +608 -190
- package/dist/client.js.map +1 -1
- package/dist/{collection-client-DnDt6EQz.d.ts → collection-client-Bmr53saa.d.ts} +6 -4
- package/dist/{collection-client-XfGLPuTf.d.cts → collection-client-DxuBDso_.d.cts} +6 -4
- package/dist/{const-CBba4lDo.d.cts → const-BKUqg96Q.d.ts} +5 -5
- package/dist/{const-DCNV5Kq7.d.ts → const-Cv1DCsp6.d.cts} +5 -5
- package/dist/embedded-admin/server.cjs +306 -0
- package/dist/embedded-admin/server.cjs.map +1 -0
- package/dist/embedded-admin/server.d.cts +33 -0
- package/dist/embedded-admin/server.d.ts +33 -0
- package/dist/embedded-admin/server.js +288 -0
- package/dist/embedded-admin/server.js.map +1 -0
- package/dist/embedded-admin.cjs +400 -0
- package/dist/embedded-admin.cjs.map +1 -0
- package/dist/embedded-admin.d.cts +61 -0
- package/dist/embedded-admin.d.ts +61 -0
- package/dist/embedded-admin.js +377 -0
- package/dist/embedded-admin.js.map +1 -0
- package/dist/errors.cjs +1 -0
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +3 -3
- package/dist/errors.d.ts +3 -3
- package/dist/errors.js +1 -0
- package/dist/errors.js.map +1 -1
- package/dist/{index-DUuIqUS8.d.ts → index-CGsT4AQ8.d.cts} +89 -10
- package/dist/{index-CbKyoEzy.d.cts → index-DFHLLqYu.d.ts} +89 -10
- package/dist/index.cjs +752 -436
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -69
- package/dist/index.d.ts +12 -69
- package/dist/index.js +752 -436
- package/dist/index.js.map +1 -1
- package/dist/{payload-types-MKbx5MuJ.d.cts → payload-types-Bk-duc5k.d.cts} +545 -141
- package/dist/{payload-types-MKbx5MuJ.d.ts → payload-types-Bk-duc5k.d.ts} +545 -141
- package/dist/query.cjs +108 -71
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.cts +5 -5
- package/dist/query.d.ts +5 -5
- package/dist/query.js +108 -71
- package/dist/query.js.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/server.cjs +502 -175
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +9 -8
- package/dist/server.d.ts +9 -8
- package/dist/server.js +502 -175
- package/dist/server.js.map +1 -1
- package/dist/storefront-cache.cjs +1 -0
- package/dist/storefront-cache.cjs.map +1 -1
- package/dist/storefront-cache.js +1 -0
- package/dist/storefront-cache.js.map +1 -1
- package/dist/{types-1UeOk1Uy.d.cts → types-CEK8uTvU.d.ts} +287 -30
- package/dist/{types-BKr_2avj.d.cts → types-CNdpj2fI.d.cts} +2 -2
- package/dist/{types-L_nh-ED7.d.ts → types-D4mjGFj4.d.ts} +2 -2
- package/dist/{types-Du4s5anw.d.ts → types-Dhk5NX7Z.d.cts} +287 -30
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/video.d.cts +1 -1
- package/dist/ui/video.d.ts +1 -1
- package/dist/webhook.cjs +135 -80
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +21 -5
- package/dist/webhook.d.ts +21 -5
- package/dist/webhook.js +135 -80
- package/dist/webhook.js.map +1 -1
- package/package.json +28 -6
package/dist/client.js
CHANGED
|
@@ -97,6 +97,7 @@ var COMMERCE_ERROR_CODES = [
|
|
|
97
97
|
"payment_session_not_cancelable",
|
|
98
98
|
"payment_session_conflict",
|
|
99
99
|
"provider_identity_mismatch",
|
|
100
|
+
"managed_refund_attestation_forbidden",
|
|
100
101
|
"payment_method_unavailable",
|
|
101
102
|
"provider_unavailable",
|
|
102
103
|
"lock_timeout",
|
|
@@ -639,11 +640,11 @@ function parseRetryAfter(response) {
|
|
|
639
640
|
permitsAutomaticRetry: seconds <= MAX_AUTOMATIC_RETRY_AFTER_SECONDS
|
|
640
641
|
};
|
|
641
642
|
}
|
|
642
|
-
function mapResponseToError(status, parsed, details, requestId, retryAfter,
|
|
643
|
+
function mapResponseToError(status, parsed, details, requestId, retryAfter, canonicalCommerceError) {
|
|
643
644
|
const errorDetails = {
|
|
644
645
|
...details,
|
|
645
646
|
...parsed.errors && { errors: parsed.errors },
|
|
646
|
-
|
|
647
|
+
...!canonicalCommerceError && parsed.body && { body: parsed.body }
|
|
647
648
|
};
|
|
648
649
|
const suggestion = getParsedErrorSuggestion(status, parsed);
|
|
649
650
|
const finalize = (error) => {
|
|
@@ -651,7 +652,7 @@ function mapResponseToError(status, parsed, details, requestId, retryAfter, erro
|
|
|
651
652
|
if (retryAfter !== void 0) {
|
|
652
653
|
Object.assign(withRequestId, { retryAfter });
|
|
653
654
|
}
|
|
654
|
-
return
|
|
655
|
+
return canonicalCommerceError ? enhanceCommerceError(withRequestId, parsed.body, requestId) : enhanceNonCommerceError(withRequestId, parsed.reason);
|
|
655
656
|
};
|
|
656
657
|
if (status === 400 || status === 422) {
|
|
657
658
|
return finalize(
|
|
@@ -757,6 +758,7 @@ async function httpFetch(url, options) {
|
|
|
757
758
|
retry,
|
|
758
759
|
onUnauthorized,
|
|
759
760
|
errorFamily,
|
|
761
|
+
typedCommerceErrorReasons,
|
|
760
762
|
...requestInit
|
|
761
763
|
} = options || {};
|
|
762
764
|
const baseUrl = resolveApiUrl(apiUrl);
|
|
@@ -810,7 +812,8 @@ async function httpFetch(url, options) {
|
|
|
810
812
|
});
|
|
811
813
|
if (!response.ok) {
|
|
812
814
|
const parsed = await parseErrorBody(response);
|
|
813
|
-
const
|
|
815
|
+
const canonicalCommerceError = errorFamily === "commerce" || typeof parsed.reason === "string" && typedCommerceErrorReasons?.includes(parsed.reason) === true;
|
|
816
|
+
const commerceEnvelope = canonicalCommerceError ? parseCommerceErrorEnvelope(parsed.body, requestId) : void 0;
|
|
814
817
|
if (isUsageLimitExceededResponse(response)) {
|
|
815
818
|
const limit = parseInt(
|
|
816
819
|
response.headers.get("X-Usage-Limit") || "0",
|
|
@@ -838,7 +841,7 @@ async function httpFetch(url, options) {
|
|
|
838
841
|
),
|
|
839
842
|
requestId
|
|
840
843
|
);
|
|
841
|
-
throw
|
|
844
|
+
throw canonicalCommerceError ? enhanceCommerceError(usageError, parsed.body, requestId) : enhanceNonCommerceError(usageError, parsed.reason);
|
|
842
845
|
}
|
|
843
846
|
if (response.status === 401 && onUnauthorized && customerToken && !hasRetried401 && (commerceEnvelope?.reason ?? parsed.reason) === "token_expired") {
|
|
844
847
|
hasRetried401 = true;
|
|
@@ -863,7 +866,7 @@ async function httpFetch(url, options) {
|
|
|
863
866
|
details,
|
|
864
867
|
requestId,
|
|
865
868
|
retryAfter.seconds,
|
|
866
|
-
|
|
869
|
+
canonicalCommerceError
|
|
867
870
|
);
|
|
868
871
|
const method2 = (requestInit.method || "GET").toUpperCase();
|
|
869
872
|
const isTerminal = error instanceof RateLimitError || error instanceof UsageLimitError;
|
|
@@ -1041,15 +1044,15 @@ var HttpClient = class {
|
|
|
1041
1044
|
throw err;
|
|
1042
1045
|
}
|
|
1043
1046
|
}
|
|
1044
|
-
buildUrl(
|
|
1045
|
-
if (!options) return
|
|
1047
|
+
buildUrl(endpoint, options) {
|
|
1048
|
+
if (!options) return endpoint;
|
|
1046
1049
|
const { fallbackLocale, ...rest } = options;
|
|
1047
1050
|
const queryOptions = fallbackLocale === void 0 ? rest : {
|
|
1048
1051
|
...rest,
|
|
1049
1052
|
"fallback-locale": fallbackLocale === false ? "none" : fallbackLocale
|
|
1050
1053
|
};
|
|
1051
1054
|
const queryString = stringify(queryOptions, { addQueryPrefix: true });
|
|
1052
|
-
return queryString ? `${
|
|
1055
|
+
return queryString ? `${endpoint}${queryString}` : endpoint;
|
|
1053
1056
|
}
|
|
1054
1057
|
assertJsonResponse(response) {
|
|
1055
1058
|
const contentType = response.headers.get("content-type");
|
|
@@ -1191,39 +1194,39 @@ var ReadOnlyCollectionClient = class extends HttpClient {
|
|
|
1191
1194
|
from(collection) {
|
|
1192
1195
|
return new ReadOnlyCollectionQueryBuilder(this, collection);
|
|
1193
1196
|
}
|
|
1194
|
-
async requestFind(
|
|
1195
|
-
const url = this.buildUrl(
|
|
1197
|
+
async requestFind(endpoint, options) {
|
|
1198
|
+
const url = this.buildUrl(endpoint, options);
|
|
1196
1199
|
const response = await this.fetchWithTracking(url, {
|
|
1197
1200
|
...this.defaultOptions,
|
|
1198
1201
|
method: "GET"
|
|
1199
1202
|
});
|
|
1200
1203
|
return this.parseFindResponse(response);
|
|
1201
1204
|
}
|
|
1202
|
-
async requestFindEndpoint(
|
|
1203
|
-
const response = await this.fetchWithTracking(
|
|
1205
|
+
async requestFindEndpoint(endpoint, data) {
|
|
1206
|
+
const response = await this.fetchWithTracking(endpoint, {
|
|
1204
1207
|
...this.defaultOptions,
|
|
1205
1208
|
method: "POST",
|
|
1206
1209
|
body: data ? JSON.stringify(data) : void 0
|
|
1207
1210
|
});
|
|
1208
1211
|
return this.parseFindResponse(response);
|
|
1209
1212
|
}
|
|
1210
|
-
async requestFindEndpointGet(
|
|
1211
|
-
const response = await this.fetchWithTracking(
|
|
1213
|
+
async requestFindEndpointGet(endpoint) {
|
|
1214
|
+
const response = await this.fetchWithTracking(endpoint, {
|
|
1212
1215
|
...this.defaultOptions,
|
|
1213
1216
|
method: "GET"
|
|
1214
1217
|
});
|
|
1215
1218
|
return this.parseFindResponse(response);
|
|
1216
1219
|
}
|
|
1217
|
-
async requestFindById(
|
|
1218
|
-
const url = this.buildUrl(
|
|
1220
|
+
async requestFindById(endpoint, options) {
|
|
1221
|
+
const url = this.buildUrl(endpoint, options);
|
|
1219
1222
|
const response = await this.fetchWithTracking(url, {
|
|
1220
1223
|
...this.defaultOptions,
|
|
1221
1224
|
method: "GET"
|
|
1222
1225
|
});
|
|
1223
1226
|
return this.parseDocumentResponse(response);
|
|
1224
1227
|
}
|
|
1225
|
-
async requestCount(
|
|
1226
|
-
const url = this.buildUrl(
|
|
1228
|
+
async requestCount(endpoint, options) {
|
|
1229
|
+
const url = this.buildUrl(endpoint, options);
|
|
1227
1230
|
const response = await this.fetchWithTracking(url, {
|
|
1228
1231
|
...this.defaultOptions,
|
|
1229
1232
|
method: "GET"
|
|
@@ -1233,7 +1236,7 @@ var ReadOnlyCollectionClient = class extends HttpClient {
|
|
|
1233
1236
|
};
|
|
1234
1237
|
|
|
1235
1238
|
// src/core/api/parse-response.ts
|
|
1236
|
-
async function parseApiResponse(response,
|
|
1239
|
+
async function parseApiResponse(response, endpoint, options = {}) {
|
|
1237
1240
|
if (response.status === 204 || response.headers.get("content-length") === "0") {
|
|
1238
1241
|
return void 0;
|
|
1239
1242
|
}
|
|
@@ -1242,7 +1245,7 @@ async function parseApiResponse(response, endpoint2, options = {}) {
|
|
|
1242
1245
|
parsed = await response.json();
|
|
1243
1246
|
} catch {
|
|
1244
1247
|
throw createApiError(
|
|
1245
|
-
`Invalid JSON response from ${
|
|
1248
|
+
`Invalid JSON response from ${endpoint}`,
|
|
1246
1249
|
response.status,
|
|
1247
1250
|
void 0,
|
|
1248
1251
|
"Server returned an invalid response.",
|
|
@@ -1292,6 +1295,7 @@ async function parseApiResponse(response, endpoint2, options = {}) {
|
|
|
1292
1295
|
|
|
1293
1296
|
// ../../internal/http-contracts/src/index.ts
|
|
1294
1297
|
var operationBrand = /* @__PURE__ */ Symbol("sdk-http-operation");
|
|
1298
|
+
var PORTONE_STANDARD_ACTION_CAPABILITIES_MEDIA_TYPE = "application/vnd.01software.portone-action-capabilities.v1+json";
|
|
1295
1299
|
var operation = (collection, method, path, errorFamily) => Object.freeze({
|
|
1296
1300
|
[operationBrand]: true,
|
|
1297
1301
|
collection,
|
|
@@ -1300,6 +1304,10 @@ var operation = (collection, method, path, errorFamily) => Object.freeze({
|
|
|
1300
1304
|
...errorFamily && { errorFamily }
|
|
1301
1305
|
});
|
|
1302
1306
|
var strictCommerceOperation = (collection, method, path) => operation(collection, method, path, "commerce");
|
|
1307
|
+
var selectiveCommerceOperation = (collection, method, path, typedCommerceErrorReasons) => Object.freeze({
|
|
1308
|
+
...operation(collection, method, path),
|
|
1309
|
+
typedCommerceErrorReasons: Object.freeze([...typedCommerceErrorReasons])
|
|
1310
|
+
});
|
|
1303
1311
|
var COMMERCE_ENDPOINT_OPERATIONS = {
|
|
1304
1312
|
products: {
|
|
1305
1313
|
detail: operation("products", "get", "/detail"),
|
|
@@ -1331,7 +1339,12 @@ var COMMERCE_ENDPOINT_OPERATIONS = {
|
|
|
1331
1339
|
confirmPayment: operation("orders", "post", "/confirm-payment"),
|
|
1332
1340
|
byPayment: operation("orders", "get", "/by-payment"),
|
|
1333
1341
|
cancel: operation("orders", "post", "/cancel"),
|
|
1334
|
-
resolveCancelRefund:
|
|
1342
|
+
resolveCancelRefund: selectiveCommerceOperation(
|
|
1343
|
+
"orders",
|
|
1344
|
+
"post",
|
|
1345
|
+
"/resolve-cancel-refund",
|
|
1346
|
+
["managed_refund_attestation_forbidden"]
|
|
1347
|
+
),
|
|
1335
1348
|
checkout: strictCommerceOperation("orders", "post", "/checkout"),
|
|
1336
1349
|
createFulfillment: operation("orders", "post", "/create-fulfillment"),
|
|
1337
1350
|
updateFulfillment: operation("orders", "post", "/update-fulfillment")
|
|
@@ -1340,6 +1353,11 @@ var COMMERCE_ENDPOINT_OPERATIONS = {
|
|
|
1340
1353
|
detail: strictCommerceOperation("checkouts", "post", "/detail"),
|
|
1341
1354
|
query: strictCommerceOperation("checkouts", "post", "/query")
|
|
1342
1355
|
},
|
|
1356
|
+
draftOrders: {
|
|
1357
|
+
calculate: operation("draft-orders", "post", "/:id/calculate"),
|
|
1358
|
+
complete: operation("draft-orders", "post", "/:id/complete"),
|
|
1359
|
+
finalize: operation("draft-orders", "post", "/:id/finalize")
|
|
1360
|
+
},
|
|
1343
1361
|
transactions: {
|
|
1344
1362
|
update: operation("transactions", "post", "/update")
|
|
1345
1363
|
},
|
|
@@ -1402,6 +1420,16 @@ var COMMERCE_ENDPOINT_OPERATIONS = {
|
|
|
1402
1420
|
"payment-sessions",
|
|
1403
1421
|
"post",
|
|
1404
1422
|
"/server/confirm"
|
|
1423
|
+
),
|
|
1424
|
+
findPortOne: strictCommerceOperation(
|
|
1425
|
+
"payment-sessions",
|
|
1426
|
+
"post",
|
|
1427
|
+
"/server/portone/find"
|
|
1428
|
+
),
|
|
1429
|
+
reconcilePortOne: strictCommerceOperation(
|
|
1430
|
+
"payment-sessions",
|
|
1431
|
+
"post",
|
|
1432
|
+
"/server/portone/reconcile"
|
|
1405
1433
|
)
|
|
1406
1434
|
},
|
|
1407
1435
|
custom: {
|
|
@@ -1421,12 +1449,34 @@ var COMMERCE_ENDPOINT_OPERATIONS = {
|
|
|
1421
1449
|
validate: operation("discounts", "post", "/validate")
|
|
1422
1450
|
}
|
|
1423
1451
|
};
|
|
1452
|
+
var portOneStandardActionCapabilityOperations = /* @__PURE__ */ new Set([
|
|
1453
|
+
COMMERCE_ENDPOINT_OPERATIONS.paymentSessions.create,
|
|
1454
|
+
COMMERCE_ENDPOINT_OPERATIONS.paymentSessions.retrieve,
|
|
1455
|
+
COMMERCE_ENDPOINT_OPERATIONS.paymentSessions.server.create,
|
|
1456
|
+
COMMERCE_ENDPOINT_OPERATIONS.paymentSessions.server.retrieve
|
|
1457
|
+
]);
|
|
1424
1458
|
var MEMBERSHIP_ENDPOINT_OPERATIONS = {
|
|
1425
1459
|
subscribe: operation(null, "post", "/memberships/subscribe"),
|
|
1426
1460
|
cancel: operation(null, "post", "/memberships/:id/cancel"),
|
|
1427
|
-
me: operation(null, "get", "/memberships/me")
|
|
1461
|
+
me: operation(null, "get", "/memberships/me"),
|
|
1462
|
+
chargeResult: operation(null, "post", "/memberships/:id/charge-result"),
|
|
1463
|
+
refundResult: operation(null, "post", "/memberships/:id/refund-result"),
|
|
1464
|
+
revoke: operation(null, "post", "/memberships/:id/revoke")
|
|
1465
|
+
};
|
|
1466
|
+
var CUSTOMER_GROUP_ENDPOINT_OPERATIONS = {
|
|
1467
|
+
members: operation("customer-groups", "get", "/:id/members"),
|
|
1468
|
+
grant: operation("customer-groups", "post", "/:id/members/grant"),
|
|
1469
|
+
revoke: operation("customer-groups", "post", "/:id/members/revoke")
|
|
1470
|
+
};
|
|
1471
|
+
var EMBEDDED_ADMIN_ENDPOINT_OPERATIONS = {
|
|
1472
|
+
show: operation("tenants", "get", "/embedded-admin-app"),
|
|
1473
|
+
configure: operation("tenants", "patch", "/embedded-admin-app"),
|
|
1474
|
+
disable: operation("tenants", "delete", "/embedded-admin-app")
|
|
1428
1475
|
};
|
|
1429
1476
|
var EVENTS_ENDPOINT_OPERATIONS = {
|
|
1477
|
+
events: {
|
|
1478
|
+
convertSchedule: operation("events", "post", "/:id/schedule")
|
|
1479
|
+
},
|
|
1430
1480
|
occurrences: {
|
|
1431
1481
|
range: operation("event-occurrences", "get", "/range"),
|
|
1432
1482
|
admissionAvailability: operation(
|
|
@@ -1467,46 +1517,51 @@ function resolveOperationMethod(descriptor) {
|
|
|
1467
1517
|
}
|
|
1468
1518
|
|
|
1469
1519
|
// src/core/api/endpoints.ts
|
|
1470
|
-
var endpoint = (method, path) => ({ method, path });
|
|
1471
1520
|
var sharedEndpoint = (descriptor) => descriptor;
|
|
1472
|
-
function resolveOperationMethod2(descriptor) {
|
|
1473
|
-
return "collection" in descriptor ? resolveOperationMethod(descriptor) : descriptor.method;
|
|
1474
|
-
}
|
|
1475
1521
|
function resolveOperationErrorFamily(descriptor) {
|
|
1476
|
-
return
|
|
1477
|
-
}
|
|
1478
|
-
function resolveOperationPath2(descriptor, options) {
|
|
1479
|
-
if ("collection" in descriptor) {
|
|
1480
|
-
return resolveOperationPath(descriptor, options);
|
|
1481
|
-
}
|
|
1482
|
-
const query = options?.query?.toString().replace(/^\?/, "");
|
|
1483
|
-
return query ? `${descriptor.path}?${query}` : descriptor.path;
|
|
1522
|
+
return descriptor.errorFamily;
|
|
1484
1523
|
}
|
|
1485
1524
|
var COMMERCE_ENDPOINTS = {
|
|
1486
1525
|
products: {
|
|
1487
|
-
detail:
|
|
1488
|
-
detailCatalog:
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1526
|
+
detail: sharedEndpoint(COMMERCE_ENDPOINT_OPERATIONS.products.detail),
|
|
1527
|
+
detailCatalog: sharedEndpoint(
|
|
1528
|
+
COMMERCE_ENDPOINT_OPERATIONS.products.detailCatalog
|
|
1529
|
+
),
|
|
1530
|
+
listingGroups: sharedEndpoint(
|
|
1531
|
+
COMMERCE_ENDPOINT_OPERATIONS.products.listingGroups
|
|
1532
|
+
),
|
|
1533
|
+
listingGroupsCatalog: sharedEndpoint(
|
|
1534
|
+
COMMERCE_ENDPOINT_OPERATIONS.products.listingGroupsCatalog
|
|
1535
|
+
),
|
|
1536
|
+
// The manifest keeps the GET read and the POST body-query apart; this
|
|
1537
|
+
// semantic name has always resolved to the GET one.
|
|
1538
|
+
listingGroupsQuery: sharedEndpoint(
|
|
1539
|
+
COMMERCE_ENDPOINT_OPERATIONS.products.listingGroupsQueryGet
|
|
1540
|
+
),
|
|
1541
|
+
listingGroupsQueryCatalog: sharedEndpoint(
|
|
1542
|
+
COMMERCE_ENDPOINT_OPERATIONS.products.listingGroupsQueryCatalog
|
|
1493
1543
|
),
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
"GET",
|
|
1497
|
-
"/api/products/listing-groups/query/catalog"
|
|
1544
|
+
stockCheck: sharedEndpoint(
|
|
1545
|
+
COMMERCE_ENDPOINT_OPERATIONS.products.stockCheck
|
|
1498
1546
|
),
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1547
|
+
stockSnapshot: sharedEndpoint(
|
|
1548
|
+
COMMERCE_ENDPOINT_OPERATIONS.products.stockSnapshot
|
|
1549
|
+
),
|
|
1550
|
+
upsert: sharedEndpoint(COMMERCE_ENDPOINT_OPERATIONS.products.upsert)
|
|
1502
1551
|
},
|
|
1503
1552
|
orders: {
|
|
1504
|
-
update:
|
|
1505
|
-
byPayment:
|
|
1506
|
-
cancel:
|
|
1507
|
-
resolveCancelRefund:
|
|
1508
|
-
|
|
1509
|
-
|
|
1553
|
+
update: sharedEndpoint(COMMERCE_ENDPOINT_OPERATIONS.orders.update),
|
|
1554
|
+
byPayment: sharedEndpoint(COMMERCE_ENDPOINT_OPERATIONS.orders.byPayment),
|
|
1555
|
+
cancel: sharedEndpoint(COMMERCE_ENDPOINT_OPERATIONS.orders.cancel),
|
|
1556
|
+
resolveCancelRefund: sharedEndpoint(
|
|
1557
|
+
COMMERCE_ENDPOINT_OPERATIONS.orders.resolveCancelRefund
|
|
1558
|
+
),
|
|
1559
|
+
createFulfillment: sharedEndpoint(
|
|
1560
|
+
COMMERCE_ENDPOINT_OPERATIONS.orders.createFulfillment
|
|
1561
|
+
),
|
|
1562
|
+
updateFulfillment: sharedEndpoint(
|
|
1563
|
+
COMMERCE_ENDPOINT_OPERATIONS.orders.updateFulfillment
|
|
1564
|
+
)
|
|
1510
1565
|
},
|
|
1511
1566
|
checkouts: {
|
|
1512
1567
|
create: sharedEndpoint(COMMERCE_ENDPOINT_OPERATIONS.orders.checkout),
|
|
@@ -1518,18 +1573,21 @@ var COMMERCE_ENDPOINTS = {
|
|
|
1518
1573
|
}
|
|
1519
1574
|
},
|
|
1520
1575
|
returns: {
|
|
1521
|
-
create:
|
|
1522
|
-
update:
|
|
1523
|
-
returnRefund:
|
|
1576
|
+
create: sharedEndpoint(COMMERCE_ENDPOINT_OPERATIONS.returns.create),
|
|
1577
|
+
update: sharedEndpoint(COMMERCE_ENDPOINT_OPERATIONS.returns.update),
|
|
1578
|
+
returnRefund: sharedEndpoint(
|
|
1579
|
+
COMMERCE_ENDPOINT_OPERATIONS.returns.returnRefund
|
|
1580
|
+
)
|
|
1524
1581
|
},
|
|
1525
1582
|
fulfillmentOrders: {
|
|
1526
|
-
prepare:
|
|
1527
|
-
|
|
1528
|
-
"/api/fulfillment-orders/prepare-fulfillment-order"
|
|
1583
|
+
prepare: sharedEndpoint(
|
|
1584
|
+
COMMERCE_ENDPOINT_OPERATIONS.fulfillmentOrders.prepare
|
|
1529
1585
|
)
|
|
1530
1586
|
},
|
|
1531
1587
|
fulfillments: {
|
|
1532
|
-
bulkImport:
|
|
1588
|
+
bulkImport: sharedEndpoint(
|
|
1589
|
+
COMMERCE_ENDPOINT_OPERATIONS.fulfillments.bulkImport
|
|
1590
|
+
)
|
|
1533
1591
|
},
|
|
1534
1592
|
carts: {
|
|
1535
1593
|
create: sharedEndpoint(COMMERCE_ENDPOINT_OPERATIONS.carts.create),
|
|
@@ -1594,42 +1652,21 @@ var COMMERCE_ENDPOINTS = {
|
|
|
1594
1652
|
),
|
|
1595
1653
|
confirm: sharedEndpoint(
|
|
1596
1654
|
COMMERCE_ENDPOINT_OPERATIONS.paymentSessions.server.confirm
|
|
1655
|
+
),
|
|
1656
|
+
findPortOne: sharedEndpoint(
|
|
1657
|
+
COMMERCE_ENDPOINT_OPERATIONS.paymentSessions.server.findPortOne
|
|
1658
|
+
),
|
|
1659
|
+
reconcilePortOne: sharedEndpoint(
|
|
1660
|
+
COMMERCE_ENDPOINT_OPERATIONS.paymentSessions.server.reconcilePortOne
|
|
1597
1661
|
)
|
|
1598
1662
|
}
|
|
1599
1663
|
},
|
|
1600
1664
|
shippingPolicies: {
|
|
1601
|
-
calculate:
|
|
1665
|
+
calculate: sharedEndpoint(
|
|
1666
|
+
COMMERCE_ENDPOINT_OPERATIONS.shippingPolicies.calculate
|
|
1667
|
+
)
|
|
1602
1668
|
}
|
|
1603
1669
|
};
|
|
1604
|
-
function isCommerceEndpointDescriptor(value) {
|
|
1605
|
-
if (!value || typeof value !== "object") return false;
|
|
1606
|
-
const candidate = value;
|
|
1607
|
-
return typeof candidate.method === "string" && typeof candidate.path === "string";
|
|
1608
|
-
}
|
|
1609
|
-
function flattenEndpointPaths(value) {
|
|
1610
|
-
if (isCommerceEndpointDescriptor(value)) return [resolveOperationPath2(value)];
|
|
1611
|
-
if (!value || typeof value !== "object") return [];
|
|
1612
|
-
return Object.values(value).flatMap((entry) => flattenEndpointPaths(entry));
|
|
1613
|
-
}
|
|
1614
|
-
function flattenEndpointDescriptors(value) {
|
|
1615
|
-
if (isCommerceEndpointDescriptor(value)) return [value];
|
|
1616
|
-
if (!value || typeof value !== "object") return [];
|
|
1617
|
-
return Object.values(value).flatMap(
|
|
1618
|
-
(entry) => flattenEndpointDescriptors(entry)
|
|
1619
|
-
);
|
|
1620
|
-
}
|
|
1621
|
-
var COMMERCE_ENDPOINT_PATHS = flattenEndpointPaths(COMMERCE_ENDPOINTS);
|
|
1622
|
-
var COMMERCE_ENDPOINT_DESCRIPTORS = flattenEndpointDescriptors(COMMERCE_ENDPOINTS);
|
|
1623
|
-
var MEMBERSHIP_ENDPOINTS = {
|
|
1624
|
-
subscribe: "/api/memberships/subscribe",
|
|
1625
|
-
cancel: (id) => `/api/memberships/${id}/cancel`,
|
|
1626
|
-
me: "/api/memberships/me"
|
|
1627
|
-
};
|
|
1628
|
-
var MEMBERSHIP_ENDPOINT_PATHS = [
|
|
1629
|
-
MEMBERSHIP_ENDPOINTS.subscribe,
|
|
1630
|
-
MEMBERSHIP_ENDPOINTS.cancel(":id"),
|
|
1631
|
-
MEMBERSHIP_ENDPOINTS.me
|
|
1632
|
-
];
|
|
1633
1670
|
|
|
1634
1671
|
// src/core/api/customer-scoped-api.ts
|
|
1635
1672
|
var CustomerScopedTransport = class {
|
|
@@ -1650,10 +1687,10 @@ var CustomerScopedTransport = class {
|
|
|
1650
1687
|
this.onUnauthorized = options.onUnauthorized;
|
|
1651
1688
|
this.onRequestId = options.onRequestId;
|
|
1652
1689
|
}
|
|
1653
|
-
async request(
|
|
1690
|
+
async request(endpoint, options) {
|
|
1654
1691
|
const token = typeof this.customerToken === "function" ? this.customerToken() : this.customerToken;
|
|
1655
1692
|
try {
|
|
1656
|
-
const response = await httpFetch(
|
|
1693
|
+
const response = await httpFetch(endpoint, {
|
|
1657
1694
|
method: options.method,
|
|
1658
1695
|
apiUrl: this.apiUrl,
|
|
1659
1696
|
publishableKey: this.publishableKey,
|
|
@@ -1667,7 +1704,7 @@ var CustomerScopedTransport = class {
|
|
|
1667
1704
|
...options.errorFamily && { errorFamily: options.errorFamily }
|
|
1668
1705
|
});
|
|
1669
1706
|
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1670
|
-
return parseApiResponse(response,
|
|
1707
|
+
return parseApiResponse(response, endpoint, {
|
|
1671
1708
|
errorFamily: options.errorFamily
|
|
1672
1709
|
});
|
|
1673
1710
|
} catch (err) {
|
|
@@ -1681,12 +1718,12 @@ var CustomerScopedApi = class {
|
|
|
1681
1718
|
constructor(apiName, options) {
|
|
1682
1719
|
this.transport = new CustomerScopedTransport(apiName, options);
|
|
1683
1720
|
}
|
|
1684
|
-
requestEndpoint(
|
|
1685
|
-
return this.transport.request(
|
|
1686
|
-
method:
|
|
1721
|
+
requestEndpoint(endpoint, body, options) {
|
|
1722
|
+
return this.transport.request(resolveOperationPath(endpoint, options), {
|
|
1723
|
+
method: resolveOperationMethod(endpoint),
|
|
1687
1724
|
body,
|
|
1688
1725
|
headers: options?.headers,
|
|
1689
|
-
errorFamily: resolveOperationErrorFamily(
|
|
1726
|
+
errorFamily: resolveOperationErrorFamily(endpoint)
|
|
1690
1727
|
});
|
|
1691
1728
|
}
|
|
1692
1729
|
};
|
|
@@ -1694,8 +1731,8 @@ var RawCustomerScopedApi = class {
|
|
|
1694
1731
|
constructor(apiName, options) {
|
|
1695
1732
|
this.transport = new CustomerScopedTransport(apiName, options);
|
|
1696
1733
|
}
|
|
1697
|
-
request(
|
|
1698
|
-
return this.transport.request(
|
|
1734
|
+
request(endpoint, options) {
|
|
1735
|
+
return this.transport.request(endpoint, options);
|
|
1699
1736
|
}
|
|
1700
1737
|
};
|
|
1701
1738
|
|
|
@@ -1770,11 +1807,11 @@ var CommunityClient = class extends RawCustomerScopedApi {
|
|
|
1770
1807
|
if (params.page !== void 0) urlParams.set("page", String(params.page));
|
|
1771
1808
|
return `/api/comments/public?${urlParams.toString()}`;
|
|
1772
1809
|
}
|
|
1773
|
-
async execute(
|
|
1774
|
-
return this.request(
|
|
1810
|
+
async execute(endpoint, method, body) {
|
|
1811
|
+
return this.request(endpoint, { method, body });
|
|
1775
1812
|
}
|
|
1776
|
-
async executeDoc(
|
|
1777
|
-
const response = await this.execute(
|
|
1813
|
+
async executeDoc(endpoint, method, body) {
|
|
1814
|
+
const response = await this.execute(endpoint, method, body);
|
|
1778
1815
|
return unwrapPayloadDoc(response);
|
|
1779
1816
|
}
|
|
1780
1817
|
createPost(params) {
|
|
@@ -2040,8 +2077,8 @@ var CommunityClient = class extends RawCustomerScopedApi {
|
|
|
2040
2077
|
|
|
2041
2078
|
// src/core/content/content-client.ts
|
|
2042
2079
|
var PublicContentApi = class extends RawCustomerScopedApi {
|
|
2043
|
-
get(
|
|
2044
|
-
return this.request(
|
|
2080
|
+
get(endpoint) {
|
|
2081
|
+
return this.request(endpoint, { method: "GET" });
|
|
2045
2082
|
}
|
|
2046
2083
|
};
|
|
2047
2084
|
function appendCommonParams(search, options, defaultSort) {
|
|
@@ -2139,7 +2176,10 @@ var ORDER_PRIMARY_DISPLAY_STATUSES = [
|
|
|
2139
2176
|
];
|
|
2140
2177
|
var CHECKOUT_STATUSES = ["open", "placed", "abandoned"];
|
|
2141
2178
|
var ABANDONED_REASONS = ["expired", "customer", "operator"];
|
|
2142
|
-
|
|
2179
|
+
function managedPaymentProviders(providers) {
|
|
2180
|
+
return providers;
|
|
2181
|
+
}
|
|
2182
|
+
var PAYMENT_PROVIDERS = managedPaymentProviders(["toss", "portone"]);
|
|
2143
2183
|
var PAYMENT_PROVIDER_STATUSES = [
|
|
2144
2184
|
"requires_payment_method",
|
|
2145
2185
|
"requires_action",
|
|
@@ -2187,6 +2227,26 @@ function invalidCanonicalResponse(resource, details) {
|
|
|
2187
2227
|
function fail(resource, path, expected) {
|
|
2188
2228
|
invalidCanonicalResponse(resource, { path, expected });
|
|
2189
2229
|
}
|
|
2230
|
+
function providerMismatch(resource, expected, actual, operation2) {
|
|
2231
|
+
if (operation2 === "cancel") {
|
|
2232
|
+
const method = {
|
|
2233
|
+
toss: "cancel()",
|
|
2234
|
+
portone: "cancelPortOne()"
|
|
2235
|
+
}[actual];
|
|
2236
|
+
throw createApiError(
|
|
2237
|
+
`Canonical ${resource} response provider mismatch: expected ${expected}, received ${actual}.`,
|
|
2238
|
+
502,
|
|
2239
|
+
{ resource, path: "$.provider", expected, actual },
|
|
2240
|
+
`The server applied the cancellation to a ${actual} PaymentSession, but this SDK method expects ${expected}.`,
|
|
2241
|
+
`Re-invoke ${method} with the same idempotencyKey; do not create a new key.`
|
|
2242
|
+
);
|
|
2243
|
+
}
|
|
2244
|
+
invalidCanonicalResponse(resource, {
|
|
2245
|
+
path: "$.provider",
|
|
2246
|
+
expected,
|
|
2247
|
+
actual
|
|
2248
|
+
});
|
|
2249
|
+
}
|
|
2190
2250
|
function record(value, resource, path) {
|
|
2191
2251
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2192
2252
|
fail(resource, path, "object");
|
|
@@ -2222,6 +2282,9 @@ function nullableString(value, resource, path) {
|
|
|
2222
2282
|
fail(resource, path, "string or null");
|
|
2223
2283
|
}
|
|
2224
2284
|
}
|
|
2285
|
+
function nullableNonEmptyString(value, resource, path) {
|
|
2286
|
+
if (value !== null) string(value, resource, path);
|
|
2287
|
+
}
|
|
2225
2288
|
function cartLineQuantity(value, resource, path) {
|
|
2226
2289
|
const quantity = positiveInteger(value, resource, path);
|
|
2227
2290
|
if (quantity > MAX_CART_LINE_QUANTITY) {
|
|
@@ -2731,8 +2794,8 @@ function validatePublicOrderItem(value, resource, path) {
|
|
|
2731
2794
|
validateReference(item.variant, resource, `${path}.variant`);
|
|
2732
2795
|
}
|
|
2733
2796
|
string(item.productTitle, resource, `${path}.productTitle`);
|
|
2734
|
-
|
|
2735
|
-
|
|
2797
|
+
nullableNonEmptyString(item.variantTitle, resource, `${path}.variantTitle`);
|
|
2798
|
+
nullableNonEmptyString(item.sku, resource, `${path}.sku`);
|
|
2736
2799
|
validateAdmissions(item.admissions, resource, `${path}.admissions`);
|
|
2737
2800
|
const quantity = positiveInteger(item.quantity, resource, `${path}.quantity`);
|
|
2738
2801
|
const unitPrice = safeMinor(
|
|
@@ -2983,8 +3046,8 @@ function validateCheckoutItem(value, resource, path) {
|
|
|
2983
3046
|
validateReference(item.product, resource, `${path}.product`);
|
|
2984
3047
|
validateReference(item.variant, resource, `${path}.variant`);
|
|
2985
3048
|
string(item.productTitle, resource, `${path}.productTitle`);
|
|
2986
|
-
|
|
2987
|
-
|
|
3049
|
+
nullableNonEmptyString(item.variantTitle, resource, `${path}.variantTitle`);
|
|
3050
|
+
nullableNonEmptyString(item.sku, resource, `${path}.sku`);
|
|
2988
3051
|
validateAdmissions(item.admissions, resource, `${path}.admissions`);
|
|
2989
3052
|
cartLineQuantity(item.quantity, resource, `${path}.quantity`);
|
|
2990
3053
|
const unitPrice = safeMinor(
|
|
@@ -3316,11 +3379,21 @@ function validateNextAction(value, provider, resource, path) {
|
|
|
3316
3379
|
const action = record(value, resource, path);
|
|
3317
3380
|
if (provider === "portone") {
|
|
3318
3381
|
if (action.type === "portone_sdk") {
|
|
3382
|
+
const hasCancelable = "cancelable" in action;
|
|
3383
|
+
const hasRetryPolicy = "retryPolicy" in action;
|
|
3384
|
+
if (hasCancelable !== hasRetryPolicy) {
|
|
3385
|
+
fail(
|
|
3386
|
+
resource,
|
|
3387
|
+
path,
|
|
3388
|
+
"exact released action or exact negotiated capability action"
|
|
3389
|
+
);
|
|
3390
|
+
}
|
|
3319
3391
|
exact(
|
|
3320
3392
|
action,
|
|
3321
3393
|
[
|
|
3322
3394
|
"type",
|
|
3323
3395
|
"version",
|
|
3396
|
+
...hasCancelable ? ["cancelable", "retryPolicy"] : [],
|
|
3324
3397
|
"storeId",
|
|
3325
3398
|
"channelKey",
|
|
3326
3399
|
"paymentId",
|
|
@@ -3335,6 +3408,11 @@ function validateNextAction(value, provider, resource, path) {
|
|
|
3335
3408
|
path
|
|
3336
3409
|
);
|
|
3337
3410
|
if (action.version !== "v2") fail(resource, `${path}.version`, "v2");
|
|
3411
|
+
if (hasCancelable && action.cancelable !== false)
|
|
3412
|
+
fail(resource, `${path}.cancelable`, "false");
|
|
3413
|
+
if (hasRetryPolicy && action.retryPolicy !== "same_action_only") {
|
|
3414
|
+
fail(resource, `${path}.retryPolicy`, "same_action_only");
|
|
3415
|
+
}
|
|
3338
3416
|
for (const key of ["storeId", "channelKey"]) {
|
|
3339
3417
|
boundedString(action[key], 255, resource, `${path}.${key}`);
|
|
3340
3418
|
}
|
|
@@ -3381,6 +3459,10 @@ function validateNextAction(value, provider, resource, path) {
|
|
|
3381
3459
|
"HTTPS or loopback URL without embedded credentials"
|
|
3382
3460
|
);
|
|
3383
3461
|
}
|
|
3462
|
+
if (!hasCancelable) {
|
|
3463
|
+
action.cancelable = false;
|
|
3464
|
+
action.retryPolicy = "same_action_only";
|
|
3465
|
+
}
|
|
3384
3466
|
return;
|
|
3385
3467
|
}
|
|
3386
3468
|
exact(action, ["type", "checkoutUrl"], [], resource, path);
|
|
@@ -3457,7 +3539,7 @@ function validateNextAction(value, provider, resource, path) {
|
|
|
3457
3539
|
if (action.customerEmail !== void 0)
|
|
3458
3540
|
validateCustomer({ email: action.customerEmail }, resource, path);
|
|
3459
3541
|
}
|
|
3460
|
-
function validatePublicPaymentSession(value, expectedProvider) {
|
|
3542
|
+
function validatePublicPaymentSession(value, expectedProvider, operation2) {
|
|
3461
3543
|
const resource = "PaymentSession";
|
|
3462
3544
|
const session = record(value, resource, "$");
|
|
3463
3545
|
const provider = enumValue(
|
|
@@ -3466,9 +3548,6 @@ function validatePublicPaymentSession(value, expectedProvider) {
|
|
|
3466
3548
|
resource,
|
|
3467
3549
|
"$.provider"
|
|
3468
3550
|
);
|
|
3469
|
-
if (expectedProvider !== void 0 && provider !== expectedProvider) {
|
|
3470
|
-
fail(resource, "$.provider", expectedProvider);
|
|
3471
|
-
}
|
|
3472
3551
|
const status = enumValue(
|
|
3473
3552
|
session.providerStatus,
|
|
3474
3553
|
PAYMENT_PROVIDER_STATUSES,
|
|
@@ -3672,44 +3751,128 @@ function validatePublicPaymentSession(value, expectedProvider) {
|
|
|
3672
3751
|
"$.cancellationReason"
|
|
3673
3752
|
);
|
|
3674
3753
|
}
|
|
3754
|
+
if (expectedProvider !== void 0 && provider !== expectedProvider) {
|
|
3755
|
+
if (operation2 === "cancel" && status !== "canceling" && status !== "canceled") {
|
|
3756
|
+
fail(
|
|
3757
|
+
resource,
|
|
3758
|
+
"$.providerStatus",
|
|
3759
|
+
"canceling or canceled after a successful cancellation"
|
|
3760
|
+
);
|
|
3761
|
+
}
|
|
3762
|
+
providerMismatch(resource, expectedProvider, provider, operation2);
|
|
3763
|
+
}
|
|
3675
3764
|
return value;
|
|
3676
3765
|
}
|
|
3677
3766
|
|
|
3678
3767
|
// src/core/customer/customer-auth.ts
|
|
3679
3768
|
var DEFAULT_TIMEOUT2 = 15e3;
|
|
3680
|
-
|
|
3769
|
+
var REFRESH_LOCK_TIMEOUT = DEFAULT_TIMEOUT2 * 3;
|
|
3770
|
+
var SESSION_RECORD_VERSION = 1;
|
|
3771
|
+
var STORAGE_UNUSABLE = /* @__PURE__ */ Symbol("storage-unusable");
|
|
3772
|
+
function canonicalApiIdentity(apiUrl) {
|
|
3681
3773
|
try {
|
|
3682
|
-
|
|
3774
|
+
const url = new URL(apiUrl);
|
|
3775
|
+
return `${url.protocol.toLowerCase()}//${url.host.toLowerCase()}${url.pathname.replace(/\/+$/, "")}`;
|
|
3776
|
+
} catch {
|
|
3777
|
+
return apiUrl.trim().toLowerCase().replace(/\/+$/, "");
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
function buildRefreshLockName(apiUrl, publishableKey, storageKey) {
|
|
3781
|
+
return `01software:customer-auth-refresh:${canonicalApiIdentity(apiUrl)}:${publishableKey}:${storageKey}`;
|
|
3782
|
+
}
|
|
3783
|
+
function getRefreshLockRequester() {
|
|
3784
|
+
try {
|
|
3785
|
+
if (typeof navigator === "undefined") return null;
|
|
3786
|
+
const locks = navigator.locks;
|
|
3787
|
+
if (!locks || typeof locks.request !== "function") return null;
|
|
3788
|
+
return (name, options, callback) => locks.request(
|
|
3789
|
+
name,
|
|
3790
|
+
options,
|
|
3791
|
+
() => callback()
|
|
3792
|
+
);
|
|
3683
3793
|
} catch {
|
|
3684
3794
|
return null;
|
|
3685
3795
|
}
|
|
3686
3796
|
}
|
|
3797
|
+
function parseSessionRecord(raw) {
|
|
3798
|
+
try {
|
|
3799
|
+
const parsed = JSON.parse(raw);
|
|
3800
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
3801
|
+
const candidate = parsed;
|
|
3802
|
+
if (candidate.v !== SESSION_RECORD_VERSION) return null;
|
|
3803
|
+
if (typeof candidate.rev !== "number" || !Number.isFinite(candidate.rev)) {
|
|
3804
|
+
return null;
|
|
3805
|
+
}
|
|
3806
|
+
const token = candidate.token;
|
|
3807
|
+
const refreshToken = candidate.refreshToken;
|
|
3808
|
+
if (token !== null && typeof token !== "string") return null;
|
|
3809
|
+
if (refreshToken !== null && typeof refreshToken !== "string") return null;
|
|
3810
|
+
return {
|
|
3811
|
+
v: SESSION_RECORD_VERSION,
|
|
3812
|
+
rev: candidate.rev,
|
|
3813
|
+
token,
|
|
3814
|
+
refreshToken
|
|
3815
|
+
};
|
|
3816
|
+
} catch {
|
|
3817
|
+
return null;
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
function readSessionRecord(recordKey, mirrorKey, mirrorRefreshKey) {
|
|
3821
|
+
let raw;
|
|
3822
|
+
try {
|
|
3823
|
+
raw = localStorage.getItem(recordKey);
|
|
3824
|
+
} catch {
|
|
3825
|
+
return STORAGE_UNUSABLE;
|
|
3826
|
+
}
|
|
3827
|
+
if (raw !== null) {
|
|
3828
|
+
const parsed = parseSessionRecord(raw);
|
|
3829
|
+
return parsed ? { record: parsed, stored: true } : STORAGE_UNUSABLE;
|
|
3830
|
+
}
|
|
3831
|
+
try {
|
|
3832
|
+
return {
|
|
3833
|
+
record: {
|
|
3834
|
+
v: SESSION_RECORD_VERSION,
|
|
3835
|
+
rev: 0,
|
|
3836
|
+
token: localStorage.getItem(mirrorKey),
|
|
3837
|
+
refreshToken: localStorage.getItem(mirrorRefreshKey)
|
|
3838
|
+
},
|
|
3839
|
+
stored: false
|
|
3840
|
+
};
|
|
3841
|
+
} catch {
|
|
3842
|
+
return STORAGE_UNUSABLE;
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3687
3845
|
var CustomerAuth = class {
|
|
3688
3846
|
constructor(publishableKey, options, apiUrl) {
|
|
3689
3847
|
this.refreshPromise = null;
|
|
3848
|
+
/** Canonical record key; null unless persistence is active in a browser. */
|
|
3849
|
+
this.persistRecordKey = null;
|
|
3850
|
+
/** Advisory single-value access mirror, plus the retired refresh mirror. */
|
|
3851
|
+
this.persistMirrorKey = null;
|
|
3852
|
+
this.persistMirrorRefreshKey = null;
|
|
3853
|
+
/** Cross-context refresh lock name; null when there is no shared storage. */
|
|
3854
|
+
this.refreshLockName = null;
|
|
3855
|
+
/** Revision of the canonical record this instance last observed. */
|
|
3856
|
+
this.sessionRev = 0;
|
|
3690
3857
|
this.publishableKey = publishableKey;
|
|
3691
3858
|
this.baseUrl = resolveApiUrl(apiUrl);
|
|
3692
3859
|
const persist = options?.persist ?? true;
|
|
3693
3860
|
if (persist) {
|
|
3694
3861
|
const key = typeof persist === "string" ? persist : "customer-token";
|
|
3695
|
-
const refreshKey = `${key}:refresh`;
|
|
3696
3862
|
const isBrowser = typeof window !== "undefined";
|
|
3697
|
-
this.token =
|
|
3698
|
-
this.refreshTokenValue =
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
} catch {
|
|
3711
|
-
}
|
|
3712
|
-
} : void 0;
|
|
3863
|
+
this.token = null;
|
|
3864
|
+
this.refreshTokenValue = null;
|
|
3865
|
+
if (isBrowser) {
|
|
3866
|
+
this.persistRecordKey = `${key}:session`;
|
|
3867
|
+
this.persistMirrorKey = key;
|
|
3868
|
+
this.persistMirrorRefreshKey = `${key}:refresh`;
|
|
3869
|
+
this.refreshLockName = buildRefreshLockName(
|
|
3870
|
+
this.baseUrl,
|
|
3871
|
+
publishableKey,
|
|
3872
|
+
key
|
|
3873
|
+
);
|
|
3874
|
+
this.syncFromPersistedStorage();
|
|
3875
|
+
}
|
|
3713
3876
|
} else {
|
|
3714
3877
|
this.token = options?.token ?? null;
|
|
3715
3878
|
this.refreshTokenValue = options?.refreshToken ?? null;
|
|
@@ -3737,66 +3900,299 @@ var CustomerAuth = class {
|
|
|
3737
3900
|
body: JSON.stringify(data)
|
|
3738
3901
|
}
|
|
3739
3902
|
);
|
|
3740
|
-
this.
|
|
3741
|
-
|
|
3903
|
+
if (result.refreshToken) this.setSession(result.token, result.refreshToken);
|
|
3904
|
+
else this.setToken(result.token);
|
|
3742
3905
|
return result;
|
|
3743
3906
|
}
|
|
3744
3907
|
/**
|
|
3745
3908
|
* Refresh the current token. Uses a refresh token when available, otherwise
|
|
3746
3909
|
* falls back to the legacy bearer-token compatibility path.
|
|
3910
|
+
*
|
|
3911
|
+
* Refresh tokens are single-use, so a persisted session shared by several
|
|
3912
|
+
* tabs or SDK instances must rotate at most once: concurrent rotations of the
|
|
3913
|
+
* same token are replay from the server's point of view and revoke the whole
|
|
3914
|
+
* family. Persisted browser sessions therefore serialize rotation across
|
|
3915
|
+
* contexts and re-read the canonical persisted credentials inside the lock.
|
|
3747
3916
|
*/
|
|
3748
3917
|
async refreshToken() {
|
|
3918
|
+
this.syncFromPersistedStorage();
|
|
3749
3919
|
if (!this.refreshTokenValue && !this.token) {
|
|
3750
3920
|
throw new ApiError("Not authenticated", 401);
|
|
3751
3921
|
}
|
|
3752
3922
|
if (this.refreshPromise) return this.refreshPromise;
|
|
3753
|
-
|
|
3923
|
+
const intended = this.currentFence();
|
|
3924
|
+
this.refreshPromise = this.rotateAcrossContexts(intended);
|
|
3754
3925
|
try {
|
|
3755
3926
|
return await this.refreshPromise;
|
|
3756
3927
|
} finally {
|
|
3757
3928
|
this.refreshPromise = null;
|
|
3758
3929
|
}
|
|
3759
3930
|
}
|
|
3760
|
-
|
|
3761
|
-
|
|
3931
|
+
/**
|
|
3932
|
+
* Re-read the canonical persisted session. In persisted mode the shared
|
|
3933
|
+
* record — not this instance's memory — is the source of truth, so another
|
|
3934
|
+
* context's rotation, login, or logout is observed before the next credential
|
|
3935
|
+
* use. Change callbacks are deliberately not invoked: this is a read.
|
|
3936
|
+
*/
|
|
3937
|
+
syncFromPersistedStorage() {
|
|
3938
|
+
const canonical = this.readCanonicalSession();
|
|
3939
|
+
if (canonical === STORAGE_UNUSABLE) {
|
|
3940
|
+
this.disablePersistence();
|
|
3941
|
+
return;
|
|
3942
|
+
}
|
|
3943
|
+
if (!canonical) return;
|
|
3944
|
+
this.sessionRev = canonical.record.rev;
|
|
3945
|
+
this.token = canonical.record.token;
|
|
3946
|
+
this.refreshTokenValue = canonical.record.refreshToken;
|
|
3947
|
+
}
|
|
3948
|
+
/** `null` when this instance does not persist. */
|
|
3949
|
+
readCanonicalSession() {
|
|
3950
|
+
if (!this.persistRecordKey || !this.persistMirrorKey || !this.persistMirrorRefreshKey) {
|
|
3951
|
+
return null;
|
|
3952
|
+
}
|
|
3953
|
+
return readSessionRecord(
|
|
3954
|
+
this.persistRecordKey,
|
|
3955
|
+
this.persistMirrorKey,
|
|
3956
|
+
this.persistMirrorRefreshKey
|
|
3957
|
+
);
|
|
3958
|
+
}
|
|
3959
|
+
/**
|
|
3960
|
+
* Publish a change to canonical storage.
|
|
3961
|
+
*
|
|
3962
|
+
* The canonical pair is re-read immediately before the single record write so
|
|
3963
|
+
* that a field this call does not change is taken from that read rather than
|
|
3964
|
+
* from this instance's possibly stale copy. That merge is BEST-EFFORT: it
|
|
3965
|
+
* removes the stale counterpart an instance would otherwise carry, but it is
|
|
3966
|
+
* not a compare-and-swap. Web Storage offers none, and the synchronous public
|
|
3967
|
+
* mutators cannot await the refresh lease, so a commit that lands between
|
|
3968
|
+
* this read and the store operation is lost by ordering — including the
|
|
3969
|
+
* counterpart just read, which is then republished stale. The window is one
|
|
3970
|
+
* store operation wide and the server remains authoritative; see
|
|
3971
|
+
* `CUST-AUTH-033` for the stated limit.
|
|
3972
|
+
*
|
|
3973
|
+
* Returns `false` only when `requireLiveSession` refused a write because the
|
|
3974
|
+
* canonical session had already been cleared.
|
|
3975
|
+
*/
|
|
3976
|
+
commitPersistedSession(change, options) {
|
|
3977
|
+
const recordKey = this.persistRecordKey;
|
|
3978
|
+
const mirrorKey = this.persistMirrorKey;
|
|
3979
|
+
const mirrorRefreshKey = this.persistMirrorRefreshKey;
|
|
3980
|
+
const canonical = this.readCanonicalSession();
|
|
3981
|
+
if (canonical === null || !recordKey || !mirrorKey || !mirrorRefreshKey) {
|
|
3982
|
+
return true;
|
|
3983
|
+
}
|
|
3984
|
+
if (canonical === STORAGE_UNUSABLE) {
|
|
3985
|
+
this.disablePersistence();
|
|
3986
|
+
return true;
|
|
3987
|
+
}
|
|
3988
|
+
const current = canonical.record;
|
|
3989
|
+
if (options?.requireLiveSession && !current.token && !current.refreshToken) {
|
|
3990
|
+
return false;
|
|
3991
|
+
}
|
|
3992
|
+
const next = {
|
|
3993
|
+
v: SESSION_RECORD_VERSION,
|
|
3994
|
+
rev: current.rev + 1,
|
|
3995
|
+
token: "token" in change ? change.token ?? null : current.token,
|
|
3996
|
+
refreshToken: "refreshToken" in change ? change.refreshToken ?? null : current.refreshToken
|
|
3997
|
+
};
|
|
3998
|
+
if (
|
|
3999
|
+
// Only a state that is genuinely persisted can already say what this
|
|
4000
|
+
// write would say. An absent record reads as an empty pair, and treating
|
|
4001
|
+
// that as a no-op would drop the very tombstone that stops a later
|
|
4002
|
+
// migration from resurrecting the session out of the earlier layout.
|
|
4003
|
+
canonical.stored && next.token === current.token && next.refreshToken === current.refreshToken
|
|
4004
|
+
) {
|
|
4005
|
+
this.sessionRev = current.rev;
|
|
4006
|
+
this.token = current.token;
|
|
4007
|
+
this.refreshTokenValue = current.refreshToken;
|
|
4008
|
+
return true;
|
|
4009
|
+
}
|
|
4010
|
+
try {
|
|
4011
|
+
localStorage.setItem(recordKey, JSON.stringify(next));
|
|
4012
|
+
} catch {
|
|
4013
|
+
this.disablePersistence();
|
|
4014
|
+
return true;
|
|
4015
|
+
}
|
|
4016
|
+
this.sessionRev = next.rev;
|
|
4017
|
+
this.token = next.token;
|
|
4018
|
+
this.refreshTokenValue = next.refreshToken;
|
|
4019
|
+
try {
|
|
4020
|
+
localStorage.removeItem(mirrorRefreshKey);
|
|
4021
|
+
} catch {
|
|
4022
|
+
return true;
|
|
4023
|
+
}
|
|
4024
|
+
try {
|
|
4025
|
+
if (next.token) localStorage.setItem(mirrorKey, next.token);
|
|
4026
|
+
else localStorage.removeItem(mirrorKey);
|
|
4027
|
+
} catch {
|
|
4028
|
+
}
|
|
4029
|
+
return true;
|
|
4030
|
+
}
|
|
4031
|
+
/**
|
|
4032
|
+
* Storage rejected a write or a read, so it is not a canonical store for this
|
|
4033
|
+
* instance. Fall back to the memory-only session and take no lease: there is
|
|
4034
|
+
* no shared state left to coordinate. Nothing is purged — the record commit
|
|
4035
|
+
* is atomic, so no inconsistent state was published, and another context's
|
|
4036
|
+
* valid session must not be destroyed by this instance's local failure.
|
|
4037
|
+
*/
|
|
4038
|
+
disablePersistence() {
|
|
4039
|
+
this.persistRecordKey = null;
|
|
4040
|
+
this.persistMirrorKey = null;
|
|
4041
|
+
this.persistMirrorRefreshKey = null;
|
|
4042
|
+
this.refreshLockName = null;
|
|
4043
|
+
}
|
|
4044
|
+
/**
|
|
4045
|
+
* Compare the canonical record with the fence this call captured before it
|
|
4046
|
+
* left. Every persisted decision — rotate, converge, or fail closed — goes
|
|
4047
|
+
* through this one comparison. The revision catches a logout, a fresh login,
|
|
4048
|
+
* or a manual setter even when token values coincide; the value comparison
|
|
4049
|
+
* additionally catches a mirror-only writer that cannot bump a revision.
|
|
4050
|
+
*/
|
|
4051
|
+
classifyCanonicalState(intended) {
|
|
4052
|
+
if (!this.persistRecordKey) return "current";
|
|
4053
|
+
this.syncFromPersistedStorage();
|
|
4054
|
+
if (!this.persistRecordKey) return "current";
|
|
4055
|
+
if (!this.token && !this.refreshTokenValue) return "cleared";
|
|
4056
|
+
if (intended.refreshToken !== null) {
|
|
4057
|
+
return this.refreshTokenValue === intended.refreshToken ? "current" : "superseded";
|
|
4058
|
+
}
|
|
4059
|
+
return this.token === intended.token && !this.refreshTokenValue ? "current" : "superseded";
|
|
4060
|
+
}
|
|
4061
|
+
/** The canonical persisted pair, as a refresh result. */
|
|
4062
|
+
canonicalPair(token) {
|
|
4063
|
+
return {
|
|
4064
|
+
token,
|
|
4065
|
+
...this.refreshTokenValue ? { refreshToken: this.refreshTokenValue } : {}
|
|
4066
|
+
};
|
|
4067
|
+
}
|
|
4068
|
+
/** Current credentials plus the revision they were observed at. */
|
|
4069
|
+
currentFence() {
|
|
4070
|
+
return { token: this.token, refreshToken: this.refreshTokenValue };
|
|
4071
|
+
}
|
|
4072
|
+
/**
|
|
4073
|
+
* Clear an access credential a 401 just proved unusable — but only while it
|
|
4074
|
+
* is still the canonical one. A rotation committed by another context while
|
|
4075
|
+
* the request was in flight is newer than this 401, so it must survive; the
|
|
4076
|
+
* instance adopts it instead. The refresh entry is never touched here: a
|
|
4077
|
+
* rejected access token says nothing about the refresh family.
|
|
4078
|
+
*/
|
|
4079
|
+
invalidateAccessToken(spentToken) {
|
|
4080
|
+
this.syncFromPersistedStorage();
|
|
4081
|
+
if (this.token !== spentToken) return;
|
|
4082
|
+
this.setToken(null);
|
|
4083
|
+
}
|
|
4084
|
+
/** Commit both credentials in one canonical write. */
|
|
4085
|
+
setSession(token, refreshToken, options) {
|
|
4086
|
+
this.token = token;
|
|
4087
|
+
this.refreshTokenValue = refreshToken;
|
|
4088
|
+
const committed = this.commitPersistedSession(
|
|
4089
|
+
{ token, refreshToken },
|
|
4090
|
+
options
|
|
4091
|
+
);
|
|
4092
|
+
if (!committed) return false;
|
|
4093
|
+
this.onTokenChange?.(this.token);
|
|
4094
|
+
this.onRefreshTokenChange?.(this.refreshTokenValue);
|
|
4095
|
+
return true;
|
|
4096
|
+
}
|
|
4097
|
+
/**
|
|
4098
|
+
* Serialize rotation against every other context sharing the same storage
|
|
4099
|
+
* key and API/tenant identity. Coordination is best-effort: when it is
|
|
4100
|
+
* unavailable or times out the rotation still happens, so server-side reuse
|
|
4101
|
+
* detection is never bypassed by a client that failed to coordinate.
|
|
4102
|
+
*/
|
|
4103
|
+
async rotateAcrossContexts(intended) {
|
|
4104
|
+
const lockName = this.refreshLockName;
|
|
4105
|
+
const requestLock = lockName ? getRefreshLockRequester() : null;
|
|
4106
|
+
if (!lockName || !requestLock) {
|
|
4107
|
+
return this.rotateWithCanonicalState(intended);
|
|
4108
|
+
}
|
|
4109
|
+
const controller = new AbortController();
|
|
4110
|
+
const timer = setTimeout(() => controller.abort(), REFRESH_LOCK_TIMEOUT);
|
|
4111
|
+
let entered = false;
|
|
4112
|
+
try {
|
|
4113
|
+
return await requestLock(
|
|
4114
|
+
lockName,
|
|
4115
|
+
{ signal: controller.signal },
|
|
4116
|
+
async () => {
|
|
4117
|
+
entered = true;
|
|
4118
|
+
return this.rotateWithCanonicalState(intended);
|
|
4119
|
+
}
|
|
4120
|
+
);
|
|
4121
|
+
} catch (error) {
|
|
4122
|
+
if (entered) throw error;
|
|
4123
|
+
return this.rotateWithCanonicalState(intended);
|
|
4124
|
+
} finally {
|
|
4125
|
+
clearTimeout(timer);
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
/**
|
|
4129
|
+
* Decide, against freshly re-read canonical state, whether this context still
|
|
4130
|
+
* owns the rotation.
|
|
4131
|
+
*/
|
|
4132
|
+
async rotateWithCanonicalState(intended) {
|
|
4133
|
+
let fence = intended;
|
|
4134
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
4135
|
+
const state = this.classifyCanonicalState(fence);
|
|
4136
|
+
if (state === "cleared") throw new ApiError("Not authenticated", 401);
|
|
4137
|
+
if (state === "current") return this._doRefreshToken(fence);
|
|
4138
|
+
if (this.token) return this.canonicalPair(this.token);
|
|
4139
|
+
fence = this.currentFence();
|
|
4140
|
+
}
|
|
4141
|
+
throw new ApiError("Not authenticated", 401);
|
|
4142
|
+
}
|
|
4143
|
+
async _doRefreshToken(intended) {
|
|
4144
|
+
const init = intended.refreshToken ? {
|
|
3762
4145
|
method: "POST",
|
|
3763
|
-
body: JSON.stringify({ refreshToken:
|
|
4146
|
+
body: JSON.stringify({ refreshToken: intended.refreshToken })
|
|
3764
4147
|
} : {
|
|
3765
4148
|
method: "POST",
|
|
3766
|
-
headers: { Authorization: `Bearer ${
|
|
4149
|
+
headers: { Authorization: `Bearer ${intended.token}` }
|
|
3767
4150
|
};
|
|
3768
4151
|
const result = await this.requestJson(
|
|
3769
4152
|
"/api/customers/refresh",
|
|
3770
4153
|
init
|
|
3771
4154
|
);
|
|
3772
|
-
this.
|
|
3773
|
-
if (
|
|
3774
|
-
|
|
4155
|
+
const state = this.classifyCanonicalState(intended);
|
|
4156
|
+
if (state === "cleared") throw new ApiError("Not authenticated", 401);
|
|
4157
|
+
if (state === "superseded") {
|
|
4158
|
+
if (this.token) return this.canonicalPair(this.token);
|
|
4159
|
+
throw new ApiError("Not authenticated", 401);
|
|
4160
|
+
}
|
|
4161
|
+
const replacedAccessToken = this.token !== null && this.token !== intended.token ? this.token : null;
|
|
4162
|
+
const nextToken = replacedAccessToken ?? result.token;
|
|
4163
|
+
const nextRefresh = result.refreshToken ?? null;
|
|
4164
|
+
if (!this.setSession(nextToken, nextRefresh, { requireLiveSession: true })) {
|
|
4165
|
+
throw new ApiError("Not authenticated", 401);
|
|
4166
|
+
}
|
|
4167
|
+
return {
|
|
4168
|
+
token: nextToken,
|
|
4169
|
+
...nextRefresh ? { refreshToken: nextRefresh } : {}
|
|
4170
|
+
};
|
|
3775
4171
|
}
|
|
3776
4172
|
/**
|
|
3777
4173
|
* Clear the stored token
|
|
3778
4174
|
*/
|
|
3779
4175
|
logout() {
|
|
3780
|
-
this.
|
|
3781
|
-
this.setRefreshToken(null);
|
|
4176
|
+
this.setSession(null, null);
|
|
3782
4177
|
}
|
|
3783
4178
|
/**
|
|
3784
4179
|
* Get the current authenticated customer's profile
|
|
3785
4180
|
*/
|
|
3786
4181
|
async me() {
|
|
3787
|
-
|
|
4182
|
+
const token = this.getToken();
|
|
4183
|
+
if (!token) return null;
|
|
3788
4184
|
try {
|
|
3789
4185
|
const data = await this.requestJson(
|
|
3790
4186
|
"/api/customers/me",
|
|
3791
4187
|
{
|
|
3792
4188
|
method: "GET",
|
|
3793
|
-
headers: { Authorization: `Bearer ${
|
|
4189
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
3794
4190
|
}
|
|
3795
4191
|
);
|
|
3796
4192
|
return data.customer ?? null;
|
|
3797
4193
|
} catch (error) {
|
|
3798
4194
|
if (error instanceof SDKError && error.status === 401) {
|
|
3799
|
-
this.
|
|
4195
|
+
this.invalidateAccessToken(token);
|
|
3800
4196
|
return null;
|
|
3801
4197
|
}
|
|
3802
4198
|
throw error;
|
|
@@ -3824,12 +4220,13 @@ var CustomerAuth = class {
|
|
|
3824
4220
|
* Update the authenticated customer's profile (name, phone, marketingConsent)
|
|
3825
4221
|
*/
|
|
3826
4222
|
async updateProfile(data) {
|
|
3827
|
-
|
|
4223
|
+
const token = this.getToken();
|
|
4224
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
3828
4225
|
const result = await this.requestJson(
|
|
3829
4226
|
"/api/customers/me",
|
|
3830
4227
|
{
|
|
3831
4228
|
method: "PATCH",
|
|
3832
|
-
headers: { Authorization: `Bearer ${
|
|
4229
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
3833
4230
|
body: JSON.stringify(data)
|
|
3834
4231
|
}
|
|
3835
4232
|
);
|
|
@@ -3839,10 +4236,11 @@ var CustomerAuth = class {
|
|
|
3839
4236
|
* Change the password of the currently authenticated customer
|
|
3840
4237
|
*/
|
|
3841
4238
|
async changePassword(currentPassword, newPassword) {
|
|
3842
|
-
|
|
4239
|
+
const token = this.getToken();
|
|
4240
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
3843
4241
|
await this.requestJson("/api/customers/change-password", {
|
|
3844
4242
|
method: "POST",
|
|
3845
|
-
headers: { Authorization: `Bearer ${
|
|
4243
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
3846
4244
|
body: JSON.stringify({ currentPassword, newPassword })
|
|
3847
4245
|
});
|
|
3848
4246
|
}
|
|
@@ -3850,7 +4248,8 @@ var CustomerAuth = class {
|
|
|
3850
4248
|
* Get the authenticated customer's orders with pagination and optional status filter
|
|
3851
4249
|
*/
|
|
3852
4250
|
async getMyOrders(options) {
|
|
3853
|
-
|
|
4251
|
+
const token = this.getToken();
|
|
4252
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
3854
4253
|
const params = new URLSearchParams();
|
|
3855
4254
|
if (options?.page) params.set("page", String(options.page));
|
|
3856
4255
|
if (options?.limit) params.set("limit", String(options.limit));
|
|
@@ -3872,21 +4271,27 @@ var CustomerAuth = class {
|
|
|
3872
4271
|
`/api/customers/me/orders${qs ? `?${qs}` : ""}`,
|
|
3873
4272
|
{
|
|
3874
4273
|
method: "GET",
|
|
3875
|
-
headers: { Authorization: `Bearer ${
|
|
4274
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
3876
4275
|
}
|
|
3877
4276
|
);
|
|
3878
4277
|
return validatePublicOrderList(result);
|
|
3879
4278
|
}
|
|
3880
4279
|
/**
|
|
3881
|
-
* Get the current token (or null if not authenticated)
|
|
4280
|
+
* Get the current token (or null if not authenticated).
|
|
4281
|
+
*
|
|
4282
|
+
* In persisted mode this re-reads shared storage first, so a rotation or
|
|
4283
|
+
* logout performed by another tab is observed before the token is used.
|
|
3882
4284
|
*/
|
|
3883
4285
|
getToken() {
|
|
4286
|
+
this.syncFromPersistedStorage();
|
|
3884
4287
|
return this.token;
|
|
3885
4288
|
}
|
|
3886
4289
|
/**
|
|
3887
|
-
* Get the current refresh token (or null if unavailable)
|
|
4290
|
+
* Get the current refresh token (or null if unavailable). Persisted mode
|
|
4291
|
+
* re-reads shared storage first, like {@link CustomerAuth.getToken}.
|
|
3888
4292
|
*/
|
|
3889
4293
|
getRefreshToken() {
|
|
4294
|
+
this.syncFromPersistedStorage();
|
|
3890
4295
|
return this.refreshTokenValue;
|
|
3891
4296
|
}
|
|
3892
4297
|
/**
|
|
@@ -3894,6 +4299,7 @@ var CustomerAuth = class {
|
|
|
3894
4299
|
*/
|
|
3895
4300
|
setToken(token) {
|
|
3896
4301
|
this.token = token;
|
|
4302
|
+
this.commitPersistedSession({ token });
|
|
3897
4303
|
this.onTokenChange?.(token);
|
|
3898
4304
|
}
|
|
3899
4305
|
/**
|
|
@@ -3901,13 +4307,14 @@ var CustomerAuth = class {
|
|
|
3901
4307
|
*/
|
|
3902
4308
|
setRefreshToken(refreshToken) {
|
|
3903
4309
|
this.refreshTokenValue = refreshToken;
|
|
4310
|
+
this.commitPersistedSession({ refreshToken });
|
|
3904
4311
|
this.onRefreshTokenChange?.(refreshToken);
|
|
3905
4312
|
}
|
|
3906
4313
|
/**
|
|
3907
4314
|
* Check if the customer is currently authenticated
|
|
3908
4315
|
*/
|
|
3909
4316
|
isAuthenticated() {
|
|
3910
|
-
return this.
|
|
4317
|
+
return this.getToken() !== null;
|
|
3911
4318
|
}
|
|
3912
4319
|
/**
|
|
3913
4320
|
* Internal: make a request with timeout and error handling.
|
|
@@ -4111,8 +4518,8 @@ var CartApi = class extends CustomerScopedApi {
|
|
|
4111
4518
|
endpoint(key) {
|
|
4112
4519
|
return this.serverTransport ? COMMERCE_ENDPOINTS.carts.server[key] : COMMERCE_ENDPOINTS.carts[key];
|
|
4113
4520
|
}
|
|
4114
|
-
execute(
|
|
4115
|
-
return this.requestEndpoint(
|
|
4521
|
+
execute(endpoint, body, idempotencyKey) {
|
|
4522
|
+
return this.requestEndpoint(endpoint, body, {
|
|
4116
4523
|
...idempotencyKey && {
|
|
4117
4524
|
headers: { "X-Idempotency-Key": idempotencyKey }
|
|
4118
4525
|
}
|
|
@@ -4129,26 +4536,26 @@ var CartApi = class extends CustomerScopedApi {
|
|
|
4129
4536
|
);
|
|
4130
4537
|
}
|
|
4131
4538
|
mine(params = {}) {
|
|
4132
|
-
const
|
|
4133
|
-
return this.execute(
|
|
4539
|
+
const endpoint = this.endpoint("mine");
|
|
4540
|
+
return this.execute(endpoint, params).then(
|
|
4134
4541
|
(value) => validateCartMineResult(value)
|
|
4135
4542
|
);
|
|
4136
4543
|
}
|
|
4137
4544
|
merge(params) {
|
|
4138
4545
|
const { idempotencyKey, ...body } = params;
|
|
4139
4546
|
assertCommerceIdempotencyKey(idempotencyKey);
|
|
4140
|
-
const
|
|
4141
|
-
return this.execute(
|
|
4547
|
+
const endpoint = this.endpoint("merge");
|
|
4548
|
+
return this.execute(endpoint, body, idempotencyKey).then(
|
|
4142
4549
|
(value) => validateCartMergeResult(value)
|
|
4143
4550
|
);
|
|
4144
4551
|
}
|
|
4145
4552
|
addItem(params) {
|
|
4146
4553
|
return this.mutate(this.endpoint("addItem"), params);
|
|
4147
4554
|
}
|
|
4148
|
-
mutate(
|
|
4555
|
+
mutate(endpoint, params) {
|
|
4149
4556
|
const { idempotencyKey, ...body } = params;
|
|
4150
4557
|
assertCommerceIdempotencyKey(idempotencyKey);
|
|
4151
|
-
return this.execute(
|
|
4558
|
+
return this.execute(endpoint, body, idempotencyKey).then(
|
|
4152
4559
|
(value) => validateCartMutationResult(value)
|
|
4153
4560
|
);
|
|
4154
4561
|
}
|
|
@@ -4174,8 +4581,8 @@ var CheckoutApi = class extends CustomerScopedApi {
|
|
|
4174
4581
|
constructor(options) {
|
|
4175
4582
|
super("CheckoutApi", { ...options, requiresCredential: false });
|
|
4176
4583
|
}
|
|
4177
|
-
post(
|
|
4178
|
-
return this.requestEndpoint(
|
|
4584
|
+
post(endpoint, body, idempotencyKey) {
|
|
4585
|
+
return this.requestEndpoint(endpoint, body, {
|
|
4179
4586
|
...idempotencyKey && {
|
|
4180
4587
|
headers: { "X-Idempotency-Key": idempotencyKey }
|
|
4181
4588
|
}
|
|
@@ -4242,15 +4649,23 @@ var PaymentSessionApi = class extends CustomerScopedApi {
|
|
|
4242
4649
|
super("PaymentSessionApi", { ...options, requiresCredential: false });
|
|
4243
4650
|
this.serverTransport = Boolean(options.secretKey);
|
|
4244
4651
|
}
|
|
4245
|
-
post(
|
|
4246
|
-
|
|
4247
|
-
...idempotencyKey && {
|
|
4248
|
-
|
|
4652
|
+
post(endpoint, body, idempotencyKey, negotiateStandardActionCapabilities = false) {
|
|
4653
|
+
const headers = {
|
|
4654
|
+
...idempotencyKey && { "X-Idempotency-Key": idempotencyKey },
|
|
4655
|
+
...negotiateStandardActionCapabilities && {
|
|
4656
|
+
Accept: PORTONE_STANDARD_ACTION_CAPABILITIES_MEDIA_TYPE
|
|
4249
4657
|
}
|
|
4658
|
+
};
|
|
4659
|
+
return this.requestEndpoint(endpoint, body, {
|
|
4660
|
+
...Object.keys(headers).length > 0 && { headers }
|
|
4250
4661
|
});
|
|
4251
4662
|
}
|
|
4252
|
-
async paymentSession(result, expectedProvider) {
|
|
4253
|
-
return validatePublicPaymentSession(
|
|
4663
|
+
async paymentSession(result, expectedProvider, operation2) {
|
|
4664
|
+
return validatePublicPaymentSession(
|
|
4665
|
+
await result,
|
|
4666
|
+
expectedProvider,
|
|
4667
|
+
operation2
|
|
4668
|
+
);
|
|
4254
4669
|
}
|
|
4255
4670
|
create(params) {
|
|
4256
4671
|
const { idempotencyKey, ...body } = params;
|
|
@@ -4271,41 +4686,44 @@ var PaymentSessionApi = class extends CustomerScopedApi {
|
|
|
4271
4686
|
this.post(
|
|
4272
4687
|
this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.create : COMMERCE_ENDPOINTS.paymentSessions.create,
|
|
4273
4688
|
body,
|
|
4274
|
-
idempotencyKey
|
|
4689
|
+
idempotencyKey,
|
|
4690
|
+
body.providerOptions.profile === "standard_v2"
|
|
4275
4691
|
),
|
|
4276
4692
|
"portone"
|
|
4277
4693
|
);
|
|
4278
4694
|
}
|
|
4279
4695
|
retrieve(params) {
|
|
4280
|
-
const
|
|
4696
|
+
const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.retrieve : COMMERCE_ENDPOINTS.paymentSessions.retrieve;
|
|
4281
4697
|
return this.paymentSession(
|
|
4282
|
-
this.post(
|
|
4698
|
+
this.post(endpoint, params),
|
|
4283
4699
|
"toss"
|
|
4284
4700
|
);
|
|
4285
4701
|
}
|
|
4286
4702
|
retrievePortOne(params) {
|
|
4287
|
-
const
|
|
4703
|
+
const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.retrieve : COMMERCE_ENDPOINTS.paymentSessions.retrieve;
|
|
4288
4704
|
return this.paymentSession(
|
|
4289
|
-
this.post(
|
|
4705
|
+
this.post(endpoint, params, void 0, true),
|
|
4290
4706
|
"portone"
|
|
4291
4707
|
);
|
|
4292
4708
|
}
|
|
4293
4709
|
cancel(params) {
|
|
4294
4710
|
const { idempotencyKey, ...body } = params;
|
|
4295
4711
|
assertCommerceIdempotencyKey(idempotencyKey);
|
|
4296
|
-
const
|
|
4712
|
+
const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.cancel : COMMERCE_ENDPOINTS.paymentSessions.cancel;
|
|
4297
4713
|
return this.paymentSession(
|
|
4298
|
-
this.post(
|
|
4299
|
-
"toss"
|
|
4714
|
+
this.post(endpoint, body, idempotencyKey),
|
|
4715
|
+
"toss",
|
|
4716
|
+
"cancel"
|
|
4300
4717
|
);
|
|
4301
4718
|
}
|
|
4302
4719
|
cancelPortOne(params) {
|
|
4303
4720
|
const { idempotencyKey, ...body } = params;
|
|
4304
4721
|
assertCommerceIdempotencyKey(idempotencyKey);
|
|
4305
|
-
const
|
|
4722
|
+
const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.cancel : COMMERCE_ENDPOINTS.paymentSessions.cancel;
|
|
4306
4723
|
return this.paymentSession(
|
|
4307
|
-
this.post(
|
|
4308
|
-
"portone"
|
|
4724
|
+
this.post(endpoint, body, idempotencyKey),
|
|
4725
|
+
"portone",
|
|
4726
|
+
"cancel"
|
|
4309
4727
|
);
|
|
4310
4728
|
}
|
|
4311
4729
|
confirm(params) {
|
|
@@ -5445,8 +5863,8 @@ function withProductListingCards(response, options) {
|
|
|
5445
5863
|
|
|
5446
5864
|
// src/core/commerce/commerce-client.ts
|
|
5447
5865
|
var BrowserCommerceApi = class extends CustomerScopedApi {
|
|
5448
|
-
requestEndpoint(
|
|
5449
|
-
return super.requestEndpoint(
|
|
5866
|
+
requestEndpoint(endpoint, body, options) {
|
|
5867
|
+
return super.requestEndpoint(endpoint, body, options);
|
|
5450
5868
|
}
|
|
5451
5869
|
};
|
|
5452
5870
|
var CommerceClient = class {
|
|
@@ -5646,11 +6064,11 @@ var EventsClient = class {
|
|
|
5646
6064
|
{ useCustomerAuth: false }
|
|
5647
6065
|
);
|
|
5648
6066
|
}
|
|
5649
|
-
async execute(
|
|
6067
|
+
async execute(endpoint, method, body, options = {}) {
|
|
5650
6068
|
const useCustomerAuth = options.useCustomerAuth === true;
|
|
5651
6069
|
const token = useCustomerAuth ? typeof this.customerToken === "function" ? this.customerToken() : this.customerToken : void 0;
|
|
5652
6070
|
try {
|
|
5653
|
-
const response = await httpFetch(
|
|
6071
|
+
const response = await httpFetch(endpoint, {
|
|
5654
6072
|
method,
|
|
5655
6073
|
apiUrl: this.apiUrl,
|
|
5656
6074
|
publishableKey: this.publishableKey,
|
|
@@ -5659,7 +6077,7 @@ var EventsClient = class {
|
|
|
5659
6077
|
...body !== void 0 && { body: JSON.stringify(body) }
|
|
5660
6078
|
});
|
|
5661
6079
|
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
5662
|
-
return parseApiResponse(response,
|
|
6080
|
+
return parseApiResponse(response, endpoint);
|
|
5663
6081
|
} catch (err) {
|
|
5664
6082
|
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
5665
6083
|
this.onRequestId?.(id);
|