From c1f4af5d5b096122f350ecb7f28083e500ecd452 Mon Sep 17 00:00:00 2001 From: CodeMash Date: Mon, 19 Oct 2026 13:57:48 +0300 Subject: [PATCH 1/2] feat(payments): payment orders and checkout on the Api client, regenerated types Regenerated api2 / hub2 types and endpoint modules from the gateway campaign audit/payments-checkout (merged with refactoringV2): api.payments.createPaymentOrder, getPaymentOrder, getPaymentOrders, startPaymentCheckout; Montonio and the seven other provider save requests; PaymentOrderEventDto; PaymentFactDto.orderId; plus the base routes the SDK had not picked up (notification inbox on Api and Hub, trigger definition, pre-execute-code test, updateAiServiceUser). Co-Authored-By: Claude Opus 5.5 --- docs/README.md | 4 +- docs/api/_index.md | 5 +- docs/api/notifications.md | 107 ++++- docs/api/payments.md | 106 +++++ docs/hub/_index.md | 8 +- docs/hub/account.md | 24 ++ docs/hub/notifications.md | 89 ++++ docs/hub/triggers.md | 53 ++- src/api/index.ts | 3 + src/api/notifications.ts | 78 +++- src/api/payments.ts | 89 ++++ src/hub/account.ts | 21 +- src/hub/notifications.ts | 78 +++- src/hub/triggers.ts | 40 +- src/types/api2.contract.json | 32 ++ src/types/api2.dtos.ts | 520 +++++++++++++++++++++- src/types/hub2.contract.json | 28 ++ src/types/hub2.dtos.ts | 735 ++++++++++++++++++++++++++++++-- tests/api/notifications.test.ts | 100 ++++- tests/api/payments.test.ts | 129 ++++++ tests/hub/account.test.ts | 28 +- tests/hub/notifications.test.ts | 100 ++++- tests/hub/triggers.test.ts | 54 ++- 23 files changed, 2360 insertions(+), 71 deletions(-) create mode 100644 docs/api/payments.md create mode 100644 src/api/payments.ts create mode 100644 tests/api/payments.test.ts diff --git a/docs/README.md b/docs/README.md index 45e170f..7e1630d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,8 +8,8 @@ integration guides live under `integrations/`. | Surface | Modules | Description | | ------------------------ | -------------------------: | ------------------------------------------------------------------------------------------------ | -| [`API`](./api/_index.md) | 10 modules · 112 endpoints | Project-scoped data operations: database collections, users, AI chat, auth, API keys. | -| [`Hub`](./hub/_index.md) | 27 modules · 575 endpoints | Project & account configuration: schemas, integrations, team management, billing, observability. | +| [`API`](./api/_index.md) | 11 modules · 120 endpoints | Project-scoped data operations: database collections, users, AI chat, auth, API keys. | +| [`Hub`](./hub/_index.md) | 27 modules · 582 endpoints | Project & account configuration: schemas, integrations, team management, billing, observability. | ## Integration guides diff --git a/docs/api/_index.md b/docs/api/_index.md index af19e55..e49468c 100644 --- a/docs/api/_index.md +++ b/docs/api/_index.md @@ -4,7 +4,7 @@ Project-scoped data operations: database collections, users, AI chat, auth, API keys. -**112 endpoints across 10 modules.** Click a module +**120 endpoints across 11 modules.** Click a module name for the full method reference and TypeScript examples. | Module | Endpoints | Description | @@ -17,5 +17,6 @@ name for the full method reference and TypeScript examples. | [`echo`](./echo.md) | 1 | Echo helpers used by the gateway smoke checks. | | [`files`](./files.md) | 14 | File storage integrations and triggers. Upload + download is in `api.database` (FileResource fields). | | [`membership`](./membership.md) | 43 | Roles, policies, and user preferences (Hub side). For user CRUD and registration see `api.membership`. | -| [`notifications`](./notifications.md) | 7 | Email and push templates, integrations, campaigns, devices, signatures, footers, and one-click unsubscribe. | +| [`notifications`](./notifications.md) | 11 | Email and push templates, integrations, campaigns, devices, signatures, footers, and one-click unsubscribe. | +| [`payments`](./payments.md) | 4 | Payment provider integrations and triggers (Stripe, etc.). | | [`public`](./public.md) | 3 | | diff --git a/docs/api/notifications.md b/docs/api/notifications.md index d9d0648..0497716 100644 --- a/docs/api/notifications.md +++ b/docs/api/notifications.md @@ -8,15 +8,19 @@ Accessed as `norbix.api.notifications` on the [`Norbix`](../../README.md#authent ## Endpoints -| Method | Verb | Path | Scope | -| ----------------------------------------------------------- | -------- | -------------------------------------------------- | --------- | -| [`registerOwnPushDevice`](#registerownpushdevice) | `POST` | `/{version}/notifications/push/devices` | `project` | -| [`getOwnPushDevices`](#getownpushdevices) | `GET` | `/{version}/notifications/push/devices` | `project` | -| [`deleteOwnPushDevice`](#deleteownpushdevice) | `DELETE` | `/{version}/notifications/push/devices/{Id}` | `project` | -| [`deleteOwnPushDeviceByToken`](#deleteownpushdevicebytoken) | `DELETE` | `/{version}/notifications/push/devices` | `project` | -| [`getOwnPushMessages`](#getownpushmessages) | `GET` | `/{version}/notifications/push/messages` | `project` | -| [`markOwnPushMessageRead`](#markownpushmessageread) | `POST` | `/{version}/notifications/push/messages/{Id}/read` | `project` | -| [`markAllOwnPushMessagesRead`](#markallownpushmessagesread) | `POST` | `/{version}/notifications/push/messages/read` | `project` | +| Method | Verb | Path | Scope | +| ----------------------------------------------------------- | -------- | ------------------------------------------------------ | --------- | +| [`registerOwnPushDevice`](#registerownpushdevice) | `POST` | `/{version}/notifications/push/devices` | `project` | +| [`getOwnPushDevices`](#getownpushdevices) | `GET` | `/{version}/notifications/push/devices` | `project` | +| [`deleteOwnPushDevice`](#deleteownpushdevice) | `DELETE` | `/{version}/notifications/push/devices/{Id}` | `project` | +| [`deleteOwnPushDeviceByToken`](#deleteownpushdevicebytoken) | `DELETE` | `/{version}/notifications/push/devices` | `project` | +| [`getOwnPushMessages`](#getownpushmessages) | `GET` | `/{version}/notifications/push/messages` | `project` | +| [`markOwnPushMessageRead`](#markownpushmessageread) | `POST` | `/{version}/notifications/push/messages/{Id}/read` | `project` | +| [`markAllOwnPushMessagesRead`](#markallownpushmessagesread) | `POST` | `/{version}/notifications/push/messages/read` | `project` | +| [`getNotificationInbox`](#getnotificationinbox) | `GET` | `/{version}/notifications/inbox` | `project` | +| [`getUnreadCount`](#getunreadcount) | `GET` | `/{version}/notifications/inbox/unread-count` | `project` | +| [`markNotificationRead`](#marknotificationread) | `POST` | `/{version}/notifications/inbox/{notificationId}/read` | `project` | +| [`markAllNotificationsRead`](#markallnotificationsread) | `POST` | `/{version}/notifications/inbox/read-all` | `project` | ## Reference @@ -171,3 +175,88 @@ const result = await norbix.api.notifications.markAllOwnPushMessagesRead({ ``` [↑ Top](#endpoints) + +### getNotificationInbox + +`GET` `/{version}/notifications/inbox` + +Fetch a single item by ID. + +**Request DTO**: `CodeMashApi2.GetNotificationInboxRequest` +**Response**: `CodeMashApi2.GetNotificationInboxResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.api.notifications.getNotificationInbox({ + // See CodeMash type for the full request shape. +}); +// → typed as CodeMashApi2.GetNotificationInboxResponse +``` + +[↑ Top](#endpoints) + +### getUnreadCount + +`GET` `/{version}/notifications/inbox/unread-count` + +Fetch a single item by ID. + +**Request DTO**: `CodeMashApi2.GetUnreadCountRequest` +**Response**: `CodeMashApi2.GetUnreadCountResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.api.notifications.getUnreadCount({ + // See CodeMash type for the full request shape. +}); +// → typed as CodeMashApi2.GetUnreadCountResponse +``` + +[↑ Top](#endpoints) + +### markNotificationRead + +`POST` `/{version}/notifications/inbox/{notificationId}/read` + +**Request DTO**: `CodeMashApi2.MarkNotificationReadRequest` +**Response**: `CodeMashApi2.MarkNotificationReadResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.api.notifications.markNotificationRead({ + notificationId: 'notificationId-here', + // Other fields: see CodeMash type for the full request shape. +}); +// → typed as CodeMashApi2.MarkNotificationReadResponse +``` + +[↑ Top](#endpoints) + +### markAllNotificationsRead + +`POST` `/{version}/notifications/inbox/read-all` + +**Request DTO**: `CodeMashApi2.MarkAllNotificationsReadRequest` +**Response**: `CodeMashApi2.MarkAllNotificationsReadResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.api.notifications.markAllNotificationsRead({ + // See CodeMash type for the full request shape. +}); +// → typed as CodeMashApi2.MarkAllNotificationsReadResponse +``` + +[↑ Top](#endpoints) diff --git a/docs/api/payments.md b/docs/api/payments.md new file mode 100644 index 0000000..21b9add --- /dev/null +++ b/docs/api/payments.md @@ -0,0 +1,106 @@ +# API · Payments + +[← Back to API index](./_index.md) · [↑ Back to project README](../../README.md) + +Payment provider integrations and triggers (Stripe, etc.). + +Accessed as `norbix.api.payments` on the [`Norbix`](../../README.md#authentication) client. + +## Endpoints + +| Method | Verb | Path | Scope | +| ----------------------------------------------- | ------ | ------------------------------------------ | --------- | +| [`createPaymentOrder`](#createpaymentorder) | `POST` | `/{version}/payments/orders` | `project` | +| [`getPaymentOrder`](#getpaymentorder) | `GET` | `/{version}/payments/orders/{Id}` | `project` | +| [`getPaymentOrders`](#getpaymentorders) | `GET` | `/{version}/payments/orders` | `project` | +| [`startPaymentCheckout`](#startpaymentcheckout) | `POST` | `/{version}/payments/orders/{Id}/checkout` | `project` | + +## Reference + +### createPaymentOrder + +`POST` `/{version}/payments/orders` + +Create a new item. + +**Request DTO**: `CodeMashApi2.CreatePaymentOrderRequest` +**Response**: `CodeMashApi2.IdResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.api.payments.createPaymentOrder({ + // See CodeMash type for the full request shape. +}); +// → typed as CodeMashApi2.IdResponse +``` + +[↑ Top](#endpoints) + +### getPaymentOrder + +`GET` `/{version}/payments/orders/{Id}` + +Fetch a single item by ID. + +**Request DTO**: `CodeMashApi2.GetPaymentOrderRequest` +**Response**: `CodeMashApi2.GetPaymentOrderResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.api.payments.getPaymentOrder({ + Id: 'Id-here', + // Other fields: see CodeMash type for the full request shape. +}); +// → typed as CodeMashApi2.GetPaymentOrderResponse +``` + +[↑ Top](#endpoints) + +### getPaymentOrders + +`GET` `/{version}/payments/orders` + +Fetch a single item by ID. + +**Request DTO**: `CodeMashApi2.GetPaymentOrdersRequest` +**Response**: `CodeMashApi2.GetPaymentOrdersResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.api.payments.getPaymentOrders({ + // See CodeMash type for the full request shape. +}); +// → typed as CodeMashApi2.GetPaymentOrdersResponse +``` + +[↑ Top](#endpoints) + +### startPaymentCheckout + +`POST` `/{version}/payments/orders/{Id}/checkout` + +**Request DTO**: `CodeMashApi2.StartPaymentCheckoutRequest` +**Response**: `CodeMashApi2.StartPaymentCheckoutResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.api.payments.startPaymentCheckout({ + Id: 'Id-here', + // Other fields: see CodeMash type for the full request shape. +}); +// → typed as CodeMashApi2.StartPaymentCheckoutResponse +``` + +[↑ Top](#endpoints) diff --git a/docs/hub/_index.md b/docs/hub/_index.md index 2dffeee..cbc28a4 100644 --- a/docs/hub/_index.md +++ b/docs/hub/_index.md @@ -4,13 +4,13 @@ Project & account configuration: schemas, integrations, team management, billing, observability. -**575 endpoints across 27 modules.** Click a module +**582 endpoints across 27 modules.** Click a module name for the full method reference and TypeScript examples. | Module | Endpoints | Description | | ------------------------------------- | --------: | ---------------------------------------------------------------------------------------------------------------------------------------- | | [`accessToken`](./access_token.md) | 1 | Refresh-token exchange to get a new bearer token. | -| [`account`](./account.md) | 121 | Account profile, status, verification, team invites, and Stripe billing portal. Most write endpoints require `accountId` on the client. | +| [`account`](./account.md) | 122 | Account profile, status, verification, team invites, and Stripe billing portal. Most write endpoints require `accountId` on the client. | | [`ai`](./ai.md) | 26 | | | [`apikeys`](./apikeys.md) | 2 | List and regenerate per-environment API keys for service auth. | | [`auth`](./auth.md) | 5 | Sign-in / sign-out and federated provider flows. Most apps prefer `norbix.login(...)` over calling these directly. | @@ -25,7 +25,7 @@ name for the full method reference and TypeScript examples. | [`licensing`](./licensing.md) | 3 | | | [`logs`](./logs.md) | 14 | Logging integrations and module on/off switches. | | [`membership`](./membership.md) | 48 | Roles, policies, and user preferences (Hub side). For user CRUD and registration see `api.membership`. | -| [`notifications`](./notifications.md) | 124 | Email and push templates, integrations, campaigns, devices, signatures, footers, and one-click unsubscribe. | +| [`notifications`](./notifications.md) | 128 | Email and push templates, integrations, campaigns, devices, signatures, footers, and one-click unsubscribe. | | [`oauth`](./oauth.md) | 6 | | | [`payments`](./payments.md) | 17 | Payment provider integrations and triggers (Stripe, etc.). | | [`projects`](./projects.md) | 5 | | @@ -33,6 +33,6 @@ name for the full method reference and TypeScript examples. | [`resources`](./resources.md) | 1 | | | [`scheduler`](./scheduler.md) | 9 | | | [`support`](./support.md) | 7 | | -| [`triggers`](./triggers.md) | 1 | | +| [`triggers`](./triggers.md) | 3 | | | [`webhooks`](./webhooks.md) | 9 | | | [`wellKnown`](./well_known.md) | 2 | | diff --git a/docs/hub/account.md b/docs/hub/account.md index 8623500..559429a 100644 --- a/docs/hub/account.md +++ b/docs/hub/account.md @@ -107,6 +107,7 @@ Accessed as `norbix.hub.account` on the [`Norbix`](../../README.md#authenticatio | [`listAiServiceUsers`](#listaiserviceusers) | `GET` | `/{version}/account/ai/service-users` | `project` | | [`rotateAiServiceUserKey`](#rotateaiserviceuserkey) | `POST` | `/{version}/account/ai/service-users/{Id}/keys` | `project` | | [`revokeAiServiceUserKey`](#revokeaiserviceuserkey) | `DELETE` | `/{version}/account/ai/service-users/{Id}/keys/{KeyId}` | `project` | +| [`updateAiServiceUser`](#updateaiserviceuser) | `PUT` | `/{version}/account/ai/service-users/{Id}` | `project` | | [`deleteAiServiceUser`](#deleteaiserviceuser) | `DELETE` | `/{version}/account/ai/service-users/{Id}` | `project` | | [`getAgentOnboarding`](#getagentonboarding) | `GET` | `/{version}/account/agent/onboarding` | `project` | | [`getAiContext`](#getaicontext) | `GET` | `/{version}/account/ai/context` | `project` | @@ -2279,6 +2280,29 @@ const result = await norbix.hub.account.revokeAiServiceUserKey({ [↑ Top](#endpoints) +### updateAiServiceUser + +`PUT` `/{version}/account/ai/service-users/{Id}` + +Update an existing item. + +**Request DTO**: `CodeMashHub2.UpdateAiServiceUserRequest` +**Response**: `CodeMashHub2.EmptyResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.hub.account.updateAiServiceUser({ + Id: 'Id-here', + // Other fields: see CodeMash type for the full request shape. +}); +// → typed as CodeMashHub2.EmptyResponse +``` + +[↑ Top](#endpoints) + ### deleteAiServiceUser `DELETE` `/{version}/account/ai/service-users/{Id}` diff --git a/docs/hub/notifications.md b/docs/hub/notifications.md index c41dbeb..ecb96c8 100644 --- a/docs/hub/notifications.md +++ b/docs/hub/notifications.md @@ -130,6 +130,10 @@ Accessed as `norbix.hub.notifications` on the [`Norbix`](../../README.md#authent | [`previewPushNotification`](#previewpushnotification) | `GET` | `/{version}/notifications/push/preview` | `project` | | [`stopPushCampaign`](#stoppushcampaign) | `POST` | `/{version}/notifications/push/campaigns/{Id}/stop` | `project` | | [`getPushCampaignMessages`](#getpushcampaignmessages) | `GET` | `/{version}/notifications/push/campaigns/{campaignId}/messages` | `project` | +| [`getNotificationInbox`](#getnotificationinbox) | `GET` | `/{version}/notifications/inbox` | `project` | +| [`getUnreadCount`](#getunreadcount) | `GET` | `/{version}/notifications/inbox/unread-count` | `project` | +| [`markNotificationRead`](#marknotificationread) | `POST` | `/{version}/notifications/inbox/{notificationId}/read` | `project` | +| [`markAllNotificationsRead`](#markallnotificationsread) | `POST` | `/{version}/notifications/inbox/read-all` | `project` | | [`getUserNotificationPreferences`](#getusernotificationpreferences) | `GET` | `/{version}/notifications/user/preferences` | `project` | | [`updateUserNotificationsPreferences`](#updateusernotificationspreferences) | `PUT` | `/{version}/notifications/user/preferences` | `project` | | [`grantContactConsent`](#grantcontactconsent) | `POST` | `/{version}/membership/users/{contactId}/marketing-state/{channel}/consent` | `project` | @@ -2819,6 +2823,91 @@ const result = await norbix.hub.notifications.getPushCampaignMessages({ [↑ Top](#endpoints) +### getNotificationInbox + +`GET` `/{version}/notifications/inbox` + +Fetch a single item by ID. + +**Request DTO**: `CodeMashHub2.GetNotificationInboxRequest` +**Response**: `CodeMashHub2.GetNotificationInboxResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.hub.notifications.getNotificationInbox({ + // See CodeMash type for the full request shape. +}); +// → typed as CodeMashHub2.GetNotificationInboxResponse +``` + +[↑ Top](#endpoints) + +### getUnreadCount + +`GET` `/{version}/notifications/inbox/unread-count` + +Fetch a single item by ID. + +**Request DTO**: `CodeMashHub2.GetUnreadCountRequest` +**Response**: `CodeMashHub2.GetUnreadCountResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.hub.notifications.getUnreadCount({ + // See CodeMash type for the full request shape. +}); +// → typed as CodeMashHub2.GetUnreadCountResponse +``` + +[↑ Top](#endpoints) + +### markNotificationRead + +`POST` `/{version}/notifications/inbox/{notificationId}/read` + +**Request DTO**: `CodeMashHub2.MarkNotificationReadRequest` +**Response**: `CodeMashHub2.MarkNotificationReadResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.hub.notifications.markNotificationRead({ + notificationId: 'notificationId-here', + // Other fields: see CodeMash type for the full request shape. +}); +// → typed as CodeMashHub2.MarkNotificationReadResponse +``` + +[↑ Top](#endpoints) + +### markAllNotificationsRead + +`POST` `/{version}/notifications/inbox/read-all` + +**Request DTO**: `CodeMashHub2.MarkAllNotificationsReadRequest` +**Response**: `CodeMashHub2.MarkAllNotificationsReadResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.hub.notifications.markAllNotificationsRead({ + // See CodeMash type for the full request shape. +}); +// → typed as CodeMashHub2.MarkAllNotificationsReadResponse +``` + +[↑ Top](#endpoints) + ### getUserNotificationPreferences `GET` `/{version}/notifications/user/preferences` diff --git a/docs/hub/triggers.md b/docs/hub/triggers.md index 81ab799..5d20cdf 100644 --- a/docs/hub/triggers.md +++ b/docs/hub/triggers.md @@ -6,12 +6,37 @@ Accessed as `norbix.hub.triggers` on the [`Norbix`](../../README.md#authenticati ## Endpoints -| Method | Verb | Path | Scope | -| ------------------------------------------------------------- | ----- | ------------------------------- | --------- | -| [`getTriggersNeedingAttention`](#gettriggersneedingattention) | `GET` | `/{version}/triggers/attention` | `project` | +| Method | Verb | Path | Scope | +| ------------------------------------------------------------- | ------ | ------------------------------------------- | --------- | +| [`getTriggerDefinition`](#gettriggerdefinition) | `GET` | `/{version}/triggers/{id}/definition` | `project` | +| [`getTriggersNeedingAttention`](#gettriggersneedingattention) | `GET` | `/{version}/triggers/attention` | `project` | +| [`testTriggerPreExecuteCode`](#testtriggerpreexecutecode) | `POST` | `/{version}/triggers/pre-execute-code/test` | `project` | ## Reference +### getTriggerDefinition + +`GET` `/{version}/triggers/{id}/definition` + +Fetch a single item by ID. + +**Request DTO**: `CodeMashHub2.GetTriggerDefinition` +**Response**: `CodeMashHub2.GetTriggerDefinitionResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.hub.triggers.getTriggerDefinition({ + id: 'id-here', + // Other fields: see CodeMash type for the full request shape. +}); +// → typed as CodeMashHub2.GetTriggerDefinitionResponse +``` + +[↑ Top](#endpoints) + ### getTriggersNeedingAttention `GET` `/{version}/triggers/attention` @@ -33,3 +58,25 @@ const result = await norbix.hub.triggers.getTriggersNeedingAttention({ ``` [↑ Top](#endpoints) + +### testTriggerPreExecuteCode + +`POST` `/{version}/triggers/pre-execute-code/test` + +Run a connection / delivery test against the integration. + +**Request DTO**: `CodeMashHub2.TestTriggerPreExecuteCode` +**Response**: `CodeMashHub2.TestTriggerPreExecuteCodeResponse` + +```ts +import { Norbix } from '@norbix.ai/ts'; + +const norbix = new Norbix(); + +const result = await norbix.hub.triggers.testTriggerPreExecuteCode({ + // See CodeMash type for the full request shape. +}); +// → typed as CodeMashHub2.TestTriggerPreExecuteCodeResponse +``` + +[↑ Top](#endpoints) diff --git a/src/api/index.ts b/src/api/index.ts index d3b92e6..7d78f59 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -9,6 +9,7 @@ import { EchoModule } from './echo.js'; import { FilesModule } from './files.js'; import { MembershipModule } from './membership.js'; import { NotificationsModule } from './notifications.js'; +import { PaymentsModule } from './payments.js'; import { PublicModule } from './public.js'; /** @@ -25,6 +26,7 @@ export class ApiNamespace { public readonly files: FilesModule; public readonly membership: MembershipModule; public readonly notifications: NotificationsModule; + public readonly payments: PaymentsModule; public readonly public: PublicModule; constructor(transport: Transport) { @@ -37,6 +39,7 @@ export class ApiNamespace { this.files = new FilesModule(transport); this.membership = new MembershipModule(transport); this.notifications = new NotificationsModule(transport); + this.payments = new PaymentsModule(transport); this.public = new PublicModule(transport); } } diff --git a/src/api/notifications.ts b/src/api/notifications.ts index 2723d3b..7388005 100644 --- a/src/api/notifications.ts +++ b/src/api/notifications.ts @@ -6,7 +6,7 @@ import type { CodeMashApi2 } from '../types/api2.dtos.js'; * to refresh this file from the DTO definitions. * * Group: notifications - * Endpoints: 7 + * Endpoints: 11 */ export class NotificationsModule { constructor(private readonly transport: Transport) {} @@ -143,4 +143,80 @@ export class NotificationsModule { ...options, }); }; + + /** + * GET /{version}/notifications/inbox + * Request DTO: GetNotificationInboxRequest + */ + getNotificationInbox = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'api', + path: '/{version}/notifications/inbox', + method: 'GET', + request, + pathParams: [], + scope: 'project', + ...options, + }); + }; + + /** + * GET /{version}/notifications/inbox/unread-count + * Request DTO: GetUnreadCountRequest + */ + getUnreadCount = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'api', + path: '/{version}/notifications/inbox/unread-count', + method: 'GET', + request, + pathParams: [], + scope: 'project', + ...options, + }); + }; + + /** + * POST /{version}/notifications/inbox/{notificationId}/read + * Request DTO: MarkNotificationReadRequest + */ + markNotificationRead = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'api', + path: '/{version}/notifications/inbox/{notificationId}/read', + method: 'POST', + request, + pathParams: ['notificationId'], + scope: 'project', + ...options, + }); + }; + + /** + * POST /{version}/notifications/inbox/read-all + * Request DTO: MarkAllNotificationsReadRequest + */ + markAllNotificationsRead = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'api', + path: '/{version}/notifications/inbox/read-all', + method: 'POST', + request, + pathParams: [], + scope: 'project', + ...options, + }); + }; } diff --git a/src/api/payments.ts b/src/api/payments.ts new file mode 100644 index 0000000..189c0c1 --- /dev/null +++ b/src/api/payments.ts @@ -0,0 +1,89 @@ +import type { RequestOverrideOptions, Transport } from '../client/transport.js'; +import type { CodeMashApi2 } from '../types/api2.dtos.js'; + +/** + * Auto-generated. Do not edit by hand — run `npm run generate-endpoints` + * to refresh this file from the DTO definitions. + * + * Group: payments + * Endpoints: 4 + */ +export class PaymentsModule { + constructor(private readonly transport: Transport) {} + + /** + * POST /{version}/payments/orders + * Request DTO: CreatePaymentOrderRequest + */ + createPaymentOrder = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'api', + path: '/{version}/payments/orders', + method: 'POST', + request, + pathParams: [], + scope: 'project', + ...options, + }); + }; + + /** + * GET /{version}/payments/orders/{Id} + * Request DTO: GetPaymentOrderRequest + */ + getPaymentOrder = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'api', + path: '/{version}/payments/orders/{Id}', + method: 'GET', + request, + pathParams: ['Id'], + scope: 'project', + ...options, + }); + }; + + /** + * GET /{version}/payments/orders + * Request DTO: GetPaymentOrdersRequest + */ + getPaymentOrders = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'api', + path: '/{version}/payments/orders', + method: 'GET', + request, + pathParams: [], + scope: 'project', + ...options, + }); + }; + + /** + * POST /{version}/payments/orders/{Id}/checkout + * Request DTO: StartPaymentCheckoutRequest + */ + startPaymentCheckout = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'api', + path: '/{version}/payments/orders/{Id}/checkout', + method: 'POST', + request, + pathParams: ['Id'], + scope: 'project', + ...options, + }); + }; +} diff --git a/src/hub/account.ts b/src/hub/account.ts index b3479ba..8a8e5c2 100644 --- a/src/hub/account.ts +++ b/src/hub/account.ts @@ -6,7 +6,7 @@ import type { CodeMashHub2 } from '../types/hub2.dtos.js'; * to refresh this file from the DTO definitions. * * Group: account - * Endpoints: 121 + * Endpoints: 122 */ export class AccountModule { constructor(private readonly transport: Transport) {} @@ -1859,6 +1859,25 @@ export class AccountModule { }); }; + /** + * PUT /{version}/account/ai/service-users/{Id} + * Request DTO: UpdateAiServiceUserRequest + */ + updateAiServiceUser = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'hub', + path: '/{version}/account/ai/service-users/{Id}', + method: 'PUT', + request, + pathParams: ['Id'], + scope: 'project', + ...options, + }); + }; + /** * DELETE /{version}/account/ai/service-users/{Id} * Request DTO: DeleteAiServiceUserRequest diff --git a/src/hub/notifications.ts b/src/hub/notifications.ts index 8bffaba..03af05e 100644 --- a/src/hub/notifications.ts +++ b/src/hub/notifications.ts @@ -6,7 +6,7 @@ import type { CodeMashHub2 } from '../types/hub2.dtos.js'; * to refresh this file from the DTO definitions. * * Group: notifications - * Endpoints: 124 + * Endpoints: 128 */ export class NotificationsModule { constructor(private readonly transport: Transport) {} @@ -2291,6 +2291,82 @@ export class NotificationsModule { }); }; + /** + * GET /{version}/notifications/inbox + * Request DTO: GetNotificationInboxRequest + */ + getNotificationInbox = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'hub', + path: '/{version}/notifications/inbox', + method: 'GET', + request, + pathParams: [], + scope: 'project', + ...options, + }); + }; + + /** + * GET /{version}/notifications/inbox/unread-count + * Request DTO: GetUnreadCountRequest + */ + getUnreadCount = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'hub', + path: '/{version}/notifications/inbox/unread-count', + method: 'GET', + request, + pathParams: [], + scope: 'project', + ...options, + }); + }; + + /** + * POST /{version}/notifications/inbox/{notificationId}/read + * Request DTO: MarkNotificationReadRequest + */ + markNotificationRead = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'hub', + path: '/{version}/notifications/inbox/{notificationId}/read', + method: 'POST', + request, + pathParams: ['notificationId'], + scope: 'project', + ...options, + }); + }; + + /** + * POST /{version}/notifications/inbox/read-all + * Request DTO: MarkAllNotificationsReadRequest + */ + markAllNotificationsRead = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'hub', + path: '/{version}/notifications/inbox/read-all', + method: 'POST', + request, + pathParams: [], + scope: 'project', + ...options, + }); + }; + /** * GET /{version}/notifications/user/preferences * Request DTO: GetUserNotificationPreferences diff --git a/src/hub/triggers.ts b/src/hub/triggers.ts index 6ba95f4..ef2b953 100644 --- a/src/hub/triggers.ts +++ b/src/hub/triggers.ts @@ -6,11 +6,30 @@ import type { CodeMashHub2 } from '../types/hub2.dtos.js'; * to refresh this file from the DTO definitions. * * Group: triggers - * Endpoints: 1 + * Endpoints: 3 */ export class TriggersModule { constructor(private readonly transport: Transport) {} + /** + * GET /{version}/triggers/{id}/definition + * Request DTO: GetTriggerDefinition + */ + getTriggerDefinition = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'hub', + path: '/{version}/triggers/{id}/definition', + method: 'GET', + request, + pathParams: ['id'], + scope: 'project', + ...options, + }); + }; + /** * GET /{version}/triggers/attention * Request DTO: GetTriggersNeedingAttention @@ -29,4 +48,23 @@ export class TriggersModule { ...options, }); }; + + /** + * POST /{version}/triggers/pre-execute-code/test + * Request DTO: TestTriggerPreExecuteCode + */ + testTriggerPreExecuteCode = ( + request: Partial = {} as Partial, + options: RequestOverrideOptions = {}, + ): Promise => { + return this.transport.send({ + target: 'hub', + path: '/{version}/triggers/pre-execute-code/test', + method: 'POST', + request, + pathParams: [], + scope: 'project', + ...options, + }); + }; } diff --git a/src/types/api2.contract.json b/src/types/api2.contract.json index e40f9f6..047e3d5 100644 --- a/src/types/api2.contract.json +++ b/src/types/api2.contract.json @@ -47,6 +47,10 @@ "requiresAuth": true, "returnsBytes": false }, + "CreatePaymentOrderRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "DeleteEndUserChatAttachmentRequest": { "requiresAuth": true, "returnsBytes": false @@ -175,6 +179,10 @@ "requiresAuth": true, "returnsBytes": false }, + "GetNotificationInboxRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "GetOwnPushDevicesRequest": { "requiresAuth": true, "returnsBytes": false @@ -183,6 +191,14 @@ "requiresAuth": true, "returnsBytes": false }, + "GetPaymentOrderRequest": { + "requiresAuth": true, + "returnsBytes": false + }, + "GetPaymentOrdersRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "GetPublicFileRequest": { "requiresAuth": false, "returnsBytes": true @@ -203,6 +219,10 @@ "requiresAuth": true, "returnsBytes": false }, + "GetUnreadCountRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "GetUserPreferencesRequest": { "requiresAuth": true, "returnsBytes": false @@ -267,10 +287,18 @@ "requiresAuth": true, "returnsBytes": false }, + "MarkAllNotificationsReadRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "MarkAllOwnPushMessagesReadRequest": { "requiresAuth": true, "returnsBytes": false }, + "MarkNotificationReadRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "MarkOwnPushMessageReadRequest": { "requiresAuth": true, "returnsBytes": false @@ -387,6 +415,10 @@ "requiresAuth": true, "returnsBytes": false }, + "StartPaymentCheckoutRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "TestFilesIntegrationRequest": { "requiresAuth": true, "returnsBytes": false diff --git a/src/types/api2.dtos.ts b/src/types/api2.dtos.ts index 2c17e9d..f5ebb16 100644 --- a/src/types/api2.dtos.ts +++ b/src/types/api2.dtos.ts @@ -1,6 +1,6 @@ // @ts-nocheck /* Options: -Date: 2026-10-14 16:40:15 +Date: 2026-10-19 10:55:02 Version: 10.20 Tip: To override a DTO option, remove "//" prefix before updating BaseUrl: http://localhost:5002 @@ -548,6 +548,25 @@ export module CodeMashApi2 { } } + // @DataContract + export class PaymentOrderLineInput { + // @DataMember + public sku?: string; + + // @DataMember + public title?: string; + + // @DataMember + public quantity: number; + + // @DataMember + public unitAmountMinor: number; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + // @DataContract export enum CodeMashRelease { NotSet = 'NotSet', @@ -1032,6 +1051,9 @@ export module CodeMashApi2 { // @DataMember public meta?: Object; + // @DataMember + public origin?: string; + public constructor(init?: Partial) { (Object as any).assign(this, init); } @@ -1058,6 +1080,15 @@ export module CodeMashApi2 { } } + export class SchemaUniqueIndexDto { + // @DataMember + public fields: string[] = []; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + export class SchemaSettingsDto { // @DataMember public softDelete: boolean; @@ -1068,6 +1099,9 @@ export module CodeMashApi2 { // @DataMember public description?: string; + // @DataMember + public uniqueIndexes?: SchemaUniqueIndexDto[]; + public constructor(init?: Partial) { (Object as any).assign(this, init); } @@ -1414,6 +1448,140 @@ export module CodeMashApi2 { } } + // @DataContract + export class PaymentOrderLineDto { + // @DataMember + public sku: string; + + // @DataMember + public title: string; + + // @DataMember + public quantity: number; + + // @DataMember + public unitAmountMinor: number; + + // @DataMember + public amountMinor: number; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + + // @DataContract + export class PaymentOrderCheckoutDto { + // @DataMember + public attempt: number; + + // @DataMember + public integrationId: string; + + // @DataMember + public provider: string; + + // @DataMember + public providerOrderId: string; + + // @DataMember + public providerReference: string; + + // @DataMember + public paymentUrl: string; + + // @DataMember + public method: string; + + // @DataMember + public startedOnUtc: string; + + // @DataMember + public expiresAtUtc: string; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + + // @DataContract + export class PaymentOrderDto { + // @DataMember + public id: string; + + // @DataMember + public env: string; + + // @DataMember + public buyerUserId: string; + + // @DataMember + public merchantReference: string; + + // @DataMember + public currency: string; + + // @DataMember + public priceVersion: string; + + // @DataMember + public lines: PaymentOrderLineDto[] = []; + + // @DataMember + public totalMinor: number; + + // @DataMember + public metadata: { [index: string]: string } = {}; + + // @DataMember + public status: string; + + // @DataMember + public createdOnUtc: string; + + // @DataMember + public updatedOnUtc: string; + + // @DataMember + public checkouts: PaymentOrderCheckoutDto[] = []; + + // @DataMember + public settledMinor?: number; + + // @DataMember + public refundedMinor: number; + + // @DataMember + public paidOnUtc?: string; + + // @DataMember + public providerPaymentId?: string; + + // @DataMember + public mismatches: string[] = []; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + + export class InAppNotificationDto { + public id: string; + public projectId: string; + public userId: string; + public channel: string; + public eventName: string; + public payloadJson?: string; + public isRead: boolean; + public seenAtUtc?: string; + public readAtUtc?: string; + public createdAtUtc: string; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + // @DataContract export class ResponseError { // @DataMember(Order=1) @@ -1915,6 +2083,67 @@ export module CodeMashApi2 { } } + export class GetPaymentOrderResponse extends ResponseBase { + public item?: PaymentOrderDto; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class GetPaymentOrdersResponse extends ResponseBase { + public list?: PaginatedResponse; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class StartPaymentCheckoutResponse extends ResponseBase { + public checkout?: PaymentOrderCheckoutDto; + public reused: boolean; + public order?: PaymentOrderDto; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class GetNotificationInboxResponse extends ResponseBase { + public list?: PaginatedResponse; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class GetUnreadCountResponse extends ResponseBase { + public count: number; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class MarkNotificationReadResponse extends ResponseBase { + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class MarkAllNotificationsReadResponse extends ResponseBase { + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + // @DataContract export class AuthenticateResponse implements IHasSessionId, IHasBearerToken { // @DataMember(Order=1) @@ -3008,11 +3237,21 @@ export module CodeMashApi2 { // @Api(Description="Membership") // @DataContract export class InviteUserRequest extends CodeMashRequestBase implements IReturn { - /** @description Email address the invitation is sent to. */ + /** @description Email address the invitation is sent to. It cannot be changed later. */ // @DataMember - // @ApiMember(Description="Email address the invitation is sent to.", IsRequired=true) + // @ApiMember(Description="Email address the invitation is sent to. It cannot be changed later.", IsRequired=true) public email: string; + /** @description The invited person's full name, e.g. "Rūta Petraitė". Stored as the full name, first name (first word) and last name (the rest). The user may change it when finishing. */ + // @DataMember + // @ApiMember(Description="The invited person's full name, e.g. \"Rūta Petraitė\". Stored as the full name, first name (first word) and last name (the rest). The user may change it when finishing.") + public fullName?: string; + + /** @description Optional. How e-mails greet the person ("Hi {salutation},"), e.g. "Rūta". Stored as the display name. When empty the full name is used. The user may change it when finishing. */ + // @DataMember + // @ApiMember(Description="Optional. How e-mails greet the person (\"Hi {salutation},\"), e.g. \"Rūta\". Stored as the display name. When empty the full name is used. The user may change it when finishing.") + public salutation?: string; + /** @description Database integration id. Optional — defaults to the request environment's default integration. */ // @DataMember // @ApiMember(Description="Database integration id. Optional — defaults to the request environment's default integration.") @@ -3051,19 +3290,29 @@ export module CodeMashApi2 { // @ApiMember(Description="The user's password. Validated against the project's complexity policy.", IsRequired=true) public password: string; - /** @description Optional display name. */ + /** @description Optional. The user's full name; replaces the one the admin entered (full name, first name = first word, last name = the rest). Empty = keep the admin's. */ // @DataMember - // @ApiMember(Description="Optional display name.") + // @ApiMember(Description="Optional. The user's full name; replaces the one the admin entered (full name, first name = first word, last name = the rest). Empty = keep the admin's.") + public fullName?: string; + + /** @description Optional. How e-mails greet the user (stored as the display name). Empty = keep the admin's, or the full name when the admin gave none. */ + // @DataMember + // @ApiMember(Description="Optional. How e-mails greet the user (stored as the display name). Empty = keep the admin's, or the full name when the admin gave none.") + public salutation?: string; + + /** @description Optional. Older form of Salutation (the display name); Salutation wins when both are sent. */ + // @DataMember + // @ApiMember(Description="Optional. Older form of Salutation (the display name); Salutation wins when both are sent.") public displayName?: string; - /** @description Optional first name. */ + /** @description Optional first name; wins over the one taken from FullName. */ // @DataMember - // @ApiMember(Description="Optional first name.") + // @ApiMember(Description="Optional first name; wins over the one taken from FullName.") public firstName?: string; - /** @description Optional last name. */ + /** @description Optional last name; wins over the one taken from FullName. */ // @DataMember - // @ApiMember(Description="Optional last name.") + // @ApiMember(Description="Optional last name; wins over the one taken from FullName.") public lastName?: string; public constructor(init?: Partial) { @@ -3913,7 +4162,9 @@ export module CodeMashApi2 { // @ApiMember(Description="Set true to keep name / description as stored even when Accept-Language is sent (editors). The full translations are always in names / descriptions.") public allLanguages: boolean; + /** @description The taxonomy: its name (slug or title; URL-encode spaces) or its id (txn_…). */ // @DataMember + // @ApiMember(Description="The taxonomy: its name (slug or title; URL-encode spaces) or its id (txn_…).", IsRequired=true) public taxonomyName: string; // @DataMember @@ -3981,7 +4232,9 @@ export module CodeMashApi2 { // @ApiMember(Description="Set true to keep name / description as stored even when Accept-Language is sent (editors). The full translations are always in names / descriptions.") public allLanguages: boolean; + /** @description The taxonomy: its name (slug or title; URL-encode spaces) or its id (txn_…). */ // @DataMember + // @ApiMember(Description="The taxonomy: its name (slug or title; URL-encode spaces) or its id (txn_…).", IsRequired=true) public taxonomyName: string; // @DataMember @@ -4024,7 +4277,9 @@ export module CodeMashApi2 { // @ApiMember(Description="Set true to keep name / description as stored even when Accept-Language is sent (editors). The full translations are always in names / descriptions.") public allLanguages: boolean; + /** @description The taxonomy: its name (slug or title; URL-encode spaces) or its id (txn_…). */ // @DataMember + // @ApiMember(Description="The taxonomy: its name (slug or title; URL-encode spaces) or its id (txn_…).", IsRequired=true) public taxonomyName: string; // @DataMember @@ -4067,7 +4322,9 @@ export module CodeMashApi2 { // @ApiMember(Description="Set true to keep name / description as stored even when Accept-Language is sent (editors). The full translations are always in names / descriptions.") public allLanguages: boolean; + /** @description The taxonomy: its name (slug or title; URL-encode spaces) or its id (txn_…). */ // @DataMember + // @ApiMember(Description="The taxonomy: its name (slug or title; URL-encode spaces) or its id (txn_…).", IsRequired=true) public taxonomyName: string; // @DataMember @@ -5080,8 +5337,8 @@ export module CodeMashApi2 { extends CodeMashRequestBase implements IReturn { - /** @description Who issued the token: expo (getExpoPushTokenAsync), fcm (Android / Firebase) or apns (iOS). */ - // @ApiMember(Description="Who issued the token: expo (getExpoPushTokenAsync), fcm (Android / Firebase) or apns (iOS).", IsRequired=true) + /** @description Who issued the token: fcm (Android / Firebase) or apns (iOS). The NATIVE device token — in an Expo app use getDevicePushTokenAsync(). Expo push tokens (provider 'expo') are refused. */ + // @ApiMember(Description="Who issued the token: fcm (Android / Firebase) or apns (iOS). The NATIVE device token — in an Expo app use getDevicePushTokenAsync(). Expo push tokens (provider 'expo') are refused.", IsRequired=true) public provider: string; /** @description The push token, exactly as the provider gave it. */ @@ -5289,6 +5546,247 @@ export module CodeMashApi2 { } } + /** @description Stores a payment order with a fixed price for one of the project's users. Server only (service user API key). */ + // @Route("/{version}/payments/orders", "POST") + // @Api(Description="Stores a payment order with a fixed price for one of the project's users. Server only (service user API key).") + export class CreatePaymentOrderRequest + extends CodeMashRequestBase + implements IReturn + { + /** @description The user the order is for (usr_…). */ + // @ApiMember(Description="The user the order is for (usr_…).", IsRequired=true) + public buyerUserId: string; + + /** @description The project's own order reference, unique per project and environment (1–64 of A–Z a–z 0–9 . _ : -). */ + // @ApiMember(Description="The project's own order reference, unique per project and environment (1–64 of A–Z a–z 0–9 . _ : -).", IsRequired=true) + public merchantReference: string; + + /** @description 8–128 characters. Same key + same body = the stored order; same key + other body = 409. May be sent as the Idempotency-Key header instead. */ + // @ApiMember(Description="8–128 characters. Same key + same body = the stored order; same key + other body = 409. May be sent as the Idempotency-Key header instead.") + public idempotencyKey?: string; + + /** @description EUR. */ + // @ApiMember(Description="EUR.", IsRequired=true) + public currency: string; + + /** @description The version of the price list the server priced the lines with. */ + // @ApiMember(Description="The version of the price list the server priced the lines with.", IsRequired=true) + public priceVersion: string; + + /** @description 1–50 lines: sku, title, quantity, unitAmountMinor (cents). Norbix computes the amounts and the total. */ + // @ApiMember(Description="1–50 lines: sku, title, quantity, unitAmountMinor (cents). Norbix computes the amounts and the total.", IsRequired=true) + public lines: PaymentOrderLineInput[] = []; + + /** @description Up to 20 project values (e.g. adId). No personal or billing data. */ + // @ApiMember(Description="Up to 20 project values (e.g. adId). No personal or billing data.") + public metadata?: { [index: string]: string }; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'CreatePaymentOrderRequest'; + } + public getMethod() { + return 'POST'; + } + public createResponse() { + return new IdResponse(); + } + } + + /** @description Reads one payment order. A buyer reads only their own orders. */ + // @Route("/{version}/payments/orders/{Id}", "GET") + // @Api(Description="Reads one payment order. A buyer reads only their own orders.") + export class GetPaymentOrderRequest + extends CodeMashRequestBase + implements IReturn + { + /** @description The order id (ord_…). */ + // @ApiMember(Description="The order id (ord_…).", IsRequired=true) + public id: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'GetPaymentOrderRequest'; + } + public getMethod() { + return 'GET'; + } + public createResponse() { + return new GetPaymentOrderResponse(); + } + } + + /** @description Lists payment orders, newest first. A buyer gets only their own orders. */ + // @Route("/{version}/payments/orders", "GET") + // @Api(Description="Lists payment orders, newest first. A buyer gets only their own orders.") + export class GetPaymentOrdersRequest + extends CodeMashListPaginationRequestBase + implements IReturn + { + /** @description Server only: one buyer's orders (usr_…). A buyer always gets their own. */ + // @ApiMember(Description="Server only: one buyer's orders (usr_…). A buyer always gets their own.") + public buyerUserId?: string; + + /** @description Only the order with this merchant reference. */ + // @ApiMember(Description="Only the order with this merchant reference.") + public merchantReference?: string; + + /** @description Only orders in this status (Created, CheckoutStarted, Paid, …). */ + // @ApiMember(Description="Only orders in this status (Created, CheckoutStarted, Paid, …).") + public status?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'GetPaymentOrdersRequest'; + } + public getMethod() { + return 'GET'; + } + public createResponse() { + return new GetPaymentOrdersResponse(); + } + } + + /** @description Opens a Montonio checkout for a stored order and answers where the buyer pays. The buyer (own order) or the server. */ + // @Route("/{version}/payments/orders/{Id}/checkout", "POST") + // @Api(Description="Opens a Montonio checkout for a stored order and answers where the buyer pays. The buyer (own order) or the server.") + export class StartPaymentCheckoutRequest + extends CodeMashRequestBase + implements IReturn + { + /** @description The order id (ord_…). */ + // @ApiMember(Description="The order id (ord_…).", IsRequired=true) + public id: string; + + /** @description The Montonio integration (int_…) of the project in the request's environment. */ + // @ApiMember(Description="The Montonio integration (int_…) of the project in the request's environment.", IsRequired=true) + public integrationId: string; + + /** @description bankLink (default), card, applePay or googlePay. */ + // @ApiMember(Description="bankLink (default), card, applePay or googlePay.") + public method?: string; + + /** @description Where the buyer comes back after paying; its origin must be one of the project's allowed origins. */ + // @ApiMember(Description="Where the buyer comes back after paying; its origin must be one of the project's allowed origins.", IsRequired=true) + public returnUrl: string; + + /** @description Bank links only: the country whose banks are shown first (default LT). */ + // @ApiMember(Description="Bank links only: the country whose banks are shown first (default LT).") + public preferredCountry?: string; + + /** @description Bank links only: one bank to open directly (the provider's bank code). */ + // @ApiMember(Description="Bank links only: one bank to open directly (the provider's bank code).") + public preferredProvider?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'StartPaymentCheckoutRequest'; + } + public getMethod() { + return 'POST'; + } + public createResponse() { + return new StartPaymentCheckoutResponse(); + } + } + + // @Route("/{version}/notifications/inbox", "GET") + export class GetNotificationInboxRequest + extends CodeMashListPaginationRequestBase + implements IReturn + { + public unreadOnly: boolean; + public channel?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'GetNotificationInboxRequest'; + } + public getMethod() { + return 'GET'; + } + public createResponse() { + return new GetNotificationInboxResponse(); + } + } + + // @Route("/{version}/notifications/inbox/unread-count", "GET") + export class GetUnreadCountRequest + extends CodeMashRequestBase + implements IReturn + { + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'GetUnreadCountRequest'; + } + public getMethod() { + return 'GET'; + } + public createResponse() { + return new GetUnreadCountResponse(); + } + } + + // @Route("/{version}/notifications/inbox/{notificationId}/read", "POST") + export class MarkNotificationReadRequest + extends CodeMashRequestBase + implements IReturn + { + public notificationId: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'MarkNotificationReadRequest'; + } + public getMethod() { + return 'POST'; + } + public createResponse() { + return new MarkNotificationReadResponse(); + } + } + + // @Route("/{version}/notifications/inbox/read-all", "POST") + export class MarkAllNotificationsReadRequest + extends CodeMashRequestBase + implements IReturn + { + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'MarkAllNotificationsReadRequest'; + } + public getMethod() { + return 'POST'; + } + public createResponse() { + return new MarkAllNotificationsReadResponse(); + } + } + /** @description Sign In */ // @Route("/auth", "GET,POST") // @Route("/auth/{provider}", "GET,POST") diff --git a/src/types/hub2.contract.json b/src/types/hub2.contract.json index 95bdf14..387ce4e 100644 --- a/src/types/hub2.contract.json +++ b/src/types/hub2.contract.json @@ -1207,6 +1207,10 @@ "requiresAuth": true, "returnsBytes": false }, + "GetNotificationInboxRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "GetPasskeySettings": { "requiresAuth": true, "returnsBytes": false @@ -1459,10 +1463,18 @@ "requiresAuth": true, "returnsBytes": false }, + "GetTriggerDefinition": { + "requiresAuth": true, + "returnsBytes": false + }, "GetTriggersNeedingAttention": { "requiresAuth": true, "returnsBytes": false }, + "GetUnreadCountRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "GetWebhookIntegration": { "requiresAuth": true, "returnsBytes": false @@ -1539,10 +1551,18 @@ "requiresAuth": true, "returnsBytes": false }, + "MarkAllNotificationsReadRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "MarkNeedsYouDoneRequest": { "requiresAuth": true, "returnsBytes": false }, + "MarkNotificationReadRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "McpRequest": { "requiresAuth": true, "returnsBytes": false @@ -2071,6 +2091,10 @@ "requiresAuth": true, "returnsBytes": false }, + "TestTriggerPreExecuteCode": { + "requiresAuth": true, + "returnsBytes": false + }, "UnArchiveEmailTemplateRequest": { "requiresAuth": true, "returnsBytes": false @@ -2095,6 +2119,10 @@ "requiresAuth": true, "returnsBytes": false }, + "UpdateAiServiceUserRequest": { + "requiresAuth": true, + "returnsBytes": false + }, "UpdateAuthenticationSettings": { "requiresAuth": true, "returnsBytes": false diff --git a/src/types/hub2.dtos.ts b/src/types/hub2.dtos.ts index f696c64..be3529c 100644 --- a/src/types/hub2.dtos.ts +++ b/src/types/hub2.dtos.ts @@ -1,6 +1,6 @@ // @ts-nocheck /* Options: -Date: 2026-10-14 16:40:18 +Date: 2026-10-19 10:55:03 Version: 10.20 Tip: To override a DTO option, remove "//" prefix before updating BaseUrl: http://localhost:5001 @@ -357,6 +357,9 @@ export module CodeMashHub2 { OnInserted = 'OnInserted', OnDeleted = 'OnDeleted', OnUpdated = 'OnUpdated', + OnInsertedMany = 'OnInsertedMany', + OnUpdatedMany = 'OnUpdatedMany', + OnDeletedMany = 'OnDeletedMany', } export class SchemaTriggerRequest extends SaveTriggerRequest { @@ -391,6 +394,7 @@ export module CodeMashHub2 { OnOrderCreated = 'OnOrderCreated', OnOrderPaid = 'OnOrderPaid', OnWebhookCallReceived = 'OnWebhookCallReceived', + OnOrderPaymentChanged = 'OnOrderPaymentChanged', } export class PaymentTriggerRequest extends SaveTriggerRequest { @@ -973,6 +977,7 @@ export module CodeMashHub2 { CheckOutCom = 'CheckOutCom', Mollie = 'Mollie', Worldpay = 'Worldpay', + Montonio = 'Montonio', } export class PaymentIntegrationRequest { @@ -1099,6 +1104,104 @@ export module CodeMashHub2 { } } + export class MontonioPaymentIntegrationRequest extends PaymentIntegrationRequest { + public provider: PaymentGatewayPlatform; + public accessKey: string; + public secretKey: string; + public isSandbox: boolean; + public locale?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class ShopifyPaymentIntegrationRequest extends PaymentIntegrationRequest { + public provider: PaymentGatewayPlatform; + public shopDomain: string; + public webhookSecret: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class WooCommercePaymentIntegrationRequest extends PaymentIntegrationRequest { + public provider: PaymentGatewayPlatform; + public storeUrl: string; + public webhookSecret: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class MagentoPaymentIntegrationRequest extends PaymentIntegrationRequest { + public provider: PaymentGatewayPlatform; + public storeUrl: string; + public webhookSecret: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class BraintreePaymentIntegrationRequest extends PaymentIntegrationRequest { + public provider: PaymentGatewayPlatform; + public merchantId: string; + public environment: string; + public privateKey: string; + public webhookSecret?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class AuthorizeNetPaymentIntegrationRequest extends PaymentIntegrationRequest { + public provider: PaymentGatewayPlatform; + public merchantLoginId: string; + public environment: string; + public transactionKey: string; + public webhookSignatureKey?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class CheckOutComPaymentIntegrationRequest extends PaymentIntegrationRequest { + public provider: PaymentGatewayPlatform; + public merchantAccount: string; + public environment: string; + public secretKey: string; + public webhookSecret?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class WorldpayPaymentIntegrationRequest extends PaymentIntegrationRequest { + public provider: PaymentGatewayPlatform; + public merchantCode: string; + public environment: string; + public apiKey: string; + public webhookSecret?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + // @DataContract export enum PushProvider { AppleApns = 'AppleApns', @@ -2901,6 +3004,180 @@ export module CodeMashHub2 { } } + export class MontonioPaymentIntegrationDto extends PaymentsIntegrationDto { + public accessKey: string; + public isSandbox: boolean; + public locale?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + // @DataContract + export class PaymentOrderLineDto { + // @DataMember + public sku: string; + + // @DataMember + public title: string; + + // @DataMember + public quantity: number; + + // @DataMember + public unitAmountMinor: number; + + // @DataMember + public amountMinor: number; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + + // @DataContract + export class PaymentOrderCheckoutDto { + // @DataMember + public attempt: number; + + // @DataMember + public integrationId: string; + + // @DataMember + public provider: string; + + // @DataMember + public providerOrderId: string; + + // @DataMember + public providerReference: string; + + // @DataMember + public paymentUrl: string; + + // @DataMember + public method: string; + + // @DataMember + public startedOnUtc: string; + + // @DataMember + public expiresAtUtc: string; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + + // @DataContract + export class PaymentOrderDto { + // @DataMember + public id: string; + + // @DataMember + public env: string; + + // @DataMember + public buyerUserId: string; + + // @DataMember + public merchantReference: string; + + // @DataMember + public currency: string; + + // @DataMember + public priceVersion: string; + + // @DataMember + public lines: PaymentOrderLineDto[] = []; + + // @DataMember + public totalMinor: number; + + // @DataMember + public metadata: { [index: string]: string } = {}; + + // @DataMember + public status: string; + + // @DataMember + public createdOnUtc: string; + + // @DataMember + public updatedOnUtc: string; + + // @DataMember + public checkouts: PaymentOrderCheckoutDto[] = []; + + // @DataMember + public settledMinor?: number; + + // @DataMember + public refundedMinor: number; + + // @DataMember + public paidOnUtc?: string; + + // @DataMember + public providerPaymentId?: string; + + // @DataMember + public mismatches: string[] = []; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + + export class PaymentEventDto { + public integrationId: string; + public provider: string; + public providerEventId: string; + public orderId?: string; + public eventName: string; + public status: string; + public amountMinor?: number; + public currency?: string; + public externalIds?: { [index: string]: string }; + public occurredOn?: string; + public receivedOn: string; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + + // @DataContract + export class PaymentOrderEventDto { + // @DataMember + public orderEventId: string; + + // @DataMember + public event: string; + + // @DataMember + public from: string; + + // @DataMember + public to: string; + + // @DataMember + public reason: string; + + // @DataMember + public order: PaymentOrderDto; + + // @DataMember + public payment: PaymentEventDto; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + export class MembershipIntegrationDto extends IntegrationDto { public provider: MembershipProvider; @@ -3628,6 +3905,12 @@ export module CodeMashHub2 { // @DataMember public updatedAtUnix?: number; + // @DataMember + public endsAtUnix?: number; + + // @DataMember + public hasEnded: boolean; + // @DataMember public isFailing: boolean; @@ -4649,6 +4932,25 @@ export module CodeMashHub2 { } } + // @DataContract + export class DatabaseRecordsBatchDto { + // @DataMember + public ids: string[] = []; + + // @DataMember + public count: number; + + // @DataMember + public truncated: boolean; + + // @DataMember + public documents?: { [index: string]: Object }[]; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + // @DataContract export class DatabaseRecordChangeDto { // @DataMember @@ -4678,6 +4980,9 @@ export module CodeMashHub2 { // @DataMember public newRef?: DocumentRefDto; + // @DataMember + public records?: DatabaseRecordsBatchDto; + public constructor(init?: Partial) { (Object as any).assign(this, init); } @@ -4726,6 +5031,9 @@ export module CodeMashHub2 { // @DataMember public providerEventId: string; + // @DataMember + public orderId?: string; + // @DataMember public eventName: string; @@ -4814,6 +5122,9 @@ export module CodeMashHub2 { // @DataMember public payment?: PaymentFactDto; + // @DataMember + public order?: PaymentOrderEventDto; + // @DataMember public ai?: AiUsageEventDto; @@ -5619,6 +5930,15 @@ export module CodeMashHub2 { } } + export class SchemaUniqueIndexDto { + // @DataMember + public fields: string[] = []; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + export class SchemaSettingsDto { // @DataMember public softDelete: boolean; @@ -5629,6 +5949,9 @@ export module CodeMashHub2 { // @DataMember public description?: string; + // @DataMember + public uniqueIndexes?: SchemaUniqueIndexDto[]; + public constructor(init?: Partial) { (Object as any).assign(this, init); } @@ -5686,6 +6009,9 @@ export module CodeMashHub2 { // @DataMember public defaultSort?: SchemaListSortDto; + // @DataMember + public filters: string[] = []; + public constructor(init?: Partial) { (Object as any).assign(this, init); } @@ -5904,6 +6230,8 @@ export module CodeMashHub2 { } } + export interface INoSessionCookies {} + export enum EmbeddingProvider { Voyage = 'Voyage', OpenAI = 'OpenAI', @@ -6096,6 +6424,17 @@ export module CodeMashHub2 { } } + export class TriggerActivationTestProblem { + public code: string; + public message: string; + public line: number; + public column: number; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + // @DataContract export class AccountOwnerDto { // @DataMember @@ -6279,6 +6618,25 @@ export module CodeMashHub2 { } } + // @DataContract + export class PromotionTermChangeDto { + // @DataMember + public taxonomyId: string; + + // @DataMember + public termId: string; + + // @DataMember + public slug?: string; + + // @DataMember + public change: string; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + // @DataContract export class PromotionResultDto { // @DataMember @@ -6299,6 +6657,12 @@ export module CodeMashHub2 { // @DataMember public blockers: PromotionBlockerDto[] = []; + // @DataMember + public termChanges: PromotionTermChangeDto[] = []; + + // @DataMember + public warnings: PromotionBlockerDto[] = []; + // @DataMember public fromVersion?: number; @@ -7050,6 +7414,12 @@ export module CodeMashHub2 { // @DataMember public additionalRegions?: ProjectRegionDto[]; + // @DataMember + public modulesCount: number; + + // @DataMember + public usersCount: number; + public constructor(init?: Partial) { (Object as any).assign(this, init); } @@ -8181,6 +8551,9 @@ export module CodeMashHub2 { // @DataMember public description?: string; + // @DataMember + public env?: string; + // @DataMember public termsMetaDataSchema?: DataSchemaDto; @@ -8232,6 +8605,9 @@ export module CodeMashHub2 { // @DataMember public dependencyRefs?: TaxonomyRef[]; + // @DataMember + public env?: string; + public constructor(init?: Partial) { (Object as any).assign(this, init); } @@ -8361,6 +8737,9 @@ export module CodeMashHub2 { // @DataMember public meta?: Object; + // @DataMember + public origin?: string; + public constructor(init?: Partial) { (Object as any).assign(this, init); } @@ -9412,6 +9791,9 @@ export module CodeMashHub2 { // @DataMember public content?: EmailMessageContentDto; + // @DataMember + public transactionalKind?: string; + public constructor(init?: Partial) { super(init); (Object as any).assign(this, init); @@ -10187,6 +10569,23 @@ export module CodeMashHub2 { } } + export class InAppNotificationDto { + public id: string; + public projectId: string; + public userId: string; + public channel: string; + public eventName: string; + public payloadJson?: string; + public isRead: boolean; + public seenAtUtc?: string; + public readAtUtc?: string; + public createdAtUtc: string; + + public constructor(init?: Partial) { + (Object as any).assign(this, init); + } + } + export class PaymentsWebhookLogEntry { public integrationId: string; public source: string; @@ -10949,6 +11348,12 @@ export module CodeMashHub2 { // @DataMember public lastFailedAtUnix?: number; + // @DataMember + public endsAtUnix?: number; + + // @DataMember + public hasEnded: boolean; + // @DataMember public viewId: string; @@ -11777,6 +12182,17 @@ export module CodeMashHub2 { } } + // @DataContract + export class GetTriggerDefinitionResponse extends ResponseBase { + // @DataMember + public trigger?: SaveTriggerRequest; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + // @DataContract export class GetTriggersNeedingAttentionResponse extends ResponseBase { // @DataMember @@ -11788,6 +12204,23 @@ export module CodeMashHub2 { } } + // @DataContract + export class TestTriggerPreExecuteCodeResponse extends ResponseBase { + // @DataMember + public outcome: string; + + // @DataMember + public triggerRuns: boolean; + + // @DataMember + public problems: TriggerActivationTestProblem[] = []; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + export class GetAccountProfileResponse extends ResponseBase { public item?: AccountOwnerDto; @@ -13638,6 +14071,38 @@ export module CodeMashHub2 { } } + export class GetNotificationInboxResponse extends ResponseBase { + public list?: PaginatedResponse; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class GetUnreadCountResponse extends ResponseBase { + public count: number; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class MarkNotificationReadResponse extends ResponseBase { + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + + export class MarkAllNotificationsReadResponse extends ResponseBase { + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + } + export class GetPaymentsWebhookLogResponse extends ResponseBase { public list?: IReadOnlyList; @@ -15222,6 +15687,14 @@ export module CodeMashHub2 { public typegen_53_StripePaymentIntegrationRequest?: StripePaymentIntegrationRequest; public typegen_54_AppleInAppPaymentIntegrationRequest?: AppleInAppPaymentIntegrationRequest; public typegen_55_GoogleInAppPaymentIntegrationRequest?: GoogleInAppPaymentIntegrationRequest; + public typegen_285_MontonioPaymentIntegrationRequest?: MontonioPaymentIntegrationRequest; + public typegen_287_ShopifyPaymentIntegrationRequest?: ShopifyPaymentIntegrationRequest; + public typegen_288_WooCommercePaymentIntegrationRequest?: WooCommercePaymentIntegrationRequest; + public typegen_289_MagentoPaymentIntegrationRequest?: MagentoPaymentIntegrationRequest; + public typegen_290_BraintreePaymentIntegrationRequest?: BraintreePaymentIntegrationRequest; + public typegen_291_AuthorizeNetPaymentIntegrationRequest?: AuthorizeNetPaymentIntegrationRequest; + public typegen_292_CheckOutComPaymentIntegrationRequest?: CheckOutComPaymentIntegrationRequest; + public typegen_293_WorldpayPaymentIntegrationRequest?: WorldpayPaymentIntegrationRequest; public typegen_56_EdgeWebPushIntegrationRequest?: EdgeWebPushIntegrationRequest; public typegen_57_ChromePluginPushIntegrationRequest?: ChromePluginPushIntegrationRequest; public typegen_58_SafariPushIntegrationRequest?: SafariPushIntegrationRequest; @@ -15305,6 +15778,8 @@ export module CodeMashHub2 { public typegen_188_AuthorizeNetPaymentIntegrationDto?: AuthorizeNetPaymentIntegrationDto; public typegen_189_CheckOutComPaymentIntegrationDto?: CheckOutComPaymentIntegrationDto; public typegen_190_WorldpayPaymentIntegrationDto?: WorldpayPaymentIntegrationDto; + public typegen_286_MontonioPaymentIntegrationDto?: MontonioPaymentIntegrationDto; + public typegen_294_PaymentOrderEventDto?: PaymentOrderEventDto; public typegen_128_AppleSignInMembershipIntegrationDto?: AppleSignInMembershipIntegrationDto; public typegen_129_GitHubMembershipIntegrationDto?: GitHubMembershipIntegrationDto; public typegen_130_GoogleMembershipIntegrationDto?: GoogleMembershipIntegrationDto; @@ -15557,6 +16032,44 @@ export module CodeMashHub2 { } } + /** @description Get one trigger in the shape its save takes (read → change → save) */ + // @Route("/{version}/triggers/{id}/definition", "GET") + // @Api(Description="Get one trigger in the shape its save takes (read → change → save)") + // @DataContract + export class GetTriggerDefinition + extends CodeMashRequestBase + implements IReturn + { + /** @description The trigger id (trg_…). */ + // @DataMember + // @ApiMember(Description="The trigger id (trg_…).", IsRequired=true) + public id: string; + + /** @description Which module the trigger belongs to: Schema, Membership, Files, Payments or Ai. */ + // @DataMember + // @ApiMember(Description="Which module the trigger belongs to: Schema, Membership, Files, Payments or Ai.", IsRequired=true) + public triggerType: TriggerType; + + /** @description The collection (schema) id the trigger watches. Required for Schema triggers. */ + // @DataMember + // @ApiMember(Description="The collection (schema) id the trigger watches. Required for Schema triggers.") + public schemaId?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'GetTriggerDefinition'; + } + public getMethod() { + return 'GET'; + } + public createResponse() { + return new GetTriggerDefinitionResponse(); + } + } + /** @description Get triggers that need attention */ // @Route("/{version}/triggers/attention", "GET") // @Api(Description="Get triggers that need attention") @@ -15585,6 +16098,59 @@ export module CodeMashHub2 { } } + /** @description Test a trigger's pre-action code against a sample record */ + // @Route("/{version}/triggers/pre-execute-code/test", "POST") + // @Api(Description="Test a trigger's pre-action code against a sample record") + // @DataContract + export class TestTriggerPreExecuteCode + extends CodeMashRequestBase + implements IReturn + { + /** @description The code to test, as saved in preExecuteCode: C# statements ending in 'return true;' or 'return false;', e.g. 'return New.status == "published";'. */ + // @DataMember + // @ApiMember(Description="The code to test, as saved in preExecuteCode: C# statements ending in 'return true;' or 'return false;', e.g. 'return New.status == \"published\";'.", IsRequired=true) + public code: string; + + /** @description Which kind of trigger the code is for: Schema, Membership, Files, Payments or Ai. */ + // @DataMember + // @ApiMember(Description="Which kind of trigger the code is for: Schema, Membership, Files, Payments or Ai.", IsRequired=true) + public triggerType: TriggerType; + + /** @description The sample record AFTER the change (the code's New), as a JSON object. Omit = empty (a delete). */ + // @DataMember + // @ApiMember(Description="The sample record AFTER the change (the code's New), as a JSON object. Omit = empty (a delete).") + public newRecord?: string; + + /** @description The sample record BEFORE the change (the code's Old), as a JSON object. Omit = empty (an insert). */ + // @DataMember + // @ApiMember(Description="The sample record BEFORE the change (the code's Old), as a JSON object. Omit = empty (an insert).") + public oldRecord?: string; + + /** @description What the code sees as Event.Kind: Inserted, Updated, Deleted or Other. Omit = Updated when both records are given, Inserted when only newRecord, Deleted when only oldRecord. */ + // @DataMember + // @ApiMember(Description="What the code sees as Event.Kind: Inserted, Updated, Deleted or Other. Omit = Updated when both records are given, Inserted when only newRecord, Deleted when only oldRecord.") + public eventKind?: string; + + /** @description What the code sees as Event.Name, e.g. database.record.updated. Optional. */ + // @DataMember + // @ApiMember(Description="What the code sees as Event.Name, e.g. database.record.updated. Optional.") + public eventName?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'TestTriggerPreExecuteCode'; + } + public getMethod() { + return 'POST'; + } + public createResponse() { + return new TestTriggerPreExecuteCodeResponse(); + } + } + // @Route("/{version}/account/profile", "GET") export class GetAccountProfile extends RequestBase implements IReturn { public constructor(init?: Partial) { @@ -18149,6 +18715,31 @@ export module CodeMashHub2 { } } + // @Route("/{version}/account/ai/service-users/{Id}", "PUT") + export class UpdateAiServiceUserRequest extends RequestBase implements IReturn { + /** @description The service user id (aisu_…). */ + // @ApiMember(Description="The service user id (aisu_…).", IsRequired=true) + public id: string; + + /** @description The new name, unique in the account, e.g. "Claude Code on my laptop". */ + // @ApiMember(Description="The new name, unique in the account, e.g. \"Claude Code on my laptop\".", IsRequired=true) + public name: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'UpdateAiServiceUserRequest'; + } + public getMethod() { + return 'PUT'; + } + public createResponse() { + return new EmptyResponse(); + } + } + // @Route("/{version}/account/ai/service-users/{Id}", "DELETE") export class DeleteAiServiceUserRequest extends RequestBase implements IReturn { /** @description The service user id (aisu_…). */ @@ -19534,9 +20125,9 @@ export module CodeMashHub2 { extends CodeMashRequestBase implements IReturn { - /** @description Taxonomy id that owns the term, from get_database_taxonomies. */ + /** @description Taxonomy id that owns the term, from get_database_taxonomies — or the taxonomy name (slug or title). */ // @DataMember - // @ApiMember(Description="Taxonomy id that owns the term, from get_database_taxonomies.", IsRequired=true) + // @ApiMember(Description="Taxonomy id that owns the term, from get_database_taxonomies — or the taxonomy name (slug or title).", IsRequired=true) public taxonomyId: string; /** @description Term id to delete, from get_database_taxonomy_term_tree. */ @@ -19572,9 +20163,9 @@ export module CodeMashHub2 { extends CodeMashRequestBase implements IReturn { - /** @description Taxonomy id whose terms to delete, from get_database_taxonomies. */ + /** @description Taxonomy id whose terms to delete, from get_database_taxonomies — or the taxonomy name (slug or title). */ // @DataMember - // @ApiMember(Description="Taxonomy id whose terms to delete, from get_database_taxonomies.", IsRequired=true) + // @ApiMember(Description="Taxonomy id whose terms to delete, from get_database_taxonomies — or the taxonomy name (slug or title).", IsRequired=true) public taxonomyId: string; /** @description Optional database integration id. When omitted, the project's default database integration is used. */ @@ -19615,9 +20206,9 @@ export module CodeMashHub2 { // @ApiMember(Description="Set true to keep name / description as stored even when Accept-Language is sent (editors). The full translations are always in names / descriptions.") public allLanguages: boolean; - /** @description Taxonomy id from get_database_taxonomies. */ + /** @description Taxonomy id from get_database_taxonomies — or the taxonomy name (slug or title). */ // @DataMember - // @ApiMember(Description="Taxonomy id from get_database_taxonomies.", IsRequired=true) + // @ApiMember(Description="Taxonomy id from get_database_taxonomies — or the taxonomy name (slug or title).", IsRequired=true) public taxonomyId: string; /** @description Term id from get_database_taxonomy_term_tree. */ @@ -19658,9 +20249,9 @@ export module CodeMashHub2 { // @ApiMember(Description="Set true to keep name / description as stored even when Accept-Language is sent (editors). The full translations are always in names / descriptions.") public allLanguages: boolean; - /** @description Root taxonomy slug/name (from get_database_taxonomies). Its terms are the roots; child-taxonomy terms nest under them. */ + /** @description Root taxonomy slug/name or id, txn_… (from get_database_taxonomies). Its terms are the roots; child-taxonomy terms nest under them. */ // @DataMember - // @ApiMember(Description="Root taxonomy slug/name (from get_database_taxonomies). Its terms are the roots; child-taxonomy terms nest under them.", IsRequired=true) + // @ApiMember(Description="Root taxonomy slug/name or id, txn_… (from get_database_taxonomies). Its terms are the roots; child-taxonomy terms nest under them.", IsRequired=true) public taxonomyName: string; /** @description Optional database integration id. When omitted, the project's default database integration is used. */ @@ -19696,9 +20287,9 @@ export module CodeMashHub2 { // @ApiMember(Description="Set true to keep name / description as stored even when Accept-Language is sent (editors). The full translations are always in names / descriptions.") public allLanguages: boolean; - /** @description Taxonomy slug/name to fetch the term tree for, from get_database_taxonomies. */ + /** @description Taxonomy slug/name (or its id, txn_…) to fetch the term tree for, from get_database_taxonomies. */ // @DataMember - // @ApiMember(Description="Taxonomy slug/name to fetch the term tree for, from get_database_taxonomies.", IsRequired=true) + // @ApiMember(Description="Taxonomy slug/name (or its id, txn_…) to fetch the term tree for, from get_database_taxonomies.", IsRequired=true) public taxonomyName: string; /** @description Optional term id to root the returned tree at a sub-tree instead of the whole taxonomy. */ @@ -19739,9 +20330,9 @@ export module CodeMashHub2 { extends CodeMashRequestBase implements IReturn { - /** @description Taxonomy id to insert the term into, from get_database_taxonomies. */ + /** @description Taxonomy id to insert the term into, from get_database_taxonomies — or the taxonomy name (slug or title). */ // @DataMember - // @ApiMember(Description="Taxonomy id to insert the term into, from get_database_taxonomies.", IsRequired=true) + // @ApiMember(Description="Taxonomy id to insert the term into, from get_database_taxonomies — or the taxonomy name (slug or title).", IsRequired=true) public taxonomyId: string; /** @description Optional database integration id. When omitted, the project's default database integration is used. */ @@ -19777,9 +20368,9 @@ export module CodeMashHub2 { extends CodeMashRequestBase implements IReturn { - /** @description Taxonomy id that owns the term, from get_database_taxonomies. */ + /** @description Taxonomy id that owns the term, from get_database_taxonomies — or the taxonomy name (slug or title). */ // @DataMember - // @ApiMember(Description="Taxonomy id that owns the term, from get_database_taxonomies.", IsRequired=true) + // @ApiMember(Description="Taxonomy id that owns the term, from get_database_taxonomies — or the taxonomy name (slug or title).", IsRequired=true) public taxonomyId: string; /** @description Term id to update, from get_database_taxonomy_term_tree. */ @@ -25417,9 +26008,9 @@ export module CodeMashHub2 { // @ApiMember(Description="The push integration id to test. Get it from get_push_integrations.", IsRequired=true) public integrationId: string; - /** @description The device token the test is sent to — a device registered for the person receiving the test (get it from get_push_devices). Required for every real provider (only the Fake integration tests without one). For a web device it is the browser subscription JSON. Requires DeliveryFamily. */ + /** @description The device token the test is sent to — a device registered for the person receiving the test (get it from get_push_devices). Required for every real provider (only the Sandbox integration tests without one). For a web device it is the browser subscription JSON. Requires DeliveryFamily. */ // @DataMember - // @ApiMember(Description="The device token the test is sent to — a device registered for the person receiving the test (get it from get_push_devices). Required for every real provider (only the Fake integration tests without one). For a web device it is the browser subscription JSON. Requires DeliveryFamily.") + // @ApiMember(Description="The device token the test is sent to — a device registered for the person receiving the test (get it from get_push_devices). Required for every real provider (only the Sandbox integration tests without one). For a web device it is the browser subscription JSON. Requires DeliveryFamily.") public testToken?: string; /** @description The delivery family of TestToken: ios, android, chrome (any browser web push) or safari. Requires TestToken. */ @@ -25955,6 +26546,91 @@ export module CodeMashHub2 { } } + // @Route("/{version}/notifications/inbox", "GET") + export class GetNotificationInboxRequest + extends CodeMashListPaginationRequestBase + implements IReturn + { + public unreadOnly: boolean; + public channel?: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'GetNotificationInboxRequest'; + } + public getMethod() { + return 'GET'; + } + public createResponse() { + return new GetNotificationInboxResponse(); + } + } + + // @Route("/{version}/notifications/inbox/unread-count", "GET") + export class GetUnreadCountRequest + extends CodeMashRequestBase + implements IReturn + { + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'GetUnreadCountRequest'; + } + public getMethod() { + return 'GET'; + } + public createResponse() { + return new GetUnreadCountResponse(); + } + } + + // @Route("/{version}/notifications/inbox/{notificationId}/read", "POST") + export class MarkNotificationReadRequest + extends CodeMashRequestBase + implements IReturn + { + public notificationId: string; + + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'MarkNotificationReadRequest'; + } + public getMethod() { + return 'POST'; + } + public createResponse() { + return new MarkNotificationReadResponse(); + } + } + + // @Route("/{version}/notifications/inbox/read-all", "POST") + export class MarkAllNotificationsReadRequest + extends CodeMashRequestBase + implements IReturn + { + public constructor(init?: Partial) { + super(init); + (Object as any).assign(this, init); + } + public getTypeName() { + return 'MarkAllNotificationsReadRequest'; + } + public getMethod() { + return 'POST'; + } + public createResponse() { + return new MarkAllNotificationsReadResponse(); + } + } + /** @description Disable payments service */ // @Route("/{version}/payments/disable", "PUT") // @Api(Description="Disable payments service") @@ -27547,7 +28223,7 @@ export module CodeMashHub2 { // @Api(Description="Starts a browser sign-in for the Norbix CLI (OAuth device authorization, RFC 8628): a device code for polling and a user code the person confirms on the dashboard.") export class AuthDeviceStartRequest extends RequestBase - implements IReturn + implements IReturn, INoSessionCookies { /** @description Who asks, shown on the dashboard, e.g. "norbix-cli". 1–80 printable characters. */ // @ApiMember(Description="Who asks, shown on the dashboard, e.g. \"norbix-cli\". 1–80 printable characters.", IsRequired=true) @@ -27581,7 +28257,7 @@ export module CodeMashHub2 { // @Api(Description="Polls a CLI browser sign-in (RFC 8628). Always HTTP 200: an error name (authorization_pending, slow_down, access_denied, expired_token, invalid_grant, invalid_request) or the tokens, once.") export class AuthDeviceTokenRequest extends RequestBase - implements IReturn + implements IReturn, INoSessionCookies { /** @description The deviceCode from POST /auth/device/start. */ // @ApiMember(Description="The deviceCode from POST /auth/device/start.", IsRequired=true) @@ -27670,7 +28346,7 @@ export module CodeMashHub2 { // @Route("/.well-known/oauth-protected-resource", "GET") // @Route("/.well-known/oauth-protected-resource/{Path*}", "GET") // @Api(Description="OAuth protected-resource metadata (RFC 9728) for the Hub MCP endpoint.") - export class OAuthProtectedResourceMetadataRequest implements IReturn { + export class OAuthProtectedResourceMetadataRequest implements IReturn, INoSessionCookies { public path?: string; public constructor(init?: Partial) { @@ -27691,7 +28367,9 @@ export module CodeMashHub2 { // @Route("/.well-known/oauth-authorization-server", "GET") // @Route("/.well-known/oauth-authorization-server/{Path*}", "GET") // @Api(Description="OAuth authorization-server metadata (RFC 8414).") - export class OAuthAuthorizationServerMetadataRequest implements IReturn { + export class OAuthAuthorizationServerMetadataRequest + implements IReturn, INoSessionCookies + { public path?: string; public constructor(init?: Partial) { @@ -27711,7 +28389,7 @@ export module CodeMashHub2 { /** @description Registers an MCP client for OAuth (RFC 7591). Public clients only: PKCE, no secret. */ // @Route("/{version}/oauth/register", "POST") // @Api(Description="Registers an MCP client for OAuth (RFC 7591). Public clients only: PKCE, no secret.") - export class OAuthRegisterRequest implements IReturn { + export class OAuthRegisterRequest implements IReturn, INoSessionCookies { public version?: string; public requestStream: string; @@ -27732,7 +28410,7 @@ export module CodeMashHub2 { /** @description OAuth authorization endpoint: checks the request and redirects to the dashboard consent screen. */ // @Route("/{version}/oauth/authorize", "GET") // @Api(Description="OAuth authorization endpoint: checks the request and redirects to the dashboard consent screen.") - export class OAuthAuthorizeRequest implements IReturn { + export class OAuthAuthorizeRequest implements IReturn, INoSessionCookies { public version?: string; public constructor(init?: Partial) { @@ -27873,7 +28551,7 @@ export module CodeMashHub2 { /** @description OAuth token endpoint: authorization code (PKCE S256) or refresh token → access token. */ // @Route("/{version}/oauth/token", "POST") // @Api(Description="OAuth token endpoint: authorization code (PKCE S256) or refresh token → access token.") - export class OAuthTokenRequest implements IReturn { + export class OAuthTokenRequest implements IReturn, INoSessionCookies { public version?: string; public constructor(init?: Partial) { @@ -27893,7 +28571,7 @@ export module CodeMashHub2 { /** @description OAuth token revocation (RFC 7009): revokes the grant behind a refresh or access token. */ // @Route("/{version}/oauth/revoke", "POST") // @Api(Description="OAuth token revocation (RFC 7009): revokes the grant behind a refresh or access token.") - export class OAuthRevokeRequest implements IReturn { + export class OAuthRevokeRequest implements IReturn, INoSessionCookies { public version?: string; public constructor(init?: Partial) { @@ -28788,6 +29466,9 @@ export module CodeMashHub2 { // @DataMember public stopOnError: boolean; + // @DataMember + public endsAtUnix?: number; + // @DataMember public task: SchedulerTaskRequest; @@ -28925,8 +29606,8 @@ export module CodeMashHub2 { // @Route("/{version}/membership/users/{contactId}", "GET") // @Api(Description="Get a contact") export class GetContact extends CodeMashRequestBase implements IReturn { - /** @description The contact id (ct_…) to fetch. Get it from get_all_contacts. */ - // @ApiMember(Description="The contact id (ct_…) to fetch. Get it from get_all_contacts.", IsRequired=true) + /** @description The contact id (ct_…) to fetch, or a login id (usr_…) — then the contact that owns that login is returned. Get it from get_all_contacts. */ + // @ApiMember(Description="The contact id (ct_…) to fetch, or a login id (usr_…) — then the contact that owns that login is returned. Get it from get_all_contacts.", IsRequired=true) public contactId: string; public constructor(init?: Partial) { diff --git a/tests/api/notifications.test.ts b/tests/api/notifications.test.ts index 19c048d..efa0f71 100644 --- a/tests/api/notifications.test.ts +++ b/tests/api/notifications.test.ts @@ -7,7 +7,7 @@ import { createMockFetch, expectedUrl, makeClient, stubRequestForPath } from '.. * Auto-generated. Do not edit by hand — run `npm run generate-endpoints` * to refresh this file from the DTO definitions. * - * Tests for api.notifications (7 endpoints). + * Tests for api.notifications (11 endpoints). * * Each method is asserted against: * - presence on the module (smoke check) @@ -17,7 +17,7 @@ import { createMockFetch, expectedUrl, makeClient, stubRequestForPath } from '.. * - account-scope guard: throws NORBIX_ACCOUNT_SCOPE_REQUIRED without accountId */ describe('api.notifications', () => { - it('module exposes 7 method(s)', () => { + it('module exposes 11 method(s)', () => { const mock = createMockFetch(); const mod = new NotificationsModule({} as never); void mod; // silence unused — we only need the type @@ -36,6 +36,10 @@ describe('api.notifications', () => { expect(typeof ns['getOwnPushMessages']).toBe('function'); expect(typeof ns['markOwnPushMessageRead']).toBe('function'); expect(typeof ns['markAllOwnPushMessagesRead']).toBe('function'); + expect(typeof ns['getNotificationInbox']).toBe('function'); + expect(typeof ns['getUnreadCount']).toBe('function'); + expect(typeof ns['markNotificationRead']).toBe('function'); + expect(typeof ns['markAllNotificationsRead']).toBe('function'); }); it('registerOwnPushDevice: POST /{version}/notifications/push/devices', async () => { @@ -198,4 +202,96 @@ describe('api.notifications', () => { expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); }); + + it('getNotificationInbox: GET /{version}/notifications/inbox', async () => { + const stub = {}; + const expected = expectedUrl({ + baseUrl: 'https://api.norbix.io', + path: '/{version}/notifications/inbox', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.api as unknown as Record< + string, + Record Promise> + > + )['notifications']!['getNotificationInbox']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('GET'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); + + it('getUnreadCount: GET /{version}/notifications/inbox/unread-count', async () => { + const stub = {}; + const expected = expectedUrl({ + baseUrl: 'https://api.norbix.io', + path: '/{version}/notifications/inbox/unread-count', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.api as unknown as Record< + string, + Record Promise> + > + )['notifications']!['getUnreadCount']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('GET'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); + + it('markNotificationRead: POST /{version}/notifications/inbox/{notificationId}/read', async () => { + const stub = stubRequestForPath('/{version}/notifications/inbox/{notificationId}/read'); + const expected = expectedUrl({ + baseUrl: 'https://api.norbix.io', + path: '/{version}/notifications/inbox/{notificationId}/read', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.api as unknown as Record< + string, + Record Promise> + > + )['notifications']!['markNotificationRead']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('POST'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); + + it('markAllNotificationsRead: POST /{version}/notifications/inbox/read-all', async () => { + const stub = {}; + const expected = expectedUrl({ + baseUrl: 'https://api.norbix.io', + path: '/{version}/notifications/inbox/read-all', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.api as unknown as Record< + string, + Record Promise> + > + )['notifications']!['markAllNotificationsRead']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('POST'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); }); diff --git a/tests/api/payments.test.ts b/tests/api/payments.test.ts new file mode 100644 index 0000000..f28ffd5 --- /dev/null +++ b/tests/api/payments.test.ts @@ -0,0 +1,129 @@ +import { describe, expect, it } from 'vitest'; + +import { PaymentsModule } from '../../src/api/payments.js'; +import { createMockFetch, expectedUrl, makeClient, stubRequestForPath } from '../_helpers.js'; + +/** + * Auto-generated. Do not edit by hand — run `npm run generate-endpoints` + * to refresh this file from the DTO definitions. + * + * Tests for api.payments (4 endpoints). + * + * Each method is asserted against: + * - presence on the module (smoke check) + * - issued HTTP verb + URL with version segment substituted and path + * tokens interpolated from a stubbed request + * - auth, project, and (when applicable) account headers + * - account-scope guard: throws NORBIX_ACCOUNT_SCOPE_REQUIRED without accountId + */ +describe('api.payments', () => { + it('module exposes 4 method(s)', () => { + const mock = createMockFetch(); + const mod = new PaymentsModule({} as never); + void mod; // silence unused — we only need the type + // Sanity check the auto-mapped surface exists on the namespaced client. + const { norbix } = makeClient(); + const ns = (norbix.api as unknown as Record)['payments'] as Record< + string, + unknown + >; + expect(ns).toBeDefined(); + void mock; + expect(typeof ns['createPaymentOrder']).toBe('function'); + expect(typeof ns['getPaymentOrder']).toBe('function'); + expect(typeof ns['getPaymentOrders']).toBe('function'); + expect(typeof ns['startPaymentCheckout']).toBe('function'); + }); + + it('createPaymentOrder: POST /{version}/payments/orders', async () => { + const stub = {}; + const expected = expectedUrl({ + baseUrl: 'https://api.norbix.io', + path: '/{version}/payments/orders', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.api as unknown as Record< + string, + Record Promise> + > + )['payments']!['createPaymentOrder']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('POST'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); + + it('getPaymentOrder: GET /{version}/payments/orders/{Id}', async () => { + const stub = stubRequestForPath('/{version}/payments/orders/{Id}'); + const expected = expectedUrl({ + baseUrl: 'https://api.norbix.io', + path: '/{version}/payments/orders/{Id}', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.api as unknown as Record< + string, + Record Promise> + > + )['payments']!['getPaymentOrder']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('GET'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); + + it('getPaymentOrders: GET /{version}/payments/orders', async () => { + const stub = {}; + const expected = expectedUrl({ + baseUrl: 'https://api.norbix.io', + path: '/{version}/payments/orders', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.api as unknown as Record< + string, + Record Promise> + > + )['payments']!['getPaymentOrders']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('GET'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); + + it('startPaymentCheckout: POST /{version}/payments/orders/{Id}/checkout', async () => { + const stub = stubRequestForPath('/{version}/payments/orders/{Id}/checkout'); + const expected = expectedUrl({ + baseUrl: 'https://api.norbix.io', + path: '/{version}/payments/orders/{Id}/checkout', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.api as unknown as Record< + string, + Record Promise> + > + )['payments']!['startPaymentCheckout']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('POST'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); +}); diff --git a/tests/hub/account.test.ts b/tests/hub/account.test.ts index dfa0ce2..505e07b 100644 --- a/tests/hub/account.test.ts +++ b/tests/hub/account.test.ts @@ -7,7 +7,7 @@ import { createMockFetch, expectedUrl, makeClient, stubRequestForPath } from '.. * Auto-generated. Do not edit by hand — run `npm run generate-endpoints` * to refresh this file from the DTO definitions. * - * Tests for hub.account (121 endpoints). + * Tests for hub.account (122 endpoints). * * Each method is asserted against: * - presence on the module (smoke check) @@ -17,7 +17,7 @@ import { createMockFetch, expectedUrl, makeClient, stubRequestForPath } from '.. * - account-scope guard: throws NORBIX_ACCOUNT_SCOPE_REQUIRED without accountId */ describe('hub.account', () => { - it('module exposes 121 method(s)', () => { + it('module exposes 122 method(s)', () => { const mock = createMockFetch(); const mod = new AccountModule({} as never); void mod; // silence unused — we only need the type @@ -126,6 +126,7 @@ describe('hub.account', () => { expect(typeof ns['listAiServiceUsers']).toBe('function'); expect(typeof ns['rotateAiServiceUserKey']).toBe('function'); expect(typeof ns['revokeAiServiceUserKey']).toBe('function'); + expect(typeof ns['updateAiServiceUser']).toBe('function'); expect(typeof ns['deleteAiServiceUser']).toBe('function'); expect(typeof ns['getAgentOnboarding']).toBe('function'); expect(typeof ns['getAiContext']).toBe('function'); @@ -2413,6 +2414,29 @@ describe('hub.account', () => { expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); }); + it('updateAiServiceUser: PUT /{version}/account/ai/service-users/{Id}', async () => { + const stub = stubRequestForPath('/{version}/account/ai/service-users/{Id}'); + const expected = expectedUrl({ + baseUrl: 'https://hub.norbix.io', + path: '/{version}/account/ai/service-users/{Id}', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.hub as unknown as Record< + string, + Record Promise> + > + )['account']!['updateAiServiceUser']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('PUT'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); + it('deleteAiServiceUser: DELETE /{version}/account/ai/service-users/{Id}', async () => { const stub = stubRequestForPath('/{version}/account/ai/service-users/{Id}'); const expected = expectedUrl({ diff --git a/tests/hub/notifications.test.ts b/tests/hub/notifications.test.ts index f91bdc5..4d95b90 100644 --- a/tests/hub/notifications.test.ts +++ b/tests/hub/notifications.test.ts @@ -7,7 +7,7 @@ import { createMockFetch, expectedUrl, makeClient, stubRequestForPath } from '.. * Auto-generated. Do not edit by hand — run `npm run generate-endpoints` * to refresh this file from the DTO definitions. * - * Tests for hub.notifications (124 endpoints). + * Tests for hub.notifications (128 endpoints). * * Each method is asserted against: * - presence on the module (smoke check) @@ -17,7 +17,7 @@ import { createMockFetch, expectedUrl, makeClient, stubRequestForPath } from '.. * - account-scope guard: throws NORBIX_ACCOUNT_SCOPE_REQUIRED without accountId */ describe('hub.notifications', () => { - it('module exposes 124 method(s)', () => { + it('module exposes 128 method(s)', () => { const mock = createMockFetch(); const mod = new NotificationsModule({} as never); void mod; // silence unused — we only need the type @@ -149,6 +149,10 @@ describe('hub.notifications', () => { expect(typeof ns['previewPushNotification']).toBe('function'); expect(typeof ns['stopPushCampaign']).toBe('function'); expect(typeof ns['getPushCampaignMessages']).toBe('function'); + expect(typeof ns['getNotificationInbox']).toBe('function'); + expect(typeof ns['getUnreadCount']).toBe('function'); + expect(typeof ns['markNotificationRead']).toBe('function'); + expect(typeof ns['markAllNotificationsRead']).toBe('function'); expect(typeof ns['getUserNotificationPreferences']).toBe('function'); expect(typeof ns['updateUserNotificationsPreferences']).toBe('function'); expect(typeof ns['grantContactConsent']).toBe('function'); @@ -2931,6 +2935,98 @@ describe('hub.notifications', () => { expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); }); + it('getNotificationInbox: GET /{version}/notifications/inbox', async () => { + const stub = {}; + const expected = expectedUrl({ + baseUrl: 'https://hub.norbix.io', + path: '/{version}/notifications/inbox', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.hub as unknown as Record< + string, + Record Promise> + > + )['notifications']!['getNotificationInbox']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('GET'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); + + it('getUnreadCount: GET /{version}/notifications/inbox/unread-count', async () => { + const stub = {}; + const expected = expectedUrl({ + baseUrl: 'https://hub.norbix.io', + path: '/{version}/notifications/inbox/unread-count', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.hub as unknown as Record< + string, + Record Promise> + > + )['notifications']!['getUnreadCount']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('GET'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); + + it('markNotificationRead: POST /{version}/notifications/inbox/{notificationId}/read', async () => { + const stub = stubRequestForPath('/{version}/notifications/inbox/{notificationId}/read'); + const expected = expectedUrl({ + baseUrl: 'https://hub.norbix.io', + path: '/{version}/notifications/inbox/{notificationId}/read', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.hub as unknown as Record< + string, + Record Promise> + > + )['notifications']!['markNotificationRead']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('POST'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); + + it('markAllNotificationsRead: POST /{version}/notifications/inbox/read-all', async () => { + const stub = {}; + const expected = expectedUrl({ + baseUrl: 'https://hub.norbix.io', + path: '/{version}/notifications/inbox/read-all', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.hub as unknown as Record< + string, + Record Promise> + > + )['notifications']!['markAllNotificationsRead']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('POST'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); + it('getUserNotificationPreferences: GET /{version}/notifications/user/preferences', async () => { const stub = {}; const expected = expectedUrl({ diff --git a/tests/hub/triggers.test.ts b/tests/hub/triggers.test.ts index d887038..25d4d09 100644 --- a/tests/hub/triggers.test.ts +++ b/tests/hub/triggers.test.ts @@ -1,13 +1,13 @@ import { describe, expect, it } from 'vitest'; import { TriggersModule } from '../../src/hub/triggers.js'; -import { createMockFetch, expectedUrl, makeClient } from '../_helpers.js'; +import { createMockFetch, expectedUrl, makeClient, stubRequestForPath } from '../_helpers.js'; /** * Auto-generated. Do not edit by hand — run `npm run generate-endpoints` * to refresh this file from the DTO definitions. * - * Tests for hub.triggers (1 endpoints). + * Tests for hub.triggers (3 endpoints). * * Each method is asserted against: * - presence on the module (smoke check) @@ -17,7 +17,7 @@ import { createMockFetch, expectedUrl, makeClient } from '../_helpers.js'; * - account-scope guard: throws NORBIX_ACCOUNT_SCOPE_REQUIRED without accountId */ describe('hub.triggers', () => { - it('module exposes 1 method(s)', () => { + it('module exposes 3 method(s)', () => { const mock = createMockFetch(); const mod = new TriggersModule({} as never); void mod; // silence unused — we only need the type @@ -29,7 +29,32 @@ describe('hub.triggers', () => { >; expect(ns).toBeDefined(); void mock; + expect(typeof ns['getTriggerDefinition']).toBe('function'); expect(typeof ns['getTriggersNeedingAttention']).toBe('function'); + expect(typeof ns['testTriggerPreExecuteCode']).toBe('function'); + }); + + it('getTriggerDefinition: GET /{version}/triggers/{id}/definition', async () => { + const stub = stubRequestForPath('/{version}/triggers/{id}/definition'); + const expected = expectedUrl({ + baseUrl: 'https://hub.norbix.io', + path: '/{version}/triggers/{id}/definition', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.hub as unknown as Record< + string, + Record Promise> + > + )['triggers']!['getTriggerDefinition']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('GET'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); }); it('getTriggersNeedingAttention: GET /{version}/triggers/attention', async () => { @@ -54,4 +79,27 @@ describe('hub.triggers', () => { expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); }); + + it('testTriggerPreExecuteCode: POST /{version}/triggers/pre-execute-code/test', async () => { + const stub = {}; + const expected = expectedUrl({ + baseUrl: 'https://hub.norbix.io', + path: '/{version}/triggers/pre-execute-code/test', + version: 'v2', + stub, + }); + const { norbix, mock } = makeClient({}); + const fn = ( + norbix.hub as unknown as Record< + string, + Record Promise> + > + )['triggers']!['testTriggerPreExecuteCode']!; + await fn(stub); + expect(mock.lastCall).toBeDefined(); + expect(mock.lastCall?.method).toBe('POST'); + expect(mock.lastCall?.url.startsWith(expected)).toBe(true); + expect(mock.lastCall?.headers.get('Authorization')).toBe('Bearer test-token'); + expect(mock.lastCall?.headers.get('norbix-project-id')).toBe('test-project'); + }); }); From 2cf2a9b164c38cc45a22e737b261e3bb6804919d Mon Sep 17 00:00:00 2001 From: CodeMash Date: Mon, 19 Oct 2026 13:57:53 +0300 Subject: [PATCH 2/2] feat(payments): typed payments.order.* webhooks and asPaymentOrder() for Code functions Five order events (paid, partially_refunded, refunded, reversed, expired) carry PaymentOrderEventDto; event.metadata.order holds the order id, its status and orderEventId (the de-duplication key). Code functions read data.order with asPaymentOrder(); asPayment() stays the callback. Co-Authored-By: Claude Opus 5.5 --- src/functions/event.ts | 10 ++++ src/webhooks/event-data.ts | 9 ++++ src/webhooks/events.ts | 28 ++++++++++- src/webhooks/normalize.ts | 26 +++++++--- tests/functions/parse.test.ts | 63 ++++++++++++++++++++++++ tests/webhooks/event-data.test.ts | 18 +++++++ tests/webhooks/receiver.test.ts | 79 +++++++++++++++++++++++++++++++ 7 files changed, 225 insertions(+), 8 deletions(-) diff --git a/src/functions/event.ts b/src/functions/event.ts index a3cbc93..b6c405b 100644 --- a/src/functions/event.ts +++ b/src/functions/event.ts @@ -68,6 +68,15 @@ export class CodeFunctionEvent implements CodeMashHub2.CodeFunctionEventDto { return this.requireSlot('payment', 'asPayment'); } + /** + * Payments order trigger (`payments.order.*`) — `data.order`: the stored order + * after the change, with `orderEventId` to de-duplicate on. `asPayment()` still + * returns the callback that made the change. + */ + asPaymentOrder(): CodeMashHub2.PaymentOrderEventDto { + return this.requireSlot('order', 'asPaymentOrder'); + } + /** AI trigger — `data.ai`. */ asAiUsage(): CodeMashHub2.AiUsageEventDto { return this.requireSlot('ai', 'asAiUsage'); @@ -145,6 +154,7 @@ const slotSource: Record = { user: 'membership', file: 'files', payment: 'payments', + order: 'payments order (payments.order.*)', ai: 'ai', task: 'scheduler', payload: 'manual / scheduler', diff --git a/src/webhooks/event-data.ts b/src/webhooks/event-data.ts index d9120c8..5abe7a6 100644 --- a/src/webhooks/event-data.ts +++ b/src/webhooks/event-data.ts @@ -45,6 +45,13 @@ export interface NorbixWebhookPayloadMap> { // payments — the normalized payment fact (never the provider's raw payload). // The provider's own event name is `eventName`; amounts are minor units. 'payments.webhook.received': CodeMashHub2.PaymentFactDto; + // payments — a stored order changed: the order as stored, with the callback + // that changed it; `orderEventId` is the de-duplication key. + 'payments.order.paid': CodeMashHub2.PaymentOrderEventDto; + 'payments.order.partially_refunded': CodeMashHub2.PaymentOrderEventDto; + 'payments.order.refunded': CodeMashHub2.PaymentOrderEventDto; + 'payments.order.reversed': CodeMashHub2.PaymentOrderEventDto; + 'payments.order.expired': CodeMashHub2.PaymentOrderEventDto; } /** Resolve the typed payload for a known event (with optional document type). */ @@ -70,4 +77,6 @@ export interface NorbixWebhookEventMetadata { integrationId?: string; /** payments.* events: the provider and its own event id / name (e.g. Stripe `evt_…`, `payment_intent.succeeded`). */ payment?: { provider: string; providerEventId: string; eventName: string }; + /** payments.order.* events: the stored order, its status after the change and the de-duplication key. */ + order?: { id: string; orderEventId: string; status: string }; } diff --git a/src/webhooks/events.ts b/src/webhooks/events.ts index 2a3f9a1..89d36e2 100644 --- a/src/webhooks/events.ts +++ b/src/webhooks/events.ts @@ -21,6 +21,11 @@ export const NORBIX_WEBHOOK_EVENT_NAMES = [ 'files.file.uploaded', 'files.file.deleted', 'payments.webhook.received', + 'payments.order.paid', + 'payments.order.partially_refunded', + 'payments.order.refunded', + 'payments.order.reversed', + 'payments.order.expired', ] as const; export type NorbixWebhookEventName = (typeof NORBIX_WEBHOOK_EVENT_NAMES)[number]; @@ -64,6 +69,20 @@ export const NorbixWebhookEvents = { * `payload.eventName`. */ WebhookReceived: 'payments.webhook.received', + /** + * A verified provider callback made a stored payment order Paid. The payload + * is the order as stored after the change (`PaymentOrderEventDto`); + * de-duplicate on `payload.orderEventId` — it is the same on every retry. + */ + OrderPaid: 'payments.order.paid', + /** Part of a paid order was refunded (`payload.order.refundedMinor`). */ + OrderPartiallyRefunded: 'payments.order.partially_refunded', + /** The whole order was refunded. */ + OrderRefunded: 'payments.order.refunded', + /** The provider reversed (voided) the payment. */ + OrderReversed: 'payments.order.reversed', + /** The checkout ran out without a payment. */ + OrderExpired: 'payments.order.expired', }, } as const satisfies Record>; @@ -109,6 +128,13 @@ export const NORBIX_WEBHOOK_EVENT_GROUPS: NorbixWebhookEventGroup[] = [ { group: 'payments', label: 'Payments', - events: ['payments.webhook.received'], + events: [ + 'payments.webhook.received', + 'payments.order.paid', + 'payments.order.partially_refunded', + 'payments.order.refunded', + 'payments.order.reversed', + 'payments.order.expired', + ], }, ]; diff --git a/src/webhooks/normalize.ts b/src/webhooks/normalize.ts index a16a0e5..a26fa6a 100644 --- a/src/webhooks/normalize.ts +++ b/src/webhooks/normalize.ts @@ -99,18 +99,30 @@ export function normalizeNorbixWebhook(envelope: NorbixWebhookEnvelope): NorbixW // provider ids onto metadata so a handler can de-duplicate / look them up. if (event.startsWith('payments.')) { const metadata: NorbixWebhookEventMetadata = {}; - if (typeof d.integrationId === 'string') metadata.integrationId = d.integrationId; + // payments.order.*: the data is the order event; its `payment` is the fact. + const isOrderEvent = event.startsWith('payments.order.'); + const fact = (isOrderEvent ? d.payment : d) as Record | undefined; + if (typeof fact?.integrationId === 'string') metadata.integrationId = fact.integrationId; if ( - typeof d.provider === 'string' && - typeof d.providerEventId === 'string' && - typeof d.eventName === 'string' + typeof fact?.provider === 'string' && + typeof fact.providerEventId === 'string' && + typeof fact.eventName === 'string' ) { metadata.payment = { - provider: d.provider, - providerEventId: d.providerEventId, - eventName: d.eventName, + provider: fact.provider, + providerEventId: fact.providerEventId, + eventName: fact.eventName, }; } + const order = d.order as Record | undefined; + if ( + isOrderEvent && + typeof order?.id === 'string' && + typeof d.orderEventId === 'string' && + typeof order.status === 'string' + ) { + metadata.order = { id: order.id, orderEventId: d.orderEventId, status: order.status }; + } return { payload: data, metadata }; } diff --git a/tests/functions/parse.test.ts b/tests/functions/parse.test.ts index abacd85..c44ca2d 100644 --- a/tests/functions/parse.test.ts +++ b/tests/functions/parse.test.ts @@ -304,6 +304,69 @@ describe('CodeFunctionEvent typed views', () => { ); }); + it('asPaymentOrder() for a payments order event, and asPayment() is its callback', () => { + const orderEvent = { + orderEventId: 'ord_2ToIiRM03X63TZaJ2dJdv2:2', + event: 'payments.order.paid', + from: 'CheckoutStarted', + to: 'Paid', + reason: 'paid in attempt 1', + order: { + id: 'ord_2ToIiRM03X63TZaJ2dJdv2', + env: 'PROD', + buyerUserId: 'usr_1szWVIyZFBV8HfvC4SfVw', + merchantReference: 'ad-1042-promo-1', + currency: 'EUR', + priceVersion: '2026-10-01', + lines: [ + { + sku: 'star-day', + title: 'Star × day', + quantity: 21, + unitAmountMinor: 30, + amountMinor: 630, + }, + ], + totalMinor: 630, + metadata: { adId: 'ad_1042' }, + status: 'Paid', + checkouts: [], + settledMinor: 630, + refundedMinor: 0, + providerPaymentId: 'mnt-1', + mismatches: [], + }, + payment: { + integrationId: 'int_montonio', + provider: 'Montonio', + providerEventId: 'order:mnt-1:PAID', + orderId: 'ord_2ToIiRM03X63TZaJ2dJdv2', + eventName: 'order.paid', + status: 'Succeeded', + amountMinor: 630, + currency: 'EUR', + receivedOn: '2026-10-19T09:54:43Z', + }, + }; + const event = parseFunctionEvent( + envelope('payments', 'payments.order.paid', { + order: orderEvent, + payment: orderEvent.payment, + }), + ); + const order = event.asPaymentOrder(); + expect(order.orderEventId).toBe('ord_2ToIiRM03X63TZaJ2dJdv2:2'); + expect(order.order.status).toBe('Paid'); + expect(order.order.settledMinor).toBe(630); + expect(order.order.metadata?.adId).toBe('ad_1042'); + expect(event.asPayment().providerEventId).toBe('order:mnt-1:PAID'); + expect(() => + parseFunctionEvent( + envelope('payments', 'payments.webhook.received', { payment: orderEvent.payment }), + ).asPaymentOrder(), + ).toThrow(/asPaymentOrder\(\) is for a payments order \(payments\.order\.\*\) event/); + }); + it('payload() for a manual invoke, undefined when the call sent none', () => { const withPayload = parseFunctionEvent( envelope('manual', 'manual.invoke', { payload: { orderId: '6ac54a59212c29912eaddd81' } }), diff --git a/tests/webhooks/event-data.test.ts b/tests/webhooks/event-data.test.ts index 1caea85..09cfb97 100644 --- a/tests/webhooks/event-data.test.ts +++ b/tests/webhooks/event-data.test.ts @@ -35,6 +35,24 @@ describe('webhooks payload types', () => { >().toEqualTypeOf(); }); + it('every payments.order.* payload is the stored order event', () => { + expectTypeOf< + NorbixWebhookPayload<'payments.order.paid'> + >().toEqualTypeOf(); + expectTypeOf< + NorbixWebhookPayload<'payments.order.partially_refunded'> + >().toEqualTypeOf(); + expectTypeOf< + NorbixWebhookPayload<'payments.order.refunded'> + >().toEqualTypeOf(); + expectTypeOf< + NorbixWebhookPayload<'payments.order.reversed'> + >().toEqualTypeOf(); + expectTypeOf< + NorbixWebhookPayload<'payments.order.expired'> + >().toEqualTypeOf(); + }); + it('database.records.inserted payload is the document array', () => { expectTypeOf< NorbixWebhookPayload<'database.records.inserted', CodeMashHub2.UserDto> diff --git a/tests/webhooks/receiver.test.ts b/tests/webhooks/receiver.test.ts index 2ddd7fb..119e253 100644 --- a/tests/webhooks/receiver.test.ts +++ b/tests/webhooks/receiver.test.ts @@ -217,6 +217,85 @@ describe('NorbixWebhookReceiver', () => { }); }); + it('payments.order.paid hands over the stored order and lifts its ids onto metadata', async () => { + const orderEvent = { + orderEventId: 'ord_2ToIiRM03X63TZaJ2dJdv2:2', + event: 'payments.order.paid', + from: 'CheckoutStarted', + to: 'Paid', + reason: 'paid in attempt 1', + order: { + id: 'ord_2ToIiRM03X63TZaJ2dJdv2', + env: 'PROD', + buyerUserId: 'usr_1szWVIyZFBV8HfvC4SfVw', + merchantReference: 'ad-1042-promo-1', + currency: 'EUR', + priceVersion: '2026-10-01', + lines: [ + { + sku: 'star-day', + title: 'Star × day', + quantity: 21, + unitAmountMinor: 30, + amountMinor: 630, + }, + ], + totalMinor: 630, + metadata: { adId: 'ad_1042' }, + status: 'Paid', + checkouts: [], + settledMinor: 630, + refundedMinor: 0, + providerPaymentId: 'mnt-1', + mismatches: [], + }, + payment: { + integrationId: 'int_montonio', + provider: 'Montonio', + providerEventId: 'order:mnt-1:PAID', + orderId: 'ord_2ToIiRM03X63TZaJ2dJdv2', + eventName: 'order.paid', + status: 'Succeeded', + amountMinor: 630, + currency: 'EUR', + receivedOn: '2026-10-19T09:54:43Z', + }, + }; + const receiver = new NorbixWebhookReceiver(); + const handler = vi.fn(); + receiver.on(NorbixWebhookEvents.Payments.OrderPaid, handler); + + const result = await receiver.handle({ + rawBody: JSON.stringify({ + id: 'dlv_o', + eventId: 'evt_order_1', + event: 'payments.order.paid', + createdOn: '2026-10-19T09:54:44Z', + accountId: 'acc_1', + projectId: 'pr_1', + triggerId: 'trg_o', + data: orderEvent, + }), + headers: {}, + }); + + expect(result.handled).toBe(true); + expect(handler.mock.calls[0]?.[0]).toEqual(orderEvent); + const event = handler.mock.calls[0]?.[1]; + expect(event.name).toBe('payments.order.paid'); + expect(event.metadata.integrationId).toBe('int_montonio'); + expect(event.metadata.payment).toEqual({ + provider: 'Montonio', + providerEventId: 'order:mnt-1:PAID', + eventName: 'order.paid', + }); + expect(event.metadata.order).toEqual({ + id: 'ord_2ToIiRM03X63TZaJ2dJdv2', + orderEventId: 'ord_2ToIiRM03X63TZaJ2dJdv2:2', + status: 'Paid', + }); + }); + it('lifts schema + record id onto event.metadata for db events', async () => { const doc = { id: 'rec_1', email: 'a@b.io' }; const receiver = new NorbixWebhookReceiver();