From 177b6e3c984809a4fb7aa1f7c98745df2885c0f8 Mon Sep 17 00:00:00 2001 From: navin10sharma <3096611+navin10sharma@users.noreply.github.com> Date: Thu, 24 Sep 2026 07:22:24 +0530 Subject: [PATCH 1/4] Pin runtime 0.98.0 The map loads SeatLayer runtime 0.98.0 instead of 0.84.1. The bridge protocol range is unchanged (1-2); the bridge changes in between are additive (maintenance pause message, seats added after checkout listed in the runtime snapshot). --- CHANGELOG.md | 5 +++++ lib/src/seat_layer_configuration.dart | 2 +- test/payloads_test.dart | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9496d00..a2b474b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +- **Runtime pin 0.98.0.** The map now loads SeatLayer runtime 0.98.0 + (was 0.84.1): the section overview, seat rendering and row labels of the + current web picker, the maintenance-pause message, and seats added after + checkout listed in the runtime's own snapshot. The bridge protocol range is + unchanged (1–2). - **A sold-out accessibility filter can still be turned off.** A provision whose count reached zero went dark even while its switch was on, which trapped the buyer holding the last space on a filtered map. diff --git a/lib/src/seat_layer_configuration.dart b/lib/src/seat_layer_configuration.dart index 7d36688..7a99cc2 100644 --- a/lib/src/seat_layer_configuration.dart +++ b/lib/src/seat_layer_configuration.dart @@ -7,7 +7,7 @@ import 'payloads.dart'; const String seatLayerSdkVersion = '0.9.1'; /// The SeatLayer renderer version this SDK release is pinned to. -const String seatLayerHostedWebVersion = '0.84.1'; +const String seatLayerHostedWebVersion = '0.98.0'; /// Renderer version retained only for the example app's offline fixture. /// diff --git a/test/payloads_test.dart b/test/payloads_test.dart index 16117f5..bcc4dab 100644 --- a/test/payloads_test.dart +++ b/test/payloads_test.dart @@ -5,7 +5,7 @@ import 'package:seatlayer/src/seat_layer_configuration.dart'; void main() { test('hosted and fixture runtime versions remain explicitly pinned', () { - expect(seatLayerHostedWebVersion, '0.84.1'); + expect(seatLayerHostedWebVersion, '0.98.0'); expect(seatLayerLegacyFixtureWebVersion, '0.68.0'); expect( seatLayerMobilePageUrl, From fae7d6c655b993d9b015c6582e049553cc609136 Mon Sep 17 00:00:00 2001 From: navin10sharma <3096611+navin10sharma@users.noreply.github.com> Date: Thu, 24 Sep 2026 08:09:05 +0530 Subject: [PATCH 2/4] Send host prices to the map (host-pricing-v1) SeatLayerPickerOptions.pricing.categories was declared but never sent, so a host selling from its own catalogue saw the chart's prices on the rail, the seat card and the cart. The picker now boots the runtime with them in init.config.pricing, and SeatLayerPickerController.setPricing changes them without a reload. A runtime without host-pricing-v1 keeps the chart's prices. Display only: the hold is still priced by SeatLayer. --- CHANGELOG.md | 7 ++ design/picker-spec.md | 1 + lib/src/picker/picker_options.dart | 36 ++++++++- .../picker/seat_layer_picker_controller.dart | 20 +++++ test/picker_host_pricing_test.dart | 79 +++++++++++++++++++ 5 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 test/picker_host_pricing_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index a2b474b..5ef8c30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +- **Host prices reach the map.** `SeatLayerPickerOptions.pricing.categories` + was declared but never sent, so a host that sells from its own catalogue + saw the chart's prices on the rail, the seat card and the cart. The picker + now boots the runtime with them (`init.config.pricing`) and + `SeatLayerPickerController.setPricing` changes them without a reload. + Needs a runtime advertising `host-pricing-v1`; an older one keeps the + chart's prices. Display only — the hold is still priced by SeatLayer. - **Runtime pin 0.98.0.** The map now loads SeatLayer runtime 0.98.0 (was 0.84.1): the section overview, seat rendering and row labels of the current web picker, the maintenance-pause message, and seats added after diff --git a/design/picker-spec.md b/design/picker-spec.md index 81f0490..8698388 100644 --- a/design/picker-spec.md +++ b/design/picker-spec.md @@ -2748,6 +2748,7 @@ from the wrong one. | `hold-ownership-v1`, `hold-selection-v1` | who owns a hold, and holding a named selection | | `checkout-handoff-v1`, `checkout-handoff-reject-v1` | the hold crossing to the host, and giving it back | | `cart-line-remove-v1` | removing a cart line the selection cannot name | +| `host-pricing-v1` | the host's own category and tier prices (`pricing.categories`) on the rail, the seat card, the cart and the handoff, sent in `init.config.pricing` and changed live with `picker.setPricing`; a category the host leaves out keeps the chart's price, and an older runtime keeps the chart's prices for all | A capability the runtime does not advertise is a feature that is **not offered**, never a feature that fails. diff --git a/lib/src/picker/picker_options.dart b/lib/src/picker/picker_options.dart index 2bf90eb..5a364ec 100644 --- a/lib/src/picker/picker_options.dart +++ b/lib/src/picker/picker_options.dart @@ -16,6 +16,10 @@ typedef SeatLayerCheckoutCallback = FutureOr Function( typedef SeatLayerMoneyFormatter = String Function( double amount, String currency); +/// The runtime capability that carries [SeatLayerPickerPricing.categories] +/// to the map: `init.config.pricing` and `cmd picker.setPricing`. +const String seatLayerHostPricingCapability = 'host-pricing-v1'; + /// Host-supplied prices for one category and its tiers. @immutable class SeatLayerCategoryPricing { @@ -25,14 +29,32 @@ class SeatLayerCategoryPricing { this.tiers = const {}, }); - /// Price when the category has no tiers. + /// Price when the category has no tiers, and for a tier not in [tiers]. final double? base; /// Price per tier id. final Map tiers; + + /// The runtime's shape: a bare number for a flat price, else + /// `{ base, tiers }`; null when this entry sets nothing. + Object? toBridgeJson() { + if (tiers.isEmpty) return base; + return { + if (base != null) 'base': base, + 'tiers': Map.of(tiers), + }; + } } /// Host-supplied pricing and money formatting. +/// +/// [categories] are the prices the buyer is shown — on the price rail, the +/// seat card, the cart and the checkout handoff — in place of the chart's own. +/// A ticketing host that charges from its own catalogue sets them so the map +/// says what checkout will charge; a category it leaves out keeps the chart's +/// price. Display only: the hold is still priced by SeatLayer. Needs a runtime +/// advertising [seatLayerHostPricingCapability]; an older one keeps the chart's +/// prices. @immutable class SeatLayerPickerPricing { /// Creates a pricing override. @@ -46,6 +68,16 @@ class SeatLayerPickerPricing { /// How every amount in the native chrome is rendered. final SeatLayerMoneyFormatter? formatter; + + /// The runtime's `pricing` value, or null when no category sets a price. + Map? toBridgeConfig() { + final prices = { + for (final entry in categories.entries) + if (entry.value.toBridgeJson() != null) + entry.key: entry.value.toBridgeJson(), + }; + return prices.isEmpty ? null : {'prices': prices}; + } } /// Visibility of the turnkey picker's native chrome. @@ -401,6 +433,8 @@ class SeatLayerPickerOptions { 'hideEventDetails': hideEventDetails, 'panelCollapsed': panelInitiallyCollapsed, 'languages': languages.map((locale) => locale.toLanguageTag()).toList(), + if (pricing?.toBridgeConfig() != null) + 'pricing': pricing!.toBridgeConfig(), }; } diff --git a/lib/src/picker/seat_layer_picker_controller.dart b/lib/src/picker/seat_layer_picker_controller.dart index e2fa04a..f070369 100644 --- a/lib/src/picker/seat_layer_picker_controller.dart +++ b/lib/src/picker/seat_layer_picker_controller.dart @@ -843,6 +843,26 @@ class SeatLayerPickerController extends ValueNotifier /// contain. Runtimes that do not report `floorMode` ignore it. Future showAllFloors() => setFloor(seatLayerAllFloors); + /// Whether the runtime takes host prices ([SeatLayerPickerPricing]). + bool get supportsHostPricing { + final bundle = mapController.bundleInfo; + return bundle != null && + bundle.supportsCapability(seatLayerHostPricingCapability); + } + + /// Show [pricing]'s category prices in place of the chart's without a + /// reload (null: the chart's own). Boot prices come from + /// [SeatLayerPickerOptions.pricing]; a runtime without + /// [seatLayerHostPricingCapability] keeps the chart's prices. + Future setPricing(SeatLayerPickerPricing? pricing) { + if (!supportsHostPricing) return Future.value(); + return _mutation( + 'picker.setPricing', + {'pricing': pricing?.toBridgeConfig()}, + SeatLayerPickerBusyAction.none, + ); + } + Future setColorblindSafe(bool enabled) => _mutation( 'picker.setColorblindSafe', {'on': enabled}, diff --git a/test/picker_host_pricing_test.dart b/test/picker_host_pricing_test.dart new file mode 100644 index 0000000..bb804c0 --- /dev/null +++ b/test/picker_host_pricing_test.dart @@ -0,0 +1,79 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:seatlayer/seatlayer.dart'; +import 'package:seatlayer/src/bridge/bridge_profile.dart'; + +void main() { + group('SeatLayerPickerPricing → host-pricing-v1', () { + test('a flat price travels as a number, a tiered one as { base, tiers }', + () { + const pricing = SeatLayerPickerPricing( + categories: { + 'gold': SeatLayerCategoryPricing(base: 486), + 'vip': SeatLayerCategoryPricing( + base: 935, + tiers: {'child': 673}, + ), + 'kids': SeatLayerCategoryPricing( + tiers: {'u12': 120}, + ), + 'unset': SeatLayerCategoryPricing(), + }, + ); + expect(pricing.toBridgeConfig(), { + 'prices': { + 'gold': 486.0, + 'vip': { + 'base': 935.0, + 'tiers': {'child': 673}, + }, + 'kids': { + 'tiers': {'u12': 120}, + }, + }, + }); + }); + + test('no category price means no pricing on the wire', () { + expect(const SeatLayerPickerPricing().toBridgeConfig(), isNull); + const formatterOnly = SeatLayerPickerOptions( + pricing: SeatLayerPickerPricing(formatter: _kroner), + ); + expect(formatterOnly.toBridgeConfig().containsKey('pricing'), isFalse); + }); + + test('the picker boots the runtime with the prices in init.config', () { + const options = SeatLayerPickerOptions( + pricing: SeatLayerPickerPricing( + categories: { + 'gold': SeatLayerCategoryPricing(base: 486), + }, + ), + ); + final payload = SeatLayerBridgeProfile.picker( + config: options.toBridgeConfig(), + ).initPayload(SeatLayerConfiguration(event: 'ev_dkk', currency: 'DKK')); + final config = payload['config']! as Map; + expect(config['currency'], 'DKK'); + expect(config['pricing'], { + 'prices': {'gold': 486.0}, + }); + }); + + test('equal prices build an equivalent profile, so nothing reboots', () { + SeatLayerBridgeProfile profile(double gold) => + SeatLayerBridgeProfile.picker( + config: SeatLayerPickerOptions( + pricing: SeatLayerPickerPricing( + categories: { + 'gold': SeatLayerCategoryPricing(base: gold), + }, + ), + ).toBridgeConfig(), + ); + expect(profile(486).equivalentTo(profile(486)), isTrue); + expect(profile(486).equivalentTo(profile(500)), isFalse); + }); + }); +} + +String _kroner(double amount, String currency) => '$currency $amount'; From 45bd9f23a2fc086dfcbc97b08a3667f4a5b58c3f Mon Sep 17 00:00:00 2001 From: navin10sharma <3096611+navin10sharma@users.noreply.github.com> Date: Thu, 24 Sep 2026 08:24:29 +0530 Subject: [PATCH 3/4] Version 0.10.0 Cut with runtime 0.99.0; the runtime pin moves to 0.99.0 once that runtime is on the CDN. --- CHANGELOG.md | 5 ++++- lib/src/seat_layer_configuration.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ef8c30..d8713d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog -## Unreleased +## 0.10.0 (unreleased) + +Cut with runtime 0.99.0: the pin moves from 0.98.0 to 0.99.0 when that +runtime is on the CDN, since `host-pricing-v1` first ships there. - **Host prices reach the map.** `SeatLayerPickerOptions.pricing.categories` was declared but never sent, so a host that sells from its own catalogue diff --git a/lib/src/seat_layer_configuration.dart b/lib/src/seat_layer_configuration.dart index 7a99cc2..f3ad55f 100644 --- a/lib/src/seat_layer_configuration.dart +++ b/lib/src/seat_layer_configuration.dart @@ -4,7 +4,7 @@ import 'open_enums.dart'; import 'payloads.dart'; /// This SDK's version. -const String seatLayerSdkVersion = '0.9.1'; +const String seatLayerSdkVersion = '0.10.0'; /// The SeatLayer renderer version this SDK release is pinned to. const String seatLayerHostedWebVersion = '0.98.0'; diff --git a/pubspec.yaml b/pubspec.yaml index f21067a..90c2689 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: >- Flutter seat map SDK: a native seating chart and seat picker for iOS and Android ticketing apps, with live availability, best available, seat holds, 3D views and secure checkout. -version: 0.9.1 +version: 0.10.0 homepage: https://docs.seatlayer.io/buyer-sdk/flutter/ repository: https://github.com/seatlayer/seatlayer-flutter issue_tracker: https://github.com/seatlayer/seatlayer-flutter/issues From 645c647502135a126c7e3803f553115da6a751d0 Mon Sep 17 00:00:00 2001 From: navin10sharma <3096611+navin10sharma@users.noreply.github.com> Date: Thu, 24 Sep 2026 10:42:31 +0530 Subject: [PATCH 4/4] Pin runtime 0.99.0 The first runtime with host-pricing-v1, now on the CDN and promotable. --- CHANGELOG.md | 15 +++++++-------- lib/src/seat_layer_configuration.dart | 2 +- test/payloads_test.dart | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8713d1..a0e8e30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,8 @@ # Changelog -## 0.10.0 (unreleased) +## 0.10.0 (2026-09-24) -Cut with runtime 0.99.0: the pin moves from 0.98.0 to 0.99.0 when that -runtime is on the CDN, since `host-pricing-v1` first ships there. +Runtime pin 0.99.0, the first runtime with `host-pricing-v1`. - **Host prices reach the map.** `SeatLayerPickerOptions.pricing.categories` was declared but never sent, so a host that sells from its own catalogue @@ -12,11 +11,11 @@ runtime is on the CDN, since `host-pricing-v1` first ships there. `SeatLayerPickerController.setPricing` changes them without a reload. Needs a runtime advertising `host-pricing-v1`; an older one keeps the chart's prices. Display only — the hold is still priced by SeatLayer. -- **Runtime pin 0.98.0.** The map now loads SeatLayer runtime 0.98.0 - (was 0.84.1): the section overview, seat rendering and row labels of the - current web picker, the maintenance-pause message, and seats added after - checkout listed in the runtime's own snapshot. The bridge protocol range is - unchanged (1–2). +- **Runtime pin 0.99.0** (was 0.84.1): the section overview, seat rendering + and row labels of the current web picker, the maintenance-pause message, + seats added after checkout listed in the runtime's own snapshot, host + pricing, and a seat card that no longer freezes 3D drag and pinch. The + bridge protocol range is unchanged (1–2). - **A sold-out accessibility filter can still be turned off.** A provision whose count reached zero went dark even while its switch was on, which trapped the buyer holding the last space on a filtered map. diff --git a/lib/src/seat_layer_configuration.dart b/lib/src/seat_layer_configuration.dart index f3ad55f..5cec083 100644 --- a/lib/src/seat_layer_configuration.dart +++ b/lib/src/seat_layer_configuration.dart @@ -7,7 +7,7 @@ import 'payloads.dart'; const String seatLayerSdkVersion = '0.10.0'; /// The SeatLayer renderer version this SDK release is pinned to. -const String seatLayerHostedWebVersion = '0.98.0'; +const String seatLayerHostedWebVersion = '0.99.0'; /// Renderer version retained only for the example app's offline fixture. /// diff --git a/test/payloads_test.dart b/test/payloads_test.dart index bcc4dab..2f25461 100644 --- a/test/payloads_test.dart +++ b/test/payloads_test.dart @@ -5,7 +5,7 @@ import 'package:seatlayer/src/seat_layer_configuration.dart'; void main() { test('hosted and fixture runtime versions remain explicitly pinned', () { - expect(seatLayerHostedWebVersion, '0.98.0'); + expect(seatLayerHostedWebVersion, '0.99.0'); expect(seatLayerLegacyFixtureWebVersion, '0.68.0'); expect( seatLayerMobilePageUrl,