@checkoutpage/sdk 0.1.31 → 0.1.33
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/README.md +334 -2
- package/dist/index.d.mts +15290 -5286
- package/dist/index.d.ts +15290 -5286
- package/dist/index.js +421 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +421 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -127,6 +127,40 @@ const results = await checkoutpage.coupons.create({
|
|
|
127
127
|
});
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
Without `rules` a coupon discounts the whole order on every checkout page and event. `rules`
|
|
131
|
+
limits it, one rule per checkout page or event, combined with OR:
|
|
132
|
+
|
|
133
|
+
- Checkout pages: `wholeOrder`, `prices` (only when the customer buys one of `priceIds`, and
|
|
134
|
+
only that price is discounted) or `pricesAndBumps` (only when the customer buys one of
|
|
135
|
+
`priceIds` **and** one of `bumpProductIds`, and both are discounted).
|
|
136
|
+
- Events: `wholeOrder`, `tickets` or `ticketsAndAddOns`, the same with `ticketTypeIds` and
|
|
137
|
+
add-ons.
|
|
138
|
+
- An empty or missing `priceIds`, `ticketTypeIds` or `bumpProductIds` means all of them,
|
|
139
|
+
including ones added later. `priceIds` are the `id`s of the page's main product `prices`, not
|
|
140
|
+
Stripe price ids.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const results = await checkoutpage.coupons.create({
|
|
144
|
+
type: 'percent',
|
|
145
|
+
label: 'Monthly plus workbook',
|
|
146
|
+
code: 'BUNDLE20',
|
|
147
|
+
percentOff: 20,
|
|
148
|
+
duration: 'once',
|
|
149
|
+
rules: [
|
|
150
|
+
{
|
|
151
|
+
pageId,
|
|
152
|
+
appliesTo: 'pricesAndBumps',
|
|
153
|
+
priceIds: [monthlyPriceId],
|
|
154
|
+
bumpProductIds: [workbookProductId],
|
|
155
|
+
},
|
|
156
|
+
{ pageId: eventId, appliesTo: 'tickets', ticketTypeIds: [vipTicketTypeId] },
|
|
157
|
+
],
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`pageIds` and `ticketTypeIds` are deprecated in favour of `rules`. They still work, but only
|
|
162
|
+
without `rules`, and responses derive them from the rules.
|
|
163
|
+
|
|
130
164
|
### Subscriptions
|
|
131
165
|
|
|
132
166
|
#### List subscriptions
|
|
@@ -241,6 +275,67 @@ const pageBookings = await checkoutpage.bookings.list({
|
|
|
241
275
|
});
|
|
242
276
|
```
|
|
243
277
|
|
|
278
|
+
#### Create a booking
|
|
279
|
+
|
|
280
|
+
`bookings.create` issues a real booking without collecting card payment: tickets
|
|
281
|
+
are issued, the customer gets a confirmation email with the ticket PDF, and
|
|
282
|
+
booking webhooks fire. Field values are addressed by `fieldId`, and every field
|
|
283
|
+
the event marks as required must be supplied.
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
const { data: booking } = await checkoutpage.bookings.create({
|
|
287
|
+
eventId: '67fcbdac6a91c25ef2d3534a',
|
|
288
|
+
tickets: { '65f4a1c2e4a9f3d2b1c0a9e8': 2 },
|
|
289
|
+
fields: [
|
|
290
|
+
{ fieldId: '507f1f77bcf86cd799439030', value: 'ada@example.com' },
|
|
291
|
+
{ fieldId: '507f1f77bcf86cd799439031', value: 'Ada Lovelace' },
|
|
292
|
+
],
|
|
293
|
+
paymentOption: { manualType: 'invoice' },
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
console.log(booking.status); // 'unpaid'
|
|
297
|
+
console.log(booking.amountDue); // 5000, settled outside checkout
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
#### Create a complimentary booking
|
|
301
|
+
|
|
302
|
+
There are two ways to settle a booking, and exactly one of them is required.
|
|
303
|
+
Pass `paymentOption`, as above, and the booking is recorded as `unpaid` with the
|
|
304
|
+
amount as its balance due, settled outside checkout. Pass `complimentary: true`
|
|
305
|
+
instead and the tickets are issued at no charge: the booking is recorded as
|
|
306
|
+
`paid` with `amount`, `amountPaid` and `amountDue` of 0 and
|
|
307
|
+
`isComplimentary: true`, while the ticket lines keep their face value and
|
|
308
|
+
`complimentaryDiscountAmount` records what they would have cost. `complimentary`
|
|
309
|
+
cannot be combined with `paymentOption` or `couponId`.
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
const { data: booking } = await checkoutpage.bookings.create({
|
|
313
|
+
eventId: '67fcbdac6a91c25ef2d3534a',
|
|
314
|
+
tickets: { '65f4a1c2e4a9f3d2b1c0a9e8': 2 },
|
|
315
|
+
fields: [
|
|
316
|
+
{ fieldId: '507f1f77bcf86cd799439030', value: 'ada@example.com' },
|
|
317
|
+
{ fieldId: '507f1f77bcf86cd799439031', value: 'Ada Lovelace' },
|
|
318
|
+
],
|
|
319
|
+
complimentary: true,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
console.log(booking.status); // 'paid'
|
|
323
|
+
console.log(booking.amount); // 0
|
|
324
|
+
console.log(booking.complimentaryDiscountAmount); // 5000 (2 x 2500 face value)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
#### Filter complimentary bookings
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
const complimentary = await checkoutpage.bookings.list({
|
|
331
|
+
isComplimentary: 'true',
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const paidFor = await checkoutpage.bookings.list({
|
|
335
|
+
isComplimentary: 'false',
|
|
336
|
+
});
|
|
337
|
+
```
|
|
338
|
+
|
|
244
339
|
### Products
|
|
245
340
|
|
|
246
341
|
#### Get a product
|
|
@@ -249,6 +344,15 @@ const pageBookings = await checkoutpage.bookings.list({
|
|
|
249
344
|
const product = await checkoutpage.products.get('product_id');
|
|
250
345
|
```
|
|
251
346
|
|
|
347
|
+
#### List products
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
const products = await checkoutpage.products.list({
|
|
351
|
+
search: 'shirt',
|
|
352
|
+
limit: 20,
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
252
356
|
#### Update a product
|
|
253
357
|
|
|
254
358
|
```typescript
|
|
@@ -299,6 +403,64 @@ await checkoutpage.checkoutPages.create({
|
|
|
299
403
|
> report them by mirroring the product — but new integrations should set them
|
|
300
404
|
> under `productData` or via `products.update()`.
|
|
301
405
|
|
|
406
|
+
### Order bumps
|
|
407
|
+
|
|
408
|
+
An order bump is an extra product offered on a checkout page or event (shown as
|
|
409
|
+
an add-on on an event). Create the order bump product once, then offer it on as
|
|
410
|
+
many pages as you like.
|
|
411
|
+
|
|
412
|
+
#### Create an order bump product
|
|
413
|
+
|
|
414
|
+
Order bump products use a single one-time price, which can have a setup fee or be
|
|
415
|
+
pay what you want. Recurring prices and payment plans are refused with a
|
|
416
|
+
`ValidationError`.
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
const { data: bump } = await checkoutpage.products.create({
|
|
420
|
+
role: 'orderbump',
|
|
421
|
+
title: 'Gift wrapping',
|
|
422
|
+
price: { amount: 500, currency: 'usd' },
|
|
423
|
+
callToActionText: 'Yes! Add gift wrapping',
|
|
424
|
+
shortDescription: 'Wrapped in recycled paper with a handwritten note.',
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const bumps = await checkoutpage.products.list({ role: 'orderbump' });
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
#### Offer it on a page
|
|
431
|
+
|
|
432
|
+
The same methods are on `checkoutpage.events.orderBumps` for events.
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
await checkoutpage.checkoutPages.orderBumps.create('page_id', {
|
|
436
|
+
productId: bump.id,
|
|
437
|
+
preselected: true,
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const { data: placed } = await checkoutpage.checkoutPages.orderBumps.list('page_id');
|
|
441
|
+
|
|
442
|
+
await checkoutpage.checkoutPages.orderBumps.update('page_id', bump.id, {
|
|
443
|
+
allowQuantity: true,
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// List every order bump on the page, once, in the new order
|
|
447
|
+
await checkoutpage.checkoutPages.orderBumps.reorder('page_id', {
|
|
448
|
+
productIds: ['other_bump_id', bump.id],
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// Stops offering it on this page; the product is kept
|
|
452
|
+
await checkoutpage.checkoutPages.orderBumps.delete('page_id', bump.id);
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
#### Delete an order bump product
|
|
456
|
+
|
|
457
|
+
Only order bump products that have never been bought can be deleted. A product
|
|
458
|
+
still offered on a page is refused unless you pass `fromAllPages: true`.
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
await checkoutpage.products.delete(bump.id, { fromAllPages: true });
|
|
462
|
+
```
|
|
463
|
+
|
|
302
464
|
### Files
|
|
303
465
|
|
|
304
466
|
#### Upload an image
|
|
@@ -327,6 +489,115 @@ const result = await checkoutpage.files.upload({
|
|
|
327
489
|
console.log('File uploaded:', result.data.id);
|
|
328
490
|
```
|
|
329
491
|
|
|
492
|
+
### Themes
|
|
493
|
+
|
|
494
|
+
A theme is a page design you can reuse across checkout pages, events and forms. `list()` returns
|
|
495
|
+
your own themes (`scope: 'seller'`) and the built-in ones (`scope: 'global'`), which are read-only.
|
|
496
|
+
|
|
497
|
+
#### List themes
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
const { data: themes, has_more } = await checkoutpage.themes.list();
|
|
501
|
+
|
|
502
|
+
const builtIn = await checkoutpage.themes.list({ scope: 'global' });
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
#### Create a theme
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
const { data: theme } = await checkoutpage.themes.create({
|
|
509
|
+
name: 'Brand',
|
|
510
|
+
// Optional: start from a copy of a built-in theme or one of yours
|
|
511
|
+
baseThemeId: builtIn.data[0].id,
|
|
512
|
+
variables: { tokens: { colorPrimary: { light: '#4f46e5' } } },
|
|
513
|
+
});
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
`variables` takes the same shape as a page's `themeOverrides`. A copy of a built-in theme does not
|
|
517
|
+
follow your store's brand colour, so set `tokens.colorPrimary` if you want it. A name another of
|
|
518
|
+
your themes already uses is rejected with a `ConflictError`.
|
|
519
|
+
|
|
520
|
+
#### Get and update a theme
|
|
521
|
+
|
|
522
|
+
```typescript
|
|
523
|
+
const { data: theme } = await checkoutpage.themes.get(themeId);
|
|
524
|
+
|
|
525
|
+
const { data: updated } = await checkoutpage.themes.update(themeId, {
|
|
526
|
+
name: 'Brand v2',
|
|
527
|
+
variables: { tokens: { colorPrimary: { light: '#0f766e' }, radius: null } },
|
|
528
|
+
});
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
Every page that uses the theme changes too. Keys you leave out keep their value, and `null` resets a
|
|
532
|
+
property, or everything under a key. Built-in themes can't be changed: copy one with `baseThemeId`.
|
|
533
|
+
|
|
534
|
+
#### Use a theme on a page
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
await checkoutpage.checkoutPages.update(pageId, { themeId: theme.id });
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
`events` and `forms` take `themeId` on `create` and `update` too, and page responses include it.
|
|
541
|
+
Changing a page's theme replaces the page's own theme settings, including ones set in the
|
|
542
|
+
dashboard such as custom CSS, so the page looks like the theme. `themeOverrides` sent in the same
|
|
543
|
+
request apply on top, and the page's locale is kept.
|
|
544
|
+
|
|
545
|
+
#### Delete a theme
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
const { data: deleted } = await checkoutpage.themes.delete(themeId);
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
A theme that any of your pages uses, archived ones included, or that is your store's default theme,
|
|
552
|
+
can't be deleted and throws a `ConflictError`. Move those pages to another theme with `themeId`
|
|
553
|
+
first.
|
|
554
|
+
|
|
555
|
+
### Form layout
|
|
556
|
+
|
|
557
|
+
`formLayout` arranges a page's form, top to bottom: fields, headings, dividers, the discount code
|
|
558
|
+
box and groups that put up to 4 items side by side. Checkout pages, events and forms take it on
|
|
559
|
+
`create` and `update`, and page responses always include it. On `create`, reference a field by
|
|
560
|
+
`fieldKey`, the `key` you gave it in `fields` in the same request.
|
|
561
|
+
|
|
562
|
+
```typescript
|
|
563
|
+
import type { CreateFormLayout } from '@checkoutpage/sdk';
|
|
564
|
+
|
|
565
|
+
const formLayout: CreateFormLayout = [
|
|
566
|
+
{ type: 'heading', id: 'your-details', text: 'Your details', level: 'heading' },
|
|
567
|
+
{
|
|
568
|
+
type: 'group',
|
|
569
|
+
id: 'name-row',
|
|
570
|
+
columns: 2,
|
|
571
|
+
items: [
|
|
572
|
+
{ type: 'field', fieldKey: 'first-name' },
|
|
573
|
+
{ type: 'field', fieldKey: 'last-name' },
|
|
574
|
+
],
|
|
575
|
+
},
|
|
576
|
+
{ type: 'divider', id: 'divider-1' },
|
|
577
|
+
{ type: 'discount-code' },
|
|
578
|
+
];
|
|
579
|
+
|
|
580
|
+
const { data: page } = await checkoutpage.checkoutPages.create({
|
|
581
|
+
name: 'Workshop',
|
|
582
|
+
productData: { title: 'Workshop', price: { amount: 4900, currency: 'usd' } },
|
|
583
|
+
fields: [
|
|
584
|
+
{ label: 'Email', element: 'email', type: 'email', required: true },
|
|
585
|
+
{ key: 'first-name', element: 'text', label: 'First name' },
|
|
586
|
+
{ key: 'last-name', element: 'text', label: 'Last name' },
|
|
587
|
+
],
|
|
588
|
+
showCouponCodeField: true,
|
|
589
|
+
formLayout,
|
|
590
|
+
});
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
Responses and `update` reference fields by `fieldId` (the `FormLayout` type). `update` replaces the
|
|
594
|
+
whole layout, so read `page.formLayout`, change it and send it back. Fields the layout leaves out
|
|
595
|
+
are added after it. A `fieldKey` that matches no field, a field id that is not on the page, or a
|
|
596
|
+
`discount-code` item on a form, throws a `ValidationError`. The `discount-code` item only places
|
|
597
|
+
the box: `showCouponCodeField` switches it on, and a saved placement is returned while the box is
|
|
598
|
+
off too. More item types may be added later: skip any type you do not recognise when reading, and
|
|
599
|
+
send it back as you received it when updating.
|
|
600
|
+
|
|
330
601
|
### Webhooks
|
|
331
602
|
|
|
332
603
|
#### List webhook endpoints
|
|
@@ -362,8 +633,36 @@ saveSecret(webhook.secret);
|
|
|
362
633
|
> and a URL already registered on the account is rejected with a `ConflictError`.
|
|
363
634
|
|
|
364
635
|
> The create response is the **only** place the signing `secret` is ever returned.
|
|
365
|
-
> It is not included in `list()` responses and cannot be retrieved later
|
|
366
|
-
> when you create the endpoint. Omit `secret` to have one generated for you.
|
|
636
|
+
> It is not included in `list()` or `get()` responses and cannot be retrieved later, so store
|
|
637
|
+
> it when you create the endpoint. Omit `secret` to have one generated for you.
|
|
638
|
+
|
|
639
|
+
#### Get a webhook endpoint
|
|
640
|
+
|
|
641
|
+
```typescript
|
|
642
|
+
const { data: webhook } = await checkoutpage.webhooks.get(webhookId);
|
|
643
|
+
|
|
644
|
+
console.log(webhook.status, webhook.deliveryCount, webhook.lastFailureAt);
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
The signing `secret` is not included.
|
|
648
|
+
|
|
649
|
+
#### Update a webhook endpoint
|
|
650
|
+
|
|
651
|
+
```typescript
|
|
652
|
+
const { data: updated } = await checkoutpage.webhooks.update(webhookId, {
|
|
653
|
+
url: 'https://example.com/hooks/checkoutpage-v2',
|
|
654
|
+
events: ['payment.paid', 'checkout_page.updated', 'product.updated'],
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
// Pause deliveries without deleting the endpoint, then resume them
|
|
658
|
+
await checkoutpage.webhooks.update(webhookId, { status: 'inactive' });
|
|
659
|
+
await checkoutpage.webhooks.update(webhookId, { status: 'active' });
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
Only the fields you send change. `events` and `customHeaders` replace the existing values rather
|
|
663
|
+
than merging with them. A new `url` must use https and must not already be registered on another of
|
|
664
|
+
your webhooks (`ValidationError` / `ConflictError`). The signing secret cannot be changed through
|
|
665
|
+
the API.
|
|
367
666
|
|
|
368
667
|
#### Delete a webhook endpoint
|
|
369
668
|
|
|
@@ -373,6 +672,39 @@ const { data: deleted } = await checkoutpage.webhooks.delete(webhook.id);
|
|
|
373
672
|
|
|
374
673
|
Deliveries stop immediately and the endpoint no longer appears in `list()`.
|
|
375
674
|
|
|
675
|
+
### Test mode
|
|
676
|
+
|
|
677
|
+
Switch a checkout page, event or form into test mode with `testmode: true`. While it is on, every
|
|
678
|
+
purchase on that page is a test purchase: no money is taken, the buyer gets the product for free,
|
|
679
|
+
and the records are created with `livemode: false`.
|
|
680
|
+
|
|
681
|
+
```typescript
|
|
682
|
+
await checkoutpage.checkoutPages.update(pageId, { testmode: true });
|
|
683
|
+
|
|
684
|
+
const { data: pagesInTestMode } = await checkoutpage.checkoutPages.list({ testmode: true });
|
|
685
|
+
|
|
686
|
+
// Turn it off to take real payments again
|
|
687
|
+
await checkoutpage.checkoutPages.update(pageId, { testmode: false });
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
`events` and `forms` take the same field on `create`, `update` and `list`. Page responses carry
|
|
691
|
+
`testmode` and `testmodeEnabledAt` (when test mode was switched on, `null` while it is off).
|
|
692
|
+
|
|
693
|
+
#### Read test records
|
|
694
|
+
|
|
695
|
+
Pass `livemode: false` to read test records from payments, bookings, subscriptions, subscription
|
|
696
|
+
payments, invoices, submissions and tickets:
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
const { data: testPayments } = await checkoutpage.payments.list({ pageId, livemode: false });
|
|
700
|
+
const { data: testPayment } = await checkoutpage.payments.get(paymentId, { livemode: false });
|
|
701
|
+
const pdf = await checkoutpage.bookings.downloadTicketPdf(bookingId, { livemode: false });
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
Without `livemode`, reads return records in the mode of your API key. A live key (`sk_live_`)
|
|
705
|
+
reads live data by default and test data with `livemode: false`. A test key (`sk_test_`) only
|
|
706
|
+
reads test data: `livemode: true` with a test key throws an `AuthenticationError` (403).
|
|
707
|
+
|
|
376
708
|
## Error Handling
|
|
377
709
|
|
|
378
710
|
The SDK provides typed error classes for different error scenarios:
|