@fire-spark/core 0.10.0 → 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,
@@ -402,6 +407,25 @@ type ReverseAllOptions = {
402
407
  declare const coveredAmount: (intents: Intents) => number;
403
408
  declare const remainingAmount: (order: Order$1) => number;
404
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
405
429
  //#region src/order/recommendations.d.ts
406
430
  type RecommendationType = Recommendation["type"];
407
431
  declare const DEFAULT_RECOMMENDATION_TYPES: RecommendationType[];
@@ -418,6 +442,13 @@ type OrderDeviceCredential = {
418
442
  uid: string;
419
443
  secret: string;
420
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
+ };
421
452
  type CreateOrderOptions = {
422
453
  scope?: OrderScope;
423
454
  customer?: OrderCustomerSource;
@@ -498,6 +529,8 @@ declare class Order {
498
529
  */
499
530
  private requestedRedeem;
500
531
  private readonly device?;
532
+ /** Device voids a failed cancel / refund call did not report yet. */
533
+ private unreported;
501
534
  private constructor();
502
535
  /**
503
536
  * Opens a cart session: reuses an existing OPEN order for the scope, or a
@@ -514,6 +547,8 @@ declare class Order {
514
547
  debounceMs?: number;
515
548
  persisted?: boolean;
516
549
  sleep?: (ms: number) => Promise<void>;
550
+ /** Sent by `cancel()` (and `placeOrder`) when present. */
551
+ device?: CreateOrderOptions["device"];
517
552
  }, notify?: () => void): Order;
518
553
  /** Whether this session has a server row yet. */
519
554
  isPersisted(): boolean;
@@ -648,6 +683,25 @@ declare class Order {
648
683
  replaceTender(tenderId: string, payment: MerchantPayment, collectAt?: PaymentCollectAt$1): Promise<Order$1>;
649
684
  /** "Cancel and refund": reverses device charges, then cancels the order. */
650
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;
651
705
  /** Voids one tender (operator flow); idempotent per call. */
652
706
  voidTender(tenderId: string, reason?: string): Promise<Order$1>;
653
707
  /** Refunds one tender: all of it, an amount, or some units. */
@@ -1601,4 +1655,4 @@ declare const validateCardholderName: (value: string) => CardholderNameError | n
1601
1655
  */
1602
1656
  declare const cardPreviewGroups: (value: string) => CardPreviewSlot[][];
1603
1657
  //#endregion
1604
- 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 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, 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 };
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
@@ -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`, {
@@ -3345,6 +3358,63 @@ const isOrderPaymentRequiredError = (error) => {
3345
3358
  return error instanceof OrderPaymentRequiredError;
3346
3359
  };
3347
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
3348
3418
  //#region src/order/retry.ts
3349
3419
  const defaultSleep$1 = (ms) => {
3350
3420
  return new Promise((resolve) => {
@@ -4788,6 +4858,34 @@ var PaymentSession = class {
4788
4858
  }
4789
4859
  return outcomes;
4790
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
+ }
4791
4889
  /** Runs a terminal operation (init, test, closeBatch, status, extra). */
4792
4890
  async runTerminal(payment, key) {
4793
4891
  const terminal = this.adapterFor(payment)?.terminal;
@@ -5528,6 +5626,8 @@ var Order = class Order {
5528
5626
  */
5529
5627
  requestedRedeem = null;
5530
5628
  device;
5629
+ /** Device voids a failed cancel / refund call did not report yet. */
5630
+ unreported = [];
5531
5631
  constructor(config, notify) {
5532
5632
  this.draft = cloneOrder(config.order);
5533
5633
  this.synced = cloneOrder(config.order);
@@ -5670,7 +5770,10 @@ var Order = class Order {
5670
5770
  debounceMs,
5671
5771
  persisted
5672
5772
  };
5673
- const d2 = { sleep: config.sleep };
5773
+ const d2 = {
5774
+ sleep: config.sleep,
5775
+ device: config.device
5776
+ };
5674
5777
  return new Order({
5675
5778
  ...d1,
5676
5779
  ...d2
@@ -6138,6 +6241,80 @@ var Order = class Order {
6138
6241
  await this.cancelOrder();
6139
6242
  return outcomes;
6140
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
+ }
6141
6318
  /** Voids one tender (operator flow); idempotent per call. */
6142
6319
  async voidTender(tenderId, reason) {
6143
6320
  const idempotency_key = createAttemptReference();
@@ -8982,4 +9159,4 @@ const cardPreviewGroups = (value) => {
8982
9159
  return groups;
8983
9160
  };
8984
9161
  //#endregion
8985
- 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, 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 };
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.10.0",
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.10.0",
24
- "@fire-spark/types": "^0.10.0"
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",