@debugged-development/ticketapp-sdk 1.0.8 → 1.0.10-dev.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.
Files changed (44) hide show
  1. package/README.md +620 -339
  2. package/dist/index.js +1949 -1995
  3. package/dist/sdk.d.ts +9 -14
  4. package/dist/sdk.d.ts.map +1 -1
  5. package/dist/services/basket/basket.service.d.ts +39 -0
  6. package/dist/services/basket/basket.service.d.ts.map +1 -0
  7. package/dist/services/basket/basket.service.generated.d.ts +202 -0
  8. package/dist/services/basket/basket.service.generated.d.ts.map +1 -0
  9. package/dist/services/category/category.service.d.ts +15 -0
  10. package/dist/services/category/category.service.d.ts.map +1 -0
  11. package/dist/services/category/category.service.generated.d.ts +41 -0
  12. package/dist/services/category/category.service.generated.d.ts.map +1 -0
  13. package/dist/services/event/event.service.d.ts +31 -0
  14. package/dist/services/event/event.service.d.ts.map +1 -0
  15. package/dist/services/event/event.service.generated.d.ts +122 -0
  16. package/dist/services/event/event.service.generated.d.ts.map +1 -0
  17. package/dist/services/package/package.service.d.ts +29 -0
  18. package/dist/services/package/package.service.d.ts.map +1 -0
  19. package/dist/services/package/package.service.generated.d.ts +122 -0
  20. package/dist/services/package/package.service.generated.d.ts.map +1 -0
  21. package/dist/services/payment/payment.service.d.ts +14 -0
  22. package/dist/services/payment/payment.service.d.ts.map +1 -0
  23. package/dist/services/payment/payment.service.generated.d.ts +53 -0
  24. package/dist/services/payment/payment.service.generated.d.ts.map +1 -0
  25. package/dist/store/store.d.ts +3132 -11
  26. package/dist/store/store.d.ts.map +1 -1
  27. package/dist/types/index.d.ts +105 -27
  28. package/dist/types/index.d.ts.map +1 -1
  29. package/dist/{graphql/generated.d.ts → types/types.generated.d.ts} +263 -903
  30. package/dist/types/types.generated.d.ts.map +1 -0
  31. package/package.json +14 -12
  32. package/dist/graphql/generated.d.ts.map +0 -1
  33. package/dist/services/basketService.d.ts +0 -39
  34. package/dist/services/basketService.d.ts.map +0 -1
  35. package/dist/services/eventService.d.ts +0 -37
  36. package/dist/services/eventService.d.ts.map +0 -1
  37. package/dist/services/packageService.d.ts +0 -65
  38. package/dist/services/packageService.d.ts.map +0 -1
  39. package/dist/services/paymentService.d.ts +0 -18
  40. package/dist/services/paymentService.d.ts.map +0 -1
  41. package/dist/store/basketSlice.d.ts +0 -55
  42. package/dist/store/basketSlice.d.ts.map +0 -1
  43. package/dist/store/eventSlice.d.ts +0 -75
  44. package/dist/store/eventSlice.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ticketapp SDK
2
2
 
3
- A TypeScript SDK for integrating Ticketapp's event ticketing and package management system into your application.
3
+ A TypeScript SDK for integrating Ticketapp's event ticketing, package, and category management system into your application.
4
4
 
5
5
  ## Installation
6
6
 
@@ -15,20 +15,34 @@ import { TicketappSDK } from '@ticketapp/sdk';
15
15
 
16
16
  const sdk = new TicketappSDK({
17
17
  organizationId: 'your-organization-id',
18
- shopId: 'your-shop-id',
19
- shopSlug: 'your-shop-slug',
20
18
  debug: true, // Enable debug logging (optional)
21
19
  });
22
20
  ```
23
21
 
22
+ > Who is this for?
23
+ > - Teams who want to show events, let visitors pick tickets, and pay online.
24
+ > - You don’t need to be a developer to follow the examples below—each step is explained in plain English.
25
+ >
26
+ > Core concepts in 60 seconds:
27
+ > - Organization: your brand or company in Ticketapp.
28
+ > - Event: something you sell tickets for (e.g., Friday Night Show).
29
+ > - Product: a sellable ticket or add-on (e.g., Regular Ticket, VIP, Door ticket).
30
+ > - Basket: the visitor’s shopping cart (we keep it in the browser so it survives page reloads).
31
+ > - Package: a bundle of tickets/events (e.g., Weekend Pass with several included events).
32
+ > - Category: a label to group products or events (e.g., Music, Sports).
33
+ > - Payment: the step where the visitor selects a method (iDEAL, card, etc.) and pays.
34
+
24
35
  ## Table of Contents
25
36
 
26
37
  - [Configuration](#configuration)
27
38
  - [Events](#events)
28
- - [Basket (Cart)](#basket-cart)
39
+ - [Categories](#categories)
29
40
  - [Packages](#packages)
41
+ - [Basket](#basket)
30
42
  - [Payment](#payment)
31
43
  - [Examples](#examples)
44
+ - [Services Overview](#services-overview)
45
+ - [FAQ and Troubleshooting](#faq-and-troubleshooting)
32
46
 
33
47
  ---
34
48
 
@@ -39,8 +53,6 @@ const sdk = new TicketappSDK({
39
53
  ```typescript
40
54
  const sdk = new TicketappSDK({
41
55
  organizationId: 'eed7fb11-f5c4-4467-8393-6da072b7f752',
42
- shopId: '1fbdf73d-6ee6-438b-b8e5-a76907a6d76b',
43
- shopSlug: 'hvrmq',
44
56
  debug: true,
45
57
  });
46
58
  ```
@@ -50,12 +62,8 @@ const sdk = new TicketappSDK({
50
62
  ```typescript
51
63
  const sdk = new TicketappSDK({
52
64
  organizationId: 'your-organization-id',
53
- shopId: 'your-shop-id',
54
- shopSlug: 'your-shop-slug',
55
65
  debug: true,
56
- filteredLocationIds: ['location-id-1', 'location-id-2'], // Optional: Filter events by location
57
- filteredHostingIds: ['host-id-1', 'host-id-2'], // Optional: Filter events by host
58
- enableDoorTickets: true, // Optional: Include door tickets
66
+ // Optionally add shopId, shopSlug, filteredLocationIds, filteredHostingIds, enableDoorTickets, etc.
59
67
  });
60
68
  ```
61
69
 
@@ -63,68 +71,90 @@ const sdk = new TicketappSDK({
63
71
 
64
72
  ## Events
65
73
 
66
- ### Fetching Events
74
+ // In plain English: Use EventService to show a list of events, open one event’s details, and load its ticket types (products). You can filter by date or status, and sort or paginate results.
75
+
76
+ ### EventService overview
77
+
78
+ Methods:
79
+ - fetchEvents(options?): Promise<[Event[], number]>
80
+ - options.statuses?: EventStatus[]
81
+ - options.hostingIds?: string[]
82
+ - options.dateRange?: { from: DateTime; till: DateTime }
83
+ - options.page?: { index: number; size: number }
84
+ - options.sorts?: { field: string; order: SortOrder }[]
85
+ - fetchEvent(eventId: string): Promise<Event | undefined>
86
+ - fetchProducts(eventId: string, productTypes?: ProductType[], promoCode?: string): Promise<Product[] | undefined>
67
87
 
68
- Load all available events for your organization:
88
+ Types available from the SDK:
89
+ - EventStatus: ACTIVE | INACTIVE | PAUSED | SOLD_OUT
90
+ - ProductType: TICKET | DOOR | ADDON | ...
91
+ - SortOrder: ASC | DESC
92
+
93
+ You can import these enums directly:
69
94
 
70
95
  ```typescript
71
- await sdk.event.fetchEvents();
96
+ import { EventStatus, ProductType, SortOrder } from '@ticketapp/sdk';
97
+ import { DateTime } from 'luxon';
72
98
  ```
73
99
 
74
- ### Subscribing to Event Updates
100
+ ### Fetching Events (with options)
75
101
 
76
- Get real-time updates when events change:
102
+ Load events with optional filters, pagination, and sorting. Returns a tuple: [events, totalCount].
77
103
 
78
104
  ```typescript
79
- const unsubscribe = sdk.event.subscribe(() => {
80
- const state = sdk.event.getState();
81
- console.log('Events:', state.events);
82
- console.log('Loading:', state.processing);
83
- console.log('Error:', state.error);
105
+ import { DateTime } from 'luxon';
106
+ import { EventStatus, SortOrder } from '@ticketapp/sdk';
107
+
108
+ // 1) Ask the server: "give me events that are ACTIVE, starting in the next 3 months"
109
+ const [events, total] = await sdk.event.fetchEvents({
110
+ statuses: [EventStatus.Active], // Only active events
111
+ hostingIds: undefined, // Or filter by specific locations/hosts
112
+ dateRange: { // Time window
113
+ from: DateTime.local(),
114
+ till: DateTime.local().plus({ months: 3 })
115
+ },
116
+ page: { index: 0, size: 20 }, // First page, 20 items
117
+ sorts: [{ field: 'startAt', order: SortOrder.Asc }], // Soonest first
84
118
  });
85
119
 
86
- // Later, when you want to stop listening:
87
- unsubscribe();
120
+ // 2) You can now render these events in a grid or list
121
+ console.log(`Found ${total} events`, events.map(e => e.name));
88
122
  ```
89
123
 
90
- ### Getting Current Event State
124
+ ### Fetching a Single Event
91
125
 
92
126
  ```typescript
93
- const state = sdk.event.getState();
94
-
95
- // state structure:
96
- {
97
- events: Event[], // Array of events
98
- processing: boolean, // Loading state
99
- error: string | null, // Error message if any
100
- loadingProducts: {} // Products loading state per event
127
+ // When a visitor clicks an event, load its extra details (like sales times)
128
+ const event = await sdk.event.fetchEvent('event-id');
129
+ if (!event) {
130
+ // No event found (maybe it was removed)
101
131
  }
102
132
  ```
103
133
 
104
134
  ### Fetching Products for an Event
105
135
 
106
136
  ```typescript
107
- // Fetch all ticket types
108
- await sdk.event.fetchProductsForEvent(
109
- 'event-id',
110
- ['TICKET', 'DOOR', 'ADDON']
111
- );
137
+ import { ProductType } from '@ticketapp/sdk';
112
138
 
113
- // Fetch only regular tickets
114
- await sdk.event.fetchProductsForEvent(
139
+ // Load all ticket types for a chosen event
140
+ const products = await sdk.event.fetchProducts(
115
141
  'event-id',
116
- ['TICKET']
142
+ [ProductType.Ticket, ProductType.Door, ProductType.Addon], // Which types to include
143
+ 'PROMO2024' // Optional discount code
117
144
  );
118
145
 
119
- // Fetch with promo code
120
- await sdk.event.fetchProductsForEvent(
121
- 'event-id',
122
- ['TICKET'],
123
- 'PROMO2024'
124
- );
146
+ // Show each product with price so visitors can pick one
147
+ products?.forEach(p => console.log(p.name, p.price));
125
148
  ```
126
149
 
127
- ### Event Object Structure
150
+ ### Event object shapes
151
+
152
+ There are two event shapes depending on the call you use.
153
+
154
+ - Overview events (from `fetchEvents`) are lightweight for listing
155
+ - Detailed event (from `fetchEvent`) contains full metadata
156
+
157
+ Overview (fetchEvents):
128
158
 
129
159
  ```typescript
130
160
  {
@@ -133,153 +163,302 @@ await sdk.event.fetchProductsForEvent(
133
163
  icon: string | null,
134
164
  banner: string | null,
135
165
  description: string | null,
136
- startAt: DateTime, // Luxon DateTime object
166
+ addonDescription?: string | null,
167
+ startAt: DateTime,
137
168
  endAt: DateTime,
138
169
  timezone: string,
139
- location: {
170
+ slug: string,
171
+ status: string,
172
+ location?: {
140
173
  id: string,
141
174
  name: string,
142
175
  address: string | null
143
176
  },
144
- products: Product[] // Available after fetchProductsForEvent
177
+ infoDescription?: string | null
145
178
  }
146
179
  ```
147
180
 
148
- ---
149
-
150
- ## Basket (Cart)
181
+ Detail (fetchEvent):
151
182
 
152
- The basket manages the user's shopping cart and order.
183
+ ```typescript
184
+ {
185
+ id: string,
186
+ name: string,
187
+ icon: string | null,
188
+ banner: string | null,
189
+ description: string | null,
190
+ addonDescription?: string | null,
191
+ startAt: DateTime,
192
+ endAt: DateTime,
193
+ timezone: string,
194
+ startSalesAt: string | null,
195
+ endSalesAt: string | null,
196
+ slug: string,
197
+ facebookPixelId: string | null,
198
+ status: string,
199
+ location?: {
200
+ id: string,
201
+ name: string,
202
+ address: string | null
203
+ },
204
+ infoDescription?: string | null
205
+ }
206
+ ```
153
207
 
154
- ### Adding a Product
208
+ ### Product Object Structure (from fetchProducts)
155
209
 
156
210
  ```typescript
157
- await sdk.basket.addProduct({
158
- id: 'product-id',
159
- name: 'VIP Ticket',
160
- price: 50,
161
- serviceFee: 5,
162
- amount: 2,
163
- currency: 'EUR',
164
- });
211
+ {
212
+ id: string,
213
+ name: string,
214
+ category?: {
215
+ id: string,
216
+ name: string,
217
+ description?: string
218
+ },
219
+ currency: 'EUR' | 'USD' | string, // see Currency in types
220
+ depositPrice?: number,
221
+ description?: string,
222
+ discountPrice?: number,
223
+ startSalesAt?: string,
224
+ endSalesAt?: string,
225
+ icon?: string,
226
+ maxAmountOfAddons?: number,
227
+ minAmountOfAddons?: number,
228
+ maxAmountPerOrder?: number,
229
+ price?: number,
230
+ serviceFee?: number,
231
+ showEndSalesAtTag: boolean,
232
+ status: ProductStatus,
233
+ type: ProductType
234
+ }
165
235
  ```
166
236
 
167
- ### Adding a Product with Seat Assignment
237
+ ---
238
+
239
+ ## Categories
240
+
241
+ // In plain English: Categories are simple labels you can show to help people browse. You can list them or open one category by its id.
242
+
243
+ ### CategoryService overview
244
+
245
+ Methods:
246
+ - getCategories(): Promise<Category[]>
247
+ - getCategory(categoryId: string): Promise<Category | null>
248
+
249
+ Types available from the SDK:
250
+ - Category
251
+
252
+ You can import the Category type directly:
168
253
 
169
254
  ```typescript
170
- await sdk.basket.addProduct({
171
- id: 'product-id',
172
- name: 'VIP Ticket',
173
- price: 50,
174
- serviceFee: 5,
175
- amount: 1,
176
- currency: 'EUR',
177
- seat: {
178
- id: 'seat-a1',
179
- label: 'A1',
180
- holdToken: 'hold-token-123'
181
- }
182
- });
255
+ import { Category } from '@ticketapp/sdk';
183
256
  ```
184
257
 
185
- ### Removing a Product
258
+ ### Fetching Categories
259
+
260
+ Load all available categories for your organization:
186
261
 
187
262
  ```typescript
188
- await sdk.basket.removeProduct({
189
- id: 'product-id',
190
- name: 'VIP Ticket',
191
- currency: 'EUR',
192
- amount: 1,
193
- });
263
+ const categories: Category[] = await sdk.category.getCategories();
264
+ console.log('Categories:', categories.map(c => c.name));
194
265
  ```
195
266
 
196
- ### Adding Customer Information
267
+ ### Fetching a Single Category by ID
197
268
 
198
269
  ```typescript
199
- await sdk.basket.createCustomer({
200
- email: 'customer@example.com',
201
- firstName: 'John',
202
- lastName: 'Doe',
203
- age: 30, // Optional
204
- gender: 'Male', // Optional
205
- residence: 'Amsterdam', // Optional
206
- extraInfo: [ // Optional: Custom key-value pairs
207
- { key: 'dietaryRestrictions', value: 'Vegetarian' },
208
- { key: 'tshirtSize', value: 'L' },
209
- { key: 'emergencyContact', value: '+31612345678' }
210
- ]
211
- });
270
+ const category = await sdk.category.getCategory('category-id');
271
+ if (category) {
272
+ console.log('Category:', category.name);
273
+ } else {
274
+ console.log('Category not found');
275
+ }
212
276
  ```
213
277
 
214
- ### Getting Current Order
278
+ ### Category Object Structure
215
279
 
216
280
  ```typescript
217
- const order = sdk.basket.getCurrentOrder();
218
-
219
- // order structure:
220
281
  {
221
282
  id: string,
222
- currency: string,
223
- items: OrderItem[],
224
- customer: Customer,
225
- count: number, // Total number of items
226
- expiredAt: string
283
+ name: string,
284
+ icon?: string | null
227
285
  }
228
286
  ```
229
287
 
230
- ### Subscribing to Basket Updates
288
+ ---
289
+
290
+ ## Packages
291
+
292
+ // In plain English: Packages are bundles (like a Weekend Pass) that include multiple events. You can show all packages, open one, and list the events inside it.
293
+
294
+ ### PackageService overview
295
+
296
+ Methods:
297
+ - getPackages(options?: { page?: { index?: number; size?: number }; tab?: PackageTabType; statuses?: PackageStatus[] }): Promise<{ count: number; data: any[] } | null>
298
+ - getPackage(packageId: string): Promise<any | null>
299
+ - getPackageItems(packageId: string, types?: PackageItemType[]): Promise<{ count: number; data: any[] } | null>
300
+
301
+ Types available from the SDK:
302
+ - PackageItemType: REGULAR | ADDITIONAL_EVENT
303
+ - PackageStatus: ACTIVE | CONCEPT | PAUSED | SOLD_OUT
304
+ - PackageTabType: ACTIVE | INACTIVE
305
+
306
+ You can import these enums directly:
231
307
 
232
308
  ```typescript
233
- const unsubscribe = sdk.basket.subscribe(() => {
234
- const order = sdk.basket.getCurrentOrder();
235
- console.log('Order updated:', order);
236
- });
309
+ import { PackageItemType, PackageStatus, PackageTabType } from '@ticketapp/sdk';
310
+ ```
237
311
 
238
- // Cleanup
239
- unsubscribe();
312
+ ### Fetching Packages (with options)
313
+
314
+ ```typescript
315
+ // Example: show available bundles on your homepage
316
+ const result = await sdk.package.getPackages();
317
+ if (result) {
318
+ console.log('We have these bundles:', result.data.map(p => p.name));
319
+ }
240
320
  ```
241
321
 
242
- ### Canceling an Order
322
+ ### Fetch a Single Package by ID
243
323
 
244
324
  ```typescript
245
- await sdk.basket.cancelOrder();
325
+ const pkg = await sdk.package.getPackage('package-id');
326
+ console.log('Package:', pkg);
246
327
  ```
247
328
 
248
- ### Clearing Session
329
+ ### Fetch Package Items (by types)
249
330
 
250
331
  ```typescript
251
- sdk.basket.clearOrderFromSession();
332
+ // Default: REGULAR and ADDITIONAL_EVENT
333
+ const itemsDefault = await sdk.package.getPackageItems('package-id');
334
+ console.log('Items:', itemsDefault?.data);
335
+
336
+ // Explicit types
337
+ const items = await sdk.package.getPackageItems('package-id', [
338
+ PackageItemType.Regular,
339
+ PackageItemType.AdditionalEvent,
340
+ ]);
341
+ console.log('Filtered items:', items?.data);
252
342
  ```
253
343
 
254
- ---
344
+ ### Package Object Structure
345
+
346
+ ```typescript
347
+ {
348
+ id: string,
349
+ name: string,
350
+ shortDescription: string,
351
+ status: 'ACTIVE' | 'PAUSED' | 'SOLD_OUT' | 'CONCEPT',
352
+ prices: {
353
+ price: number,
354
+ discount: number | null
355
+ },
356
+ amountOfEvents: number,
357
+ maxAmountOfPersonsPerOrder: number | null
358
+ }
359
+ ```
255
360
 
256
- ## Packages
257
361
 
258
- Packages are bundled event tickets (e.g., weekend passes, season tickets).
259
362
 
260
- ### Fetching Packages
363
+ ## Basket
364
+
365
+ The basket manages the user's shopping cart and order. It maintains internal state and restores active orders from session automatically.
366
+
367
+ // In plain English: Think of the basket like a shopping cart. When someone clicks “Add to cart” we reserve that ticket for a short time (so nobody else can grab it). If they refresh the page, we restore the cart from the browser.
368
+
369
+ ### BasketService overview
370
+
371
+ Methods:
372
+ - subscribe(listener: () => void): () => void
373
+ - getCurrentOrder(): { id: string; currency: string; items: OrderItem[]; count: number; expiredAt?: DateTime | null } | null
374
+ - getSelectedPackageEvent(): string[]
375
+ - addProduct(input: ProductInput): Promise<void>
376
+ - removeProduct(input: ProductInput): Promise<void>
377
+ - configurePackage(input: ConfigurePackageInput): Promise<{ error: unknown } | null>
378
+ - configureDelivery(input?: ConfigureDeliveryInput): Promise<void>
379
+ - configureCustomer(input: ConfigureCustomerInput): Promise<void>
380
+ - reserveAdditionalPackageItem(input: ReserveAdditionalPackageItemInput): Promise<{ error: unknown } | null>
381
+ - cancelOrder(): Promise<void>
382
+ - clearOrderFromSession(): void
383
+
384
+ Types available from the SDK:
385
+ - ProductInput, ConfigurePackageInput, ConfigureDeliveryInput, ConfigureCustomerInput, ReserveAdditionalPackageItemInput, OrderItem
386
+
387
+ You can import these types directly:
261
388
 
262
389
  ```typescript
263
- const result = await sdk.package.getPackages();
390
+ import {
391
+ ProductInput,
392
+ ConfigurePackageInput,
393
+ ConfigureDeliveryInput,
394
+ ConfigureCustomerInput,
395
+ ReserveAdditionalPackageItemInput,
396
+ } from '@ticketapp/sdk';
397
+ ```
264
398
 
265
- console.log('Total packages:', result.count);
266
- console.log('Packages:', result.data);
399
+ ### Subscribing to Basket Updates
400
+
401
+ ```typescript
402
+ // This runs every time the basket changes (e.g., added/removed a ticket)
403
+ const unsubscribe = sdk.basket.subscribe(() => {
404
+ const order = sdk.basket.getCurrentOrder();
405
+ console.log('Order updated:', order);
406
+ });
407
+
408
+ // Later: stop listening
409
+ unsubscribe();
267
410
  ```
268
411
 
269
- ### Fetching Package Items
412
+ ### Getting Current Order
413
+
414
+ ```typescript
415
+ // Ask: what’s currently in the cart?
416
+ const order = sdk.basket.getCurrentOrder();
417
+ if (order) {
418
+ console.log('Total items:', order.count);
419
+ console.log('Expires at:', order.expiredAt?.toISO()); // Tickets are held for a limited time
420
+ }
421
+ ```
270
422
 
271
- Get the individual events/items that make up a package:
423
+ ### Add and Remove Products
272
424
 
273
425
  ```typescript
274
- const result = await sdk.package.getPackageItems(
275
- 'package-id',
276
- ['REGULAR', 'ADDITIONAL_EVENT']
277
- );
426
+ // Add a regular ticket
427
+ await sdk.basket.addProduct({
428
+ id: 'product-id', // Ticket/product id from fetchProducts
429
+ name: 'VIP Ticket',
430
+ price: 50,
431
+ serviceFee: 5,
432
+ amount: 2, // How many
433
+ currency: 'EUR',
434
+ });
435
+
436
+ // Add a seated ticket (we also pass a seat)
437
+ await sdk.basket.addProduct({
438
+ id: 'seated-ticket-id',
439
+ name: 'Seated Ticket',
440
+ price: 75,
441
+ serviceFee: 5,
442
+ amount: 1,
443
+ currency: 'EUR',
444
+ seat: { id: 'seat-a1', label: 'A1', holdToken: 'hold-abc' },
445
+ });
278
446
 
279
- console.log('Package items:', result.data);
447
+ // Remove one from the basket
448
+ await sdk.basket.removeProduct({
449
+ id: 'product-id',
450
+ name: 'VIP Ticket',
451
+ currency: 'EUR',
452
+ amount: 1,
453
+ });
280
454
  ```
281
455
 
282
- ### Configuring and Adding a Package to Order
456
+ Notes:
457
+ - When you add the first item, we create an order and remember it in the browser.
458
+ - If you remove everything, we automatically cancel the order on the server and clear the browser.
459
+ - If you see SOLD_OUT or RATE_LIMIT errors, it means the product is gone or you clicked too fast—try again.
460
+
461
+ ### Configure a Package
283
462
 
284
463
  ```typescript
285
464
  await sdk.basket.configurePackage({
@@ -291,60 +470,77 @@ await sdk.basket.configurePackage({
291
470
  serviceFee: 20,
292
471
  amount: 1, // Number of persons
293
472
  items: [
294
- {
295
- packageItemId: 'item-1-id',
296
- eventId: 'event-1-id',
297
- },
298
- {
299
- packageItemId: 'item-2-id',
300
- eventId: 'event-2-id',
301
- }
302
- ]
473
+ { packageItemId: 'item-1-id', eventId: 'event-1-id' },
474
+ { packageItemId: 'item-2-id', eventId: 'event-2-id' },
475
+ ],
476
+ });
477
+ ```
478
+
479
+ - Returns null on success; returns an error object if no items are reserved.
480
+
481
+ ### Reserve Additional Package Items
482
+
483
+ ```typescript
484
+ await sdk.basket.reserveAdditionalPackageItem({
485
+ packageId: 'package-id',
486
+ packageItemId: 'package-item-id',
487
+ eventId: 'event-id',
488
+ amount: 1,
489
+ currency: 'EUR',
303
490
  });
304
491
  ```
305
492
 
306
- ### Adding Additional Package Items
493
+ - Use this to add additional events to an existing package reservation.
307
494
 
308
- Add extra events to an existing package order:
495
+ ### Configure Delivery and Customer
309
496
 
310
497
  ```typescript
311
- const sessionId = sessionStorage.getItem('ORDER_ID_hvrmq');
498
+ // Delivery: use this to show “e-ticket” or “pickup” in your order summary
499
+ sdk.basket.configureDelivery({ id: 'delivery-id', name: 'E-ticket', serviceFee: 0, currency: 'EUR' });
312
500
 
313
- await sdk.package.setAdditionalPackageItem(
314
- sessionId,
315
- 'package-item-id',
316
- 'event-id',
317
- 1 // amount
318
- );
501
+ // Customer: minimum is email, but more fields help customer support
502
+ await sdk.basket.configureCustomer({
503
+ email: 'customer@example.com',
504
+ firstName: 'John',
505
+ lastName: 'Doe',
506
+ age: 30,
507
+ });
319
508
  ```
320
509
 
321
- ### Package Object Structure
510
+ ### Get Selected Package Event IDs
322
511
 
323
512
  ```typescript
324
- {
325
- id: string,
326
- name: string,
327
- shortDescription: string,
328
- status: 'ACTIVE' | 'PAUSED' | 'SOLD_OUT' | 'CONCEPT',
329
- prices: {
330
- price: number,
331
- discount: number | null
332
- },
333
- amountOfEvents: number,
334
- maxAmountOfPersonsPerOrder: number | null
335
- }
513
+ const selectedEventIds = sdk.basket.getSelectedPackageEvent();
514
+ // e.g., ['event-1', 'event-2']
515
+ ```
516
+
517
+ ### Cancel and Clear Order
518
+
519
+ ```typescript
520
+ // Cancel current order on server (if ORDER_ID exists) and clear local session
521
+ await sdk.basket.cancelOrder();
522
+
523
+ // Clear local basket state and session key without contacting the server
524
+ sdk.basket.clearOrderFromSession();
336
525
  ```
337
526
 
527
+ Behavior notes:
528
+ - On page load, we try to restore a previous order (if it still exists) so your visitors don’t lose their cart.
529
+ - Each reserved ticket has an expiration. We track the earliest one and show it as the order’s expiration time.
530
+ - You can “listen” for changes with subscribe() to keep your UI in sync (e.g., update the cart icon).
531
+
338
532
  ---
339
533
 
340
534
  ## Payment
341
535
 
342
- ### Getting Payment Methods
536
+ // In plain English: First ask “what payment methods are available for this order?”, then create the payment and send the visitor to the checkout page.
537
+
538
+ ### Getting Payment Details
343
539
 
344
- Fetch available payment methods for an order:
540
+ Fetch available payment methods and details for an order:
345
541
 
346
542
  ```typescript
347
- const paymentDetails = await sdk.payment.getPaymentMethods({
543
+ const paymentDetails = await sdk.payment.getPaymentDetails({
348
544
  orderId: 'order-id',
349
545
  orderItemId: 'item-id', // Optional
350
546
  amountOfTickets: 2, // Optional
@@ -354,9 +550,10 @@ const paymentDetails = await sdk.payment.getPaymentMethods({
354
550
  // paymentDetails structure:
355
551
  {
356
552
  transactionPrice: number,
357
- baseTransactionFee: number | null,
358
- additionalTransactionFee: number | null,
359
- methods: PaymentMethod[]
553
+ transactionDiscount?: number | null,
554
+ transactionFee?: number | null,
555
+ paymentSpesificTransactionFee?: number | null,
556
+ methods?: PaymentMethod[] | null
360
557
  }
361
558
  ```
362
559
 
@@ -379,202 +576,261 @@ const paymentDetails = await sdk.payment.getPaymentMethods({
379
576
  }
380
577
  ```
381
578
 
382
- ### Calculating Payment Fees
383
-
384
- ```typescript
385
- const paymentMethod = {
386
- id: 1,
387
- name: 'iDEAL',
388
- image: 'https://...',
389
- fee: { type: 'FIXED', value: 0.29 }
390
- };
391
-
392
- // Calculate fee amount
393
- const fee = sdk.payment.calculateFee(50, paymentMethod);
394
- console.log('Fee:', fee); // 0.29
395
-
396
- // Calculate total with fee
397
- const total = sdk.payment.calculateTotalWithFee(50, paymentMethod);
398
- console.log('Total:', total); // 50.29
399
-
400
- // Format fee for display
401
- const formatted = sdk.payment.formatFee(paymentMethod);
402
- console.log('Fee:', formatted); // "€0.29"
403
- ```
404
-
405
579
  ### Creating a Payment
406
580
 
407
581
  ```typescript
582
+ // 1) Ask for details and methods
583
+ const details = await sdk.payment.getPaymentDetails({ orderId: 'order-id' });
584
+ const method = details.methods?.[0]; // e.g., iDEAL, Credit Card
585
+
586
+ // 2) Start the payment. The server gives back a URL.
408
587
  const response = await sdk.payment.createPayment({
409
588
  orderId: 'order-id',
410
- paymentMethodId: '1',
589
+ paymentMethodId: String(method?.id),
411
590
  redirectUrl: 'https://yoursite.com/payment/return',
412
- customer: {
413
- firstName: 'John',
414
- lastName: 'Doe',
415
- email: 'john@example.com',
416
- phone: '+31612345678',
417
- gender: 'Male',
418
- company: { // Optional
419
- name: 'Company BV',
420
- cocNumber: '12345678',
421
- vatNumber: 'NL123456789B01'
422
- }
423
- },
424
- issuerId: '1234', // Required for bank selection (iDEAL, etc.)
425
- orderItemId: 'item-id', // Optional
426
- amountOfTickets: 2, // Optional
591
+ customer: { firstName: 'John', lastName: 'Doe', email: 'john@example.com' },
427
592
  });
428
593
 
429
- // Open payment URL
430
- window.open(response.paymentUrl, '_blank');
594
+ // 3) Send the visitor to the payment page
595
+ window.location.href = response.paymentUrl;
431
596
  ```
432
597
 
433
598
  ---
434
599
 
435
600
  ## Examples
436
601
 
437
- ### Complete Event Browsing Flow
602
+ // End-to-end example: Browse → Pick ticket → Pay
438
603
 
439
604
  ```typescript
440
- import { TicketappSDK } from '@ticketapp/sdk';
605
+ import { TicketappSDK, ProductType, SortOrder, EventStatus } from '@ticketapp/sdk';
606
+ import { DateTime } from 'luxon';
441
607
 
442
- const sdk = new TicketappSDK({
443
- organizationId: 'your-org-id',
444
- shopId: 'your-shop-id',
445
- shopSlug: 'your-shop-slug',
446
- });
608
+ // Create the SDK once
609
+ const sdk = new TicketappSDK({ organizationId: 'your-org-id' });
447
610
 
448
- // 1. Fetch and display events
449
- await sdk.event.fetchEvents();
450
- const { events } = sdk.event.getState();
611
+ // 1) List upcoming events for the next month
612
+ const [events] = await sdk.event.fetchEvents({
613
+ statuses: [EventStatus.Active],
614
+ dateRange: { from: DateTime.local(), till: DateTime.local().plus({ months: 1 }) },
615
+ sorts: [{ field: 'startAt', order: SortOrder.Asc }]
616
+ });
451
617
 
452
- // 2. Load products for selected event
453
- await sdk.event.fetchProductsForEvent(events[0].id, ['TICKET']);
618
+ // 2) Let the visitor pick an event, then load its tickets
619
+ const products = await sdk.event.fetchProducts(events[0].id, [ProductType.Ticket]);
454
620
 
455
- // 3. Add product to basket
621
+ // 3) Add the first ticket to the basket
456
622
  await sdk.basket.addProduct({
457
- id: events[0].products[0].id,
458
- name: events[0].products[0].name,
459
- price: events[0].products[0].price,
460
- serviceFee: events[0].products[0].serviceFee,
623
+ id: products![0].id,
624
+ name: products![0].name,
625
+ price: products![0].price!,
626
+ serviceFee: products![0].serviceFee ?? 0,
461
627
  amount: 1,
462
628
  currency: 'EUR',
463
629
  });
464
630
 
465
- // 4. Check basket
466
- const order = sdk.basket.getCurrentOrder();
467
- console.log('Items in basket:', order.count);
631
+ // 4) Ask for payment methods and create a payment
632
+ const order = sdk.basket.getCurrentOrder()!;
633
+ const details = await sdk.payment.getPaymentDetails({ orderId: order.id });
634
+ const method = details.methods?.[0];
635
+ const payment = await sdk.payment.createPayment({
636
+ orderId: order.id,
637
+ paymentMethodId: String(method?.id),
638
+ redirectUrl: 'https://yoursite.com/payment/return',
639
+ });
640
+
641
+ // 5) Redirect the visitor to pay
642
+ window.location.href = payment.paymentUrl;
468
643
  ```
469
644
 
470
- ### Complete Package Purchase Flow
645
+ ---
471
646
 
472
- ```typescript
473
- // 1. Fetch available packages
474
- const packagesResult = await sdk.package.getPackages();
475
- const selectedPackage = packagesResult.data[0];
647
+ ## Services Overview
476
648
 
477
- // 2. Get package items
478
- const itemsResult = await sdk.package.getPackageItems(
479
- selectedPackage.id,
480
- ['REGULAR']
481
- );
649
+ Below are the available services with their methods, options, and usage examples.
482
650
 
483
- // 3. User selects events for each item
484
- const items = itemsResult.data.map(item => ({
485
- packageItemId: item.id,
486
- eventId: item.activeEvents[0].id // User's selection
487
- }));
651
+ ---
488
652
 
489
- // 4. Add package to basket
490
- await sdk.basket.configurePackage({
491
- id: selectedPackage.id,
492
- name: selectedPackage.name,
493
- currency: 'EUR',
494
- price: selectedPackage.prices.discount ?? selectedPackage.prices.price,
495
- depositPrice: selectedPackage.prices.discount ?? selectedPackage.prices.price,
496
- serviceFee: 0,
497
- amount: 1,
498
- items: items,
653
+ ## EventService
654
+
655
+ Methods:
656
+ - fetchEvents(options?): Promise<[Event[], number]>
657
+ - options.statuses?: EventStatus[]
658
+ - options.hostingIds?: string[]
659
+ - options.dateRange?: { from: DateTime; till: DateTime }
660
+ - options.page?: { index: number; size: number }
661
+ - options.sorts?: { field: string; order: SortOrder }[]
662
+ - fetchEvent(eventId: string): Promise<Event | undefined>
663
+ - fetchProducts(eventId: string, productTypes?: ProductType[], promoCode?: string): Promise<Product[] | undefined>
664
+
665
+ Examples:
666
+ ```typescript
667
+ import { DateTime } from 'luxon';
668
+
669
+ // Fetch events with filters and pagination
670
+ const [events, total] = await sdk.event.fetchEvents({
671
+ statuses: ['ACTIVE'],
672
+ hostingIds: ['host-1'],
673
+ dateRange: { from: DateTime.local(), till: DateTime.local().plus({ weeks: 8 }) },
674
+ page: { index: 0, size: 20 },
675
+ sorts: [{ field: 'startAt', order: 'ASC' }],
499
676
  });
677
+
678
+ // Fetch a single event
679
+ const event = await sdk.event.fetchEvent('event-id');
680
+
681
+ // Fetch products for an event
682
+ const products = await sdk.event.fetchProducts('event-id', ['TICKET', 'DOOR'], 'PROMO2024');
500
683
  ```
501
684
 
502
- ### Complete Payment Flow
685
+ ---
686
+
687
+ ## CategoryService
688
+
689
+ Methods:
690
+ - getCategories(): Promise<Category[]>
691
+ - getCategory(categoryId: string): Promise<Category | null>
503
692
 
693
+ Examples:
504
694
  ```typescript
505
- // 1. Get current order
506
- const order = sdk.basket.getCurrentOrder();
695
+ // Fetch all categories
696
+ const categories = await sdk.category.getCategories();
507
697
 
508
- // 2. Fetch payment methods
509
- const paymentDetails = await sdk.payment.getPaymentMethods({
510
- orderId: order.id,
511
- });
698
+ // Fetch a single category
699
+ const category = await sdk.category.getCategory('category-id');
700
+ ```
701
+
702
+ ---
512
703
 
513
- // 3. User selects payment method
514
- const selectedMethod = paymentDetails.methods[0];
704
+ ## BasketService
515
705
 
516
- // 4. Calculate total with fees
517
- const total = sdk.payment.calculateTotalWithFee(
518
- paymentDetails.transactionPrice,
519
- selectedMethod
520
- );
706
+ Methods:
707
+ - subscribe(listener: () => void): () => void
708
+ - getCurrentOrder(): { id: string; currency: string; items: OrderItem[]; count: number; expiredAt?: DateTime | null } | null
709
+ - getSelectedPackageEvent(): string[]
710
+ - addProduct(input: ProductInput): Promise<void>
711
+ - removeProduct(input: ProductInput): Promise<void>
712
+ - configurePackage(input: ConfigurePackageInput): Promise<{ error: unknown } | null>
713
+ - configureDelivery(input?: ConfigureDeliveryInput): Promise<void>
714
+ - configureCustomer(input: ConfigureCustomerInput): Promise<void>
715
+ - reserveAdditionalPackageItem(input: ReserveAdditionalPackageItemInput): Promise<{ error: unknown } | null>
716
+ - cancelOrder(): Promise<void>
717
+ - clearOrderFromSession(): void
521
718
 
522
- // 5. Add customer info if not already added
523
- await sdk.basket.createCustomer({
524
- email: 'customer@example.com',
525
- firstName: 'John',
526
- lastName: 'Doe',
527
- extraInfo: [
528
- { key: 'phoneNumber', value: '+31612345678' }
529
- ]
719
+ Examples:
720
+ ```typescript
721
+ // Subscribe to basket changes
722
+ const unsubscribe = sdk.basket.subscribe(() => {
723
+ const order = sdk.basket.getCurrentOrder();
724
+ console.log('Order updated:', order);
530
725
  });
531
726
 
532
- // 6. Create payment
533
- const paymentResponse = await sdk.payment.createPayment({
534
- orderId: order.id,
535
- paymentMethodId: selectedMethod.id.toString(),
536
- redirectUrl: 'https://yoursite.com/payment/return',
537
- customer: {
538
- firstName: 'John',
539
- lastName: 'Doe',
540
- email: 'customer@example.com',
541
- phone: '+31612345678',
542
- },
543
- issuerId: '1234', // If required by payment method
727
+ // Add a product
728
+ await sdk.basket.addProduct({
729
+ id: 'product-id',
730
+ name: 'VIP Ticket',
731
+ price: 50,
732
+ serviceFee: 5,
733
+ amount: 2,
734
+ currency: 'EUR',
544
735
  });
545
736
 
546
- // 7. Redirect to payment page
547
- window.location.href = paymentResponse.paymentUrl;
737
+ // Remove a product
738
+ await sdk.basket.removeProduct({
739
+ id: 'product-id',
740
+ name: 'VIP Ticket',
741
+ currency: 'EUR',
742
+ amount: 1,
743
+ });
744
+
745
+ // Configure a package
746
+ await sdk.basket.configurePackage({
747
+ id: 'package-id',
748
+ name: 'Weekend Pass',
749
+ currency: 'EUR',
750
+ price: 200,
751
+ depositPrice: 200,
752
+ serviceFee: 20,
753
+ amount: 1,
754
+ items: [
755
+ { packageItemId: 'item-1', eventId: 'event-1' },
756
+ { packageItemId: 'item-2', eventId: 'event-2' },
757
+ ],
758
+ });
759
+
760
+ // Set delivery option
761
+ await sdk.basket.configureDelivery({ id: 'delivery-id', name: 'E-ticket', serviceFee: 0, currency: 'EUR' });
762
+
763
+ // Set customer information
764
+ await sdk.basket.configureCustomer({ email: 'customer@example.com', firstName: 'John', lastName: 'Doe' });
765
+
766
+ // Cancel order
767
+ await sdk.basket.cancelOrder();
768
+
769
+ // Clear session
770
+ sdk.basket.clearOrderFromSession();
548
771
  ```
549
772
 
550
- ### Reactive UI Updates
773
+ ---
774
+
775
+ ## PackageService
551
776
 
777
+ Methods:
778
+ - getPackages(options?: { page?: { index?: number; size?: number }; tab?: PackageTabType; statuses?: PackageStatus[] }): Promise<{ count: number; data: any[] } | null>
779
+ - getPackage(packageId: string): Promise<any | null>
780
+ - getPackageItems(packageId: string, types?: PackageItemType[]): Promise<{ count: number; data: any[] } | null>
781
+
782
+ Examples:
552
783
  ```typescript
553
- // Subscribe to basket changes
554
- const unsubscribeBasket = sdk.basket.subscribe(() => {
555
- const order = sdk.basket.getCurrentOrder();
556
- updateBasketIcon(order?.count || 0);
557
- updateOrderSummary(order);
784
+ // Basic
785
+ const result = await sdk.package.getPackages();
786
+ console.log('Total packages:', result?.count);
787
+ console.log('Packages:', result?.data);
788
+
789
+ // With options (pagination, tab, statuses)
790
+ const paged = await sdk.package.getPackages({
791
+ page: { index: 0, size: 20 },
792
+ tab: PackageTabType.Active,
793
+ statuses: [PackageStatus.Active, PackageStatus.Paused],
558
794
  });
795
+ console.log('Paged packages:', paged?.data);
559
796
 
560
- // Subscribe to event changes
561
- const unsubscribeEvents = sdk.event.subscribe(() => {
562
- const state = sdk.event.getState();
563
-
564
- if (state.processing) {
565
- showLoadingSpinner();
566
- } else if (state.error) {
567
- showError(state.error);
568
- } else {
569
- displayEvents(state.events);
570
- }
571
- });
797
+ // Fetch a single package
798
+ const pkg = await sdk.package.getPackage('package-id');
799
+ console.log('Package:', pkg);
572
800
 
573
- // Cleanup when component unmounts
574
- function cleanup() {
575
- unsubscribeBasket();
576
- unsubscribeEvents();
577
- }
801
+ // Fetch package items
802
+ const itemsDefault = await sdk.package.getPackageItems('package-id');
803
+ console.log('Items:', itemsDefault?.data);
804
+
805
+ const items = await sdk.package.getPackageItems('package-id', [
806
+ PackageItemType.Regular,
807
+ PackageItemType.AdditionalEvent,
808
+ ]);
809
+ console.log('Filtered items:', items?.data);
810
+ ```
811
+
812
+ ---
813
+
814
+ ## PaymentService
815
+
816
+ Methods:
817
+ - getPaymentDetails(options: { orderId: string; orderItemId?: string; amountOfTickets?: number; paymentMethodId?: string }): Promise<PaymentDetails>
818
+ - createPayment(input: CreatePaymentInput): Promise<{ paymentUrl: string }>
819
+
820
+ Examples:
821
+ ```typescript
822
+ // Fetch payment details
823
+ const details = await sdk.payment.getPaymentDetails({ orderId: 'order-id' });
824
+ console.log(details.methods);
825
+
826
+ // Create a payment
827
+ const response = await sdk.payment.createPayment({
828
+ orderId: 'order-id',
829
+ paymentMethodId: '1',
830
+ redirectUrl: 'https://yoursite.com/payment/return',
831
+ customer: { firstName: 'John', lastName: 'Doe', email: 'john@example.com' },
832
+ });
833
+ window.location.href = response.paymentUrl;
578
834
  ```
579
835
 
580
836
  ---
@@ -587,7 +843,7 @@ The SDK is written in TypeScript and exports all necessary types:
587
843
  import {
588
844
  TicketappSDK,
589
845
  ProductInput,
590
- PackageInput,
846
+ ConfigurePackageInput,
591
847
  // ... other types
592
848
  } from '@ticketapp/sdk';
593
849
  ```
@@ -634,49 +890,74 @@ try {
634
890
 
635
891
  The SDK automatically manages sessions using `sessionStorage`:
636
892
 
637
- - Order ID is stored as `ORDER_ID_{shopSlug}`
893
+ - Order ID is stored as `ORDER_ID`
638
894
  - Session persists across page reloads
639
895
  - Use `sdk.basket.clearOrderFromSession()` to clear
640
896
 
641
897
  ---
642
898
 
643
- ## Debug Mode
899
+ ## Tracker (Attribution)
644
900
 
645
- Enable debug mode to see detailed logs:
901
+ Some marketing tools add a tracker id (for example, when a visitor arrives from a campaign). The SDK helps you keep this id during the visitor’s journey so conversions can be attributed.
902
+
903
+ How it works:
904
+ - The SDK looks for a tracker id in three places (in this order):
905
+ 1) Explicitly passed in the SDK config as `trackerId`
906
+ 2) The page URL as `?trackerId=abc123`
907
+ 3) The browser session storage under `TIC_TRACKER_ID`
908
+ - When found via config or URL, the SDK stores it in `sessionStorage` so it persists across page reloads.
909
+ - The tracker id is automatically sent with Event- and Basket-related calls to the backend when supported, so your analytics remains consistent.
910
+
911
+ Typical ways to use it:
646
912
 
647
913
  ```typescript
914
+ // A) Let campaigns append ?trackerId=abc123 to your landing page URL
915
+ // The SDK will pick it up automatically and persist it.
916
+
917
+ // B) Pass it explicitly when creating the SDK (overrides URL/session for the first load)
648
918
  const sdk = new TicketappSDK({
649
- organizationId: 'your-org-id',
650
- shopId: 'your-shop-id',
651
- shopSlug: 'your-shop-slug',
652
- debug: true, // Enable logging
919
+ organizationId: 'your-org',
920
+ trackerId: 'newsletter-jan',
653
921
  });
922
+
923
+ // C) Read the stored value later if you need it (optional)
924
+ const stored = sessionStorage.getItem('TIC_TRACKER_ID');
925
+ console.log('trackerId:', stored);
654
926
  ```
655
927
 
656
- Debug logs will appear in the browser console with prefixes like:
657
- - `[EventService]`
658
- - `[BasketService]`
659
- - `[PackageService]`
660
- - `[PaymentService]`
928
+ Notes:
929
+ - If you don’t use trackers, you can ignore this section—everything works without it.
930
+ - If your links already include `?trackerId=...`, no extra setup is needed.
931
+ - For best results, use simple, URL-safe tracker ids (letters, numbers, dashes/underscores).
661
932
 
662
933
  ---
663
934
 
664
- ## Browser Support
935
+ ## Debug Mode
665
936
 
666
- The SDK works in all modern browsers that support:
667
- - ES6+
668
- - Fetch API
669
- - sessionStorage
670
- - Promise
937
+ Enable debug mode to see detailed logs:
671
938
 
672
- ---
939
+ ```typescript
940
+ const sdk = new TicketappSDK({
941
+ organizationId: 'your-org',
942
+ debug: true, // Enable debug logs
943
+ });
944
+ ```
673
945
 
674
- ## License
946
+ - Logs appear in the browser console.
947
+ - Includes request/response details for SDK methods.
948
+ - Great for troubleshooting issues.
675
949
 
676
- MIT
950
+ Remember to disable or remove debug mode in production to avoid exposing sensitive data in logs.
677
951
 
678
952
  ---
679
953
 
680
- ## Support
954
+ ## FAQ and Troubleshooting
681
955
 
682
- For issues, questions, or feature requests, please contact support or open an issue on GitHub.
956
+ - I reloaded the page and my cart is gone.
957
+ - The basket only restores an order if it still exists on the server and contains valid items. If all items expired or were removed, we clear the session.
958
+ - I see “SOLD_OUT” when adding a ticket.
959
+ - Someone else reserved the last ticket just before you. Try again or pick another product.
960
+ - Prices look different after some time.
961
+ - A promo code may have changed the price or the event’s prices were updated. Refresh products using `sdk.event.fetchProducts(eventId)`.
962
+ - My payment didn’t start.
963
+ - Make sure you picked a payment method from `getPaymentDetails()`. Some methods (like iDEAL) require an issuer/bank id.