@bacano/sdk 1.2.7 → 1.2.8
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/CATALOG_MANAGEMENT.md +261 -0
- package/README.md +14 -5
- package/STOREFRONT_FORMS.md +38 -0
- package/STOREFRONT_LEADS.md +5 -1
- package/STOREFRONT_PAYMENTS.md +49 -2
- package/catalog-draft-types.d.ts +145 -0
- package/catalog-management-types.d.ts +143 -0
- package/catalog-management.cjs +125 -0
- package/catalog-management.d.ts +37 -0
- package/catalog-management.js +104 -0
- package/index.cjs +153 -63
- package/index.js +153 -63
- package/modules/checkout-contact.d.ts +4 -0
- package/package.json +6 -1
- package/react.cjs +153 -63
- package/react.js +153 -63
- package/skills/bacano-web/SKILL.md +10 -3
- package/skills/bacano-web/references/common.md +5 -0
- package/skills/bacano-web/references/forms.md +2 -0
- package/skills/bacano-web/references/storefront.md +15 -0
- package/skills/bacano-web/scripts/cli.mjs +28 -5
package/index.js
CHANGED
|
@@ -3277,6 +3277,78 @@ function hasRecoverableCartSessionError(errors) {
|
|
|
3277
3277
|
);
|
|
3278
3278
|
}
|
|
3279
3279
|
|
|
3280
|
+
// libs/sdk/src/modules/checkout-contact.ts
|
|
3281
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3282
|
+
var PHONE_PATTERN = /^\+?\d{7,15}$/;
|
|
3283
|
+
function validEmail(value) {
|
|
3284
|
+
if (value.length > 320 || !EMAIL_PATTERN.test(value)) return false;
|
|
3285
|
+
const [local, domain] = value.split("@");
|
|
3286
|
+
if (!local || !domain || local.length > 64 || domain.length > 255 || local.startsWith(".") || local.endsWith(".") || local.includes("..")) {
|
|
3287
|
+
return false;
|
|
3288
|
+
}
|
|
3289
|
+
return domain.split(".").every((label) => /^[a-z\d](?:[a-z\d-]*[a-z\d])?$/i.test(label));
|
|
3290
|
+
}
|
|
3291
|
+
function normalizePhone(value) {
|
|
3292
|
+
return value.trim().replace(/[\s().-]/g, "");
|
|
3293
|
+
}
|
|
3294
|
+
function invalidContact(field) {
|
|
3295
|
+
const messages = {
|
|
3296
|
+
email: "Revisa el correo electr\xF3nico: debe estar completo y sin espacios.",
|
|
3297
|
+
phone: "Revisa el n\xFAmero de tel\xE9fono: usa entre 7 y 15 d\xEDgitos.",
|
|
3298
|
+
contact: "Ingresa un correo electr\xF3nico o un n\xFAmero de tel\xE9fono v\xE1lido."
|
|
3299
|
+
};
|
|
3300
|
+
return new WebsiteError(
|
|
3301
|
+
messages[field],
|
|
3302
|
+
`INVALID_CHECKOUT_${field.toUpperCase()}`
|
|
3303
|
+
);
|
|
3304
|
+
}
|
|
3305
|
+
function normalizeCheckoutContact(contact, mode) {
|
|
3306
|
+
const email = contact.email?.trim() || void 0;
|
|
3307
|
+
const phone = contact.phone ? normalizePhone(contact.phone) : void 0;
|
|
3308
|
+
const cellphone = contact.cellphone ? normalizePhone(contact.cellphone) : void 0;
|
|
3309
|
+
const emailValid = !email || validEmail(email);
|
|
3310
|
+
const phoneValid = !phone || PHONE_PATTERN.test(phone);
|
|
3311
|
+
const cellphoneValid = !cellphone || PHONE_PATTERN.test(cellphone);
|
|
3312
|
+
if (mode === "submit") {
|
|
3313
|
+
if (!emailValid) throw invalidContact("email");
|
|
3314
|
+
if (!phoneValid || !cellphoneValid) throw invalidContact("phone");
|
|
3315
|
+
}
|
|
3316
|
+
const normalized = {
|
|
3317
|
+
...contact,
|
|
3318
|
+
name: contact.name?.trim() || void 0,
|
|
3319
|
+
firstName: contact.firstName?.trim() || void 0,
|
|
3320
|
+
lastName: contact.lastName?.trim() || void 0,
|
|
3321
|
+
email: emailValid ? email : void 0,
|
|
3322
|
+
phone: phoneValid ? phone : void 0,
|
|
3323
|
+
cellphone: cellphoneValid ? cellphone : void 0
|
|
3324
|
+
};
|
|
3325
|
+
if ((normalized.name?.length ?? 0) > 160 || (normalized.firstName?.length ?? 0) > 80 || (normalized.lastName?.length ?? 0) > 80) {
|
|
3326
|
+
throw new WebsiteError(
|
|
3327
|
+
"Revisa los datos de contacto: el nombre es demasiado largo.",
|
|
3328
|
+
"INVALID_CHECKOUT_CONTACT"
|
|
3329
|
+
);
|
|
3330
|
+
}
|
|
3331
|
+
if (!normalized.email && !normalized.phone && !normalized.cellphone) {
|
|
3332
|
+
throw invalidContact(
|
|
3333
|
+
!emailValid ? "email" : !phoneValid || !cellphoneValid ? "phone" : "contact"
|
|
3334
|
+
);
|
|
3335
|
+
}
|
|
3336
|
+
return normalized;
|
|
3337
|
+
}
|
|
3338
|
+
function explainCheckoutRequestError(error) {
|
|
3339
|
+
if (error instanceof GraphQLRequestError && error.graphqlErrors.some(
|
|
3340
|
+
(item) => /invalid request payload|hay un error en los datos del pedido/i.test(
|
|
3341
|
+
item.message
|
|
3342
|
+
)
|
|
3343
|
+
)) {
|
|
3344
|
+
throw new WebsiteError(
|
|
3345
|
+
"Hay un error en los datos del pedido. Rev\xEDsalos e intenta de nuevo.",
|
|
3346
|
+
"INVALID_CHECKOUT_DATA"
|
|
3347
|
+
);
|
|
3348
|
+
}
|
|
3349
|
+
throw error;
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3280
3352
|
// libs/sdk/src/modules/cart.ts
|
|
3281
3353
|
function mapCartItem(raw) {
|
|
3282
3354
|
return {
|
|
@@ -3352,6 +3424,15 @@ function requireCartSession(storage) {
|
|
|
3352
3424
|
return session;
|
|
3353
3425
|
}
|
|
3354
3426
|
function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage) {
|
|
3427
|
+
let pendingCartSessionOperation = Promise.resolve();
|
|
3428
|
+
function enqueueCartSessionOperation(run) {
|
|
3429
|
+
const operation = pendingCartSessionOperation.then(run, run);
|
|
3430
|
+
pendingCartSessionOperation = operation.then(
|
|
3431
|
+
() => void 0,
|
|
3432
|
+
() => void 0
|
|
3433
|
+
);
|
|
3434
|
+
return operation;
|
|
3435
|
+
}
|
|
3355
3436
|
async function fetchCart(orderId, cartToken) {
|
|
3356
3437
|
const data = await executeGraphQL(graphqlUrl, GET_CART, { orderId, cartToken }, getHeaders);
|
|
3357
3438
|
const result = data.websiteGetCart;
|
|
@@ -3374,72 +3455,80 @@ function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage
|
|
|
3374
3455
|
}
|
|
3375
3456
|
return {
|
|
3376
3457
|
async getOrCreate(contact, fulfillmentFamily) {
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
CREATE_CART,
|
|
3388
|
-
{
|
|
3389
|
-
websiteId: ctx.websiteId,
|
|
3390
|
-
cartToken: existing?.cartToken,
|
|
3391
|
-
contact,
|
|
3392
|
-
fulfillmentFamily
|
|
3393
|
-
},
|
|
3394
|
-
getHeaders
|
|
3395
|
-
);
|
|
3396
|
-
const result = data.websiteCreateCart;
|
|
3397
|
-
assertActionSuccess(result, "CART_CREATE_ERROR");
|
|
3398
|
-
cartSessionStorage.set({
|
|
3399
|
-
orderId: result.order_id,
|
|
3400
|
-
cartToken: result.cart_token
|
|
3401
|
-
});
|
|
3402
|
-
return mapCart(result.cart, result.cart_token);
|
|
3403
|
-
},
|
|
3404
|
-
async startCheckout(input) {
|
|
3405
|
-
const ctx = getContext();
|
|
3406
|
-
if (!ctx.chainId) {
|
|
3407
|
-
throw new WebsiteError(
|
|
3408
|
-
"This website has no chain configured. Checkout requires a chain_id.",
|
|
3409
|
-
"NO_CHAIN"
|
|
3410
|
-
);
|
|
3411
|
-
}
|
|
3412
|
-
const requestStartCheckout = async (cartToken) => {
|
|
3458
|
+
return enqueueCartSessionOperation(async () => {
|
|
3459
|
+
const normalizedContact = contact ? normalizeCheckoutContact(contact, "draft") : void 0;
|
|
3460
|
+
const ctx = getContext();
|
|
3461
|
+
if (!ctx.chainId) {
|
|
3462
|
+
throw new WebsiteError(
|
|
3463
|
+
"This website has no chain configured. Cart requires a chain_id.",
|
|
3464
|
+
"NO_CHAIN"
|
|
3465
|
+
);
|
|
3466
|
+
}
|
|
3467
|
+
const existing = cartSessionStorage.get();
|
|
3413
3468
|
const data = await executeGraphQL(
|
|
3414
3469
|
graphqlUrl,
|
|
3415
|
-
|
|
3470
|
+
CREATE_CART,
|
|
3416
3471
|
{
|
|
3417
3472
|
websiteId: ctx.websiteId,
|
|
3418
|
-
cartToken,
|
|
3419
|
-
contact:
|
|
3420
|
-
fulfillmentFamily
|
|
3421
|
-
items: input.items.map((item) => ({
|
|
3422
|
-
product_variant_id: item.productVariantId,
|
|
3423
|
-
quantity: item.quantity
|
|
3424
|
-
})),
|
|
3425
|
-
marketingAttribution: input.marketingAttribution ? serializeWebsiteMarketingAttribution(input.marketingAttribution) : void 0
|
|
3473
|
+
cartToken: existing?.cartToken,
|
|
3474
|
+
contact: normalizedContact,
|
|
3475
|
+
fulfillmentFamily
|
|
3426
3476
|
},
|
|
3427
3477
|
getHeaders
|
|
3428
|
-
);
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
result
|
|
3436
|
-
}
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3478
|
+
).catch(explainCheckoutRequestError);
|
|
3479
|
+
const result = data.websiteCreateCart;
|
|
3480
|
+
assertActionSuccess(result, "CART_CREATE_ERROR");
|
|
3481
|
+
cartSessionStorage.set({
|
|
3482
|
+
orderId: result.order_id,
|
|
3483
|
+
cartToken: result.cart_token
|
|
3484
|
+
});
|
|
3485
|
+
return mapCart(result.cart, result.cart_token);
|
|
3486
|
+
});
|
|
3487
|
+
},
|
|
3488
|
+
async startCheckout(input) {
|
|
3489
|
+
return enqueueCartSessionOperation(async () => {
|
|
3490
|
+
const contact = normalizeCheckoutContact(input.contact, "draft");
|
|
3491
|
+
const ctx = getContext();
|
|
3492
|
+
if (!ctx.chainId) {
|
|
3493
|
+
throw new WebsiteError(
|
|
3494
|
+
"This website has no chain configured. Checkout requires a chain_id.",
|
|
3495
|
+
"NO_CHAIN"
|
|
3496
|
+
);
|
|
3497
|
+
}
|
|
3498
|
+
const requestStartCheckout = async (cartToken) => {
|
|
3499
|
+
const data = await executeGraphQL(
|
|
3500
|
+
graphqlUrl,
|
|
3501
|
+
START_GUEST_CHECKOUT,
|
|
3502
|
+
{
|
|
3503
|
+
websiteId: ctx.websiteId,
|
|
3504
|
+
cartToken,
|
|
3505
|
+
contact,
|
|
3506
|
+
fulfillmentFamily: input.fulfillmentFamily,
|
|
3507
|
+
items: input.items.map((item) => ({
|
|
3508
|
+
product_variant_id: item.productVariantId,
|
|
3509
|
+
quantity: item.quantity
|
|
3510
|
+
})),
|
|
3511
|
+
marketingAttribution: input.marketingAttribution ? serializeWebsiteMarketingAttribution(
|
|
3512
|
+
input.marketingAttribution
|
|
3513
|
+
) : void 0
|
|
3514
|
+
},
|
|
3515
|
+
getHeaders
|
|
3516
|
+
).catch(explainCheckoutRequestError);
|
|
3517
|
+
return data.websiteStartGuestCheckout;
|
|
3518
|
+
};
|
|
3519
|
+
const existing = cartSessionStorage.get();
|
|
3520
|
+
let result = await requestStartCheckout(existing?.cartToken);
|
|
3521
|
+
if (existing && !result.success && hasRecoverableCartSessionError(result.errors)) {
|
|
3522
|
+
cartSessionStorage.clear();
|
|
3523
|
+
result = await requestStartCheckout();
|
|
3524
|
+
}
|
|
3525
|
+
assertActionSuccess(result, "CHECKOUT_START_ERROR");
|
|
3526
|
+
cartSessionStorage.set({
|
|
3527
|
+
orderId: result.order_id,
|
|
3528
|
+
cartToken: result.cart_token
|
|
3529
|
+
});
|
|
3530
|
+
return mapCart(result.cart, result.cart_token);
|
|
3441
3531
|
});
|
|
3442
|
-
return mapCart(result.cart, result.cart_token);
|
|
3443
3532
|
},
|
|
3444
3533
|
async get() {
|
|
3445
3534
|
const session = cartSessionStorage.get();
|
|
@@ -5538,6 +5627,7 @@ function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionSto
|
|
|
5538
5627
|
return mapCouponResult(data.websiteRemoveCoupon);
|
|
5539
5628
|
},
|
|
5540
5629
|
async submit(input) {
|
|
5630
|
+
const contact = normalizeCheckoutContact(input.contact, "submit");
|
|
5541
5631
|
const session = requireCartSession2(cartSessionStorage);
|
|
5542
5632
|
const data = await executeGraphQL(
|
|
5543
5633
|
graphqlUrl,
|
|
@@ -5545,7 +5635,7 @@ function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionSto
|
|
|
5545
5635
|
{
|
|
5546
5636
|
orderId: session.orderId,
|
|
5547
5637
|
cartToken: session.cartToken,
|
|
5548
|
-
contact
|
|
5638
|
+
contact,
|
|
5549
5639
|
fulfillmentFamily: input.fulfillmentFamily,
|
|
5550
5640
|
address: input.address,
|
|
5551
5641
|
shippingQuoteId: input.shippingQuoteId,
|
|
@@ -5565,7 +5655,7 @@ function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionSto
|
|
|
5565
5655
|
} : void 0
|
|
5566
5656
|
},
|
|
5567
5657
|
getHeaders
|
|
5568
|
-
);
|
|
5658
|
+
).catch(explainCheckoutRequestError);
|
|
5569
5659
|
const result = data.websiteCheckout;
|
|
5570
5660
|
if (!result.success && result.errors && result.errors.length > 0) {
|
|
5571
5661
|
throw new CheckoutError(result.errors);
|
|
@@ -5606,7 +5696,7 @@ function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionSto
|
|
|
5606
5696
|
returnUrl: input.returnUrl
|
|
5607
5697
|
},
|
|
5608
5698
|
getHeaders
|
|
5609
|
-
);
|
|
5699
|
+
).catch(explainCheckoutRequestError);
|
|
5610
5700
|
const result = data.websiteCreatePaymentSession;
|
|
5611
5701
|
if (result.success && result.payment_session_id && (result.integration_mode !== "redirect" || !result.redirect_url)) {
|
|
5612
5702
|
throw new WebsiteError(
|
|
@@ -5641,7 +5731,7 @@ function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionSto
|
|
|
5641
5731
|
gatewayTransactionId: input.gatewayTransactionId
|
|
5642
5732
|
},
|
|
5643
5733
|
getHeaders
|
|
5644
|
-
);
|
|
5734
|
+
).catch(explainCheckoutRequestError);
|
|
5645
5735
|
const result = data.websiteReconcilePaymentSession;
|
|
5646
5736
|
return {
|
|
5647
5737
|
success: result.success,
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { WebsiteGuestContact } from "../types";
|
|
2
|
+
/** Drafts can use the valid contact channel while the buyer edits the other one. */
|
|
3
|
+
export declare function normalizeCheckoutContact(contact: WebsiteGuestContact, mode: "draft" | "submit"): WebsiteGuestContact;
|
|
4
|
+
export declare function explainCheckoutRequestError(error: unknown): never;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bacano/sdk",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.8",
|
|
4
4
|
"description": "TypeScript SDK for building websites on the Bacano ERP",
|
|
5
5
|
"main": "./index.cjs",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -33,6 +33,11 @@
|
|
|
33
33
|
"import": "./content-management.js",
|
|
34
34
|
"require": "./content-management.cjs"
|
|
35
35
|
},
|
|
36
|
+
"./catalog-management": {
|
|
37
|
+
"types": "./catalog-management.d.ts",
|
|
38
|
+
"import": "./catalog-management.js",
|
|
39
|
+
"require": "./catalog-management.cjs"
|
|
40
|
+
},
|
|
36
41
|
"./react": {
|
|
37
42
|
"types": "./react.d.ts",
|
|
38
43
|
"import": "./react.js",
|
package/react.cjs
CHANGED
|
@@ -5930,6 +5930,78 @@ function hasRecoverableCartSessionError(errors) {
|
|
|
5930
5930
|
);
|
|
5931
5931
|
}
|
|
5932
5932
|
|
|
5933
|
+
// libs/sdk/src/modules/checkout-contact.ts
|
|
5934
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
5935
|
+
var PHONE_PATTERN = /^\+?\d{7,15}$/;
|
|
5936
|
+
function validEmail(value) {
|
|
5937
|
+
if (value.length > 320 || !EMAIL_PATTERN.test(value)) return false;
|
|
5938
|
+
const [local, domain] = value.split("@");
|
|
5939
|
+
if (!local || !domain || local.length > 64 || domain.length > 255 || local.startsWith(".") || local.endsWith(".") || local.includes("..")) {
|
|
5940
|
+
return false;
|
|
5941
|
+
}
|
|
5942
|
+
return domain.split(".").every((label) => /^[a-z\d](?:[a-z\d-]*[a-z\d])?$/i.test(label));
|
|
5943
|
+
}
|
|
5944
|
+
function normalizePhone(value) {
|
|
5945
|
+
return value.trim().replace(/[\s().-]/g, "");
|
|
5946
|
+
}
|
|
5947
|
+
function invalidContact(field) {
|
|
5948
|
+
const messages = {
|
|
5949
|
+
email: "Revisa el correo electr\xF3nico: debe estar completo y sin espacios.",
|
|
5950
|
+
phone: "Revisa el n\xFAmero de tel\xE9fono: usa entre 7 y 15 d\xEDgitos.",
|
|
5951
|
+
contact: "Ingresa un correo electr\xF3nico o un n\xFAmero de tel\xE9fono v\xE1lido."
|
|
5952
|
+
};
|
|
5953
|
+
return new WebsiteError(
|
|
5954
|
+
messages[field],
|
|
5955
|
+
`INVALID_CHECKOUT_${field.toUpperCase()}`
|
|
5956
|
+
);
|
|
5957
|
+
}
|
|
5958
|
+
function normalizeCheckoutContact(contact, mode) {
|
|
5959
|
+
const email = contact.email?.trim() || void 0;
|
|
5960
|
+
const phone = contact.phone ? normalizePhone(contact.phone) : void 0;
|
|
5961
|
+
const cellphone = contact.cellphone ? normalizePhone(contact.cellphone) : void 0;
|
|
5962
|
+
const emailValid = !email || validEmail(email);
|
|
5963
|
+
const phoneValid = !phone || PHONE_PATTERN.test(phone);
|
|
5964
|
+
const cellphoneValid = !cellphone || PHONE_PATTERN.test(cellphone);
|
|
5965
|
+
if (mode === "submit") {
|
|
5966
|
+
if (!emailValid) throw invalidContact("email");
|
|
5967
|
+
if (!phoneValid || !cellphoneValid) throw invalidContact("phone");
|
|
5968
|
+
}
|
|
5969
|
+
const normalized = {
|
|
5970
|
+
...contact,
|
|
5971
|
+
name: contact.name?.trim() || void 0,
|
|
5972
|
+
firstName: contact.firstName?.trim() || void 0,
|
|
5973
|
+
lastName: contact.lastName?.trim() || void 0,
|
|
5974
|
+
email: emailValid ? email : void 0,
|
|
5975
|
+
phone: phoneValid ? phone : void 0,
|
|
5976
|
+
cellphone: cellphoneValid ? cellphone : void 0
|
|
5977
|
+
};
|
|
5978
|
+
if ((normalized.name?.length ?? 0) > 160 || (normalized.firstName?.length ?? 0) > 80 || (normalized.lastName?.length ?? 0) > 80) {
|
|
5979
|
+
throw new WebsiteError(
|
|
5980
|
+
"Revisa los datos de contacto: el nombre es demasiado largo.",
|
|
5981
|
+
"INVALID_CHECKOUT_CONTACT"
|
|
5982
|
+
);
|
|
5983
|
+
}
|
|
5984
|
+
if (!normalized.email && !normalized.phone && !normalized.cellphone) {
|
|
5985
|
+
throw invalidContact(
|
|
5986
|
+
!emailValid ? "email" : !phoneValid || !cellphoneValid ? "phone" : "contact"
|
|
5987
|
+
);
|
|
5988
|
+
}
|
|
5989
|
+
return normalized;
|
|
5990
|
+
}
|
|
5991
|
+
function explainCheckoutRequestError(error) {
|
|
5992
|
+
if (error instanceof GraphQLRequestError && error.graphqlErrors.some(
|
|
5993
|
+
(item) => /invalid request payload|hay un error en los datos del pedido/i.test(
|
|
5994
|
+
item.message
|
|
5995
|
+
)
|
|
5996
|
+
)) {
|
|
5997
|
+
throw new WebsiteError(
|
|
5998
|
+
"Hay un error en los datos del pedido. Rev\xEDsalos e intenta de nuevo.",
|
|
5999
|
+
"INVALID_CHECKOUT_DATA"
|
|
6000
|
+
);
|
|
6001
|
+
}
|
|
6002
|
+
throw error;
|
|
6003
|
+
}
|
|
6004
|
+
|
|
5933
6005
|
// libs/sdk/src/modules/cart.ts
|
|
5934
6006
|
function mapCartItem(raw) {
|
|
5935
6007
|
return {
|
|
@@ -6005,6 +6077,15 @@ function requireCartSession(storage) {
|
|
|
6005
6077
|
return session;
|
|
6006
6078
|
}
|
|
6007
6079
|
function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage) {
|
|
6080
|
+
let pendingCartSessionOperation = Promise.resolve();
|
|
6081
|
+
function enqueueCartSessionOperation(run) {
|
|
6082
|
+
const operation = pendingCartSessionOperation.then(run, run);
|
|
6083
|
+
pendingCartSessionOperation = operation.then(
|
|
6084
|
+
() => void 0,
|
|
6085
|
+
() => void 0
|
|
6086
|
+
);
|
|
6087
|
+
return operation;
|
|
6088
|
+
}
|
|
6008
6089
|
async function fetchCart(orderId, cartToken) {
|
|
6009
6090
|
const data = await executeGraphQL(graphqlUrl, GET_CART, { orderId, cartToken }, getHeaders);
|
|
6010
6091
|
const result = data.websiteGetCart;
|
|
@@ -6027,72 +6108,80 @@ function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage
|
|
|
6027
6108
|
}
|
|
6028
6109
|
return {
|
|
6029
6110
|
async getOrCreate(contact, fulfillmentFamily) {
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
CREATE_CART,
|
|
6041
|
-
{
|
|
6042
|
-
websiteId: ctx.websiteId,
|
|
6043
|
-
cartToken: existing?.cartToken,
|
|
6044
|
-
contact,
|
|
6045
|
-
fulfillmentFamily
|
|
6046
|
-
},
|
|
6047
|
-
getHeaders
|
|
6048
|
-
);
|
|
6049
|
-
const result = data.websiteCreateCart;
|
|
6050
|
-
assertActionSuccess(result, "CART_CREATE_ERROR");
|
|
6051
|
-
cartSessionStorage.set({
|
|
6052
|
-
orderId: result.order_id,
|
|
6053
|
-
cartToken: result.cart_token
|
|
6054
|
-
});
|
|
6055
|
-
return mapCart(result.cart, result.cart_token);
|
|
6056
|
-
},
|
|
6057
|
-
async startCheckout(input) {
|
|
6058
|
-
const ctx = getContext();
|
|
6059
|
-
if (!ctx.chainId) {
|
|
6060
|
-
throw new WebsiteError(
|
|
6061
|
-
"This website has no chain configured. Checkout requires a chain_id.",
|
|
6062
|
-
"NO_CHAIN"
|
|
6063
|
-
);
|
|
6064
|
-
}
|
|
6065
|
-
const requestStartCheckout = async (cartToken) => {
|
|
6111
|
+
return enqueueCartSessionOperation(async () => {
|
|
6112
|
+
const normalizedContact = contact ? normalizeCheckoutContact(contact, "draft") : void 0;
|
|
6113
|
+
const ctx = getContext();
|
|
6114
|
+
if (!ctx.chainId) {
|
|
6115
|
+
throw new WebsiteError(
|
|
6116
|
+
"This website has no chain configured. Cart requires a chain_id.",
|
|
6117
|
+
"NO_CHAIN"
|
|
6118
|
+
);
|
|
6119
|
+
}
|
|
6120
|
+
const existing = cartSessionStorage.get();
|
|
6066
6121
|
const data = await executeGraphQL(
|
|
6067
6122
|
graphqlUrl,
|
|
6068
|
-
|
|
6123
|
+
CREATE_CART,
|
|
6069
6124
|
{
|
|
6070
6125
|
websiteId: ctx.websiteId,
|
|
6071
|
-
cartToken,
|
|
6072
|
-
contact:
|
|
6073
|
-
fulfillmentFamily
|
|
6074
|
-
items: input.items.map((item) => ({
|
|
6075
|
-
product_variant_id: item.productVariantId,
|
|
6076
|
-
quantity: item.quantity
|
|
6077
|
-
})),
|
|
6078
|
-
marketingAttribution: input.marketingAttribution ? serializeWebsiteMarketingAttribution(input.marketingAttribution) : void 0
|
|
6126
|
+
cartToken: existing?.cartToken,
|
|
6127
|
+
contact: normalizedContact,
|
|
6128
|
+
fulfillmentFamily
|
|
6079
6129
|
},
|
|
6080
6130
|
getHeaders
|
|
6081
|
-
);
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
result
|
|
6089
|
-
}
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6131
|
+
).catch(explainCheckoutRequestError);
|
|
6132
|
+
const result = data.websiteCreateCart;
|
|
6133
|
+
assertActionSuccess(result, "CART_CREATE_ERROR");
|
|
6134
|
+
cartSessionStorage.set({
|
|
6135
|
+
orderId: result.order_id,
|
|
6136
|
+
cartToken: result.cart_token
|
|
6137
|
+
});
|
|
6138
|
+
return mapCart(result.cart, result.cart_token);
|
|
6139
|
+
});
|
|
6140
|
+
},
|
|
6141
|
+
async startCheckout(input) {
|
|
6142
|
+
return enqueueCartSessionOperation(async () => {
|
|
6143
|
+
const contact = normalizeCheckoutContact(input.contact, "draft");
|
|
6144
|
+
const ctx = getContext();
|
|
6145
|
+
if (!ctx.chainId) {
|
|
6146
|
+
throw new WebsiteError(
|
|
6147
|
+
"This website has no chain configured. Checkout requires a chain_id.",
|
|
6148
|
+
"NO_CHAIN"
|
|
6149
|
+
);
|
|
6150
|
+
}
|
|
6151
|
+
const requestStartCheckout = async (cartToken) => {
|
|
6152
|
+
const data = await executeGraphQL(
|
|
6153
|
+
graphqlUrl,
|
|
6154
|
+
START_GUEST_CHECKOUT,
|
|
6155
|
+
{
|
|
6156
|
+
websiteId: ctx.websiteId,
|
|
6157
|
+
cartToken,
|
|
6158
|
+
contact,
|
|
6159
|
+
fulfillmentFamily: input.fulfillmentFamily,
|
|
6160
|
+
items: input.items.map((item) => ({
|
|
6161
|
+
product_variant_id: item.productVariantId,
|
|
6162
|
+
quantity: item.quantity
|
|
6163
|
+
})),
|
|
6164
|
+
marketingAttribution: input.marketingAttribution ? serializeWebsiteMarketingAttribution(
|
|
6165
|
+
input.marketingAttribution
|
|
6166
|
+
) : void 0
|
|
6167
|
+
},
|
|
6168
|
+
getHeaders
|
|
6169
|
+
).catch(explainCheckoutRequestError);
|
|
6170
|
+
return data.websiteStartGuestCheckout;
|
|
6171
|
+
};
|
|
6172
|
+
const existing = cartSessionStorage.get();
|
|
6173
|
+
let result = await requestStartCheckout(existing?.cartToken);
|
|
6174
|
+
if (existing && !result.success && hasRecoverableCartSessionError(result.errors)) {
|
|
6175
|
+
cartSessionStorage.clear();
|
|
6176
|
+
result = await requestStartCheckout();
|
|
6177
|
+
}
|
|
6178
|
+
assertActionSuccess(result, "CHECKOUT_START_ERROR");
|
|
6179
|
+
cartSessionStorage.set({
|
|
6180
|
+
orderId: result.order_id,
|
|
6181
|
+
cartToken: result.cart_token
|
|
6182
|
+
});
|
|
6183
|
+
return mapCart(result.cart, result.cart_token);
|
|
6094
6184
|
});
|
|
6095
|
-
return mapCart(result.cart, result.cart_token);
|
|
6096
6185
|
},
|
|
6097
6186
|
async get() {
|
|
6098
6187
|
const session = cartSessionStorage.get();
|
|
@@ -6631,6 +6720,7 @@ function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionSto
|
|
|
6631
6720
|
return mapCouponResult(data.websiteRemoveCoupon);
|
|
6632
6721
|
},
|
|
6633
6722
|
async submit(input) {
|
|
6723
|
+
const contact = normalizeCheckoutContact(input.contact, "submit");
|
|
6634
6724
|
const session = requireCartSession2(cartSessionStorage);
|
|
6635
6725
|
const data = await executeGraphQL(
|
|
6636
6726
|
graphqlUrl,
|
|
@@ -6638,7 +6728,7 @@ function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionSto
|
|
|
6638
6728
|
{
|
|
6639
6729
|
orderId: session.orderId,
|
|
6640
6730
|
cartToken: session.cartToken,
|
|
6641
|
-
contact
|
|
6731
|
+
contact,
|
|
6642
6732
|
fulfillmentFamily: input.fulfillmentFamily,
|
|
6643
6733
|
address: input.address,
|
|
6644
6734
|
shippingQuoteId: input.shippingQuoteId,
|
|
@@ -6658,7 +6748,7 @@ function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionSto
|
|
|
6658
6748
|
} : void 0
|
|
6659
6749
|
},
|
|
6660
6750
|
getHeaders
|
|
6661
|
-
);
|
|
6751
|
+
).catch(explainCheckoutRequestError);
|
|
6662
6752
|
const result = data.websiteCheckout;
|
|
6663
6753
|
if (!result.success && result.errors && result.errors.length > 0) {
|
|
6664
6754
|
throw new CheckoutError(result.errors);
|
|
@@ -6699,7 +6789,7 @@ function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionSto
|
|
|
6699
6789
|
returnUrl: input.returnUrl
|
|
6700
6790
|
},
|
|
6701
6791
|
getHeaders
|
|
6702
|
-
);
|
|
6792
|
+
).catch(explainCheckoutRequestError);
|
|
6703
6793
|
const result = data.websiteCreatePaymentSession;
|
|
6704
6794
|
if (result.success && result.payment_session_id && (result.integration_mode !== "redirect" || !result.redirect_url)) {
|
|
6705
6795
|
throw new WebsiteError(
|
|
@@ -6734,7 +6824,7 @@ function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionSto
|
|
|
6734
6824
|
gatewayTransactionId: input.gatewayTransactionId
|
|
6735
6825
|
},
|
|
6736
6826
|
getHeaders
|
|
6737
|
-
);
|
|
6827
|
+
).catch(explainCheckoutRequestError);
|
|
6738
6828
|
const result = data.websiteReconcilePaymentSession;
|
|
6739
6829
|
return {
|
|
6740
6830
|
success: result.success,
|