@fayz-ai/sdk 0.10.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 +1 -1
- package/dist/automations.d.ts +94 -0
- package/dist/automations.d.ts.map +1 -0
- package/dist/automations.js +61 -0
- package/dist/automations.js.map +1 -0
- package/dist/{chunk-7UXTYT6I.js → chunk-4YRHC5N7.js} +490 -31
- package/dist/chunk-4YRHC5N7.js.map +1 -0
- package/dist/chunk-7TTGX2SM.js +135 -0
- package/dist/chunk-7TTGX2SM.js.map +1 -0
- package/dist/events.d.ts +48 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +101 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +307 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/app-shell-pwa.d.ts +13 -0
- package/dist/lib/app-shell-pwa.d.ts.map +1 -0
- package/dist/lib/boot-skeleton.d.ts +33 -0
- package/dist/lib/boot-skeleton.d.ts.map +1 -0
- package/dist/lib/connector-oauth.d.ts +118 -0
- package/dist/lib/connector-oauth.d.ts.map +1 -0
- package/dist/lib/credentials.d.ts +98 -0
- package/dist/lib/credentials.d.ts.map +1 -0
- package/dist/lib/dedupe-fetch.d.ts +26 -0
- package/dist/lib/dedupe-fetch.d.ts.map +1 -0
- package/dist/lib/platform-url.d.ts +15 -0
- package/dist/lib/platform-url.d.ts.map +1 -0
- package/dist/lib/runtime-token.d.ts +104 -0
- package/dist/lib/runtime-token.d.ts.map +1 -0
- package/dist/release-channels.js +1 -1
- package/dist/release-channels.json +93 -93
- package/dist/shop.d.ts +381 -11
- package/dist/shop.d.ts.map +1 -1
- package/dist/shop.js +1 -1
- package/dist/vite.d.ts +19 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +374 -47
- package/dist/vite.js.map +1 -1
- package/package.json +9 -1
- package/dist/chunk-7UXTYT6I.js.map +0 -1
- package/dist/chunk-KVMRRT3O.js +0 -135
- package/dist/chunk-KVMRRT3O.js.map +0 -1
|
@@ -1,10 +1,130 @@
|
|
|
1
1
|
import { resetDeprecationWarnings, warnDeprecated } from './chunk-HA5O3CW7.js';
|
|
2
2
|
|
|
3
|
+
// src/lib/dedupe-fetch.ts
|
|
4
|
+
var DEFAULT_VARY = [
|
|
5
|
+
"authorization",
|
|
6
|
+
// a different user sees different rows under RLS
|
|
7
|
+
"apikey",
|
|
8
|
+
"accept",
|
|
9
|
+
"accept-profile",
|
|
10
|
+
// PostgREST schema selector
|
|
11
|
+
"content-profile",
|
|
12
|
+
"range",
|
|
13
|
+
// pagination window
|
|
14
|
+
"range-unit",
|
|
15
|
+
"prefer",
|
|
16
|
+
// count=exact changes Content-Range
|
|
17
|
+
"x-fayz-store",
|
|
18
|
+
// storefront tenant scope
|
|
19
|
+
"x-fayz-tenant"
|
|
20
|
+
];
|
|
21
|
+
var collapsed = 0;
|
|
22
|
+
var flights = /* @__PURE__ */ new Map();
|
|
23
|
+
function getDedupeStats() {
|
|
24
|
+
return { collapsed, inFlight: flights.size };
|
|
25
|
+
}
|
|
26
|
+
function resetDedupeStats() {
|
|
27
|
+
collapsed = 0;
|
|
28
|
+
flights.clear();
|
|
29
|
+
}
|
|
30
|
+
function headerValue(headers, name) {
|
|
31
|
+
if (!headers) return "";
|
|
32
|
+
if (headers instanceof Headers) return headers.get(name) ?? "";
|
|
33
|
+
if (Array.isArray(headers)) {
|
|
34
|
+
const hit2 = headers.find(([k]) => k.toLowerCase() === name);
|
|
35
|
+
return hit2?.[1] ?? "";
|
|
36
|
+
}
|
|
37
|
+
const hit = Object.entries(headers).find(([k]) => k.toLowerCase() === name);
|
|
38
|
+
return hit ? String(hit[1]) : "";
|
|
39
|
+
}
|
|
40
|
+
function bodylessStatus(status) {
|
|
41
|
+
return status === 204 || status === 205 || status === 304;
|
|
42
|
+
}
|
|
43
|
+
function createDedupedFetch(baseFetch = (...args) => fetch(...args), options = {}) {
|
|
44
|
+
const vary = [...DEFAULT_VARY, ...(options.varyHeaders ?? []).map((h) => h.toLowerCase())];
|
|
45
|
+
return async function dedupedFetch(input, init) {
|
|
46
|
+
const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
47
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
48
|
+
if (method !== "GET" && method !== "HEAD" || init?.signal || input instanceof Request) {
|
|
49
|
+
return baseFetch(input, init);
|
|
50
|
+
}
|
|
51
|
+
const key = [method, url, ...vary.map((h) => headerValue(init?.headers, h))].join("\n");
|
|
52
|
+
let flight = flights.get(key);
|
|
53
|
+
if (flight) {
|
|
54
|
+
collapsed++;
|
|
55
|
+
options.onCollapse?.(key);
|
|
56
|
+
} else {
|
|
57
|
+
flight = (async () => {
|
|
58
|
+
const res = await baseFetch(input, init);
|
|
59
|
+
return {
|
|
60
|
+
status: res.status,
|
|
61
|
+
statusText: res.statusText,
|
|
62
|
+
headers: [...res.headers.entries()],
|
|
63
|
+
// Buffered so each caller can be handed its own Response: a body can
|
|
64
|
+
// only be read once, and `clone()` on a consumed response throws.
|
|
65
|
+
body: bodylessStatus(res.status) || method === "HEAD" ? new ArrayBuffer(0) : await res.arrayBuffer()
|
|
66
|
+
};
|
|
67
|
+
})();
|
|
68
|
+
flights.set(key, flight);
|
|
69
|
+
void flight.catch(() => {
|
|
70
|
+
}).finally(() => flights.delete(key));
|
|
71
|
+
}
|
|
72
|
+
const buffered = await flight;
|
|
73
|
+
const hasBody = !bodylessStatus(buffered.status) && method !== "HEAD";
|
|
74
|
+
return new Response(hasBody ? buffered.body.slice(0) : null, {
|
|
75
|
+
status: buffered.status,
|
|
76
|
+
statusText: buffered.statusText,
|
|
77
|
+
headers: buffered.headers
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
3
82
|
// src/shop.ts
|
|
4
83
|
var _accessTokenResolver = null;
|
|
5
84
|
function setShopAccessTokenResolver(resolver) {
|
|
6
85
|
_accessTokenResolver = resolver;
|
|
7
86
|
}
|
|
87
|
+
var CARRIER_QUOTE_BUDGET_MS = 2500;
|
|
88
|
+
var CARRIER_OPTION_SEPARATOR = "::";
|
|
89
|
+
var CHECKOUT_GATEWAYS = {
|
|
90
|
+
abacatepay: { open: "shop-checkout-open", settle: "shop-checkout-settle" },
|
|
91
|
+
vindi: { open: "vindi-checkout-open", settle: "vindi-checkout-settle" }
|
|
92
|
+
};
|
|
93
|
+
function checkoutGateway(provider) {
|
|
94
|
+
const slug = String(provider ?? "").trim();
|
|
95
|
+
if (!slug) return null;
|
|
96
|
+
return CHECKOUT_GATEWAYS[slug] ?? null;
|
|
97
|
+
}
|
|
98
|
+
var _shippingItemsResolver = null;
|
|
99
|
+
function setShopShippingItemsResolver(resolver) {
|
|
100
|
+
_shippingItemsResolver = resolver;
|
|
101
|
+
}
|
|
102
|
+
var _carrierQuotesResolver = null;
|
|
103
|
+
function setShopCarrierQuotesResolver(resolver) {
|
|
104
|
+
_carrierQuotesResolver = resolver;
|
|
105
|
+
}
|
|
106
|
+
function mayAskCarrier() {
|
|
107
|
+
try {
|
|
108
|
+
return _carrierQuotesResolver?.() !== false;
|
|
109
|
+
} catch {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function getCartItemsForQuote() {
|
|
114
|
+
try {
|
|
115
|
+
return _shippingItemsResolver?.() ?? [];
|
|
116
|
+
} catch {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function cartIsFullyDigital(items) {
|
|
121
|
+
return items.length > 0 && items.every((item) => item.isPhysical === false);
|
|
122
|
+
}
|
|
123
|
+
var numOrNull = (value) => {
|
|
124
|
+
if (value == null || value === "") return null;
|
|
125
|
+
const n = Number(value);
|
|
126
|
+
return Number.isFinite(n) ? n : null;
|
|
127
|
+
};
|
|
8
128
|
var FayzShopError = class extends Error {
|
|
9
129
|
constructor(message, status, responseBody) {
|
|
10
130
|
super(message);
|
|
@@ -91,6 +211,9 @@ function rowToProduct(row, overlay) {
|
|
|
91
211
|
isPhysical: row.is_physical ?? true,
|
|
92
212
|
weight: row.weight ?? null,
|
|
93
213
|
weightUnit: row.weight_unit ?? "kg",
|
|
214
|
+
heightCm: numOrNull(row.height_cm),
|
|
215
|
+
widthCm: numOrNull(row.width_cm),
|
|
216
|
+
lengthCm: numOrNull(row.length_cm),
|
|
94
217
|
createdAt: row.created_at ?? nowIso(),
|
|
95
218
|
updatedAt: row.updated_at ?? row.created_at ?? nowIso()
|
|
96
219
|
};
|
|
@@ -155,6 +278,18 @@ function rowToOrder(row) {
|
|
|
155
278
|
paymentMethodKind: row.payment_method_kind ?? null,
|
|
156
279
|
paidAt: row.paid_at ?? null,
|
|
157
280
|
items: Array.isArray(row.items) ? row.items.map(rowToOrderItem) : [],
|
|
281
|
+
// Absent on the list endpoints, so [] means "not asked for" as much as
|
|
282
|
+
// "nothing shipped". The page that shows tracking reads a single order.
|
|
283
|
+
shipments: Array.isArray(row.shipments) ? row.shipments.map((shipment) => ({
|
|
284
|
+
id: shipment.id,
|
|
285
|
+
status: shipment.status ?? "pending",
|
|
286
|
+
carrier: shipment.carrier ?? null,
|
|
287
|
+
trackingCode: shipment.tracking_code ?? null,
|
|
288
|
+
trackingUrl: shipment.tracking_url ?? null,
|
|
289
|
+
shippedAt: shipment.shipped_at ?? null,
|
|
290
|
+
estimatedDeliveryAt: shipment.estimated_delivery_at ?? null,
|
|
291
|
+
deliveredAt: shipment.delivered_at ?? null
|
|
292
|
+
})) : [],
|
|
158
293
|
createdAt: row.created_at ?? nowIso(),
|
|
159
294
|
updatedAt: row.updated_at ?? row.created_at ?? nowIso()
|
|
160
295
|
};
|
|
@@ -180,6 +315,7 @@ function rowToCustomer(row) {
|
|
|
180
315
|
lastName: row.last_name ?? "",
|
|
181
316
|
email: row.email ?? null,
|
|
182
317
|
phone: row.phone ?? null,
|
|
318
|
+
document: row.document ?? null,
|
|
183
319
|
notes: row.notes ?? null,
|
|
184
320
|
ordersCount: row.orders_count ?? 0,
|
|
185
321
|
totalSpent: row.total_spent ?? 0,
|
|
@@ -222,10 +358,67 @@ function createFayzShopProvider(options) {
|
|
|
222
358
|
throw new FayzShopError("Fayz Shop provider requires either broker apiBaseUrl/projectId or legacy supabaseUrl/publishableKey/storeId.", 400);
|
|
223
359
|
}
|
|
224
360
|
const baseUrl = `${normalizeBaseUrl(options.supabaseUrl)}/rest/v1`;
|
|
225
|
-
const
|
|
361
|
+
const functionsUrl = options.functionsUrl ? normalizeBaseUrl(options.functionsUrl) : `${normalizeBaseUrl(options.supabaseUrl)}/functions/v1`;
|
|
362
|
+
const fetcher = createDedupedFetch(options.fetcher ?? fetch);
|
|
226
363
|
const key = options.publishableKey;
|
|
227
364
|
const storeId = options.storeId;
|
|
228
365
|
const productMetadataOverlay = buildProductMetadataOverlay(options.productMetadata);
|
|
366
|
+
async function quoteFromCarrier(postalCode, subtotal) {
|
|
367
|
+
const abort = new AbortController();
|
|
368
|
+
const timer = setTimeout(() => abort.abort(), CARRIER_QUOTE_BUDGET_MS);
|
|
369
|
+
try {
|
|
370
|
+
const response = await (options.fetcher ?? fetch)(`${functionsUrl}/shop-shipping-quote`, {
|
|
371
|
+
method: "POST",
|
|
372
|
+
signal: abort.signal,
|
|
373
|
+
headers: { apikey: key, Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
|
|
374
|
+
body: JSON.stringify({ tenantId: storeId, postalCode, subtotal, items: getCartItemsForQuote() })
|
|
375
|
+
});
|
|
376
|
+
if (!response.ok) return null;
|
|
377
|
+
const body = await response.json();
|
|
378
|
+
const rows = body.options ?? [];
|
|
379
|
+
if (rows.length === 0) return [];
|
|
380
|
+
if (!body.quoteId) return null;
|
|
381
|
+
return rows.map((row) => ({
|
|
382
|
+
// Carries the quote it came from, so a selection can name both without
|
|
383
|
+
// the storefront having to hold a second piece of state.
|
|
384
|
+
zoneId: `${body.quoteId}${CARRIER_OPTION_SEPARATOR}${row.service_id}`,
|
|
385
|
+
name: row.name,
|
|
386
|
+
carrier: row.carrier,
|
|
387
|
+
rate: Number(row.price ?? 0),
|
|
388
|
+
baseRate: Number(row.price ?? 0),
|
|
389
|
+
// A carrier price has no free-above rule of ours to re-derive.
|
|
390
|
+
freeAbove: null,
|
|
391
|
+
etaMinDays: row.eta_min_days ?? void 0,
|
|
392
|
+
etaMaxDays: row.eta_max_days ?? void 0,
|
|
393
|
+
free: false
|
|
394
|
+
}));
|
|
395
|
+
} catch {
|
|
396
|
+
return null;
|
|
397
|
+
} finally {
|
|
398
|
+
clearTimeout(timer);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
async function callPaymentFunction(slug, body) {
|
|
402
|
+
const orderId = String(body.orderId ?? "");
|
|
403
|
+
try {
|
|
404
|
+
const response = await (options.fetcher ?? fetch)(`${functionsUrl}/${slug}`, {
|
|
405
|
+
method: "POST",
|
|
406
|
+
headers: { apikey: key, Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
|
|
407
|
+
body: JSON.stringify({ tenantId: storeId, ...body })
|
|
408
|
+
});
|
|
409
|
+
const payload = await response.json().catch(() => null);
|
|
410
|
+
if (!response.ok || !payload?.state) {
|
|
411
|
+
return {
|
|
412
|
+
state: "unopened",
|
|
413
|
+
orderId,
|
|
414
|
+
providerStatus: payload?.code ?? `http_${response.status}`
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
return { ...payload, orderId: payload.orderId ?? orderId };
|
|
418
|
+
} catch {
|
|
419
|
+
return { state: "unopened", orderId, providerStatus: "unreachable" };
|
|
420
|
+
}
|
|
421
|
+
}
|
|
229
422
|
async function request(table, init = {}) {
|
|
230
423
|
const query = init.query?.toString();
|
|
231
424
|
const response = await fetcher(`${baseUrl}/${table}${query ? `?${query}` : ""}`, {
|
|
@@ -343,7 +536,13 @@ function createFayzShopProvider(options) {
|
|
|
343
536
|
inventoryCount: row.inventory_count ?? 0,
|
|
344
537
|
position: row.position ?? 0,
|
|
345
538
|
status: row.status ?? "active",
|
|
346
|
-
imageId: row.image_id ?? null
|
|
539
|
+
imageId: row.image_id ?? null,
|
|
540
|
+
// Null inherits the product's, the way `price` has since 0028.
|
|
541
|
+
weight: numOrNull(row.weight),
|
|
542
|
+
weightUnit: row.weight_unit ?? null,
|
|
543
|
+
heightCm: numOrNull(row.height_cm),
|
|
544
|
+
widthCm: numOrNull(row.width_cm),
|
|
545
|
+
lengthCm: numOrNull(row.length_cm)
|
|
347
546
|
}))
|
|
348
547
|
};
|
|
349
548
|
},
|
|
@@ -370,7 +569,7 @@ function createFayzShopProvider(options) {
|
|
|
370
569
|
},
|
|
371
570
|
async listOrders(options2) {
|
|
372
571
|
const query = tenantQuery();
|
|
373
|
-
query.set("select", "*,items:plg_shop_order_items(*)");
|
|
572
|
+
query.set("select", "*,items:plg_shop_order_items(*),shipments:fulfillments(status,delivered_at)");
|
|
374
573
|
if (options2?.status) query.set("status", `eq.${options2.status}`);
|
|
375
574
|
if (options2?.financialStatus) query.set("financial_status", `eq.${options2.financialStatus}`);
|
|
376
575
|
if (options2?.fulfillmentStatus) query.set("fulfillment_status", `eq.${options2.fulfillmentStatus}`);
|
|
@@ -394,6 +593,19 @@ function createFayzShopProvider(options) {
|
|
|
394
593
|
return (rpcRows ?? []).map(rowToOrder);
|
|
395
594
|
},
|
|
396
595
|
async getOrder(id) {
|
|
596
|
+
const signedIn = !!_accessTokenResolver?.();
|
|
597
|
+
const viaRpc = async () => {
|
|
598
|
+
try {
|
|
599
|
+
const row = await request("rpc/shop_get_order", {
|
|
600
|
+
method: "POST",
|
|
601
|
+
body: JSON.stringify({ p_order_id: id })
|
|
602
|
+
});
|
|
603
|
+
return row ? rowToOrder(row) : null;
|
|
604
|
+
} catch {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
if (!signedIn) return await viaRpc();
|
|
397
609
|
const query = singleById(id);
|
|
398
610
|
query.set("select", "*,items:plg_shop_order_items(*)");
|
|
399
611
|
let rows = [];
|
|
@@ -403,11 +615,7 @@ function createFayzShopProvider(options) {
|
|
|
403
615
|
}
|
|
404
616
|
if (rows[0]) return rowToOrder(rows[0]);
|
|
405
617
|
try {
|
|
406
|
-
|
|
407
|
-
method: "POST",
|
|
408
|
-
body: JSON.stringify({ p_order_id: id })
|
|
409
|
-
});
|
|
410
|
-
return row ? rowToOrder(row) : null;
|
|
618
|
+
return await viaRpc();
|
|
411
619
|
} catch {
|
|
412
620
|
return null;
|
|
413
621
|
}
|
|
@@ -419,6 +627,211 @@ function createFayzShopProvider(options) {
|
|
|
419
627
|
});
|
|
420
628
|
return row ? rowToOrder(row) : null;
|
|
421
629
|
},
|
|
630
|
+
/**
|
|
631
|
+
* Opens the charge, or reports what the acquirer says about it.
|
|
632
|
+
*
|
|
633
|
+
* Both go through the pool's own functions, for the same reason
|
|
634
|
+
* `quoteFromCarrier` does: the credential is redeemed server-side and the
|
|
635
|
+
* storefront never learns the acquirer's URL, let alone its token. Neither
|
|
636
|
+
* call carries an amount — the function reads it off the order row, so a
|
|
637
|
+
* tampered body buys nothing.
|
|
638
|
+
*
|
|
639
|
+
* A store with no acquirer connected answers 404, which surfaces as
|
|
640
|
+
* `unopened`: the page then simply shows the order as pending, which is the
|
|
641
|
+
* behaviour every store had before this existed.
|
|
642
|
+
*/
|
|
643
|
+
async openCharge(orderId, method = "pix") {
|
|
644
|
+
return await callPaymentFunction("shop-checkout-pay", { orderId, method });
|
|
645
|
+
},
|
|
646
|
+
async paymentState(orderId) {
|
|
647
|
+
return await callPaymentFunction("shop-payment-status", { orderId });
|
|
648
|
+
},
|
|
649
|
+
/**
|
|
650
|
+
* Open a charge for a cart that is NOT yet an order.
|
|
651
|
+
*
|
|
652
|
+
* Nothing about money leaves the browser: the function re-prices every line
|
|
653
|
+
* from the catalogue, so the total that comes back is the store's, not the
|
|
654
|
+
* caller's. `sessionId` is the only handle, and it neither reads nor pays on
|
|
655
|
+
* its own — `settleCheckoutSession` is what turns it into an order, and only
|
|
656
|
+
* after the acquirer says the money is in.
|
|
657
|
+
*/
|
|
658
|
+
async openCheckoutSession(input) {
|
|
659
|
+
const provider = String(input.provider ?? "").trim() || "abacatepay";
|
|
660
|
+
const gateway = checkoutGateway(provider);
|
|
661
|
+
if (!gateway) {
|
|
662
|
+
throw new FayzShopError("Esta loja ainda n\xE3o recebe pagamento online.", 404);
|
|
663
|
+
}
|
|
664
|
+
const body = JSON.stringify({
|
|
665
|
+
tenantId: storeId,
|
|
666
|
+
items: input.items.map((item) => ({
|
|
667
|
+
productId: item.productId,
|
|
668
|
+
optionsLabel: item.optionsLabel ?? null,
|
|
669
|
+
quantity: item.quantity
|
|
670
|
+
})),
|
|
671
|
+
customer: input.customer,
|
|
672
|
+
shippingAddress: input.shippingAddress ?? null,
|
|
673
|
+
discountCode: input.discountCode ?? null
|
|
674
|
+
});
|
|
675
|
+
const response = await (options.fetcher ?? fetch)(`${functionsUrl}/${gateway.open}`, {
|
|
676
|
+
method: "POST",
|
|
677
|
+
headers: { apikey: key, Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
|
|
678
|
+
body
|
|
679
|
+
}).catch(() => {
|
|
680
|
+
throw new FayzShopError("N\xE3o conseguimos falar com o pagamento agora. Tente de novo.", 0);
|
|
681
|
+
});
|
|
682
|
+
const payload = await response.json().catch(() => null);
|
|
683
|
+
if (!response.ok || !payload?.sessionId) {
|
|
684
|
+
const fallback = response.status === 404 ? "Esta loja ainda n\xE3o recebe pagamento online." : "N\xE3o conseguimos abrir a cobran\xE7a agora.";
|
|
685
|
+
throw new FayzShopError(payload?.error ?? fallback, response.status, payload);
|
|
686
|
+
}
|
|
687
|
+
return {
|
|
688
|
+
sessionId: payload.sessionId,
|
|
689
|
+
total: Number(payload.total ?? 0),
|
|
690
|
+
state: "open",
|
|
691
|
+
provider,
|
|
692
|
+
charge: payload.brCode ? {
|
|
693
|
+
emv: payload.brCode,
|
|
694
|
+
// O tipo promete base64 PURO, e a AbacatePay manda com o
|
|
695
|
+
// `data:image/png;base64,` já embutido (a AppMax mandava sem).
|
|
696
|
+
// Sem tirar aqui, a tela prefixava de novo e o QR não aparecia —
|
|
697
|
+
// um <img> com src inválido, silencioso.
|
|
698
|
+
qrcodeBase64: (payload.brCodeBase64 ?? "").replace(/^data:image\/\w+;base64,/u, "") || null,
|
|
699
|
+
expiresAt: payload.expiresAt ?? ""
|
|
700
|
+
} : null
|
|
701
|
+
};
|
|
702
|
+
},
|
|
703
|
+
/**
|
|
704
|
+
* Ask whether the money landed. Called on a timer, so a network blip must
|
|
705
|
+
* read as "still waiting" (`unknown`) and never as a payment that failed —
|
|
706
|
+
* one wrong `failed` here throws away a cart the buyer already paid for.
|
|
707
|
+
*/
|
|
708
|
+
async settleCheckoutSession(sessionId, provider) {
|
|
709
|
+
const gateway = checkoutGateway(String(provider ?? "").trim() || "abacatepay");
|
|
710
|
+
if (!gateway) return { state: "unknown" };
|
|
711
|
+
try {
|
|
712
|
+
const response = await (options.fetcher ?? fetch)(`${functionsUrl}/${gateway.settle}`, {
|
|
713
|
+
method: "POST",
|
|
714
|
+
headers: { apikey: key, Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
|
|
715
|
+
body: JSON.stringify({ tenantId: storeId, sessionId })
|
|
716
|
+
});
|
|
717
|
+
const payload = await response.json().catch(() => null);
|
|
718
|
+
if (!response.ok || !payload?.state) return { state: "unknown" };
|
|
719
|
+
return {
|
|
720
|
+
state: payload.state,
|
|
721
|
+
orderId: payload.orderId ?? null,
|
|
722
|
+
reason: payload.reason ?? null
|
|
723
|
+
};
|
|
724
|
+
} catch {
|
|
725
|
+
return { state: "unknown" };
|
|
726
|
+
}
|
|
727
|
+
},
|
|
728
|
+
/**
|
|
729
|
+
* The merchant's policy, as the shopper's browser is allowed to see it.
|
|
730
|
+
*
|
|
731
|
+
* Without this the settings screens wrote to a table nobody read on this
|
|
732
|
+
* side: StorefrontConfigHost skips the overlay when the provider has no
|
|
733
|
+
* such method, so the checkout kept offering whatever the app declared at
|
|
734
|
+
* build time — a store that turned boleto off still showed boleto.
|
|
735
|
+
*
|
|
736
|
+
* Through the RPC, never the table: plg_shop_settings is closed to anon on
|
|
737
|
+
* purpose and shop_store_settings() is the narrow window (storefront
|
|
738
|
+
* columns only, published stores only). A failure answers null, which is
|
|
739
|
+
* "no policy" — the declared config stands and the store still sells.
|
|
740
|
+
*/
|
|
741
|
+
async getPublicStoreSettings() {
|
|
742
|
+
let row = null;
|
|
743
|
+
try {
|
|
744
|
+
const body = await request(
|
|
745
|
+
"rpc/shop_store_settings",
|
|
746
|
+
{ method: "POST", body: JSON.stringify({ p_tenant_id: storeId }) }
|
|
747
|
+
);
|
|
748
|
+
row = Array.isArray(body) ? body[0] ?? null : body ?? null;
|
|
749
|
+
} catch {
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
if (!row) return null;
|
|
753
|
+
const bool = (v) => v == null ? void 0 : Boolean(v);
|
|
754
|
+
const methods = Array.isArray(row.payment_methods) && row.payment_methods.length > 0 ? row.payment_methods : void 0;
|
|
755
|
+
const fields = row.checkout_fields && typeof row.checkout_fields === "object" && Object.keys(row.checkout_fields).length > 0 ? row.checkout_fields : void 0;
|
|
756
|
+
return {
|
|
757
|
+
currency: row.currency ?? void 0,
|
|
758
|
+
locale: row.locale ?? void 0,
|
|
759
|
+
showOutOfStock: bool(row.show_out_of_stock),
|
|
760
|
+
paymentMethods: methods,
|
|
761
|
+
paymentProvider: row.payment_provider ?? void 0,
|
|
762
|
+
guestCheckout: bool(row.guest_checkout),
|
|
763
|
+
requireConfirmedEmail: bool(row.require_confirmed_email),
|
|
764
|
+
marketingOptIn: bool(row.marketing_opt_in),
|
|
765
|
+
discountsEnabled: bool(row.discounts_enabled),
|
|
766
|
+
checkoutFields: fields,
|
|
767
|
+
carrierQuotes: bool(row.carrier_quotes)
|
|
768
|
+
};
|
|
769
|
+
},
|
|
770
|
+
/**
|
|
771
|
+
* Dispatch by hand.
|
|
772
|
+
*
|
|
773
|
+
* All three are SECURITY DEFINER RPCs that check store membership
|
|
774
|
+
* themselves — the browser cannot ship someone else's order by holding its
|
|
775
|
+
* id, which is why these are not table writes.
|
|
776
|
+
*/
|
|
777
|
+
async validateDiscountCode(code, subtotal) {
|
|
778
|
+
const row = await request("rpc/shop_validate_discount", {
|
|
779
|
+
method: "POST",
|
|
780
|
+
body: JSON.stringify({ p_tenant_id: storeId, p_code: code, p_subtotal: subtotal ?? 0 })
|
|
781
|
+
});
|
|
782
|
+
return {
|
|
783
|
+
valid: !!row?.valid,
|
|
784
|
+
code: row?.code ?? null,
|
|
785
|
+
type: row?.type ?? null,
|
|
786
|
+
value: Number(row?.value ?? 0),
|
|
787
|
+
reason: row?.reason ?? void 0
|
|
788
|
+
};
|
|
789
|
+
},
|
|
790
|
+
async cancelOrder(orderId, reason) {
|
|
791
|
+
const row = await request("rpc/shop_cancel_order", {
|
|
792
|
+
method: "POST",
|
|
793
|
+
body: JSON.stringify({ p_order_id: orderId, p_reason: reason?.trim() || null })
|
|
794
|
+
});
|
|
795
|
+
return row ? rowToOrder(row) : null;
|
|
796
|
+
},
|
|
797
|
+
async createFulfillment(input) {
|
|
798
|
+
return await request("rpc/shop_create_fulfillment", {
|
|
799
|
+
method: "POST",
|
|
800
|
+
body: JSON.stringify({
|
|
801
|
+
p_order_id: input.orderId,
|
|
802
|
+
p_location_id: asUuid(input.locationId ?? void 0),
|
|
803
|
+
p_carrier: input.carrier?.trim() || null,
|
|
804
|
+
p_tracking: input.trackingCode?.trim() || null,
|
|
805
|
+
// snake_case inside the jsonb: the RPC reads `order_item_id`.
|
|
806
|
+
p_items: input.items?.length ? input.items.map((line) => ({ order_item_id: line.orderItemId, quantity: line.quantity })) : null,
|
|
807
|
+
p_notes: input.notes?.trim() || null
|
|
808
|
+
})
|
|
809
|
+
});
|
|
810
|
+
},
|
|
811
|
+
async shipFulfillment(id, input) {
|
|
812
|
+
await request("rpc/shop_ship_fulfillment", {
|
|
813
|
+
method: "POST",
|
|
814
|
+
body: JSON.stringify({
|
|
815
|
+
p_fulfillment_id: id,
|
|
816
|
+
p_tracking: input.trackingCode?.trim() || null,
|
|
817
|
+
p_tracking_url: input.trackingUrl?.trim() || null,
|
|
818
|
+
p_carrier: input.carrier?.trim() || null
|
|
819
|
+
})
|
|
820
|
+
});
|
|
821
|
+
},
|
|
822
|
+
/** Chegou. Só o lojista sabe disso — nenhum provedor nos avisa hoje. */
|
|
823
|
+
async deliverFulfillment(id, deliveredAt) {
|
|
824
|
+
await request("rpc/shop_deliver_fulfillment", {
|
|
825
|
+
method: "POST",
|
|
826
|
+
body: JSON.stringify({ p_fulfillment_id: id, p_delivered_at: deliveredAt ?? null })
|
|
827
|
+
});
|
|
828
|
+
},
|
|
829
|
+
async cancelFulfillment(id, note) {
|
|
830
|
+
await request("rpc/shop_cancel_fulfillment", {
|
|
831
|
+
method: "POST",
|
|
832
|
+
body: JSON.stringify({ p_fulfillment_id: id, p_note: note?.trim() || null })
|
|
833
|
+
});
|
|
834
|
+
},
|
|
422
835
|
async createOrder(input) {
|
|
423
836
|
const subtotal = input.items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);
|
|
424
837
|
const discountTotal = input.discountTotal ?? 0;
|
|
@@ -503,7 +916,10 @@ function createFayzShopProvider(options) {
|
|
|
503
916
|
country: input.shippingAddress.country ?? "BR",
|
|
504
917
|
recipient: input.shippingAddress.recipient ?? null,
|
|
505
918
|
phone: input.shippingAddress.phone ?? null,
|
|
506
|
-
label: input.shippingAddress.label ?? null
|
|
919
|
+
label: input.shippingAddress.label ?? null,
|
|
920
|
+
// Not an address field — see ShippingAddressInput.document. The
|
|
921
|
+
// 0050 trigger lifts it to a column and deletes it from here.
|
|
922
|
+
document: input.shippingAddress.document ?? null
|
|
507
923
|
} : null,
|
|
508
924
|
p_payment_method: input.paymentMethod ?? null,
|
|
509
925
|
p_notes: input.notes ?? null,
|
|
@@ -540,28 +956,71 @@ function createFayzShopProvider(options) {
|
|
|
540
956
|
if (!customer) throw new FayzShopError("shop_resolve_customer returned no row.", 500);
|
|
541
957
|
return rowToCustomer(customer);
|
|
542
958
|
},
|
|
959
|
+
/**
|
|
960
|
+
* The carrier first, our own zones when it does not answer in time.
|
|
961
|
+
*
|
|
962
|
+
* This is `createShippingQuoter` (packages/shop/src/providers/shipping.ts)
|
|
963
|
+
* by hand, and the duplication is deliberate: this package declares NO
|
|
964
|
+
* dependencies, which is what lets a storefront pull the client without
|
|
965
|
+
* dragging the admin stack in behind it. Importing the primitive for twelve
|
|
966
|
+
* lines would trade that for tidiness. The semantics are copied exactly and
|
|
967
|
+
* both are worth restating —
|
|
968
|
+
*
|
|
969
|
+
* the budget is the connector's (1s, `onFailure: 'degrade'`), because a
|
|
970
|
+
* shopper staring at "calculando" abandons faster than they accept a
|
|
971
|
+
* slightly wrong price; and
|
|
972
|
+
*
|
|
973
|
+
* an EMPTY provider answer is an ANSWER, not a failure. A carrier that
|
|
974
|
+
* does not serve a postcode has told us something, and falling back to
|
|
975
|
+
* the zones there would quote a van that does not go to São Paulo.
|
|
976
|
+
*/
|
|
543
977
|
async quoteShipping(postalCode, subtotal) {
|
|
544
978
|
const digits = (postalCode || "").replace(/\D/g, "");
|
|
545
979
|
if (digits.length !== 8) return [];
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
980
|
+
if (cartIsFullyDigital(getCartItemsForQuote())) return [];
|
|
981
|
+
const zones = async () => {
|
|
982
|
+
const rows = await request("rpc/shop_quote_shipping", {
|
|
983
|
+
method: "POST",
|
|
984
|
+
body: JSON.stringify({
|
|
985
|
+
p_tenant_id: storeId,
|
|
986
|
+
p_postal_code: digits,
|
|
987
|
+
p_subtotal: subtotal
|
|
988
|
+
})
|
|
989
|
+
});
|
|
990
|
+
return (rows ?? []).map((row) => ({
|
|
991
|
+
zoneId: row.zone_id,
|
|
992
|
+
name: row.name,
|
|
993
|
+
carrier: row.carrier ?? void 0,
|
|
994
|
+
rate: Number(row.rate ?? 0),
|
|
995
|
+
baseRate: Number(row.base_rate ?? row.rate ?? 0),
|
|
996
|
+
freeAbove: row.free_above == null ? null : Number(row.free_above),
|
|
997
|
+
etaMinDays: row.eta_min_days ?? void 0,
|
|
998
|
+
etaMaxDays: row.eta_max_days ?? void 0,
|
|
999
|
+
free: Boolean(row.free)
|
|
1000
|
+
}));
|
|
1001
|
+
};
|
|
1002
|
+
const carrier = mayAskCarrier() ? await quoteFromCarrier(digits, subtotal) : null;
|
|
1003
|
+
return carrier ?? await zones();
|
|
1004
|
+
},
|
|
1005
|
+
/**
|
|
1006
|
+
* Record which option the shopper picked, server-side.
|
|
1007
|
+
*
|
|
1008
|
+
* It sends a SERVICE, never a price: the row it points at was written by
|
|
1009
|
+
* the quote function minutes ago, so the worst a tampered call achieves is
|
|
1010
|
+
* choosing a different option the carrier actually offered — which is what
|
|
1011
|
+
* the radio buttons are for. shop_place_order reads the stored price.
|
|
1012
|
+
*/
|
|
1013
|
+
async selectShippingOption(optionId) {
|
|
1014
|
+
const [quoteId, serviceId] = String(optionId ?? "").split(CARRIER_OPTION_SEPARATOR);
|
|
1015
|
+
if (!quoteId || !serviceId) return false;
|
|
1016
|
+
try {
|
|
1017
|
+
return Boolean(await request("rpc/shop_select_shipping_quote", {
|
|
1018
|
+
method: "POST",
|
|
1019
|
+
body: JSON.stringify({ p_quote_id: quoteId, p_service_id: serviceId })
|
|
1020
|
+
}));
|
|
1021
|
+
} catch {
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
565
1024
|
},
|
|
566
1025
|
async listCustomerAddresses(customerId) {
|
|
567
1026
|
if (!asUuid(customerId)) return [];
|
|
@@ -825,6 +1284,6 @@ function createBrokerShopProvider(options) {
|
|
|
825
1284
|
};
|
|
826
1285
|
}
|
|
827
1286
|
|
|
828
|
-
export { FayzShopError, PRODUCT_METADATA_OVERLAY_DEPRECATION, createFayzShopProvider, resetProductMetadataOverlayDeprecationWarning, setShopAccessTokenResolver };
|
|
829
|
-
//# sourceMappingURL=chunk-
|
|
830
|
-
//# sourceMappingURL=chunk-
|
|
1287
|
+
export { FayzShopError, PRODUCT_METADATA_OVERLAY_DEPRECATION, createDedupedFetch, createFayzShopProvider, getDedupeStats, resetDedupeStats, resetProductMetadataOverlayDeprecationWarning, setShopAccessTokenResolver, setShopCarrierQuotesResolver, setShopShippingItemsResolver };
|
|
1288
|
+
//# sourceMappingURL=chunk-4YRHC5N7.js.map
|
|
1289
|
+
//# sourceMappingURL=chunk-4YRHC5N7.js.map
|