@sazito/checkout 0.4.13 → 0.4.15

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.
@@ -232,6 +232,8 @@ interface CheckoutActions {
232
232
  removeDiscount(): Promise<void>;
233
233
  selectPaymentMethod(id: number): void;
234
234
  placeOrder(): Promise<void>;
235
+ /** Rehydrate a failed gateway return and reopen a usable payment step. */
236
+ retryPayment(): Promise<void>;
235
237
  /** Resolve a return from the payment gateway (query params from the URL). */
236
238
  resolvePaymentReturn(params: Record<string, string>): Promise<void>;
237
239
  reset(): void;
@@ -259,6 +261,7 @@ interface Strings {
259
261
  finishPurchase: string;
260
262
  back: string;
261
263
  continueShopping: string;
264
+ backToShop: string;
262
265
  orderSummary: string;
263
266
  subtotal: string;
264
267
  shipping: string;
@@ -232,6 +232,8 @@ interface CheckoutActions {
232
232
  removeDiscount(): Promise<void>;
233
233
  selectPaymentMethod(id: number): void;
234
234
  placeOrder(): Promise<void>;
235
+ /** Rehydrate a failed gateway return and reopen a usable payment step. */
236
+ retryPayment(): Promise<void>;
235
237
  /** Resolve a return from the payment gateway (query params from the URL). */
236
238
  resolvePaymentReturn(params: Record<string, string>): Promise<void>;
237
239
  reset(): void;
@@ -259,6 +261,7 @@ interface Strings {
259
261
  finishPurchase: string;
260
262
  back: string;
261
263
  continueShopping: string;
264
+ backToShop: string;
262
265
  orderSummary: string;
263
266
  subtotal: string;
264
267
  shipping: string;
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
3
3
  import { isValidElement, cloneElement, useMemo, useState, useRef, useEffect, useCallback } from 'react';
4
- import { u as useCheckout, b as useSazitoClient, C as CheckoutProvider } from '../chunks/use-checkout-Dd2R0LU2.js';
5
- export { S as SazitoProvider } from '../chunks/use-checkout-Dd2R0LU2.js';
6
- import { m as formatPercent, E as sortCartItemsNewestFirst, H as toPersianDigits, r as isDigitalServiceGroup, t as isValidIranianMobile, z as normalizeIranianPhone, A as paymentMethodInfo, l as formatNumber } from '../chunks/labels-Dlwf3S83.js';
4
+ import { u as useCheckout, b as useSazitoClient, C as CheckoutProvider } from '../chunks/use-checkout-rmb11XwE.js';
5
+ export { S as SazitoProvider } from '../chunks/use-checkout-rmb11XwE.js';
6
+ import { m as formatPercent, E as sortCartItemsNewestFirst, H as toPersianDigits, r as isDigitalServiceGroup, t as isValidIranianMobile, z as normalizeIranianPhone, A as paymentMethodInfo, l as formatNumber } from '../chunks/labels-TSPPdjAd.js';
7
7
  import { parsePaymentReturnUrl } from './payment-return.js';
8
8
  import '@sazito/client-sdk';
9
9
 
@@ -517,7 +517,7 @@ function ShippingRateIcon({ rate }) {
517
517
  case 'delivery-man':
518
518
  return jsx(DeliveryManIcon, {});
519
519
  default:
520
- return rate.icon && isImageUrl(rate.icon) ? jsx("img", { src: rate.icon, alt: "" }) : jsx(TruckIcon, {});
520
+ return rate.icon && isImageUrl$1(rate.icon) ? jsx("img", { src: rate.icon, alt: "" }) : jsx(TruckIcon, {});
521
521
  }
522
522
  }
523
523
  function shippingRateIconKey(rate) {
@@ -616,7 +616,7 @@ function parseColorChannels(value) {
616
616
  return [Number(channels[1]), Number(channels[2]), Number(channels[3])];
617
617
  return null;
618
618
  }
619
- function isImageUrl(value) {
619
+ function isImageUrl$1(value) {
620
620
  return /^(https?:)?\/\//i.test(value) || value.startsWith('/') || value.startsWith('data:');
621
621
  }
622
622
  function PostIcon() {
@@ -709,8 +709,36 @@ function PaymentIcon({ kind }) {
709
709
  }
710
710
  }
711
711
 
712
+ function groupResultItemsByShipping(invoiceItems, shippingItems) {
713
+ const itemById = new Map();
714
+ const assignedItems = new Set();
715
+ invoiceItems.forEach((item) => {
716
+ if (item.id !== undefined)
717
+ itemById.set(String(item.id), item);
718
+ });
719
+ const shipmentGroups = shippingItems.map((shipping) => {
720
+ const items = shipping.invoiceItemIds.flatMap((itemId) => {
721
+ const item = itemById.get(String(itemId));
722
+ if (!item || assignedItems.has(item))
723
+ return [];
724
+ assignedItems.add(item);
725
+ return [item];
726
+ });
727
+ return { shipping, items };
728
+ });
729
+ // Older payment responses did not expose invoice-line IDs. A single shipping
730
+ // method is still unambiguous, so keep those orders grouped correctly.
731
+ if (shipmentGroups.length === 1 && shipmentGroups[0].items.length === 0) {
732
+ shipmentGroups[0].items = [...invoiceItems];
733
+ invoiceItems.forEach((item) => assignedItems.add(item));
734
+ }
735
+ return {
736
+ shipmentGroups: shipmentGroups.filter((group) => group.items.length > 0),
737
+ unassignedItems: invoiceItems.filter((item) => !assignedItems.has(item))
738
+ };
739
+ }
712
740
  function ResultStep({ continueShoppingUrl }) {
713
- const { state, actions, money, price, t } = useCheckout();
741
+ const { state, actions, money, t } = useCheckout();
714
742
  const result = state.result;
715
743
  const status = result?.status ?? 'pending';
716
744
  const order = result?.order;
@@ -718,21 +746,42 @@ function ResultStep({ continueShoppingUrl }) {
718
746
  const showDetails = Boolean(order && (status === 'success' || status === 'pending'));
719
747
  const showSummary = invoice?.netTotal != null || invoice?.finalTotal != null;
720
748
  const isFailure = status === 'failed' || status === 'stock_violated';
749
+ const orderDetailsUrl = order
750
+ ? buildOrderDetailsUrl(order.id, order.orderIdentifier, continueShoppingUrl)
751
+ : null;
752
+ const groupedItems = invoice
753
+ ? groupResultItemsByShipping(invoice.invoiceItems, invoice.shippingItems)
754
+ : { shipmentGroups: [], unassignedItems: [] };
755
+ const renderItems = (items) => (jsx("ul", { className: "szc-result-items", children: items.map((item, index) => (jsxs("li", { className: "szc-result-item", children: [jsxs("span", { className: "szc-result-item__product", children: [item.image?.url ? (jsx("img", { className: "szc-result-item__thumb", src: item.image.url, alt: "" })) : (jsx(ProductPlaceholder, { className: "szc-result-item__thumb" })), jsxs("span", { children: [jsx("strong", { children: item.name }), item.variantAttributes.length ? (jsx("small", { children: item.variantAttributes.map(formatAttribute).join(' · ') })) : null] })] }), jsxs("span", { className: "szc-result-item__quantity", children: [jsx("span", { className: "szc-result-item__mobile-label", children: t.quantity }), jsx("strong", { children: formatNumber(item.noOfItems, state.locale) })] }), jsxs("span", { className: "szc-result-item__total", children: [jsx("span", { className: "szc-result-item__mobile-label", children: t.lineTotal }), jsx("strong", { children: money(item.totalItemsPrice) })] })] }, `${String(item.id ?? item.productVariantId)}-${index}`))) }));
721
756
  const titleMap = {
722
757
  success: t.paymentSuccess,
723
758
  failed: t.paymentFailed,
724
759
  pending: t.paymentPending,
725
760
  stock_violated: t.paymentFailed
726
761
  };
727
- return (jsxs("section", { className: `szc-result szc-result--${status}${showDetails ? '' : ' szc-result--compact'}`, children: [jsxs("div", { className: "szc-result__hero", role: isFailure ? 'alert' : 'status', "aria-live": isFailure ? 'assertive' : 'polite', children: [jsx("div", { className: "szc-result__icon", children: jsx("span", { className: "szc-result__icon-mark", children: status === 'success' ? (jsx(CheckCircle, {})) : status === 'pending' ? (jsx(Spinner, {})) : (jsx(CrossCircle, {})) }) }), jsxs("div", { className: "szc-result__copy", children: [jsx("h2", { className: "szc-result__title", children: titleMap[status] }), status === 'pending' ? jsx("p", { className: "szc-result__hint", children: t.paymentPendingHint }) : null, result?.message ? (jsx("p", { className: "szc-result__message", dir: "auto", children: result.message })) : null, status === 'success' && order ? (jsx("p", { className: "szc-result__hint", children: t.orderConfirmedHint })) : null] }), order ? (jsxs("dl", { className: "szc-result__reference", children: [jsxs("div", { className: "szc-result__reference-primary", children: [jsx("dt", { children: t.orderNumber }), jsx("dd", { children: formatPublicId(order.orderNumber, state.locale) })] }), jsxs("div", { children: [jsx("dt", { children: t.orderId }), jsx("dd", { children: formatPublicId(order.id, state.locale) })] })] })) : null, jsxs("div", { className: "szc-result__actions", children: [status === 'failed' || status === 'stock_violated' ? (jsx(Button, { onClick: () => actions.goToStep('payment'), children: t.tryAgain })) : null, continueShoppingUrl ? (jsx(Button, { variant: status === 'success' ? 'primary' : 'outline', asChild: true, children: jsx("a", { href: continueShoppingUrl, children: t.continueShopping }) })) : null] })] }), showDetails && order && invoice ? (jsxs("div", { className: "szc-result__details", children: [jsx("h3", { className: "szc-result__details-title", children: t.orderDetails }), invoice.shippingItems.length > 0 ? (jsxs("section", { className: "szc-result__section", "aria-labelledby": "szc-result-shipping-title", children: [jsx("h4", { id: "szc-result-shipping-title", className: "szc-result__section-title", children: t.shippingMethods }), jsx("div", { className: "szc-result-shipping-list", children: invoice.shippingItems.map((shipping) => (jsx("article", { className: "szc-result-group", children: jsxs("header", { className: "szc-result-group__head", children: [jsx("span", { className: "szc-result-group__mark", "aria-hidden": "true", children: shipping.rate.icon ? jsx("img", { src: shipping.rate.icon, alt: "" }) : jsx(DeliveryIcon, {}) }), jsx("span", { className: "szc-result-group__title", children: shipping.rate.name }), jsx("span", { className: "szc-result-group__price", children: price(shipping.rate.price) })] }) }, String(shipping.id)))) })] })) : null, invoice.invoiceItems.length > 0 ? (jsxs("section", { className: "szc-result-group", "aria-labelledby": "szc-result-items-title", children: [jsxs("header", { className: "szc-result-group__head", children: [jsx("span", { className: "szc-result-group__mark", "aria-hidden": "true", children: jsx(OrderItemsIcon, {}) }), jsx("h4", { id: "szc-result-items-title", className: "szc-result-group__title", children: t.orderItems })] }), jsxs("div", { className: "szc-result-items__head", "aria-hidden": "true", children: [jsx("span", { children: t.product }), jsx("span", { children: t.quantity }), jsx("span", { children: t.lineTotal })] }), jsx("ul", { className: "szc-result-items", children: invoice.invoiceItems.map((item, index) => (jsxs("li", { className: "szc-result-item", children: [jsxs("span", { className: "szc-result-item__product", children: [item.image?.url ? (jsx("img", { className: "szc-result-item__thumb", src: item.image.url, alt: "" })) : (jsx(ProductPlaceholder, { className: "szc-result-item__thumb" })), jsxs("span", { children: [jsx("strong", { children: item.name }), item.variantAttributes.length ? (jsx("small", { children: item.variantAttributes.map(formatAttribute).join(' · ') })) : null] })] }), jsxs("span", { className: "szc-result-item__quantity", children: [jsx("span", { className: "szc-result-item__mobile-label", children: t.quantity }), formatNumber(item.noOfItems, state.locale)] }), jsxs("span", { className: "szc-result-item__total", children: [jsx("span", { className: "szc-result-item__mobile-label", children: t.lineTotal }), money(item.totalItemsPrice)] })] }, `${String(item.productVariantId)}-${index}`))) })] })) : null, showSummary ? jsxs("dl", { className: "szc-result-summary", children: [invoice.netTotal != null ? (jsx(SummaryRow, { label: t.subtotal, value: money(invoice.netTotal) })) : null, invoice.shippingTotal != null ? (jsx(SummaryRow, { label: t.shipping, value: money(invoice.shippingTotal) })) : null, invoice.discountTotal != null && invoice.discountTotal > 0 ? (jsx(SummaryRow, { label: t.discount, value: `−${money(invoice.discountTotal)}` })) : null, invoice.creditTotal != null && invoice.creditTotal > 0 ? (jsx(SummaryRow, { label: t.credit, value: `−${money(invoice.creditTotal)}` })) : null, invoice.vat != null && invoice.vat > 0 ? (jsx(SummaryRow, { label: t.vat, value: money(invoice.vat) })) : null, invoice.customerProfit != null && invoice.customerProfit > 0 ? (jsx(SummaryRow, { label: t.yourSavings, value: money(invoice.customerProfit), savings: true })) : null, invoice.finalTotal != null ? (jsx(SummaryRow, { label: t.total, value: money(invoice.finalTotal), total: true })) : null] }) : null] })) : null] }));
728
- }
729
- function formatPublicId(value, locale) {
730
- const text = String(value);
731
- return locale === 'fa' ? toPersianDigits(text) : text;
762
+ return (jsxs("section", { className: `szc-result szc-result--${status}${showDetails ? '' : ' szc-result--compact'}`, children: [jsxs("div", { className: "szc-result__hero", role: isFailure ? 'alert' : 'status', "aria-live": isFailure ? 'assertive' : 'polite', children: [jsx("div", { className: "szc-result__icon", children: jsx("span", { className: "szc-result__icon-mark", children: status === 'success' ? (jsx(CheckCircle, {})) : status === 'pending' ? (jsx(Spinner, {})) : (jsx(CrossCircle, {})) }) }), jsxs("div", { className: "szc-result__copy", children: [jsx("h2", { className: "szc-result__title", children: titleMap[status] }), status === 'pending' ? jsx("p", { className: "szc-result__hint", children: t.paymentPendingHint }) : null, !isFailure && result?.message ? (jsx("p", { className: "szc-result__message", dir: "auto", children: result.message })) : null, status === 'success' && order ? (jsx("p", { className: "szc-result__hint", children: t.orderConfirmedHint })) : null] }), order ? (jsx("dl", { className: "szc-result__reference", children: jsxs("div", { className: "szc-result__reference-primary", children: [jsx("dt", { children: t.orderNumber }), jsx("dd", { dir: "ltr", children: String(order.orderNumber) })] }) })) : null, jsxs("div", { className: "szc-result__actions", children: [status === 'failed' || status === 'stock_violated' ? (jsx(Button, { onClick: () => void actions.retryPayment(), loading: state.flags.loadingPayments, children: t.tryAgain })) : null, orderDetailsUrl ? (jsx(Button, { asChild: true, children: jsx("a", { href: orderDetailsUrl, children: t.orderDetails }) })) : null, continueShoppingUrl ? (jsx(Button, { variant: "outline", asChild: true, children: jsx("a", { href: continueShoppingUrl, children: t.backToShop }) })) : null] })] }), showDetails && order && invoice ? (jsxs("div", { className: "szc-result__details", children: [groupedItems.shipmentGroups.map(({ shipping, items }, index) => {
763
+ const description = shipping.rate.description?.trim();
764
+ return (jsxs("section", { className: "szc-result__details-card szc-result-shipment", "aria-label": `${t.shippingMethod}: ${shipping.rate.name}`, children: [jsxs("header", { className: "szc-result-shipment__head", children: [jsx("span", { className: "szc-result-shipment__mark", "aria-hidden": "true", children: isImageUrl(shipping.rate.icon) ? (jsx("img", { src: shipping.rate.icon, alt: "" })) : (jsx(DeliveryIcon, {})) }), jsxs("span", { className: "szc-result-shipment__name", children: [jsx("small", { className: "szc-result-shipment__eyebrow", children: t.shippingMethod }), jsx("strong", { children: shipping.rate.name }), description ? (jsx("span", { className: "szc-result-shipment__description", children: description })) : null] }), shipping.rate.type !== 'free' ? (jsx("span", { className: "szc-result-shipment__price", children: money(shipping.rate.price) })) : null] }), renderItems(items)] }, `${String(shipping.id)}-${index}`));
765
+ }), groupedItems.unassignedItems.length > 0 ? (jsx("section", { className: "szc-result__details-card szc-result-unassigned-items", "aria-label": t.orderItems, children: renderItems(groupedItems.unassignedItems) })) : null, showSummary ? (jsxs("dl", { className: "szc-result-summary", children: [invoice.netTotal != null ? (jsx(SummaryRow, { label: t.subtotal, value: money(invoice.netTotal) })) : null, invoice.shippingTotal != null ? (jsx(SummaryRow, { label: t.shipping, value: money(invoice.shippingTotal) })) : null, invoice.discountTotal != null && invoice.discountTotal > 0 ? (jsx(SummaryRow, { label: t.discount, value: `−${money(invoice.discountTotal)}` })) : null, invoice.creditTotal != null && invoice.creditTotal > 0 ? (jsx(SummaryRow, { label: t.credit, value: `−${money(invoice.creditTotal)}` })) : null, invoice.vat != null && invoice.vat > 0 ? (jsx(SummaryRow, { label: t.vat, value: money(invoice.vat) })) : null, invoice.customerProfit != null && invoice.customerProfit > 0 ? (jsx(SummaryRow, { label: t.yourSavings, value: money(invoice.customerProfit), savings: true })) : null, invoice.finalTotal != null ? (jsx(SummaryRow, { label: t.total, value: money(invoice.finalTotal), total: true })) : null] })) : null] })) : null] }));
766
+ }
767
+ function buildOrderDetailsUrl(orderId, orderIdentifier, continueShoppingUrl) {
768
+ const path = `/orderinfo/${encodeURIComponent(String(orderId))}/${encodeURIComponent(orderIdentifier)}`;
769
+ if (!continueShoppingUrl)
770
+ return path;
771
+ try {
772
+ const storefrontUrl = new URL(continueShoppingUrl);
773
+ return new URL(path, storefrontUrl.origin).toString();
774
+ }
775
+ catch {
776
+ return path;
777
+ }
732
778
  }
733
779
  function formatAttribute(attribute) {
734
780
  return `${attribute.name}: ${attribute.value}`;
735
781
  }
782
+ function isImageUrl(value) {
783
+ return Boolean(value && (/^https?:\/\//i.test(value) || value.startsWith('/') || value.startsWith('data:image/')));
784
+ }
736
785
  function SummaryRow({ label, value, savings = false, total = false }) {
737
786
  return (jsxs("div", { className: `${total ? 'szc-result-summary__total' : ''} ${savings ? 'szc-result-summary__savings' : ''}`.trim(), children: [jsx("dt", { children: label }), jsx("dd", { children: value })] }));
738
787
  }
@@ -745,9 +794,6 @@ function CrossCircle() {
745
794
  function DeliveryIcon() {
746
795
  return (jsxs("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "none", "aria-hidden": "true", children: [jsx("path", { d: "M3 6.5h11v10H3zM14 10h3.5l3 3v3.5H14z", stroke: "currentColor", strokeWidth: "1.6", strokeLinejoin: "round" }), jsx("circle", { cx: "7", cy: "18", r: "1.75", stroke: "currentColor", strokeWidth: "1.6" }), jsx("circle", { cx: "17.5", cy: "18", r: "1.75", stroke: "currentColor", strokeWidth: "1.6" })] }));
747
796
  }
748
- function OrderItemsIcon() {
749
- return (jsxs("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "none", "aria-hidden": "true", children: [jsx("path", { d: "M5 7.5h14v11H5zM8 7.5V5h8v2.5", stroke: "currentColor", strokeWidth: "1.6", strokeLinejoin: "round" }), jsx("path", { d: "M8.5 12h7M8.5 15h5", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" })] }));
750
- }
751
797
 
752
798
  function themeVars(theme) {
753
799
  const vars = {};
@@ -872,7 +918,7 @@ function SazitoCheckout({ theme, continueShoppingUrl, className, renderNextButto
872
918
  status: resultStatus,
873
919
  order: state.result?.order,
874
920
  continueShoppingUrl,
875
- onRetry: () => actions.goToStep('payment')
921
+ onRetry: () => void actions.retryPayment()
876
922
  })) : (jsx(ResultStep, { continueShoppingUrl: continueShoppingUrl })) })) : (jsx(Fragment, { children: jsxs("div", { className: `szc-layout${cartEmpty ? ' szc-layout--full' : ''}`, children: [jsxs("main", { className: "szc-main", children: [error && !emptyCartError ? jsx(ErrorBanner, { message: error.message }) : null, step === 'cart' ? (jsx(CartStep, { continueShoppingUrl: continueShoppingUrl, emptyCart: emptyCart, renderEmpty: renderEmptyCart })) : null, step === 'shipping' ? jsx(ShippingStep, {}) : null, step === 'payment' ? jsx(PaymentStep, {}) : null, actionsVisible && backButtonNode ? (jsx("div", { className: "szc-back-row", children: backButtonNode })) : null] }), !cartEmpty ? (jsxs("div", { className: "szc-side", children: [jsx(OrderSummary, {}), actionsVisible ? jsx("div", { className: "szc-side__cta", children: nextButtonNode }) : null] })) : null] }) })), actionsVisible ? (jsx("div", { className: "szc-footer-bar", children: jsxs("div", { className: "szc-footer", children: [jsxs("div", { className: "szc-footer__total", children: [jsx("span", { className: "szc-footer__total-label", children: t.total }), jsx("span", { className: "szc-footer__total-value", children: money(summary.total) })] }), jsx("div", { className: "szc-footer__actions", children: nextButtonNode })] }) })) : null] }));
877
923
  }
878
924