@curless/hotel-storefront 0.40.1 → 0.42.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.
@@ -1 +1 @@
1
- {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/huazhu/backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/huazhu/backend.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ import { formatMoney, minorFactor, money, multiplyMoney, toMinor } from '../mone
9
9
  import { UnionPayError, expiryMMyy, lastFour, } from '../payments/unionpay.js';
10
10
  import { MAX_ROOMS_PER_LINE, quoteLineKey } from '../trip.js';
11
11
  import { IN_REQUEST_AT_MS, NOT_FOUND, StoreWriteError, nextCheckAt, statusOf, } from './bookings.js';
12
- import { describePolicy, feeFor, freeUntilOf, nightAuditOf, parseCancelPolicy, penaltyAt, refundableFrom, } from './cancel-policy.js';
12
+ import { describePolicy, feeFor, freeUntilOf, nightAuditOf, parseCancelPolicy, penaltyAt, refundableFrom, stepsOf, } from './cancel-policy.js';
13
13
  import { haystackOf, inPlace, namesOf, roomPhoto, toHotel } from './catalog.js';
14
14
  import { HuazhuRefusal, HuazhuUnreachable } from './client.js';
15
15
  import { bedNameEn, offerNameEn, rateNameEn, roomNameEn } from './english.js';
@@ -976,7 +976,7 @@ export const createHuazhuHotels = (opts) => {
976
976
  stayTotal: money(total, cur),
977
977
  taxesAndFees: money(total - pre, cur),
978
978
  nights: hourly ? 0 : n,
979
- refundable: policy ? refundableFrom(policy, stay.checkIn, opts.tz, now()) : false,
979
+ refundable: policy ? refundableFrom(policy, stay.checkIn, opts.tz, now(), n) : false,
980
980
  ...(freeUntil
981
981
  ? {
982
982
  freeCancellationUntil: new Date(freeUntil).toISOString(),
@@ -1488,16 +1488,22 @@ export const createHuazhuHotels = (opts) => {
1488
1488
  // the guest (2026-10-05 audit, P3).
1489
1489
  rule: "This rate's cancellation terms could not be read here, so the fee shown is the most it could be. Huazhu works out the actual fee when it cancels.",
1490
1490
  freeUntil: undefined,
1491
+ feeIsCap: true,
1492
+ steps: undefined,
1491
1493
  };
1492
1494
  }
1495
+ const startsAt = b.hourly ? arrivalInstant(b.stay.checkIn, b.hourly.start, b.tz) : undefined;
1496
+ const steps = stepsOf(policy, b.stay.checkIn, b.tz, amounts, at, startsAt);
1493
1497
  // A room by the hour that has begun is a no-show, whatever the code says
1494
1498
  // about the rest of the day - see `beforeStart`.
1495
- if (b.hourly && at >= arrivalInstant(b.stay.checkIn, b.hourly.start, b.tz)) {
1499
+ if (startsAt !== undefined && at >= startsAt) {
1496
1500
  return {
1497
1501
  fee: total,
1498
1502
  forbidden: false,
1499
- rule: `This room by the hour began at ${b.hourly.start}, so cancelling now is a no-show; the fee shown is the most it could be, and Huazhu works out the actual fee when it cancels.`,
1503
+ rule: `This room by the hour began at ${b.hourly?.start}, so cancelling now is a no-show; the fee shown is the most it could be, and Huazhu works out the actual fee when it cancels.`,
1500
1504
  freeUntil: undefined,
1505
+ feeIsCap: true,
1506
+ steps,
1501
1507
  };
1502
1508
  }
1503
1509
  const penalty = penaltyAt(policy, b.stay.checkIn, b.tz, at);
@@ -1512,8 +1518,21 @@ export const createHuazhuHotels = (opts) => {
1512
1518
  ? `Free to cancel until ${b.hourly.start} on ${b.stay.checkIn}, when the room by the hour begins; after that it is a no-show.`
1513
1519
  : describePolicy(policy, b.stay.checkIn, b.tz, amounts),
1514
1520
  freeUntil,
1521
+ feeIsCap: undefined,
1522
+ steps,
1515
1523
  };
1516
1524
  };
1525
+ /** `feeNow`'s steps as the card reads them: money, and the hotel's clock. */
1526
+ const stepsTold = (b, steps) => steps
1527
+ ? {
1528
+ steps: steps.map((x) => ({
1529
+ ...(x.until !== undefined ? { untilLocalIso: localIsoAt(x.until, b.tz) } : {}),
1530
+ fee: x.fee === null ? null : money(x.fee, b.quote.total.currency),
1531
+ ...(x.noShow ? { noShow: true } : {}),
1532
+ ...(x.current ? { current: true } : {}),
1533
+ })),
1534
+ }
1535
+ : {};
1517
1536
  /**
1518
1537
  * Chase one unanswered booking call inside the guest's request - the first
1519
1538
  * of its checks (`IN_REQUEST_AT_MS`), on the reconciler's own schedule; the
@@ -3300,6 +3319,10 @@ export const createHuazhuHotels = (opts) => {
3300
3319
  nights: b.room.nights,
3301
3320
  roomsLeft: b.room.roomsLeft,
3302
3321
  freeCancellation: !!b.room.freeCancellationUntil,
3322
+ // Only where nothing of it would come back from now on (or its
3323
+ // terms could not be read): the list says "non-refundable" then,
3324
+ // and nothing otherwise (香格里拉 r7 ⑨; 华住 checked the meaning).
3325
+ ...(b.room.refundable ? {} : { refundable: false }),
3303
3326
  ...(b.room.hourly ? { hours: b.room.hourly.hours } : {}),
3304
3327
  ...(b.room.labels?.length ? { labels: b.room.labels } : {}),
3305
3328
  ...(b.room.meal ? { meal: b.room.meal } : {}),
@@ -3627,6 +3650,7 @@ export const createHuazhuHotels = (opts) => {
3627
3650
  refund: zero,
3628
3651
  rule: 'Not paid yet, and nothing is booked with Huazhu: cancelling closes the payment page, and nothing is charged.',
3629
3652
  cancellable: true,
3653
+ reason: 'unpaid',
3630
3654
  };
3631
3655
  }
3632
3656
  if (b.pendingSince !== undefined || b.status === 'unconfirmed') {
@@ -3637,6 +3661,7 @@ export const createHuazhuHotels = (opts) => {
3637
3661
  refund: zero,
3638
3662
  rule: waitingWords(b),
3639
3663
  cancellable: false,
3664
+ reason: b.status === 'unconfirmed' ? 'unconfirmed' : 'confirming',
3640
3665
  };
3641
3666
  }
3642
3667
  if (b.status !== 'confirmed' || past) {
@@ -3649,9 +3674,13 @@ export const createHuazhuHotels = (opts) => {
3649
3674
  ? 'The arrival day has passed; a booking cannot be cancelled after it.'
3650
3675
  : `This booking is ${b.status}; there is nothing left to cancel.`,
3651
3676
  cancellable: false,
3677
+ reason: past && b.status === 'confirmed' ? 'past-arrival' : 'not-active',
3652
3678
  };
3653
3679
  }
3654
- const { fee, forbidden, rule, freeUntil } = feeNow(b);
3680
+ const { fee, forbidden, rule, freeUntil, feeIsCap, steps } = feeNow(b);
3681
+ // The terms as facts, for the card's own language - `rule` stays for
3682
+ // the model (华住 / 香格里拉, 2026-10-19).
3683
+ const facts = { ...(feeIsCap ? { feeIsCap } : {}), ...stepsTold(b, steps) };
3655
3684
  // A test booking says it is one (2026-10-16 audit, P2): Huazhu's test
3656
3685
  // system charges nobody and books nothing anyone stays in.
3657
3686
  const testNote = opts.testEnvironment
@@ -3674,6 +3703,7 @@ export const createHuazhuHotels = (opts) => {
3674
3703
  : `${rule} Paid with a UnionPay card that Huazhu charged: cancelling now gives nothing back - the fee is the whole stay, and Huazhu keeps it.${testNote}`,
3675
3704
  ...(freeUntil ? { freeUntil: new Date(freeUntil).toISOString() } : {}),
3676
3705
  cancellable: !forbidden,
3706
+ ...facts,
3677
3707
  };
3678
3708
  }
3679
3709
  if (b.prepaid?.paidAt) {
@@ -3692,6 +3722,7 @@ export const createHuazhuHotels = (opts) => {
3692
3722
  : `${rule} Cancelling now gives nothing back: the fee is the whole stay.${testNote}`,
3693
3723
  ...(freeUntil ? { freeUntil: new Date(freeUntil).toISOString() } : {}),
3694
3724
  cancellable: !forbidden,
3725
+ ...facts,
3695
3726
  };
3696
3727
  }
3697
3728
  return {
@@ -3709,6 +3740,7 @@ export const createHuazhuHotels = (opts) => {
3709
3740
  : `${rule} Nothing was paid through this connector - the booking is paid at the hotel - so any fee is charged under the hotel's terms, and Huazhu works out the exact amount when it cancels.`,
3710
3741
  ...(freeUntil ? { freeUntil: new Date(freeUntil).toISOString() } : {}),
3711
3742
  cancellable: !forbidden,
3743
+ ...facts,
3712
3744
  };
3713
3745
  },
3714
3746
  async cancelBooking(ref, who) {