@tuppence/node 0.0.0-stage → 0.1.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.
- package/README.md +131 -2
- package/dist/agent-proof.d.ts +36 -0
- package/dist/agent-proof.js +35 -0
- package/dist/errors.d.ts +54 -0
- package/dist/errors.js +67 -0
- package/dist/generated/api.d.ts +63396 -0
- package/dist/http.d.ts +71 -0
- package/dist/http.js +134 -0
- package/dist/index.cjs +2719 -0
- package/dist/index.d.ts +14005 -0
- package/dist/index.js +230 -0
- package/dist/resources.d.ts +14712 -0
- package/dist/resources.js +1816 -0
- package/dist/types.d.ts +373 -0
- package/dist/types.js +1 -0
- package/dist/webhooks.d.ts +25 -0
- package/dist/webhooks.js +56 -0
- package/package.json +45 -3
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import type { components, paths } from "./generated/api.js";
|
|
2
|
+
/** Types generated from the API's OpenAPI document — never hand-written, so they cannot drift. */
|
|
3
|
+
type S = components["schemas"];
|
|
4
|
+
export type { components, paths };
|
|
5
|
+
export type Account = S["AccountModel"];
|
|
6
|
+
/** Every price the account pays now — the list's, or one agreed with Tuppence (61-12). */
|
|
7
|
+
export type AccountPricing = S["AccountPricingModel"];
|
|
8
|
+
export type Payment = S["PaymentModel"];
|
|
9
|
+
export type Refund = S["RefundModel"];
|
|
10
|
+
export type Customer = S["CustomerModel"];
|
|
11
|
+
export type CustomerPreference = S["CustomerPreferenceModel"];
|
|
12
|
+
export type CustomerPreferenceList = S["CustomerPreferenceList"];
|
|
13
|
+
export type PaymentLink = S["PaymentLinkModel"];
|
|
14
|
+
export type Plan = S["PlanModel"];
|
|
15
|
+
export type Subscription = S["SubscriptionModel"];
|
|
16
|
+
/** A discount on subscriptions (17-10). */
|
|
17
|
+
export type Coupon = S["CouponModel"];
|
|
18
|
+
/** A code a customer types for a coupon (17-10). */
|
|
19
|
+
export type PromoCode = S["PromoCodeModel"];
|
|
20
|
+
/** What a subscription charge was for (17-13). */
|
|
21
|
+
export type Invoice = S["InvoiceModel"];
|
|
22
|
+
/** A one-hour link into the customer portal (17-11). */
|
|
23
|
+
export type PortalSession = S["PortalSessionModel"];
|
|
24
|
+
/** What changing plan would cost, before it is charged. */
|
|
25
|
+
export type ProrationPreview = S["ProrationPreviewModel"];
|
|
26
|
+
export type Event = S["EventModel"];
|
|
27
|
+
/**
|
|
28
|
+
* Any of the store's webhook events (94-06), discriminated by `type`: after
|
|
29
|
+
* `if (event.type === "order.created")`, `event.data.object` is an `Order`.
|
|
30
|
+
*/
|
|
31
|
+
export type StoreEvent = S["StoreEvent"];
|
|
32
|
+
/** One store event by its type: `StoreEventOf<"store.site_published">`. */
|
|
33
|
+
export type StoreEventOf<T extends StoreEvent["type"]> = Extract<StoreEvent, {
|
|
34
|
+
type: T;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Any of Tuppence Events' webhook events (114-12), discriminated by `type`: after
|
|
38
|
+
* `if (event.type === "ticket_order.paid")`, `event.data.object` is the ticket order.
|
|
39
|
+
*/
|
|
40
|
+
export type TicketingEvent = S["TicketingEvent"];
|
|
41
|
+
/** One Tuppence Events event by its type: `TicketingEventOf<"ticket.issued">`. */
|
|
42
|
+
export type TicketingEventOf<T extends TicketingEvent["type"]> = Extract<TicketingEvent, {
|
|
43
|
+
type: T;
|
|
44
|
+
}>;
|
|
45
|
+
export type WebhookEndpoint = S["WebhookEndpointModel"];
|
|
46
|
+
/** A domain your own checkout runs on, registered so Apple Pay and Google Pay show there (09-13). */
|
|
47
|
+
export type PaymentMethodDomain = S["PaymentMethodDomainModel"];
|
|
48
|
+
export type Balance = S["BalanceModel"];
|
|
49
|
+
export type Payout = S["PayoutModel"];
|
|
50
|
+
/** What a payout covered, from the books: the balance before, its activity, the balance after. */
|
|
51
|
+
export type PayoutReconciliation = S["PayoutReconciliationModel"];
|
|
52
|
+
/**
|
|
53
|
+
* What an instant move would cost before it is made (57-03): to the bank (the default) or the
|
|
54
|
+
* card balance, when it arrives, and — when it can't be made — the `reason`, `message`, `fix_url`
|
|
55
|
+
* and `available_from`.
|
|
56
|
+
*/
|
|
57
|
+
export type InstantPayoutQuote = S["InstantPayoutQuoteModel"];
|
|
58
|
+
/** Where an instant move can go: `bank`, or `card_balance` (UK only). */
|
|
59
|
+
export type InstantPayoutDestination = InstantPayoutQuote["destination"];
|
|
60
|
+
/** The schedule, the bank, the limits — and `instant_payouts`, with each destination's answer. */
|
|
61
|
+
export type PayoutSettings = S["PayoutSettingsModel"];
|
|
62
|
+
/** Next-day payouts (UK, 61-06): whether they can be switched on now, and what is on. */
|
|
63
|
+
export type NextDayPayouts = S["NextDayModel"];
|
|
64
|
+
export type Dispute = S["DisputeModel"];
|
|
65
|
+
export type Meter = S["MeterModel"];
|
|
66
|
+
export type CreditBalance = S["CreditBalanceModel"];
|
|
67
|
+
/** A balance as listed: who holds it, and the totals topped up and used. */
|
|
68
|
+
export type CreditHolder = S["CreditHolderModel"];
|
|
69
|
+
export type CreditTopup = S["CreditTopupModel"];
|
|
70
|
+
export type UsageEvent = S["UsageEventModel"];
|
|
71
|
+
export type UsageSummary = S["UsageSummaryModel"];
|
|
72
|
+
/** Cards for agents (M14). */
|
|
73
|
+
export type Agent = S["AgentModel"];
|
|
74
|
+
/** What's normal for an agent's spending, and its latest unusual purchases (19-07). */
|
|
75
|
+
export type AgentProfile = S["AgentProfileModel"];
|
|
76
|
+
export type Cardholder = S["CardholderModel"];
|
|
77
|
+
export type Card = S["CardModel"];
|
|
78
|
+
export type SpendPolicy = S["SpendPolicyModel"];
|
|
79
|
+
export type Approval = S["ApprovalModel"];
|
|
80
|
+
export type CardAuthorization = S["CardAuthorizationModel"];
|
|
81
|
+
export type CardTransaction = S["CardTransactionModel"];
|
|
82
|
+
/** A virtual card's number, CVC and expiry (14-22). Never keep it. */
|
|
83
|
+
export type CardDetails = S["CardDetailsModel"];
|
|
84
|
+
/** A dispute of a card purchase (53-05): a draft until submitted. */
|
|
85
|
+
export type CardDispute = S["CardDisputeModel"];
|
|
86
|
+
/** A dispute as filed or submitted — with `other_recent_purchases` to dispute too. */
|
|
87
|
+
export type CardDisputeFiled = S["CardDisputeFiledModel"];
|
|
88
|
+
/** Issuing's limits for the account's trust tier (53-03). */
|
|
89
|
+
export type IssuingLimits = S["IssuingLimitsModel"];
|
|
90
|
+
/** What the cards can spend now (`spendable`), and where it lives (`spends_from`). */
|
|
91
|
+
export type IssuingBalance = S["IssuingBalanceModel"];
|
|
92
|
+
/** Money on its way to the card balance: what it waits on (`delay`), and when it lands. */
|
|
93
|
+
export type IssuingTopup = S["IssuingTopupModel"];
|
|
94
|
+
/** A hosted top-up page (54-18): `url` is shown only when it is made. */
|
|
95
|
+
export type IssuingTopupLink = S["IssuingTopupLinkModel"];
|
|
96
|
+
/** One way to top up the card balance: open or not (and why), its fee, total and timing (54-07). */
|
|
97
|
+
export type IssuingTopupOption = S["IssuingTopupOptionModel"];
|
|
98
|
+
/** Every way to top up the card balance, for an amount (or rates and timings without one). */
|
|
99
|
+
export type IssuingTopupOptions = S["IssuingTopupOptionsList"];
|
|
100
|
+
/** The auto top-up rule (54-15): below `threshold`, top up by `amount`; paused after a failure. */
|
|
101
|
+
export type IssuingAutoTopup = S["IssuingAutoTopupModel"];
|
|
102
|
+
/** The business's own card, saved for top-ups and charged with nobody there (54-15). */
|
|
103
|
+
export type IssuingTopupPaymentMethod = S["IssuingTopupPaymentMethodModel"];
|
|
104
|
+
/** What a person lets an agent top the cards up with, without asking each time (54-15, E13). */
|
|
105
|
+
export type AgentFundingMandate = S["AgentFundingMandateModel"];
|
|
106
|
+
/** The card balance's own bank details, by region. */
|
|
107
|
+
export type FundingInstructions = S["FundingInstructionsModel"];
|
|
108
|
+
/** Where the business is with Bacs Direct Debit, and what to do next (59-11). */
|
|
109
|
+
export type DirectDebit = S["DirectDebitModel"];
|
|
110
|
+
/** An application for Bacs Direct Debit, and what Tuppence decided (59-11). */
|
|
111
|
+
export type DirectDebitApplication = S["DirectDebitApplicationModel"];
|
|
112
|
+
/** Bookings (M23): where, what, who — and when a client could come. */
|
|
113
|
+
export type Location = S["LocationModel"];
|
|
114
|
+
/** Whether a mobile location covers a client's postcode or coordinates. */
|
|
115
|
+
export type Coverage = S["CoverageModel"];
|
|
116
|
+
/** A country pack (plan 31-19): what differs between countries at the till, as data. */
|
|
117
|
+
export type CountryPack = S["CountryPackModel"];
|
|
118
|
+
export type ServiceCategory = S["ServiceCategoryModel"];
|
|
119
|
+
export type Service = S["ServiceModel"];
|
|
120
|
+
export type ServiceVariant = S["ServiceVariantModel"];
|
|
121
|
+
export type ServiceAddon = S["ServiceAddonModel"];
|
|
122
|
+
/** What one service would take and cost, as chosen. */
|
|
123
|
+
export type ServiceQuote = S["ServiceQuoteModel"];
|
|
124
|
+
export type StaffMember = S["StaffMemberModel"];
|
|
125
|
+
export type Schedule = S["ScheduleModel"];
|
|
126
|
+
export type ScheduleException = S["ScheduleExceptionModel"];
|
|
127
|
+
export type WorkingHours = S["WorkingHoursModel"];
|
|
128
|
+
export type Resource = S["ResourceModel"];
|
|
129
|
+
export type ResourceCheck = S["ResourceCheckModel"];
|
|
130
|
+
export type BookingPolicy = S["BookingPolicyModel"];
|
|
131
|
+
/** The policy's client-facing words, rendered. */
|
|
132
|
+
export type PolicyRender = S["PolicyRenderModel"];
|
|
133
|
+
export type BookingPage = S["BookingPageModel"];
|
|
134
|
+
export type BookingPagePreviewToken = S["BookingPagePreviewTokenModel"];
|
|
135
|
+
/** The starts a client could book, and each one's people and resources. */
|
|
136
|
+
export type Availability = S["AvailabilityModel"];
|
|
137
|
+
export type AvailabilitySlot = S["AvailabilitySlotModel"];
|
|
138
|
+
export type Booking = S["BookingModel"];
|
|
139
|
+
export type ClassSession = S["ClassSessionModel"];
|
|
140
|
+
export type BookingSeries = S["BookingSeriesModel"];
|
|
141
|
+
export type WaitlistEntry = S["BookingWaitlistEntryModel"];
|
|
142
|
+
/** A customer's review of a booking or an order (M91). */
|
|
143
|
+
export type Review = S["ReviewModel"];
|
|
144
|
+
export type ReviewSummary = S["ReviewSummaryModel"];
|
|
145
|
+
/** One change an assistant or a person made to the store, with its undo (92-01). */
|
|
146
|
+
export type ChangeSet = S["ChangeSetModel"];
|
|
147
|
+
/** A store's move from another platform (93-01). */
|
|
148
|
+
export type StoreImport = S["StoreImportModel"];
|
|
149
|
+
export type StockMirror = S["StockMirrorModel"];
|
|
150
|
+
export type StoreImportSavings = S["StoreImportSavingsModel"];
|
|
151
|
+
/** One of the store's operator jobs (M92). */
|
|
152
|
+
export type AssistantJob = S["AssistantJobModel"];
|
|
153
|
+
export type AssistantJobRun = S["AssistantJobRunModel"];
|
|
154
|
+
export type AssistantJobBrief = S["AssistantJobBriefModel"];
|
|
155
|
+
export type AssistantJobExample = S["AssistantJobExampleModel"];
|
|
156
|
+
/** Lists answered whole, with no cursor (no `has_more`). */
|
|
157
|
+
export type AssistantJobList = S["AssistantJobList"];
|
|
158
|
+
export type AssistantJobBriefList = S["AssistantJobBriefList"];
|
|
159
|
+
export type AssistantJobExampleList = S["AssistantJobExampleList"];
|
|
160
|
+
/** The store's daily brief (M92). */
|
|
161
|
+
export type StoreBrief = S["StoreBriefModel"];
|
|
162
|
+
export type ChannelReport = S["ChannelReportModel"];
|
|
163
|
+
/** The business's one store site (85-02): its brand, navigation, address and settings. */
|
|
164
|
+
export type Site = S["SiteModel"];
|
|
165
|
+
export type SiteDomain = S["SiteDomainModel"];
|
|
166
|
+
/** A store's domain moving from its old platform (93-07). */
|
|
167
|
+
export type DomainSwitch = S["DomainSwitchModel"];
|
|
168
|
+
export type SiteFonts = S["SiteFontsModel"];
|
|
169
|
+
export type SitePage = S["SitePageModel"];
|
|
170
|
+
/** A published (or scheduled) version of the site, which a revert brings back. */
|
|
171
|
+
export type SiteVersion = S["SiteVersionModel"];
|
|
172
|
+
export type SitePreviewToken = S["SitePreviewTokenModel"];
|
|
173
|
+
export type SiteRedirect = S["SiteRedirectModel"];
|
|
174
|
+
export type SiteNotFoundList = S["SiteNotFoundList"];
|
|
175
|
+
export type SiteTranslationList = S["SiteTranslationList"];
|
|
176
|
+
export type SiteTranslationResult = S["SiteTranslationResult"];
|
|
177
|
+
export type SiteRenderContext = S["SiteRenderContextModel"];
|
|
178
|
+
/** What stands between the store and going live, each with its fix. */
|
|
179
|
+
export type SiteReadiness = S["SiteReadinessModel"];
|
|
180
|
+
export type SellingAbroad = S["SellingAbroadModel"];
|
|
181
|
+
export type StoreChecklist = S["StoreChecklistModel"];
|
|
182
|
+
export type ReturnPolicy = S["ReturnPolicyModel"];
|
|
183
|
+
/** A design agent run (95-05): what it made, its checks, its cost and its change set. */
|
|
184
|
+
export type DesignRun = S["DesignRunModel"];
|
|
185
|
+
/** A custom section in the site's library (M95), with its versions and the pages using it. */
|
|
186
|
+
export type SiteSection = S["SiteSectionModel"];
|
|
187
|
+
export type SiteSectionWrite = S["SiteSectionWriteModel"];
|
|
188
|
+
export type SiteSectionVersion = S["SiteSectionVersionModel"];
|
|
189
|
+
export type SiteSectionList = S["SiteSectionList"];
|
|
190
|
+
/** A code section: a document in a sealed frame, with a fallback section (S25). */
|
|
191
|
+
export type SiteCodeSection = S["SiteCodeSectionModel"];
|
|
192
|
+
export type SiteCodeSectionList = S["SiteCodeSectionList"];
|
|
193
|
+
export type SectionCheck = S["SectionCheckModel"];
|
|
194
|
+
/** The site's design as a file: the look, the sections' source and the draft pages' layouts. */
|
|
195
|
+
export type SiteDesignFile = S["SiteDesignFileModel"];
|
|
196
|
+
export type SiteDesignImport = S["SiteDesignImportModel"];
|
|
197
|
+
/** A change to the store proposed from a sentence, as a change set awaiting approval (86). */
|
|
198
|
+
export type StoreChange = S["StoreChangeModel"];
|
|
199
|
+
/** The store builder's proposal (86-01): stages, cost and its items in review order. */
|
|
200
|
+
export type StoreProposal = S["StoreProposalModel"];
|
|
201
|
+
export type StoreProposalList = S["StoreProposalListModel"];
|
|
202
|
+
export type ProposalItem = S["ProposalItemModel"];
|
|
203
|
+
export type ProposalPreview = S["ProposalPreviewModel"];
|
|
204
|
+
export type ProposalAcceptAll = S["AcceptAllModel"];
|
|
205
|
+
export type ProposalPublish = S["PublishModel"];
|
|
206
|
+
/** A product from a photo (86-11): its steps, cost, and once ready its draft and guesses. */
|
|
207
|
+
export type StoreSnap = S["StoreSnapModel"];
|
|
208
|
+
export type StoreSettings = S["StoreSettingsModel"];
|
|
209
|
+
/** A parcel's way to the shopper (M87): picked, packed, labelled, shipped, or collected. */
|
|
210
|
+
export type Fulfilment = S["FulfilmentModel"];
|
|
211
|
+
export type LabelBatch = S["LabelBatchModel"];
|
|
212
|
+
export type LabelRate = S["LabelRateModel"];
|
|
213
|
+
export type PackingSlip = S["PackingSlipModel"];
|
|
214
|
+
/** A return or exchange (87-09), from the request to the refund. */
|
|
215
|
+
export type Return = S["ReturnModel"];
|
|
216
|
+
export type ReturnScan = S["ReturnScanModel"];
|
|
217
|
+
export type WayBackOptionList = S["WayBackOptionList"];
|
|
218
|
+
/** The day's parcels handed to a carrier, with its manifest (87-10). */
|
|
219
|
+
export type ShippingManifest = S["ShippingManifestModel"];
|
|
220
|
+
export type CloseDay = S["CloseDayModel"];
|
|
221
|
+
export type CarrierPickup = S["CarrierPickupModel"];
|
|
222
|
+
/** Postage owed for labels bought from the balance, and the credit limit (87-03). */
|
|
223
|
+
export type Postage = S["PostageModel"];
|
|
224
|
+
/** Your own carrier account for labels (87-03b), with the carriers and fields it can take. */
|
|
225
|
+
export type CarrierAccount = S["CarrierAccountModel"];
|
|
226
|
+
export type CarrierAccountList = S["CarrierAccountListModel"];
|
|
227
|
+
/** What delivery costs where (87-01): zones, each with its rates, for a set of products. */
|
|
228
|
+
export type ShippingProfile = S["ShippingProfileModel"];
|
|
229
|
+
export type ShippingDeleted = S["ShippingDeletedModel"];
|
|
230
|
+
export type EstimatedWeight = S["EstimatedWeightModel"];
|
|
231
|
+
export type DeliveryPreview = S["DeliveryPreviewModel"];
|
|
232
|
+
export type ShippingPackage = S["ShippingPackageModel"];
|
|
233
|
+
export type ShippingService = S["ShippingServiceModel"];
|
|
234
|
+
/** A photo, video or document in the media library (85-04). */
|
|
235
|
+
export type MediaAsset = S["MediaAssetModel"];
|
|
236
|
+
export type MediaUpload = S["MediaUploadModel"];
|
|
237
|
+
/** A file a digital product delivers (88-12). */
|
|
238
|
+
export type DigitalAsset = S["DigitalAssetModel"];
|
|
239
|
+
export type ImageStudio = S["ImageStudioModel"];
|
|
240
|
+
export type ImageEdit = S["ImageEditModel"];
|
|
241
|
+
/** A store collection (85-05): chosen products, or rules that pick them. */
|
|
242
|
+
export type Collection = S["CollectionModel"];
|
|
243
|
+
/** A product the till and the store sell, with its web fields. */
|
|
244
|
+
export type Product = S["ProductModel"];
|
|
245
|
+
export type ProductVariant = S["ProductVariantModel"];
|
|
246
|
+
export type ProductLookup = S["ProductLookupModel"];
|
|
247
|
+
export type ProductBulkUpdate = S["ProductBulkUpdateModel"];
|
|
248
|
+
/** What the product still needs to go online, each with its fix (85-03). */
|
|
249
|
+
export type OnlineChecklist = S["OnlineChecklistModel"];
|
|
250
|
+
export type LicenceKeyPool = S["LicenceKeyPoolModel"];
|
|
251
|
+
export type ReturnInsightList = S["ReturnInsightList"];
|
|
252
|
+
/** An order: one list for the till, the store and every channel, with its `source`. */
|
|
253
|
+
export type Order = S["OrderModel"];
|
|
254
|
+
export type OrderEvent = S["OrderEventModel"];
|
|
255
|
+
export type OrderSync = S["OrderSyncModel"];
|
|
256
|
+
export type Tender = S["TenderModel"];
|
|
257
|
+
export type OrderTender = S["OrderTenderModel"];
|
|
258
|
+
export type OrderReturn = S["OrderReturnModel"];
|
|
259
|
+
export type ReturnPreview = S["ReturnPreviewModel"];
|
|
260
|
+
/** The order's lines, totals, fulfilment, payments and contact details. */
|
|
261
|
+
export type OrderDetails = S["OrderDetailsModel"];
|
|
262
|
+
export type OrderAddressChanged = S["OrderAddressChangedModel"];
|
|
263
|
+
export type OrderConfirmationSent = S["OrderConfirmationSentModel"];
|
|
264
|
+
export type OrderEReceipt = S["OrderEReceiptModel"];
|
|
265
|
+
export type OrderReceipt = S["OrderReceiptModel"];
|
|
266
|
+
/** The store's policy pages (88-02): terms, privacy, returns, delivery, each versioned. */
|
|
267
|
+
export type SitePolicy = S["SitePolicyModel"];
|
|
268
|
+
export type SitePolicyList = S["SitePolicyList"];
|
|
269
|
+
export type SitePolicyVersion = S["SitePolicyVersionModel"];
|
|
270
|
+
export type SitePolicyVersionList = S["SitePolicyVersionList"];
|
|
271
|
+
/** A shopper's request to see or erase their data (88-05). */
|
|
272
|
+
export type StorePrivacyRequest = S["StorePrivacyRequestModel"];
|
|
273
|
+
/** The business's own say over a product's or collection's recommendations (91-03). */
|
|
274
|
+
export type RecommendationRule = S["RecommendationRuleModel"];
|
|
275
|
+
/** Subscribe and save on a product (91-08). */
|
|
276
|
+
export type SubscriptionOffer = S["SubscriptionOfferModel"];
|
|
277
|
+
/** A store campaign, its preview and its results (91-09b). */
|
|
278
|
+
export type StoreCampaign = S["StoreCampaignModel"];
|
|
279
|
+
export type StoreCampaignPreview = S["StoreCampaignPreviewModel"];
|
|
280
|
+
export type StoreCampaignResults = S["StoreCampaignResultsModel"];
|
|
281
|
+
/** A store flow: on or off, its words, paused or not, and its last 30 days (91-17). */
|
|
282
|
+
export type StoreFlow = S["StoreFlowModel"];
|
|
283
|
+
/** A connection to another place the store sells: a marketplace, a social shop, a feed (M90). */
|
|
284
|
+
export type SalesChannel = S["SalesChannelModel"];
|
|
285
|
+
/** A variant's place on a sales channel (M90). */
|
|
286
|
+
export type Listing = S["ListingModel"];
|
|
287
|
+
/** A store inbox conversation, with its messages on retrieve (91-10). */
|
|
288
|
+
export type StoreThread = S["StoreThreadModel"];
|
|
289
|
+
export type StoreThreadTemplates = S["StoreThreadTemplates"];
|
|
290
|
+
/** A store's own sending domain and the records to add (91-13). */
|
|
291
|
+
export type MerchantSendingDomain = S["MerchantSendingDomainModel"];
|
|
292
|
+
export type MerchantSendingDomainSuggestion = S["MerchantSendingDomainSuggestion"];
|
|
293
|
+
/** A limited release with a fair queue (85-12). */
|
|
294
|
+
export type Drop = S["DropModel"];
|
|
295
|
+
export type DropQueue = S["DropQueueModel"];
|
|
296
|
+
/** Everything that moved a balance — a platform's Connect balance with `balance: "connect"`. */
|
|
297
|
+
export type BalanceTransaction = S["BalanceTransactionModel"];
|
|
298
|
+
/**
|
|
299
|
+
* Tuppence Connect (M40–M43): a platform's connected accounts, onboarding them, and the money
|
|
300
|
+
* between them. A connected account is a full Tuppence account: act as one with
|
|
301
|
+
* `{ tuppenceAccount }` on any request, or `tuppence.as("acct_…")`.
|
|
302
|
+
*/
|
|
303
|
+
export type ConnectedAccount = S["ConnectAccountModel"];
|
|
304
|
+
export type ConnectedAccountDeleted = S["ConnectAccountDeletedModel"];
|
|
305
|
+
export type ConnectedAccountStore = S["ConnectStoreModel"];
|
|
306
|
+
/** One of the people behind a connected account: names and roles, never the details given. */
|
|
307
|
+
export type Person = S["PersonModel"];
|
|
308
|
+
export type PersonDeleted = S["PersonDeletedModel"];
|
|
309
|
+
/** A person's identity check: the Stripe Identity session to send them to. */
|
|
310
|
+
export type PersonIdentityCheck = S["PersonIdentityCheckModel"];
|
|
311
|
+
/** A connected account's bank account, for its payouts. */
|
|
312
|
+
export type ExternalAccount = S["ExternalAccountModel"];
|
|
313
|
+
export type ExternalAccountDeleted = S["ExternalAccountDeletedModel"];
|
|
314
|
+
export type Capability = S["CapabilityModel"];
|
|
315
|
+
/** A URL, good once and for five minutes, onto the hosted onboarding in your branding. */
|
|
316
|
+
export type AccountLink = S["AccountLinkModel"];
|
|
317
|
+
/** A file uploaded for a connected account's verification (the `file` object). */
|
|
318
|
+
export type FileUpload = S["FileModel"];
|
|
319
|
+
export type ApplicationFee = S["ApplicationFeeModel"];
|
|
320
|
+
export type FeeRefund = S["FeeRefundModel"];
|
|
321
|
+
export type Transfer = S["TransferModel"];
|
|
322
|
+
export type TransferReversal = S["TransferReversalModel"];
|
|
323
|
+
export type TicketedEvent = S["TicketedEventModel"];
|
|
324
|
+
export type TicketType = S["TicketTypeModel"];
|
|
325
|
+
export type TicketOrder = S["TicketOrderModel"];
|
|
326
|
+
/** A refund of a ticket order: the order, every ticket (refunded ones `void`) and the refund. */
|
|
327
|
+
export type TicketOrderRefund = S["TicketOrderRefundModel"];
|
|
328
|
+
export type Ticket = S["TicketModel"];
|
|
329
|
+
export type TicketPromoCode = S["TicketPromoCodeModel"];
|
|
330
|
+
/** Ticket money held until after a date, and when it is released. */
|
|
331
|
+
export type TicketFundsHold = S["TicketFundsHoldModel"];
|
|
332
|
+
export type TicketSalesReport = S["TicketSalesReportModel"];
|
|
333
|
+
export type TicketSettlement = S["TicketSettlementModel"];
|
|
334
|
+
/** A scan at the door: `admitted` the first time, `duplicate` after (117). */
|
|
335
|
+
export type CheckIn = S["CheckInModel"];
|
|
336
|
+
export type CheckInManifest = S["CheckInManifestModel"];
|
|
337
|
+
export type CheckInSync = S["CheckInSyncModel"];
|
|
338
|
+
/** An event a door can check guests in for: one with a date not yet over (116-05). */
|
|
339
|
+
export type CheckInEvent = S["CheckInEventModel"];
|
|
340
|
+
/** An event's door, live: tickets in by gate and by type (116-07). */
|
|
341
|
+
export type CheckInCounts = S["CheckInCountsModel"];
|
|
342
|
+
/** A ticket given or resold to someone else (117-06, 117-08). */
|
|
343
|
+
export type TicketTransfer = S["TicketTransferModel"];
|
|
344
|
+
export type TicketTransferResult = S["TicketTransferResultModel"];
|
|
345
|
+
/** A place on a sold-out ticket type's waitlist (117-07). */
|
|
346
|
+
export type EventWaitlistEntry = S["EventWaitlistEntryModel"];
|
|
347
|
+
/** A partner's commission on an order made with an affiliate code (118-03). */
|
|
348
|
+
export type TicketCommission = S["TicketCommissionModel"];
|
|
349
|
+
/** A message to an event's attendees, by email, text or WhatsApp (118-04). */
|
|
350
|
+
export type EventMessage = S["EventMessageModel"];
|
|
351
|
+
/** The account's settings for every event: its ad pixels (118-06). */
|
|
352
|
+
export type TicketingSettings = S["TicketingSettingsModel"];
|
|
353
|
+
/** A page of a list: `data`, and whether there is more after it. */
|
|
354
|
+
export type List<T> = {
|
|
355
|
+
object: "list";
|
|
356
|
+
data: T[];
|
|
357
|
+
has_more: boolean;
|
|
358
|
+
};
|
|
359
|
+
/** Cursor paging, the same on every list. */
|
|
360
|
+
export type ListParams = {
|
|
361
|
+
limit?: number;
|
|
362
|
+
starting_after?: string;
|
|
363
|
+
ending_before?: string;
|
|
364
|
+
};
|
|
365
|
+
type JsonBody<O> = O extends {
|
|
366
|
+
requestBody?: infer R;
|
|
367
|
+
} ? NonNullable<R> extends {
|
|
368
|
+
content: {
|
|
369
|
+
"application/json": infer B;
|
|
370
|
+
};
|
|
371
|
+
} ? B : never : never;
|
|
372
|
+
/** The JSON body an operation takes — e.g. `Body<"/v1/payments", "post">`. */
|
|
373
|
+
export type Body<P extends keyof paths, M extends keyof paths[P]> = JsonBody<paths[P][M]>;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Event } from "./types.js";
|
|
2
|
+
export declare const SIGNATURE_HEADER = "Tuppence-Signature";
|
|
3
|
+
export declare const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
4
|
+
/**
|
|
5
|
+
* Checks a webhook really came from Tuppence, then parses it.
|
|
6
|
+
*
|
|
7
|
+
* const event = tuppence.webhooks.constructEvent(rawBody, req.headers["tuppence-signature"], secret);
|
|
8
|
+
*
|
|
9
|
+
* `payload` must be the RAW body exactly as received (a Buffer or string) — re-serialised JSON
|
|
10
|
+
* will not match. `Tuppence-Signature: t=<unix seconds>,v1=<hex>[,v1=<hex>]`: v1 is HMAC-SHA256
|
|
11
|
+
* over `"<t>.<body>"`; during a secret rotation there are two v1s and either may match.
|
|
12
|
+
*
|
|
13
|
+
* An endpoint for the store's events can say so, and `type` narrows `data.object` (94-06):
|
|
14
|
+
*
|
|
15
|
+
* const event = constructEvent<StoreEvent>(rawBody, header, secret);
|
|
16
|
+
* if (event.type === "order.created") ship(event.data.object.lines);
|
|
17
|
+
*/
|
|
18
|
+
export declare function constructEvent<E extends {
|
|
19
|
+
type: string;
|
|
20
|
+
} = Event>(payload: string | Buffer, header: string | string[] | undefined | null, secret: string, options?: {
|
|
21
|
+
toleranceSeconds?: number;
|
|
22
|
+
nowSeconds?: number;
|
|
23
|
+
}): E;
|
|
24
|
+
/** Signs a payload the way Tuppence does — for testing your own webhook handler. */
|
|
25
|
+
export declare function generateTestHeader(payload: string, secret: string, timestamp?: number): string;
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { SignatureVerificationError } from "./errors.js";
|
|
3
|
+
export const SIGNATURE_HEADER = "Tuppence-Signature";
|
|
4
|
+
export const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
5
|
+
const hmac = (secret, timestamp, payload) => createHmac("sha256", secret).update(`${timestamp}.${payload}`, "utf8").digest("hex");
|
|
6
|
+
/**
|
|
7
|
+
* Checks a webhook really came from Tuppence, then parses it.
|
|
8
|
+
*
|
|
9
|
+
* const event = tuppence.webhooks.constructEvent(rawBody, req.headers["tuppence-signature"], secret);
|
|
10
|
+
*
|
|
11
|
+
* `payload` must be the RAW body exactly as received (a Buffer or string) — re-serialised JSON
|
|
12
|
+
* will not match. `Tuppence-Signature: t=<unix seconds>,v1=<hex>[,v1=<hex>]`: v1 is HMAC-SHA256
|
|
13
|
+
* over `"<t>.<body>"`; during a secret rotation there are two v1s and either may match.
|
|
14
|
+
*
|
|
15
|
+
* An endpoint for the store's events can say so, and `type` narrows `data.object` (94-06):
|
|
16
|
+
*
|
|
17
|
+
* const event = constructEvent<StoreEvent>(rawBody, header, secret);
|
|
18
|
+
* if (event.type === "order.created") ship(event.data.object.lines);
|
|
19
|
+
*/
|
|
20
|
+
export function constructEvent(payload, header, secret, options = {}) {
|
|
21
|
+
const body = typeof payload === "string" ? payload : payload.toString("utf8");
|
|
22
|
+
const value = Array.isArray(header) ? header[0] : header;
|
|
23
|
+
if (!value) {
|
|
24
|
+
throw new SignatureVerificationError("malformed_header", `No ${SIGNATURE_HEADER} header.`);
|
|
25
|
+
}
|
|
26
|
+
let timestamp;
|
|
27
|
+
const signatures = [];
|
|
28
|
+
for (const part of value.split(",")) {
|
|
29
|
+
const index = part.indexOf("=");
|
|
30
|
+
if (index < 1)
|
|
31
|
+
throw new SignatureVerificationError("malformed_header", "The signature header is malformed.");
|
|
32
|
+
const key = part.slice(0, index).trim();
|
|
33
|
+
const v = part.slice(index + 1).trim();
|
|
34
|
+
if (key === "t" && /^\d{1,12}$/.test(v))
|
|
35
|
+
timestamp = Number(v);
|
|
36
|
+
else if (key === "v1")
|
|
37
|
+
signatures.push(v);
|
|
38
|
+
}
|
|
39
|
+
if (timestamp === undefined || signatures.length === 0) {
|
|
40
|
+
throw new SignatureVerificationError("malformed_header", "The signature header is malformed.");
|
|
41
|
+
}
|
|
42
|
+
const expected = Buffer.from(hmac(secret, timestamp, body), "hex");
|
|
43
|
+
const matched = signatures.some((candidate) => /^[0-9a-f]{64}$/i.test(candidate) && timingSafeEqual(Buffer.from(candidate, "hex"), expected));
|
|
44
|
+
if (!matched) {
|
|
45
|
+
throw new SignatureVerificationError("no_matching_signature", "The signature does not match: check the endpoint's signing secret, and that you passed the raw body.");
|
|
46
|
+
}
|
|
47
|
+
const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
48
|
+
if (Math.abs(now - timestamp) > (options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS)) {
|
|
49
|
+
throw new SignatureVerificationError("timestamp_out_of_tolerance", "The webhook's timestamp is too old (or your clock is wrong): refusing a possible replay.");
|
|
50
|
+
}
|
|
51
|
+
return JSON.parse(body);
|
|
52
|
+
}
|
|
53
|
+
/** Signs a payload the way Tuppence does — for testing your own webhook handler. */
|
|
54
|
+
export function generateTestHeader(payload, secret, timestamp = Math.floor(Date.now() / 1000)) {
|
|
55
|
+
return `t=${timestamp},v1=${hmac(secret, timestamp, payload)}`;
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tuppence/node",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The Tuppence API for Node.js — payments for developers and AI agents.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://docs.tuppence.ai",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/ZenningAI/tuppence.git",
|
|
10
|
+
"directory": "packages/node"
|
|
11
|
+
},
|
|
12
|
+
"bugs": "https://github.com/ZenningAI/tuppence/issues",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"require": "./dist/index.cjs"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"main": "./dist/index.cjs",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22.20.4",
|
|
33
|
+
"esbuild": "^0.25.12",
|
|
34
|
+
"openapi-typescript": "^7.13.0",
|
|
35
|
+
"typescript": "^5.9.3",
|
|
36
|
+
"vitest": "^4.1.12"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && mkdir -p dist/generated && cp src/generated/api.d.ts dist/generated/ && esbuild src/index.ts --bundle --platform=node --target=node18 --format=cjs --outfile=dist/index.cjs --log-level=warning",
|
|
43
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"types:generate": "openapi-typescript ../../apps/api/openapi.public.json -o src/generated/api.d.ts",
|
|
46
|
+
"types:check": "openapi-typescript ../../apps/api/openapi.public.json -o /tmp/tuppence-node-api.check.d.ts && diff -q /tmp/tuppence-node-api.check.d.ts src/generated/api.d.ts"
|
|
47
|
+
}
|
|
6
48
|
}
|