@fire-spark/core 0.9.1 → 0.11.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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Money, PaymentAdapter, PaymentCollectAt, PaymentContext, PaymentResult, PaymentStorage, PaymentUI, RedeemLike, RedeemSource, isRealPaymentMethod, orderRedeems } from "@fire-spark/payments";
2
- import { AndConditions, CheckoutMode, CheckoutResolved, CmsBlock, Condition, Customer, CustomerIdentifyInput, CustomerRegisterInput, CustomerUpdateInput, Fulfillment, FulfillmentListInput, GroupDisplayType, KioskDeviceConfig, KioskDeviceOverrides, KioskHealthBatchInput, KioskHealthBatchResult, KioskHeartbeatInput, KioskHeartbeatResult, KioskRealtimeToken, MatchResult, MatchableRule, Menu, Menu as Menu$1, MenuLookup, MenuLookup as MenuLookup$1, MerchantPayment, ModifierControlType, ModifierGroup, OrConditions, Order as Order$1, Order as Order$2, OrderDeviceListInput, OrderDeviceListResult, OrderFulfillment, OrderInsertInput, OrderLine, OrderListInput, OrderRecordPaymentInput, OrderRedeem, OrderRedemptionStep, OrderReorderInput, OrderTotals, OrderUpdateInput, OrderValidateInput, OrderValidateOutput, PaymentCollectAt as PaymentCollectAt$1, PaymentIntentPutInput, PaymentMethod, PaymentProvider, PaymentTender, PaymentTenderInput, PlaceOrderResult, PosCatalog, PosFloorInput, PosFloorOccupancy, PosFloorPlan, PosFloorRealtimeToken, PosHealthBatchInput, PosHealthBatchResult, PosHeartbeatInput, PosHeartbeatResult, PosKioskOrder, PosKioskSettleInput, PosKioskSettleResult, PosOrderInput, PosOrderResult, PosTableAttachOrderInput, PosTableCloseInput, PosTableJoinInput, PosTableSeatInput, PosTableSeatReservationInput, PosTableSplitInput, PricingModifier, Product, Product as Product$1, Recommendation, RecurringDateCronResult, RecurringDateCronValues, RulesEngineOrderLine, RulesEngineParams, RulesEngineRuntime, Schedule, SdkLogContext, SdkLogErrorInfo, SdkLogLevel, SdkLogOptions, Store, StoreGetInput, StoreListInput, StoreNearbyInput, StorefrontAvailability, StorefrontMenu, StorefrontMenuScope, StorefrontMenuStreamOptions, StorefrontPaymentsListInput, StorefrontPreferences, TenderRefundInput, TenderReplaceInput, TenderVoidInput, Variant, VariantGroup } from "@fire-spark/types";
2
+ import { AndConditions, CheckoutMode, CheckoutResolved, CmsBlock, Condition, Customer, CustomerIdentifyInput, CustomerRegisterInput, CustomerUpdateInput, Fulfillment, FulfillmentListInput, GroupDisplayType, KioskDeviceConfig, KioskDeviceOverrides, KioskHealthBatchInput, KioskHealthBatchResult, KioskHeartbeatInput, KioskHeartbeatResult, KioskRealtimeToken, MatchResult, MatchableRule, Menu, Menu as Menu$1, MenuLookup, MenuLookup as MenuLookup$1, MerchantPayment, ModifierControlType, ModifierGroup, OrConditions, Order as Order$1, Order as Order$2, OrderCancelInput, OrderCancelInput as OrderCancelInput$1, OrderCancelType, OrderCancelType as OrderCancelType$1, OrderDeviceListInput, OrderDeviceListResult, OrderFulfillment, OrderInsertInput, OrderLine, OrderListInput, OrderRecordPaymentInput, OrderRedeem, OrderRedemptionStep, OrderReorderInput, OrderTotals, OrderUpdateInput, OrderValidateInput, OrderValidateOutput, PaymentCollectAt as PaymentCollectAt$1, PaymentIntentPutInput, PaymentMethod, PaymentProvider, PaymentTender, PaymentTenderInput, PlaceOrderResult, PosCatalog, PosFloorInput, PosFloorOccupancy, PosFloorPlan, PosFloorRealtimeToken, PosHealthBatchInput, PosHealthBatchResult, PosHeartbeatInput, PosHeartbeatResult, PosKioskOrder, PosKioskSettleInput, PosKioskSettleResult, PosOrderInput, PosOrderResult, PosTableAttachOrderInput, PosTableCloseInput, PosTableJoinInput, PosTableSeatInput, PosTableSeatReservationInput, PosTableSplitInput, PricingModifier, Product, Product as Product$1, Recommendation, RecurringDateCronResult, RecurringDateCronValues, RulesEngineOrderLine, RulesEngineParams, RulesEngineRuntime, Schedule, SdkLogContext, SdkLogErrorInfo, SdkLogLevel, SdkLogOptions, Store, StoreGetInput, StoreListInput, StoreNearbyInput, StorefrontAvailability, StorefrontMenu, StorefrontMenuScope, StorefrontMenuStreamOptions, StorefrontPaymentsListInput, StorefrontPreferences, TenderRefundInput, TenderReplaceInput, TenderVoidInput, Variant, VariantGroup } from "@fire-spark/types";
3
3
  import { Big } from "big.js";
4
4
  //#region src/productCustomization/catalog.utils.d.ts
5
5
  /** Image for a product inside one modifier group, else the product image. */
@@ -272,8 +272,13 @@ declare const refundTender: (id: string, tenderId: string, body: TenderRefundInp
272
272
  declare const getOrderFulfillment: (id: string, options?: ServiceHttpOptions) => Promise<OrderFulfillment>;
273
273
  declare const listOrderFulfillments: (ids: string[], options?: ServiceHttpOptions) => Promise<OrderFulfillment[]>;
274
274
  declare const updateOrder: (id: string, body: OrderUpdateInput, options?: ServiceHttpOptions) => Promise<Order$1>;
275
- declare const cancelOrder: (id: string, options?: ServiceHttpOptions) => Promise<Order$1>;
276
- declare const refundOrder: (id: string, options?: ServiceHttpOptions) => Promise<Order$1>;
275
+ /**
276
+ * `body` is optional: `device` (checked only when sent), `type`
277
+ * (`VOID` / `REVERSE`) and what the device reversed (`tenders`).
278
+ */
279
+ declare const cancelOrder: (id: string, options?: ServiceHttpOptions, body?: OrderCancelInput$1) => Promise<Order$1>;
280
+ /** Refunds the tenders only; the order status stays. `type` is `REFUND`. */
281
+ declare const refundOrder: (id: string, options?: ServiceHttpOptions, body?: OrderCancelInput$1) => Promise<Order$1>;
277
282
  declare const reorderOrder: (id: string, body?: OrderReorderInput, options?: ServiceHttpOptions) => Promise<Order$1>;
278
283
  /**
279
284
  * `POST /storefront/v1/orders/:id/reopen`: voids every payment of a placed,
@@ -327,7 +332,10 @@ type PaymentSessionOptions = {
327
332
  * SDK charges are always saved with `collect_at: DEVICE` (§7.3).
328
333
  */
329
334
  collectAt?: PaymentCollectAt;
330
- /** Where cash is collected: `COUNTER` on a kiosk, `DOOR` on delivery. */
335
+ /**
336
+ * Where cash and the Fire Spark card terminal are collected: `COUNTER` on
337
+ * a kiosk, `DOOR` on delivery.
338
+ */
331
339
  cashCollectAt?: PaymentCollectAt;
332
340
  /** `settings_overrides.payment_settings` of this device, by payment id. */
333
341
  deviceSettings?: Record<string, Record<string, unknown>>;
@@ -399,6 +407,25 @@ type ReverseAllOptions = {
399
407
  declare const coveredAmount: (intents: Intents) => number;
400
408
  declare const remainingAmount: (order: Order$1) => number;
401
409
  //#endregion
410
+ //#region src/order/cancel.d.ts
411
+ /**
412
+ * The operation an order takes: `VOID` (nothing charged), `REVERSE`
413
+ * (charged, not delivered) or `REFUND` (delivered). An order already
414
+ * cancelled keeps its operation, so a replay stays idempotent.
415
+ */
416
+ declare const cancelTypeOf: (order: Order$1) => OrderCancelType$1;
417
+ type OrderCancelResult = {
418
+ /** The order the server answered. */
419
+ order: Order$1;
420
+ type: OrderCancelType$1;
421
+ /** Endpoint called: `cancel()` → `cancel`, `refund()` → `refund`. */
422
+ path: "cancel" | "refund";
423
+ /** What the device did with each charge it voided. */
424
+ outcomes: ReversalOutcome[];
425
+ /** Tender ids the device could not void yet (`reversal_pending`). */
426
+ pending: string[];
427
+ };
428
+ //#endregion
402
429
  //#region src/order/recommendations.d.ts
403
430
  type RecommendationType = Recommendation["type"];
404
431
  declare const DEFAULT_RECOMMENDATION_TYPES: RecommendationType[];
@@ -415,6 +442,13 @@ type OrderDeviceCredential = {
415
442
  uid: string;
416
443
  secret: string;
417
444
  };
445
+ type OrderCancelOptions = {
446
+ /** `cancel()` only: forces VOID / REVERSE; omitted, it follows the order. */
447
+ type?: Exclude<OrderCancelType, "REFUND">;
448
+ reason?: string;
449
+ /** Only these charged tenders are voided on the device. */
450
+ tenderIds?: readonly string[];
451
+ };
418
452
  type CreateOrderOptions = {
419
453
  scope?: OrderScope;
420
454
  customer?: OrderCustomerSource;
@@ -495,6 +529,8 @@ declare class Order {
495
529
  */
496
530
  private requestedRedeem;
497
531
  private readonly device?;
532
+ /** Device voids a failed cancel / refund call did not report yet. */
533
+ private unreported;
498
534
  private constructor();
499
535
  /**
500
536
  * Opens a cart session: reuses an existing OPEN order for the scope, or a
@@ -511,6 +547,8 @@ declare class Order {
511
547
  debounceMs?: number;
512
548
  persisted?: boolean;
513
549
  sleep?: (ms: number) => Promise<void>;
550
+ /** Sent by `cancel()` (and `placeOrder`) when present. */
551
+ device?: CreateOrderOptions["device"];
514
552
  }, notify?: () => void): Order;
515
553
  /** Whether this session has a server row yet. */
516
554
  isPersisted(): boolean;
@@ -645,6 +683,25 @@ declare class Order {
645
683
  replaceTender(tenderId: string, payment: MerchantPayment, collectAt?: PaymentCollectAt$1): Promise<Order$1>;
646
684
  /** "Cancel and refund": reverses device charges, then cancels the order. */
647
685
  cancelAndRefund(options?: ReverseAllOptions): Promise<ReversalOutcome[]>;
686
+ /**
687
+ * Cancels this placed, not delivered order with one call to `/cancel`:
688
+ * `VOID` (nothing charged) or `REVERSE` (charged), decided from the
689
+ * fresh order. Device charges are voided first and their results go in
690
+ * the same body (never `/payments`). The device credential of
691
+ * `createOrder({ device })` / `fromExisting({ device })` is sent when
692
+ * present. A delivered order throws before touching the device: use
693
+ * `refund()`. A forced `type` that does not match throws too.
694
+ */
695
+ cancel(options?: OrderCancelOptions): Promise<OrderCancelResult>;
696
+ /**
697
+ * Refunds this delivered order with one call to `/refund`
698
+ * (`type: REFUND`): device charges are voided first and reported in the
699
+ * body. Only the tenders change; the order status stays. An order not
700
+ * delivered throws before touching the device: use `cancel()`.
701
+ */
702
+ refund(options?: Omit<OrderCancelOptions, "type">): Promise<OrderCancelResult>;
703
+ private freshCancelType;
704
+ private runCancel;
648
705
  /** Voids one tender (operator flow); idempotent per call. */
649
706
  voidTender(tenderId: string, reason?: string): Promise<Order$1>;
650
707
  /** Refunds one tender: all of it, an amount, or some units. */
@@ -1058,6 +1115,45 @@ type PaymentOperationBody = {
1058
1115
  };
1059
1116
  /** Server half of an adapter (`POST /payments/:id/operations/:operation`). */
1060
1117
  declare const callPaymentOperation: <T>(paymentId: string, operation: string, body: PaymentOperationBody, options?: ServiceHttpOptions) => Promise<T>;
1118
+ type DeunaCallbackUrls = Partial<Record<"on_success" | "on_pending" | "on_failed" | "on_canceled" | "on_reject", string>>;
1119
+ type DeunaTokenizeBody = {
1120
+ order_id: string;
1121
+ attempt_reference: string;
1122
+ callback_urls?: DeunaCallbackUrls;
1123
+ };
1124
+ type DeunaTokenizeResult = {
1125
+ order_token: string;
1126
+ order_id: string;
1127
+ };
1128
+ type DeunaOrderStatus = {
1129
+ order_token: string;
1130
+ status: string;
1131
+ transaction_id: string | null;
1132
+ error: {
1133
+ code: string | null;
1134
+ message: string | null;
1135
+ } | null;
1136
+ };
1137
+ /** DEUNA order for the Payment Widget (`POST .../deuna/:id/tokenize`). */
1138
+ declare const tokenizeDeunaOrder: (paymentId: string, body: DeunaTokenizeBody, options?: ServiceHttpOptions) => Promise<DeunaTokenizeResult>;
1139
+ /** Payment status of a DEUNA order (`GET .../deuna/:id/orders/:token`). */
1140
+ declare const getDeunaOrder: (paymentId: string, orderToken: string, options?: ServiceHttpOptions) => Promise<DeunaOrderStatus>;
1141
+ /**
1142
+ * `api` option of `deunaWidgetPayments` (`@fire-spark/payments-deuna-widget`)
1143
+ * bound to the storefront HTTP options of the template.
1144
+ */
1145
+ declare const createDeunaWidgetApi: (options?: ServiceHttpOptions) => {
1146
+ tokenize: (input: {
1147
+ paymentId: string;
1148
+ orderId: string;
1149
+ attemptReference: string;
1150
+ callbackUrls?: DeunaCallbackUrls;
1151
+ }) => Promise<DeunaTokenizeResult>;
1152
+ order: (input: {
1153
+ paymentId: string;
1154
+ orderToken: string;
1155
+ }) => Promise<DeunaOrderStatus>;
1156
+ };
1061
1157
  type TerminalFailureBody = {
1062
1158
  operation: string;
1063
1159
  device_id?: string | null;
@@ -1208,6 +1304,11 @@ declare const services: {
1208
1304
  list: typeof listPayments;
1209
1305
  operation: typeof callPaymentOperation;
1210
1306
  terminalFailure: typeof reportTerminalFailure;
1307
+ deuna: {
1308
+ tokenize: typeof tokenizeDeunaOrder;
1309
+ order: typeof getDeunaOrder;
1310
+ widgetApi: typeof createDeunaWidgetApi;
1311
+ };
1211
1312
  };
1212
1313
  stores: {
1213
1314
  list: typeof listStores;
@@ -1554,4 +1655,4 @@ declare const validateCardholderName: (value: string) => CardholderNameError | n
1554
1655
  */
1555
1656
  declare const cardPreviewGroups: (value: string) => CardPreviewSlot[][];
1556
1657
  //#endregion
1557
- export { type ApplyRedeemInput, type AuthConfigureOptions, CARD_BRANDS, type CardBrand, type CardBrandSpec, type CardCvcError, type CardExpiry, type CardExpiryError, type CardNumberError, type CardPreviewSlot, type CardholderNameError, type CreateOrderOptions, type CreateSessionOptions, type CustomizationPath, type CustomizationSession, DEFAULT_RECOMMENDATION_TYPES, type ExchangeOptions, type FailedCharge, type FlatMenuCategory, type GenOrderIdOptions, type GroupDisplayType, ID_HASH_FORMATS, type IdBuilder, type IdHashFormat, type ListMenusStreamOptions, type ListPaymentsOptions, type Menu, type MenuCategoryNode, type MenuLookup, type MilestoneSteps, type MilestoneTrackItem, type ModifierControlType, type ModifierCtx, type ModifierGroupCtx, type NestedMenuCategory, Order, type OrderCustomerSource, type OrderDeviceCredential, type OrderDraft, type OrderIssue, type OrderIssueCode, type OrderLineDiff, type OrderLineIssue, type OrderLineIssueCode, OrderPaymentRequiredError, type OrderScope, OrderSyncPendingError, OrderSyncRejectedError, OrderValidationError, type OrderValidationResult, type PayIntentOptions, type PayIntentResult, type PayOptions, type PaymentCardBrand, type PaymentProgressEvent, type PaymentProgressListener, type PaymentRunOptions, type PaymentSessionOptions, type PricingModifier, type Product, type ProductCtx, type QuantitiesByPath, type RecommendationType, RecurringDateCron, type RedeemPaymentRow, type RemoveRedeemInput, type ReorderOrderOptions, type ResetOptions, type ReversalOutcome, type ReverseAllOptions, RulesEngine, type ScheduleWindow, type SelectionStatus, type SelectionsByPath, type SerializedError, type ServiceHttpOptions, type SimpleTenderPayment, type Snapshot, type StorefrontAccessToken, type StorefrontErrorDetails, StorefrontHttpError, type TrackWorkflow, type ValidateOptions, type ValidateOrderAgainstMenuInput, type Variant, type VariantGroup, type VariantGroupCtx, type VariantOptionCtx, type VariantSelection, type VerifyWebhookSignatureInput, type VisitedState, addLines, auth, buildCustomerSnapshot, capRedeems, cappedRedeems, cardBrandSpec, cardDigits, cardNumberGaps, cardNumberMaxLength, cardPreviewGroups, caretAfterDigits, collectOrderLineDiffs, computeStartingPrice, countCartProducts, coveredAmount, createSession, currentNodes, cvcMaxLength, defaultConditionError, detectCardBrand, fitRedeems, flattenMenuCategories, flushLogs, formatCardNumber, formatCvc, formatExpiry, fromOrderLines, genId, genOrderId, getLogContext, getScheduleWindow, indexMenu, isCardNumberComplete, isCardNumberDefinitelyInvalid, isCheckoutFieldFilled, isCvcComplete, isDeviceCharged, isDeviceNotLinked, isExpiryComplete, isGenId, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderPaymentRequiredError, isOrderValidationError, isPosDeviceNotLinked, isPosUnauthorized, isRealPaymentMethod, isStorefrontNotFound, isVariantBase, linePayableTotal, listMenusStream, localizePosError, log, logError, luhnCheck, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, normalizeCardholderName, orderPayableTotal, orderRedeemedTotal, orderRedeems, parseExpiry, paymentMatchesClientConditions, rebuildRootGroups, recomputeTotals, redeemedTotal, redemptionCap, redemptionSteps, remainingAmount, removeLine, replaceLine, resolveCheckout, resolveModifierImageUrl, reversePayment, runPaymentAttempt, selectablePayments, serializeError, services, setLineQuantity, storefrontJson, toOrderLines, toPaymentCardBrand, validateCardNumber, validateCardholderName, validateCvc, validateExpiry, validateOrderAgainstMenu, verifyWebhookSignature, visitedStates };
1658
+ export { type ApplyRedeemInput, type AuthConfigureOptions, CARD_BRANDS, type CardBrand, type CardBrandSpec, type CardCvcError, type CardExpiry, type CardExpiryError, type CardNumberError, type CardPreviewSlot, type CardholderNameError, type CreateOrderOptions, type CreateSessionOptions, type CustomizationPath, type CustomizationSession, DEFAULT_RECOMMENDATION_TYPES, type DeunaCallbackUrls, type DeunaOrderStatus, type ExchangeOptions, type FailedCharge, type FlatMenuCategory, type GenOrderIdOptions, type GroupDisplayType, ID_HASH_FORMATS, type IdBuilder, type IdHashFormat, type ListMenusStreamOptions, type ListPaymentsOptions, type Menu, type MenuCategoryNode, type MenuLookup, type MilestoneSteps, type MilestoneTrackItem, type ModifierControlType, type ModifierCtx, type ModifierGroupCtx, type NestedMenuCategory, Order, type OrderCancelInput, type OrderCancelOptions, type OrderCancelResult, type OrderCancelType, type OrderCustomerSource, type OrderDeviceCredential, type OrderDraft, type OrderIssue, type OrderIssueCode, type OrderLineDiff, type OrderLineIssue, type OrderLineIssueCode, OrderPaymentRequiredError, type OrderScope, OrderSyncPendingError, OrderSyncRejectedError, OrderValidationError, type OrderValidationResult, type PayIntentOptions, type PayIntentResult, type PayOptions, type PaymentCardBrand, type PaymentProgressEvent, type PaymentProgressListener, type PaymentRunOptions, type PaymentSessionOptions, type PricingModifier, type Product, type ProductCtx, type QuantitiesByPath, type RecommendationType, RecurringDateCron, type RedeemPaymentRow, type RemoveRedeemInput, type ReorderOrderOptions, type ResetOptions, type ReversalOutcome, type ReverseAllOptions, RulesEngine, type ScheduleWindow, type SelectionStatus, type SelectionsByPath, type SerializedError, type ServiceHttpOptions, type SimpleTenderPayment, type Snapshot, type StorefrontAccessToken, type StorefrontErrorDetails, StorefrontHttpError, type TrackWorkflow, type ValidateOptions, type ValidateOrderAgainstMenuInput, type Variant, type VariantGroup, type VariantGroupCtx, type VariantOptionCtx, type VariantSelection, type VerifyWebhookSignatureInput, type VisitedState, addLines, auth, buildCustomerSnapshot, cancelTypeOf, capRedeems, cappedRedeems, cardBrandSpec, cardDigits, cardNumberGaps, cardNumberMaxLength, cardPreviewGroups, caretAfterDigits, collectOrderLineDiffs, computeStartingPrice, countCartProducts, coveredAmount, createDeunaWidgetApi, createSession, currentNodes, cvcMaxLength, defaultConditionError, detectCardBrand, fitRedeems, flattenMenuCategories, flushLogs, formatCardNumber, formatCvc, formatExpiry, fromOrderLines, genId, genOrderId, getLogContext, getScheduleWindow, indexMenu, isCardNumberComplete, isCardNumberDefinitelyInvalid, isCheckoutFieldFilled, isCvcComplete, isDeviceCharged, isDeviceNotLinked, isExpiryComplete, isGenId, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderPaymentRequiredError, isOrderValidationError, isPosDeviceNotLinked, isPosUnauthorized, isRealPaymentMethod, isStorefrontNotFound, isVariantBase, linePayableTotal, listMenusStream, localizePosError, log, logError, luhnCheck, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, normalizeCardholderName, orderPayableTotal, orderRedeemedTotal, orderRedeems, parseExpiry, paymentMatchesClientConditions, rebuildRootGroups, recomputeTotals, redeemedTotal, redemptionCap, redemptionSteps, remainingAmount, removeLine, replaceLine, resolveCheckout, resolveModifierImageUrl, reversePayment, runPaymentAttempt, selectablePayments, serializeError, services, setLineQuantity, storefrontJson, toOrderLines, toPaymentCardBrand, validateCardNumber, validateCardholderName, validateCvc, validateExpiry, validateOrderAgainstMenu, verifyWebhookSignature, visitedStates };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { DIGITAL_COLLECT_AT, PARTIAL_APPROVAL_ERROR, REDEMPTION_METHOD, activeIntent, createAttemptReference, createPaymentError, currentIntent, defaultReversalFor, groupRedeems, hasRedeemPayment, isDeliveryFee, isPaymentError, isRealPaymentMethod, isRealPaymentMethod as isRealPaymentMethod$1, isTenderSettled, isTipFee, liveTenders, memoryPaymentStorage, missingDeviceFields, money, moneyMin, moneyNumber, moneyZero, orderRedeems, orderRedeems as orderRedeems$1, redeemsCoverTotal, resolvePaymentPolicy, resolveProviderEnvironment, scopedPaymentStorage, taxBreakdown, toChargeAmount, withTerminalLock } from "@fire-spark/payments";
1
+ import { DIGITAL_COLLECT_AT, PARTIAL_APPROVAL_ERROR, REDEMPTION_METHOD, activeIntent, createAttemptReference, createPaymentError, currentIntent, defaultReversalFor, groupRedeems, hasRedeemPayment, isDeliveryFee, isManualPayment, isPaymentError, isRealPaymentMethod, isRealPaymentMethod as isRealPaymentMethod$1, isTenderSettled, isTipFee, liveTenders, memoryPaymentStorage, missingDeviceFields, money, moneyMin, moneyNumber, moneyZero, orderRedeems, orderRedeems as orderRedeems$1, redeemsCoverTotal, resolvePaymentPolicy, resolveProviderEnvironment, scopedPaymentStorage, taxBreakdown, toChargeAmount, withTerminalLock } from "@fire-spark/payments";
2
2
  import advancedFormat from "dayjs/plugin/advancedFormat.js";
3
3
  import isBetween from "dayjs/plugin/isBetween.js";
4
4
  import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js";
@@ -2625,11 +2625,24 @@ const updateOrder = (id, body, options) => {
2625
2625
  options
2626
2626
  ]);
2627
2627
  };
2628
- const cancelOrder = (id, options) => {
2629
- return storefrontJson(`${orderPath(id)}/cancel`, { method: "POST" }, options);
2628
+ const postCancel = (path, options, body) => {
2629
+ const method = "POST";
2630
+ if (!body) return storefrontJson(path, { method }, options);
2631
+ return storefrontJson(path, {
2632
+ method,
2633
+ body: JSON.stringify(body)
2634
+ }, options);
2630
2635
  };
2631
- const refundOrder = (id, options) => {
2632
- return storefrontJson(`${orderPath(id)}/refund`, { method: "POST" }, options);
2636
+ /**
2637
+ * `body` is optional: `device` (checked only when sent), `type`
2638
+ * (`VOID` / `REVERSE`) and what the device reversed (`tenders`).
2639
+ */
2640
+ const cancelOrder = (id, options, body) => {
2641
+ return postCancel(`${orderPath(id)}/cancel`, options, body);
2642
+ };
2643
+ /** Refunds the tenders only; the order status stays. `type` is `REFUND`. */
2644
+ const refundOrder = (id, options, body) => {
2645
+ return postCancel(`${orderPath(id)}/refund`, options, body);
2633
2646
  };
2634
2647
  const reorderOrder = (id, body = {}, options) => {
2635
2648
  return storefrontJson(`${orderPath(id)}/reorder`, {
@@ -2722,6 +2735,42 @@ const callPaymentOperation = (paymentId, operation, body, options) => {
2722
2735
  body: JSON.stringify(body)
2723
2736
  }, options);
2724
2737
  };
2738
+ const deunaPath = (paymentId) => {
2739
+ return `/api/storefront/v1/payments/deuna/${encodeURIComponent(paymentId)}`;
2740
+ };
2741
+ /** DEUNA order for the Payment Widget (`POST .../deuna/:id/tokenize`). */
2742
+ const tokenizeDeunaOrder = (paymentId, body, options) => {
2743
+ return storefrontJson(`${deunaPath(paymentId)}/tokenize`, {
2744
+ method: "POST",
2745
+ body: JSON.stringify(body)
2746
+ }, options);
2747
+ };
2748
+ /** Payment status of a DEUNA order (`GET .../deuna/:id/orders/:token`). */
2749
+ const getDeunaOrder = (paymentId, orderToken, options) => {
2750
+ const token = encodeURIComponent(orderToken);
2751
+ return storefrontJson(`${deunaPath(paymentId)}/orders/${token}`, {}, options);
2752
+ };
2753
+ /**
2754
+ * `api` option of `deunaWidgetPayments` (`@fire-spark/payments-deuna-widget`)
2755
+ * bound to the storefront HTTP options of the template.
2756
+ */
2757
+ const createDeunaWidgetApi = (options) => {
2758
+ return {
2759
+ tokenize: (input) => {
2760
+ const b1 = { order_id: input.orderId };
2761
+ const b2 = { attempt_reference: input.attemptReference };
2762
+ const b3 = input.callbackUrls ? { callback_urls: input.callbackUrls } : {};
2763
+ return tokenizeDeunaOrder(input.paymentId, {
2764
+ ...b1,
2765
+ ...b2,
2766
+ ...b3
2767
+ }, options);
2768
+ },
2769
+ order: (input) => {
2770
+ return getDeunaOrder(input.paymentId, input.orderToken, options);
2771
+ }
2772
+ };
2773
+ };
2725
2774
  /** Reports a failed terminal operation (`payment.terminal_failed`). */
2726
2775
  const reportTerminalFailure = (paymentId, body, options) => {
2727
2776
  return storefrontJson(`/api/storefront/v1/payments/${encodeURIComponent(paymentId)}/terminal-failures`, {
@@ -3089,7 +3138,12 @@ const services = {
3089
3138
  payments: {
3090
3139
  list: listPayments,
3091
3140
  operation: callPaymentOperation,
3092
- terminalFailure: reportTerminalFailure
3141
+ terminalFailure: reportTerminalFailure,
3142
+ deuna: {
3143
+ tokenize: tokenizeDeunaOrder,
3144
+ order: getDeunaOrder,
3145
+ widgetApi: createDeunaWidgetApi
3146
+ }
3093
3147
  },
3094
3148
  stores: {
3095
3149
  list: listStores,
@@ -3304,6 +3358,63 @@ const isOrderPaymentRequiredError = (error) => {
3304
3358
  return error instanceof OrderPaymentRequiredError;
3305
3359
  };
3306
3360
  //#endregion
3361
+ //#region src/order/cancel.ts
3362
+ const FULFILLED = ["COMPLETED", "FULFILLED"];
3363
+ /** Same rule as the server: `status` or `fulfillment_status` delivered. */
3364
+ const isOrderFulfilled = (order) => {
3365
+ if (FULFILLED.includes(order.status)) return true;
3366
+ return FULFILLED.includes(String(order.fulfillment_status ?? ""));
3367
+ };
3368
+ /** Money was charged at some point (paid, or already given back). */
3369
+ const isOrderCharged = (order) => {
3370
+ if (order.payment_status === "PAID") return true;
3371
+ if (order.payment_status === "REFUNDED") return true;
3372
+ return liveTenders(currentIntent(order.payment_intents)).some((tender) => {
3373
+ if (tender.status === "PAID" || tender.status === "REFUNDED") return true;
3374
+ return (tender.refunded_total ?? 0) > 0;
3375
+ });
3376
+ };
3377
+ /**
3378
+ * The operation an order takes: `VOID` (nothing charged), `REVERSE`
3379
+ * (charged, not delivered) or `REFUND` (delivered). An order already
3380
+ * cancelled keeps its operation, so a replay stays idempotent.
3381
+ */
3382
+ const cancelTypeOf = (order) => {
3383
+ if (order.status === "VOIDED") return "VOID";
3384
+ if (order.status === "REVERSED") return "REVERSE";
3385
+ if (isOrderFulfilled(order)) return "REFUND";
3386
+ return isOrderCharged(order) ? "REVERSE" : "VOID";
3387
+ };
3388
+ const REVERSED_RESULTS = ["REFUNDED", "CANCELLED"];
3389
+ /** The device gave the money back. */
3390
+ const isDeviceReversed = (reversal) => {
3391
+ return REVERSED_RESULTS.includes(reversal.outcome.result.status);
3392
+ };
3393
+ /** Body `tenders` entry for one device reversal. */
3394
+ const toReportedTender = (reversal) => {
3395
+ const { tender, outcome } = reversal;
3396
+ const { status, metadata: m1, error } = outcome.result;
3397
+ const payment = m1?.payment ?? null;
3398
+ const metadata = { reversal: {
3399
+ kind: outcome.kind,
3400
+ status,
3401
+ payment,
3402
+ error: error ?? null
3403
+ } };
3404
+ const reference = payment?.provider_transaction_id ?? null;
3405
+ const b1 = { tender_id: tender.id };
3406
+ const b2 = { attempt_reference: tender.attempt_reference };
3407
+ const result = isDeviceReversed(reversal) ? "REVERSED" : "NOT_REVERSED";
3408
+ const provider_reference = reference;
3409
+ return {
3410
+ ...b1,
3411
+ ...b2,
3412
+ result,
3413
+ provider_reference,
3414
+ metadata
3415
+ };
3416
+ };
3417
+ //#endregion
3307
3418
  //#region src/order/retry.ts
3308
3419
  const defaultSleep$1 = (ms) => {
3309
3420
  return new Promise((resolve) => {
@@ -3834,10 +3945,10 @@ const simpleTenderInput = (payment) => {
3834
3945
  /**
3835
3946
  * Charge order of §6.1: deferred capture (authorize only), immediate
3836
3947
  * capture reversible with cancel/refund, manual reversal only, then cash /
3837
- * IDLE last.
3948
+ * card terminal (IDLE) last.
3838
3949
  */
3839
3950
  const chargeRank = (tender, adapter) => {
3840
- if (tender.method === "CASH") return 3;
3951
+ if (isManualPayment(tender.provider, tender.method)) return 3;
3841
3952
  if (adapter.capabilities?.deferredCapture) return 0;
3842
3953
  if (adapter.cancel || adapter.refund) return 1;
3843
3954
  return 2;
@@ -4164,6 +4275,10 @@ const reversePayment = async (adapter, ctx, result, options = {}) => {
4164
4275
  };
4165
4276
  //#endregion
4166
4277
  //#region src/order/payments/session.ts
4278
+ /** Cash or the Fire Spark card terminal: staff collects, no adapter call. */
4279
+ const isManual = (payment) => {
4280
+ return isManualPayment(payment.provider.provider_id, payment.method.method);
4281
+ };
4167
4282
  const noopUI = {
4168
4283
  message: () => void 0,
4169
4284
  action: () => void 0,
@@ -4389,12 +4504,12 @@ var PaymentSession = class {
4389
4504
  };
4390
4505
  }
4391
4506
  collectAt(payment) {
4392
- if (payment.method.method === "CASH") return this.options.cashCollectAt ?? "DOOR";
4507
+ if (isManual(payment)) return this.options.cashCollectAt ?? "DOOR";
4393
4508
  return this.options.collectAt ?? "DEVICE";
4394
4509
  }
4395
- /** `collect_at` of a new tender: cash in person, the rest digital. */
4510
+ /** `collect_at` of a new tender: manual in person, the rest digital. */
4396
4511
  tenderCollectAt(payment) {
4397
- if (payment.method.method === "CASH") return this.options.cashCollectAt ?? "DOOR";
4512
+ if (isManual(payment)) return this.options.cashCollectAt ?? "DOOR";
4398
4513
  return "DEVICE";
4399
4514
  }
4400
4515
  toRecord(tender, result) {
@@ -4509,7 +4624,7 @@ var PaymentSession = class {
4509
4624
  const amount = tender.amount.total;
4510
4625
  const attemptReference = tender.attempt_reference;
4511
4626
  const metadata = { attempt_reference: attemptReference };
4512
- if (!(payment.method.method === "CASH") && tender.status !== "PENDING") {
4627
+ if (!isManual(payment) && tender.status !== "PENDING") {
4513
4628
  const pending = {
4514
4629
  status: "PENDING",
4515
4630
  amount,
@@ -4743,6 +4858,34 @@ var PaymentSession = class {
4743
4858
  }
4744
4859
  return outcomes;
4745
4860
  }
4861
+ /**
4862
+ * Voids the `PAID` device charges of this order without reporting them:
4863
+ * the caller sends the results with one cancel / refund call
4864
+ * (`order.cancel()`), never through `/payments`.
4865
+ */
4866
+ async reverseOnDevice(options = {}) {
4867
+ const intents = this.deps.getOrder().payment_intents;
4868
+ const reversals = [];
4869
+ const only = options.tenderIds ? new Set(options.tenderIds) : null;
4870
+ for (const tender of chargedTenders(intents)) {
4871
+ if (only && !only.has(tender.id)) continue;
4872
+ if (options.skip?.has(tender.id)) continue;
4873
+ if (tender.status !== "PAID") continue;
4874
+ const found = await this.contextFor(tender);
4875
+ if (!found) continue;
4876
+ await options.onProgress?.({
4877
+ type: "reversing",
4878
+ tender
4879
+ });
4880
+ const { adapter, result, ctx } = found;
4881
+ const outcome = await reversePayment(adapter, ctx, result);
4882
+ reversals.push({
4883
+ tender,
4884
+ outcome
4885
+ });
4886
+ }
4887
+ return reversals;
4888
+ }
4746
4889
  /** Runs a terminal operation (init, test, closeBatch, status, extra). */
4747
4890
  async runTerminal(payment, key) {
4748
4891
  const terminal = this.adapterFor(payment)?.terminal;
@@ -5483,6 +5626,8 @@ var Order = class Order {
5483
5626
  */
5484
5627
  requestedRedeem = null;
5485
5628
  device;
5629
+ /** Device voids a failed cancel / refund call did not report yet. */
5630
+ unreported = [];
5486
5631
  constructor(config, notify) {
5487
5632
  this.draft = cloneOrder(config.order);
5488
5633
  this.synced = cloneOrder(config.order);
@@ -5625,7 +5770,10 @@ var Order = class Order {
5625
5770
  debounceMs,
5626
5771
  persisted
5627
5772
  };
5628
- const d2 = { sleep: config.sleep };
5773
+ const d2 = {
5774
+ sleep: config.sleep,
5775
+ device: config.device
5776
+ };
5629
5777
  return new Order({
5630
5778
  ...d1,
5631
5779
  ...d2
@@ -6093,6 +6241,80 @@ var Order = class Order {
6093
6241
  await this.cancelOrder();
6094
6242
  return outcomes;
6095
6243
  }
6244
+ /**
6245
+ * Cancels this placed, not delivered order with one call to `/cancel`:
6246
+ * `VOID` (nothing charged) or `REVERSE` (charged), decided from the
6247
+ * fresh order. Device charges are voided first and their results go in
6248
+ * the same body (never `/payments`). The device credential of
6249
+ * `createOrder({ device })` / `fromExisting({ device })` is sent when
6250
+ * present. A delivered order throws before touching the device: use
6251
+ * `refund()`. A forced `type` that does not match throws too.
6252
+ */
6253
+ async cancel(options = {}) {
6254
+ const decided = await this.freshCancelType();
6255
+ if (decided === "REFUND") throw new Error("Order is already delivered; use refund()");
6256
+ if (options.type && options.type !== decided) throw new Error(`Order takes ${decided}, not ${options.type}`);
6257
+ return this.runCancel("cancel", decided, options);
6258
+ }
6259
+ /**
6260
+ * Refunds this delivered order with one call to `/refund`
6261
+ * (`type: REFUND`): device charges are voided first and reported in the
6262
+ * body. Only the tenders change; the order status stays. An order not
6263
+ * delivered throws before touching the device: use `cancel()`.
6264
+ */
6265
+ async refund(options = {}) {
6266
+ if (await this.freshCancelType() !== "REFUND") throw new Error("Order is not delivered; use cancel()");
6267
+ return this.runCancel("refund", "REFUND", options);
6268
+ }
6269
+ async freshCancelType() {
6270
+ if (!this.persisted) throw new Error("Only a placed order is cancelled");
6271
+ this.flush.cancel();
6272
+ this.applyServerOrder(await services.orders.get(this.draft.id, this.http));
6273
+ return cancelTypeOf(this.draft);
6274
+ }
6275
+ async runCancel(path, type, options) {
6276
+ const earlier = this.unreported;
6277
+ const done = new Set(earlier.map((reversal) => reversal.tender.id));
6278
+ const r1 = {
6279
+ tenderIds: options.tenderIds?.filter((id) => !done.has(id)),
6280
+ skip: done
6281
+ };
6282
+ const fresh = type === "VOID" ? [] : await this.payments.reverseOnDevice(r1);
6283
+ const reversals = [...earlier, ...fresh];
6284
+ const b1 = {
6285
+ type,
6286
+ tenders: reversals.map(toReportedTender),
6287
+ idempotency_key: createAttemptReference()
6288
+ };
6289
+ const b2 = options.reason ? { reason: options.reason } : {};
6290
+ const b3 = this.device ? { device: this.device } : {};
6291
+ const body = {
6292
+ ...b1,
6293
+ ...b2,
6294
+ ...b3
6295
+ };
6296
+ const call = path === "refund" ? services.orders.refund : services.orders.cancel;
6297
+ let next;
6298
+ try {
6299
+ next = await call(this.draft.id, this.http, body);
6300
+ } catch (error) {
6301
+ this.unreported = reversals.filter(isDeviceReversed);
6302
+ throw error;
6303
+ }
6304
+ this.unreported = [];
6305
+ this.applyServerOrder(next);
6306
+ this.deleted = true;
6307
+ this.notify();
6308
+ const outcomes = reversals.map((reversal) => reversal.outcome);
6309
+ const pending = reversals.filter((reversal) => !isDeviceReversed(reversal)).map((reversal) => reversal.tender.id);
6310
+ return {
6311
+ order: this.getOrder(),
6312
+ type,
6313
+ path,
6314
+ outcomes,
6315
+ pending
6316
+ };
6317
+ }
6096
6318
  /** Voids one tender (operator flow); idempotent per call. */
6097
6319
  async voidTender(tenderId, reason) {
6098
6320
  const idempotency_key = createAttemptReference();
@@ -8937,4 +9159,4 @@ const cardPreviewGroups = (value) => {
8937
9159
  return groups;
8938
9160
  };
8939
9161
  //#endregion
8940
- export { CARD_BRANDS, DEFAULT_RECOMMENDATION_TYPES, ID_HASH_FORMATS, Order, OrderPaymentRequiredError, OrderSyncPendingError, OrderSyncRejectedError, OrderValidationError, RecurringDateCron, RulesEngine, StorefrontHttpError, addLines, auth, buildCustomerSnapshot, capRedeems, cappedRedeems, cardBrandSpec, cardDigits, cardNumberGaps, cardNumberMaxLength, cardPreviewGroups, caretAfterDigits, collectOrderLineDiffs, computeStartingPrice, countCartProducts, coveredAmount, createSession, currentNodes, cvcMaxLength, defaultConditionError, detectCardBrand, fitRedeems, flattenMenuCategories, flushLogs, formatCardNumber, formatCvc, formatExpiry, fromOrderLines, genId, genOrderId, getLogContext, getScheduleWindow, indexMenu, isCardNumberComplete, isCardNumberDefinitelyInvalid, isCheckoutFieldFilled, isCvcComplete, isDeviceCharged, isDeviceNotLinked, isExpiryComplete, isGenId, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderPaymentRequiredError, isOrderValidationError, isPosDeviceNotLinked, isPosUnauthorized, isRealPaymentMethod, isStorefrontNotFound, isVariantBase, linePayableTotal, listMenusStream, localizePosError, log, logError, luhnCheck, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, normalizeCardholderName, orderPayableTotal, orderRedeemedTotal, orderRedeems, parseExpiry, paymentMatchesClientConditions, rebuildRootGroups, recomputeTotals, redeemedTotal, redemptionCap, redemptionSteps, remainingAmount, removeLine, replaceLine, resolveCheckout, resolveModifierImageUrl, reversePayment, runPaymentAttempt, selectablePayments, serializeError, services, setLineQuantity, storefrontJson, toOrderLines, toPaymentCardBrand, validateCardNumber, validateCardholderName, validateCvc, validateExpiry, validateOrderAgainstMenu, verifyWebhookSignature, visitedStates };
9162
+ export { CARD_BRANDS, DEFAULT_RECOMMENDATION_TYPES, ID_HASH_FORMATS, Order, OrderPaymentRequiredError, OrderSyncPendingError, OrderSyncRejectedError, OrderValidationError, RecurringDateCron, RulesEngine, StorefrontHttpError, addLines, auth, buildCustomerSnapshot, cancelTypeOf, capRedeems, cappedRedeems, cardBrandSpec, cardDigits, cardNumberGaps, cardNumberMaxLength, cardPreviewGroups, caretAfterDigits, collectOrderLineDiffs, computeStartingPrice, countCartProducts, coveredAmount, createDeunaWidgetApi, createSession, currentNodes, cvcMaxLength, defaultConditionError, detectCardBrand, fitRedeems, flattenMenuCategories, flushLogs, formatCardNumber, formatCvc, formatExpiry, fromOrderLines, genId, genOrderId, getLogContext, getScheduleWindow, indexMenu, isCardNumberComplete, isCardNumberDefinitelyInvalid, isCheckoutFieldFilled, isCvcComplete, isDeviceCharged, isDeviceNotLinked, isExpiryComplete, isGenId, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderPaymentRequiredError, isOrderValidationError, isPosDeviceNotLinked, isPosUnauthorized, isRealPaymentMethod, isStorefrontNotFound, isVariantBase, linePayableTotal, listMenusStream, localizePosError, log, logError, luhnCheck, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, normalizeCardholderName, orderPayableTotal, orderRedeemedTotal, orderRedeems, parseExpiry, paymentMatchesClientConditions, rebuildRootGroups, recomputeTotals, redeemedTotal, redemptionCap, redemptionSteps, remainingAmount, removeLine, replaceLine, resolveCheckout, resolveModifierImageUrl, reversePayment, runPaymentAttempt, selectablePayments, serializeError, services, setLineQuantity, storefrontJson, toOrderLines, toPaymentCardBrand, validateCardNumber, validateCardholderName, validateCvc, validateExpiry, validateOrderAgainstMenu, verifyWebhookSignature, visitedStates };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fire-spark/core",
3
- "version": "0.9.1",
3
+ "version": "0.11.0",
4
4
  "description": "Fire Spark headless SDK",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -20,8 +20,8 @@
20
20
  "big.js": "6.2.1",
21
21
  "dayjs": "1.11.9",
22
22
  "@types/big.js": "6.2.2",
23
- "@fire-spark/payments": "^0.9.1",
24
- "@fire-spark/types": "^0.9.1"
23
+ "@fire-spark/payments": "^0.11.0",
24
+ "@fire-spark/types": "^0.11.0"
25
25
  },
26
26
  "devDependencies": {
27
27
  "tsdown": "^0.22.14",