From 9a88a47204e42389bd5775d8551136fdd12621a1 Mon Sep 17 00:00:00 2001 From: Perry George Date: Fri, 19 Sep 2025 08:18:10 +0100 Subject: [PATCH 1/3] docs: updated changelog for v5.0.0 --- docs/docs/changelog.md | 62 ++++++++++++++++++++++++++++++++---- src/schemas/v3/schemas-v3.ts | 6 +--- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md index b51d649..c5a14de 100644 --- a/docs/docs/changelog.md +++ b/docs/docs/changelog.md @@ -8,29 +8,79 @@ sidebar_position: 2 ### Breaking Changes -### Capabilities deprecated +The `Salable` class has been replaced with a new `initSalable` function. Using the new initialise function will enable +vendors to use `v2` or `v3` of the API within the same version of the SDK. + +**v4.0.0 implementation** +```typescript +const salable = new Salable('your-api-key', 'v2') +``` +**v5.0.0 implementation** +```typescript +const salableV2 = initSalable('your-api-key', 'v2') // still supported +const salableV3 = initSalable('your-api-key', 'v3') +``` + +### V3 Breaking changes + +#### Capabilities deprecated Capabilities used to be stored on the License at the point of creation with no way of editing them. We found this to be too rigid, for flexibility we have deprecated capabilities in favour of using the plan's feature values which are editable in the Salable app. -#### Deprecated capabilities deprecated + +##### Deprecated methods that use capabilities - `plans.capabilities` -- `product.capabilities` +- `products.capabilities` - `licenses.check` -### Licenses deprecated +#### Licenses deprecated All license methods have been deprecated in favour of managing them through the subscription instead. This gives a consistent implementation across all types of subscriptions. - `licenses.create` moved to `subscriptions.create` - the `owner` value will be applied to the `purchaser` field of the license. -- `license.check` moved to `entitlements.check` +- `licenses.check` moved to `entitlements.check` - `licenses.getAll` moved to `subscriptions.getSeats` -- `licenses.getOne` support removed +- `licenses.getOne` support removed - fetch the parent subscription instead using `subscriptions.getOne` - `licenses.getForPurchaser` moved to `subscriptions.getAll` with the owner filter applied. - `licenses.update` moved to `subscriptions.update` + - To update a seat's grantee use the `subscriptions.manageSeats` method. - `licenses.updateMany` moved to `subscriptions.manageSeats` - `licenses.getCount` moved to `subscriptions.getSeatCount` - `licenses.cancel` moved to `subscriptions.cancel` - this will cancel all the subscription's child licenses. - `licenses.cancelMany` moved to `subscriptions.cancel` - it is not possible to cancel many subscriptions in the same request. +#### Other deprecated endpoints +- `products.getFeatures` moved to `features.getAll` with the `productUuid` filter applied. +- `products.getPlans` moved to `plans.getAll` with the `productUuid` filter applied. + +#### Affected responses +- `products.getAll` now uses cursor-based pagination in the response. + +### What's new? +#### New methods +- `features.getAll` - Retrieves all features for an organisation. The response uses cursor-based pagination. +- `plans.getAll` - Retrieves all plans for an organisation. The response uses cursor-based pagination. +- `entitlements.check` - Check grantee access to specific features (replaces `licenses.check`). + +**v4.0.0 SDK with API v2** +```typescript +const salable = new Salable('your-api-key', 'v2'); +const check = await salable.licenses.check({ + productUuid: 'your-product-uuid', + granteeIds: ['your-grantee-id'], +}); +const hasAccess = check?.capabilities.find((c) => c.capability === 'your-boolean-feature'); +``` + +**v5.0.0 SDK with API v3** +```typescript +const salable = initSalable('your-api-key', 'v3'); +const check = await salable.entitlements.check({ + productUuid: 'your-product-uuid', + granteeIds: ['your-grantee-id'], +}); +const hasAccess = check.features.find((f) => f.feature === 'your-boolean-feature'); +``` + ## v4.0.0 ### Breaking Changes diff --git a/src/schemas/v3/schemas-v3.ts b/src/schemas/v3/schemas-v3.ts index 1a9d643..2362d42 100644 --- a/src/schemas/v3/schemas-v3.ts +++ b/src/schemas/v3/schemas-v3.ts @@ -1,12 +1,9 @@ import { FeatureEnumOption, FeatureV3, - Invoice, LicenseV3, OrganisationPaymentIntegrationV3, PaginatedLicenses, - PaginatedSubscription, - PaginatedSubscriptionInvoice, PlanCurrency, PlanFeatureV3, PlanV3, @@ -14,9 +11,8 @@ import { ProductCurrency, ProductPricingTableV3, ProductV3, - Subscription, } from '../../types'; -import { EnumValueSchema, LicenseSchema, SubscriptionSchema } from '../v2/schemas-v2'; +import { EnumValueSchema} from '../v2/schemas-v2'; export const ProductSchemaV3: ProductV3 = { uuid: expect.any(String), From 8aae55ea61c54a8d02b4c2bc24963918eb253a8f Mon Sep 17 00:00:00 2001 From: Perry George Date: Fri, 19 Sep 2025 14:33:02 +0100 Subject: [PATCH 2/3] docs: added notice for add and remove seats in changelog --- docs/docs/changelog.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md index c5a14de..b59dd2a 100644 --- a/docs/docs/changelog.md +++ b/docs/docs/changelog.md @@ -13,12 +13,12 @@ vendors to use `v2` or `v3` of the API within the same version of the SDK. **v4.0.0 implementation** ```typescript -const salable = new Salable('your-api-key', 'v2') +const salable = new Salable('your-api-key', 'v2'); ``` **v5.0.0 implementation** ```typescript -const salableV2 = initSalable('your-api-key', 'v2') // still supported -const salableV3 = initSalable('your-api-key', 'v3') +const salableV2 = initSalable('your-api-key', 'v2'); // v2 still supported +const salableV3 = initSalable('your-api-key', 'v3'); ``` ### V3 Breaking changes @@ -51,6 +51,8 @@ consistent implementation across all types of subscriptions. #### Other deprecated endpoints - `products.getFeatures` moved to `features.getAll` with the `productUuid` filter applied. - `products.getPlans` moved to `plans.getAll` with the `productUuid` filter applied. +- `subscriptions.addSeats` moved to `subscriptions.updateSeatCount` with `increment` set. +- `subscriptions.removeSeats` moved to `subscriptions.updateSeatCount` with `decerement` set. #### Affected responses - `products.getAll` now uses cursor-based pagination in the response. @@ -59,6 +61,7 @@ consistent implementation across all types of subscriptions. #### New methods - `features.getAll` - Retrieves all features for an organisation. The response uses cursor-based pagination. - `plans.getAll` - Retrieves all plans for an organisation. The response uses cursor-based pagination. +- `subscriptions.updateSeatCount` - v2 of the API required two different endpoints to add and remove seats on a per-seat subscription. In v3 this has been aligned under one method `subscriptions.updateSeatCount`. - `entitlements.check` - Check grantee access to specific features (replaces `licenses.check`). **v4.0.0 SDK with API v2** From e495ae43b3901ed32c11cbbc5e0fc29fdf1b41c2 Mon Sep 17 00:00:00 2001 From: Perry George Date: Fri, 19 Sep 2025 15:02:53 +0100 Subject: [PATCH 3/3] test: updated pricing table tests to create the UUID in the test itself --- src/pricing-tables/v2/pricing-table-v2.test.ts | 6 +++--- src/pricing-tables/v3/pricing-table-v3.test.ts | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pricing-tables/v2/pricing-table-v2.test.ts b/src/pricing-tables/v2/pricing-table-v2.test.ts index 7efee32..37a9cba 100644 --- a/src/pricing-tables/v2/pricing-table-v2.test.ts +++ b/src/pricing-tables/v2/pricing-table-v2.test.ts @@ -4,13 +4,13 @@ import { testUuids } from '../../../test-utils/scripts/create-salable-test-data' import { randomUUID } from 'crypto'; import { PricingTableSchema } from '../../schemas/v2/schemas-v2'; -const pricingTableUuid = randomUUID(); describe('Pricing Table V2 Tests', () => { const apiKey = testUuids.devApiKeyV2; const salable = initSalable(apiKey, 'v2'); + const pricingTableUuid = randomUUID(); beforeAll(async() => { - await generateTestData() + await generateTestData(pricingTableUuid) }) it('getAll: should successfully fetch all pricing tables', async () => { @@ -20,7 +20,7 @@ describe('Pricing Table V2 Tests', () => { }); -const generateTestData = async () => { +const generateTestData = async (pricingTableUuid: string) => { const product = await prismaClient.product.findUnique({ where: { uuid: testUuids.productUuid }, diff --git a/src/pricing-tables/v3/pricing-table-v3.test.ts b/src/pricing-tables/v3/pricing-table-v3.test.ts index 8dd9a0b..cebee3f 100644 --- a/src/pricing-tables/v3/pricing-table-v3.test.ts +++ b/src/pricing-tables/v3/pricing-table-v3.test.ts @@ -4,12 +4,13 @@ import { PricingTableSchemaV3 } from '../../schemas/v3/schemas-v3'; import { initSalable } from '../../index'; import { randomUUID } from 'crypto'; -const pricingTableUuid = randomUUID(); describe('Pricing Table V3 Tests', () => { const apiKey = testUuids.devApiKeyV3; const salable = initSalable(apiKey, 'v3'); + const pricingTableUuid = randomUUID(); + beforeAll(async() => { - await generateTestData() + await generateTestData(pricingTableUuid) }) it('getOne: should successfully fetch all pricing tables', async () => { @@ -19,7 +20,7 @@ describe('Pricing Table V3 Tests', () => { }); -const generateTestData = async () => { +const generateTestData = async (pricingTableUuid: string) => { const product = await prismaClient.product.findUnique({ where: { uuid: testUuids.productUuid },