seatlayer 0.7.0 → 0.8.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.
- checksums.yaml +4 -4
- data/README.md +65 -32
- data/lib/seatlayer/inventory.rb +13 -1
- data/lib/seatlayer/resources.rb +12 -2
- data/lib/seatlayer/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 841def7560b63781babf931924a29204c724ba5b4513eff1de283a370cb22699
|
|
4
|
+
data.tar.gz: 7027c38a8aeeee9aa5c339d90d04b33c4d708e47362687951d30dc280402bef4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2ef313b0db74c36aef5d0f512264586448d99330e1257c8a28b835da54a7b539fd3f9ab7183a91ae912cf1e55ec2c5dcede861260d80b77a4ed0eaaa6d1faaba
|
|
7
|
+
data.tar.gz: e85e01d772d6639a36d7fa7154a062d60564fbbe750258c1080fa4f7cc17eba25277ebc0e7e31ad0f04589b9d716402c486c6c325b3d202e7c7fd2ed9e96c903
|
data/README.md
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
[](https://www.ruby-lang.org/)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
SeatLayer's official Ruby server SDK is the trusted side of its reserved seating and seat booking
|
|
9
|
+
API. Inspect what a hold really contains, price from server-owned seating-chart data, and book
|
|
10
|
+
with a stable `booking_ref`, while managing charts, events, inventory, allocations, and webhooks
|
|
11
|
+
through one typed ticketing API client.
|
|
12
12
|
|
|
13
13
|
[`seatlayer` gem on RubyGems](https://rubygems.org/gems/seatlayer) ·
|
|
14
|
-
[
|
|
14
|
+
[Ruby server SDK guide](https://docs.seatlayer.io/server-sdk/ruby/) ·
|
|
15
15
|
[SeatLayer developer platform](https://seatlayer.io/developers/) ·
|
|
16
16
|
[SeatLayer JavaScript seat map SDK](https://www.npmjs.com/package/@seatlayer/js) ·
|
|
17
17
|
[Server API reference](https://docs.seatlayer.io/server-api/events/)
|
|
@@ -19,7 +19,7 @@ allocations, and webhooks through one typed ticketing API client.
|
|
|
19
19
|
> **Server-side only.** This gem authenticates with your secret key. Never load it anywhere a
|
|
20
20
|
> ticket buyer can reach — browser surfaces get short-lived, origin-bound tokens that you mint here.
|
|
21
21
|
|
|
22
|
-
## Install
|
|
22
|
+
## Install the Ruby seat booking SDK
|
|
23
23
|
|
|
24
24
|
```ruby
|
|
25
25
|
gem "seatlayer"
|
|
@@ -40,11 +40,16 @@ require "seatlayer"
|
|
|
40
40
|
client = SeatLayer::Client.new(ENV.fetch("SEATLAYER_SECRET_KEY"))
|
|
41
41
|
|
|
42
42
|
# 1. Provision a venue for a new organiser from a public template.
|
|
43
|
-
|
|
43
|
+
# Replace this placeholder with a template id from your catalog.
|
|
44
|
+
chart = client.templates.instantiate_template("your-published-template")["meta"]
|
|
44
45
|
client.charts.publish(chart["id"])
|
|
45
46
|
|
|
46
47
|
# 2. Create an event on it.
|
|
47
|
-
event = client.events.create(
|
|
48
|
+
event = client.events.create(
|
|
49
|
+
chart_id: chart["id"], name: "Spring Gala",
|
|
50
|
+
currency: "EUR", # omit to inherit the workspace currency
|
|
51
|
+
region: "western-europe" # India: "asia-pacific"
|
|
52
|
+
)["meta"]
|
|
48
53
|
|
|
49
54
|
# 3. Sell four seats over the phone.
|
|
50
55
|
held = client.inventory.hold_best_available(event["key"], qty: 4)
|
|
@@ -52,11 +57,23 @@ held = client.inventory.hold_best_available(event["key"], qty: 4)
|
|
|
52
57
|
client.inventory.book(event["key"], hold_id: held["holdId"], booking_ref: "order-8842")
|
|
53
58
|
```
|
|
54
59
|
|
|
60
|
+
## Event hosting region
|
|
61
|
+
|
|
62
|
+
Pass `region:` to `events.create` based on the **event venue**, not your API server or office. It
|
|
63
|
+
controls the initial placement of the Event's live inventory; an existing Event
|
|
64
|
+
cannot be moved later. Omit it to inherit the workspace default (`western-europe` for new accounts).
|
|
65
|
+
Set that default with `workspaces.create(default_region: ...)` or
|
|
66
|
+
`workspaces.update(workspace_id, default_region: ...)`; changing it affects only future Events.
|
|
67
|
+
|
|
68
|
+
- `western-europe`, `eastern-europe`, `north-america-east`, `north-america-west`, `south-america`
|
|
69
|
+
- `asia-pacific`, `northeast-asia`, `southeast-asia`, `oceania`, `africa`, `middle-east`
|
|
70
|
+
|
|
71
|
+
The hint is best effort, not a data-residency guarantee. See the
|
|
72
|
+
[full Event region guide](https://docs.seatlayer.io/server-api/event-regions/).
|
|
73
|
+
|
|
55
74
|
Nullable event-create fields distinguish omission from an explicit reset: passing, for example,
|
|
56
75
|
`venue: nil` sends JSON `null`; leaving `venue` out sends no field.
|
|
57
76
|
|
|
58
|
-
## Test vs live
|
|
59
|
-
|
|
60
77
|
## Fixed Renewable Seasons
|
|
61
78
|
|
|
62
79
|
Version `0.7.0` exposes all 48 trusted organizer operations through
|
|
@@ -84,6 +101,7 @@ returned operation identity. Buyer-session minting and domain-exact booking,
|
|
|
84
101
|
cancellation, and renewal actions remain single-attempt; only declared
|
|
85
102
|
header-replay catalogue mutations retry automatically.
|
|
86
103
|
|
|
104
|
+
## Test vs live
|
|
87
105
|
|
|
88
106
|
Keys carry their own mode. `sk_test_…` keys can only touch test-mode events and `sk_live_…` only
|
|
89
107
|
live ones; crossing them returns `403 mode_mismatch`, surfaced as `AuthError` with `mode_mismatch?`.
|
|
@@ -97,15 +115,21 @@ raise "Refusing to boot production against test-mode seating data." if
|
|
|
97
115
|
A publishable `pk_` key is rejected at construction with a message naming the mistake, rather than
|
|
98
116
|
failing as a `401` three round-trips later.
|
|
99
117
|
|
|
100
|
-
##
|
|
118
|
+
## Book reserved seats from Ruby
|
|
101
119
|
|
|
102
120
|
**Buyer picks seats in the browser.** Your frontend holds them; your backend confirms the price and
|
|
103
121
|
books. Never price from what the browser sent you — `retrieve_hold` is authoritative.
|
|
104
122
|
|
|
105
123
|
```ruby
|
|
106
124
|
hold = client.inventory.retrieve_hold(event_key, hold_id)
|
|
107
|
-
|
|
108
|
-
|
|
125
|
+
currencies = hold["items"].map { |item| item.fetch("currency") }.uniq
|
|
126
|
+
raise "A hold must use one currency" unless currencies.one?
|
|
127
|
+
|
|
128
|
+
currency = currencies.first
|
|
129
|
+
total = hold["items"].sum do |item|
|
|
130
|
+
item.fetch("unitPrice") * item.fetch("quantity", 1)
|
|
131
|
+
end
|
|
132
|
+
# … charge `total` in `currency` …
|
|
109
133
|
client.inventory.book(event_key, hold_id: hold_id, booking_ref: charge.id)
|
|
110
134
|
```
|
|
111
135
|
|
|
@@ -205,12 +229,12 @@ The full set, all opt-in:
|
|
|
205
229
|
|---|---|
|
|
206
230
|
| `event:view` | Read the seat map and its live states |
|
|
207
231
|
| `event:block` | Block and unblock seats |
|
|
208
|
-
| `event:cancel` |
|
|
232
|
+
| `event:cancel` | Cancel a Platform/SDK booking by reference and return its inventory to sale; does not move gateway money |
|
|
209
233
|
| `event:reports` | Read sales and availability reports |
|
|
210
234
|
| `event:channels:view` | Read sales channels and their allocations |
|
|
211
235
|
| `event:channels:manage` | Create, pause and archive channels; rotate access links |
|
|
212
236
|
| `event:orders:read` | Read SeatLayer-managed orders |
|
|
213
|
-
| `event:refund` | Refund
|
|
237
|
+
| `event:refund` | Refund an eligible Managed Ticketing order through its connected gateway |
|
|
214
238
|
| `event:tickets:send` | Send SeatLayer-managed tickets |
|
|
215
239
|
| `event:door:view` | Read the door list |
|
|
216
240
|
| `event:door:checkin` | Check tickets in and out |
|
|
@@ -289,17 +313,20 @@ error carries `status`, `code`, `body` and `request_id` — quote the request id
|
|
|
289
313
|
## Reliability
|
|
290
314
|
|
|
291
315
|
**Retries.** Reads (`GET`/`HEAD`) retry 429, 408 and 5xx with exponential backoff and full jitter;
|
|
292
|
-
`Retry-After` wins when the server sends it.
|
|
293
|
-
|
|
294
|
-
`
|
|
316
|
+
`Retry-After` wins when the server sends it. Fourteen mutations use exact header replay:
|
|
317
|
+
`charts.create`, `charts.copy`, `templates.instantiate_template`, `events.create`,
|
|
318
|
+
`workspaces.create`, `performance_groups.create`, `seasons.create_season`,
|
|
319
|
+
`seasons.update_season`, `seasons.delete_season`, `seasons.create_season_plan`,
|
|
320
|
+
`seasons.duplicate_season_to_live`, `seasons.create_season_holder_import`,
|
|
321
|
+
`seasons.create_season_renewal_offers`, and `seasons.create_season_amendment`. Other 4xx responses
|
|
295
322
|
are never retried.
|
|
296
323
|
|
|
297
|
-
**Idempotency.** Those
|
|
298
|
-
do not supply one and reused across attempts.
|
|
299
|
-
|
|
300
|
-
inventory holds and bookings, show-once credential or secret creation, unsupported
|
|
301
|
-
raw `request` mutations. Keep `booking_ref` in the booking body for reconciliation,
|
|
302
|
-
unknown network outcome explicitly instead of automatically repeating the sale.
|
|
324
|
+
**Idempotency.** Those 14 replay-backed operations carry an `Idempotency-Key`, generated when you
|
|
325
|
+
do not supply one and reused across attempts. All remaining SDK mutations are single-attempt. Some
|
|
326
|
+
have a server-side domain idempotency contract, but the SDK does not retry them automatically. This
|
|
327
|
+
includes inventory holds and bookings, show-once credential or secret creation, unsupported
|
|
328
|
+
operations, and raw `request` mutations. Keep `booking_ref` in the booking body for reconciliation,
|
|
329
|
+
but handle an unknown network outcome explicitly instead of automatically repeating the sale.
|
|
303
330
|
|
|
304
331
|
```ruby
|
|
305
332
|
client.events.create(chart_id: chart_id, idempotency_key: "provision-event-#{event_id}")
|
|
@@ -324,6 +351,10 @@ client.request("POST", "/v1/events/ev_1/some-new-route", body: { "qty" => 2 })
|
|
|
324
351
|
|
|
325
352
|
## API surface
|
|
326
353
|
|
|
354
|
+
The client exposes these resources. Performance Groups cover runs, sessions, holds, and bookings;
|
|
355
|
+
Seasons cover catalogue, plan, sales, buyer-session, booking, renewal, occurrence, reporting,
|
|
356
|
+
outbox, and support operations.
|
|
357
|
+
|
|
327
358
|
| Resource | Methods |
|
|
328
359
|
| --- | --- |
|
|
329
360
|
| `charts` | `list` `list_all` `create` `retrieve` `update` `delete` `copy` `archive` `unarchive` `publish` |
|
|
@@ -334,8 +365,10 @@ client.request("POST", "/v1/events/ev_1/some-new-route", body: { "qty" => 2 })
|
|
|
334
365
|
| `sessions` | `create_manage_session` `revoke_manage_session` `create_designer_session` `revoke_designer_session` |
|
|
335
366
|
| `webhooks` | `list` `create` `update` `delete` `list_deliveries` |
|
|
336
367
|
| `workspaces` | `list` `create` `retrieve` `update` |
|
|
368
|
+
| `performance_groups` | `list` `create` `retrieve` `delete` `activate` `close` `retrieve_lifecycle` `create_buyer_access_session` `list_buyer_access_sessions` `revoke_buyer_access_session` `retrieve_hold` `book_hold` `retrieve_booking` |
|
|
369
|
+
| `seasons` | 48 operations for catalogue and Plan lifecycle, sales windows, buyer access and booking, holder imports, renewals, occurrence amendments, reports, audit, outbox, and support export |
|
|
337
370
|
|
|
338
|
-
Full reference: [
|
|
371
|
+
Full reference: [SeatLayer Ruby server SDK guide](https://docs.seatlayer.io/server-sdk/ruby/)
|
|
339
372
|
|
|
340
373
|
### Deliberately not in this SDK
|
|
341
374
|
|
|
@@ -386,16 +419,16 @@ outcome before trying again.
|
|
|
386
419
|
|
|
387
420
|
### Can I use my own payment provider?
|
|
388
421
|
|
|
389
|
-
Yes.
|
|
390
|
-
the
|
|
391
|
-
money through whichever provider you already use
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
422
|
+
Yes. This server SDK does not process payment in a Platform/SDK integration. Inspect the hold,
|
|
423
|
+
compute the charge from each returned item's authoritative `unitPrice`, `quantity`, and `currency`,
|
|
424
|
+
take the money through whichever provider you already use, and then book the hold with your order
|
|
425
|
+
id as `booking_ref`. SeatLayer owns seating state, holds, booking concurrency, and the inventory
|
|
426
|
+
ledger in this integration; your platform owns payments, commercial orders, tickets, delivery,
|
|
427
|
+
and refunds. Managed Ticketing is a separate product path with organizer-connected payments.
|
|
395
428
|
|
|
396
429
|
## Continue your Ruby integration
|
|
397
430
|
|
|
398
|
-
- [Follow the
|
|
431
|
+
- [Follow the Ruby server SDK guide](https://docs.seatlayer.io/server-sdk/ruby/)
|
|
399
432
|
for installation, authentication, and the full hold-to-booking flow.
|
|
400
433
|
- [Handle errors, retries, and safe booking repeats](https://docs.seatlayer.io/server-sdk/reliability/)
|
|
401
434
|
before connecting a production order flow.
|
data/lib/seatlayer/inventory.rb
CHANGED
|
@@ -252,9 +252,14 @@ module SeatLayer
|
|
|
252
252
|
@client.get("/v1/workspaces")
|
|
253
253
|
end
|
|
254
254
|
|
|
255
|
-
def create(name:, external_ref: UNSET, idempotency_key: nil)
|
|
255
|
+
def create(name:, external_ref: UNSET, default_region: nil, idempotency_key: nil)
|
|
256
|
+
unless default_region.nil? || EVENT_HOSTING_REGIONS.include?(default_region)
|
|
257
|
+
raise ArgumentError, "default_region must be a supported SeatLayer Event region"
|
|
258
|
+
end
|
|
259
|
+
|
|
256
260
|
body = { "name" => name }
|
|
257
261
|
body.merge!(supplied({ "externalRef" => external_ref }))
|
|
262
|
+
body["defaultRegion"] = default_region unless default_region.nil?
|
|
258
263
|
@client.post(
|
|
259
264
|
"/v1/workspaces", body, idempotency_key: idempotency_key, retry_policy: :header_replay
|
|
260
265
|
)
|
|
@@ -269,6 +274,13 @@ module SeatLayer
|
|
|
269
274
|
# The organisation's default workspace cannot be disabled — the API answers
|
|
270
275
|
# 409 +default_workspace_required+. Promote another one first.
|
|
271
276
|
def update(workspace_id, fields)
|
|
277
|
+
region = fields[:default_region] || fields["defaultRegion"]
|
|
278
|
+
unless region.nil? || EVENT_HOSTING_REGIONS.include?(region)
|
|
279
|
+
raise ArgumentError, "default_region must be a supported SeatLayer Event region"
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
fields = fields.dup
|
|
283
|
+
fields["defaultRegion"] = fields.delete(:default_region) if fields.key?(:default_region)
|
|
272
284
|
@client.patch("/v1/workspaces/#{encode(workspace_id)}", fields)
|
|
273
285
|
end
|
|
274
286
|
end
|
data/lib/seatlayer/resources.rb
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SeatLayer
|
|
4
|
+
EVENT_HOSTING_REGIONS = %w[
|
|
5
|
+
western-europe eastern-europe north-america-east north-america-west
|
|
6
|
+
south-america asia-pacific northeast-asia southeast-asia oceania africa middle-east
|
|
7
|
+
].freeze
|
|
8
|
+
|
|
4
9
|
# Shared plumbing for the resource namespaces.
|
|
5
10
|
class Resource
|
|
6
11
|
UNSET = Object.new.freeze
|
|
@@ -179,8 +184,13 @@ module SeatLayer
|
|
|
179
184
|
def create(chart_id:, name: nil, slug: nil, starts_at: UNSET, venue: UNSET,
|
|
180
185
|
external_ref: UNSET, currency: UNSET, idempotency_key: nil,
|
|
181
186
|
description: UNSET, ends_at: UNSET, timezone: UNSET, locale: UNSET,
|
|
182
|
-
poster_asset_id: UNSET, mode: nil)
|
|
183
|
-
|
|
187
|
+
poster_asset_id: UNSET, mode: nil, region: nil)
|
|
188
|
+
unless region.nil? || EVENT_HOSTING_REGIONS.include?(region)
|
|
189
|
+
raise ArgumentError, "region must be a supported SeatLayer Event region"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
body = compact({ "chartId" => chart_id, "name" => name, "slug" => slug,
|
|
193
|
+
"mode" => mode, "region" => region })
|
|
184
194
|
body.merge!(supplied({ "startsAt" => starts_at, "venue" => venue,
|
|
185
195
|
"externalRef" => external_ref, "currency" => currency,
|
|
186
196
|
"description" => description, "endsAt" => ends_at,
|
data/lib/seatlayer/version.rb
CHANGED