@skalpgg/sdk 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,7 +47,7 @@ Create the key on the API keys page of your dashboard. Each key is bound to one
47
47
  | `orders.get(id)` | `orders:read` |
48
48
  | `orders.cancel(id)` | `orders:write` |
49
49
  | `orders.replace(id, { serialIds?, values?, reason? })` | `orders:write` |
50
- | `products.list({ status? })` | `products:read` |
50
+ | `products.list({ status?, limit?, offset? })` | `products:read` |
51
51
  | `products.get(idOrSlug)` | `products:read` |
52
52
  | `products.create({ name, price, fulfillmentType?, submit?, ... })` | `products:write` |
53
53
  | `products.update(idOrSlug, { price?, status?: 'DRAFT' \| 'ARCHIVED', ... })` | `products:write` |
@@ -1,6 +1,6 @@
1
1
  import type { AccessRule, AddAccessRuleParams, AddSerialsParams, AddSerialsResult, AppealFeedbackParams, ChangeSubscriptionPlanParams, CheckoutSession, CheckoutSessionStatus, CoinCheckout, CoinCheckoutStatus, Coupon, CreateCheckoutParams, CreateCoinCheckoutParams, CreateCouponParams, CreateCustomerParams, CreateProductGroupParams, CreateProductParams, CreateStoreCategoryParams, CreateSubscriptionCheckoutParams, CreateUsageChargeParams, Customer, CustomerDetails, CustomerList, Deleted, Feedback, FeedbackList, HeartbeatParams, Invoice, LicenseKeyInfo, LicenseValidation, LicenseVerification, ListAccessRulesParams, ListCustomersParams, ListFeedbackParams, ListOrdersParams, ListProductsParams, ListQueriesParams, ListSerialsParams, ListSubscriptionsParams, Order, OrderKeysReplaced, OrderList, Product, ProductGroup, ProductStock, ReplaceOrderKeysParams, ReplyParams, SerialList, SkalpEvent, StoreCategory, StoreCategoryWithCount, Subscription, SubscriptionCheckout, SubscriptionCheckoutStatus, SubscriptionList, SupportQueryDetails, SupportQueryList, ResetLicenseDevicesParams, UpdateCouponParams, UpdateCustomerParams, UpdateProductGroupParams, UpdateProductParams, UpdateStoreCategoryParams, UsageCharge, ValidateLicenseParams, VerifyLicenseParams } from './types.js';
2
2
  export type * from './types.js';
3
- export declare const VERSION = "1.4.0";
3
+ export declare const VERSION = "1.5.0";
4
4
  export interface SkalpOptions {
5
5
  apiKey?: string;
6
6
  webhookSecret?: string;
@@ -42,7 +42,7 @@ export declare class Skalp {
42
42
  replace: (id: string, { serialIds, values, reason }?: ReplaceOrderKeysParams) => Promise<OrderKeysReplaced>;
43
43
  };
44
44
  readonly products: {
45
- list: ({ status }?: ListProductsParams) => Promise<Product[]>;
45
+ list: ({ status, limit, offset }?: ListProductsParams) => Promise<Array<Product>>;
46
46
  get: (idOrSlug: string) => Promise<Product>;
47
47
  create: (params: CreateProductParams) => Promise<Product>;
48
48
  update: (idOrSlug: string, params: UpdateProductParams) => Promise<Product>;
package/dist/cjs/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Skalp = exports.SkalpSignatureError = exports.SkalpConnectionError = exports.SkalpError = exports.VERSION = void 0;
4
- exports.VERSION = '1.4.0';
4
+ exports.VERSION = '1.5.0';
5
5
  const SIGNATURE_HEADER = 'skalp-signature';
6
6
  const SIGNATURE_TOLERANCE_SECONDS = 300;
7
+ const PRODUCT_PAGE = 100;
7
8
  const USER_AGENT = `skalp-node/${exports.VERSION}`;
8
9
  const DEFAULT_TIMEOUT_MS = 30_000;
9
10
  const MAX_RETRY_WAIT_SECONDS = 10;
@@ -128,7 +129,21 @@ class Skalp {
128
129
  }),
129
130
  };
130
131
  products = {
131
- list: ({ status } = {}) => this.#request('GET', '/v1/products', { query: { status } }),
132
+ list: async ({ status, limit, offset } = {}) => {
133
+ if (limit !== undefined || offset !== undefined) {
134
+ return (await this.#send('GET', '/v1/products', { query: { status, limit, offset } })).data;
135
+ }
136
+ const products = [];
137
+ for (;;) {
138
+ const page = await this.#send('GET', '/v1/products', {
139
+ query: { status, limit: PRODUCT_PAGE, offset: products.length },
140
+ });
141
+ products.push(...page.data);
142
+ const total = Number(page.headers.get('x-total-count'));
143
+ if (page.data.length < PRODUCT_PAGE || products.length >= total)
144
+ return products;
145
+ }
146
+ },
132
147
  get: (idOrSlug) => this.#request('GET', `/v1/products/${segment(idOrSlug)}`),
133
148
  create: (params) => this.#request('POST', '/v1/products', {
134
149
  body: pick(params, [...PRODUCT_FIELDS, 'productType', 'submit']),
@@ -294,7 +309,10 @@ class Skalp {
294
309
  const hmac = await crypto.subtle.importKey('raw', encoder.encode(key), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']);
295
310
  return (await crypto.subtle.verify('HMAC', hmac, parsed.signature, signed)) ? undefined : 'invalid';
296
311
  }
297
- async #request(method, path, { body, query, auth = true } = {}) {
312
+ async #request(method, path, options = {}) {
313
+ return (await this.#send(method, path, options)).data;
314
+ }
315
+ async #send(method, path, { body, query, auth = true } = {}) {
298
316
  if (auth && !this.#apiKey) {
299
317
  throw new Error('Skalp: missing API key. Pass it to new Skalp(apiKey) or set SKALP_API_KEY.');
300
318
  }
@@ -339,7 +357,7 @@ class Skalp {
339
357
  }
340
358
  if (!res.ok)
341
359
  throw toError(res.status, data);
342
- return data;
360
+ return { data: data, headers: res.headers };
343
361
  }
344
362
  }
345
363
  }
@@ -205,6 +205,13 @@ export interface Product {
205
205
  deliveryType: DeliveryType | null;
206
206
  /** Server stores: what the game server runs after payment. */
207
207
  deliveryPayload: string | null;
208
+ revokePayload: string | null;
209
+ coinConfig: CoinConfig | null;
210
+ pendingChanges: {
211
+ submittedAt: string;
212
+ changes: Partial<Record<ReviewedProductField, unknown>>;
213
+ } | null;
214
+ pendingChangesRejection: string | null;
208
215
  visibility: ProductVisibility;
209
216
  /** Higher comes first on the store page. */
210
217
  sortPriority: number;
@@ -232,8 +239,24 @@ export interface Product {
232
239
  createdAt: string;
233
240
  updatedAt: string;
234
241
  }
242
+ export interface CoinBonusTier {
243
+ thresholdCoins: number;
244
+ bonusPercent: number;
245
+ }
246
+ export interface CoinConfig {
247
+ unitPriceCents: number;
248
+ minQty: number;
249
+ maxQty: number;
250
+ step: number;
251
+ currency: string;
252
+ bonusTiers: Array<CoinBonusTier>;
253
+ }
254
+ export type CoinConfigInput = Omit<CoinConfig, 'currency'>;
255
+ export type ReviewedProductField = 'name' | 'shortDescription' | 'description' | 'images' | 'videoUrl' | 'features' | 'requirements' | 'customFields' | 'deliveryNote' | 'termsOfService' | 'warrantyText' | 'deliveryType' | 'deliveryPayload' | 'revokePayload';
235
256
  export interface ListProductsParams {
236
257
  status?: ProductStatus | 'all';
258
+ limit?: number;
259
+ offset?: number;
237
260
  }
238
261
  interface ProductFields {
239
262
  slug?: string;
@@ -255,6 +278,8 @@ interface ProductFields {
255
278
  deliveryType?: DeliveryType;
256
279
  /** Required for a server store; needs a placeholder such as {steamhex}. */
257
280
  deliveryPayload?: string;
281
+ revokePayload?: string;
282
+ coinConfig?: CoinConfigInput;
258
283
  visibility?: ProductVisibility;
259
284
  sortPriority?: number;
260
285
  /** null or an empty string clears it. */
@@ -279,7 +304,7 @@ export interface CreateProductParams extends ProductFields {
279
304
  name: string;
280
305
  price: number;
281
306
  comparePrice?: number;
282
- productType?: 'ONE_TIME' | 'RECURRING';
307
+ productType?: 'ONE_TIME' | 'RECURRING' | 'COINS';
283
308
  submit?: boolean;
284
309
  }
285
310
  export interface UpdateProductParams extends ProductFields {
@@ -799,6 +824,16 @@ export interface OrderRefundedData {
799
824
  granted?: boolean;
800
825
  fullRefund: boolean;
801
826
  licenses: Array<WebhookLicense>;
827
+ revocations: Array<Revocation>;
828
+ }
829
+ export interface Revocation {
830
+ productId: string;
831
+ productSlug: string;
832
+ productName: string;
833
+ deliveryKey: string;
834
+ delivered: boolean;
835
+ deliveryType: DeliveryType;
836
+ revokeCommand: string;
802
837
  }
803
838
  export interface OrderDisputedData {
804
839
  orderId: string;
@@ -914,6 +949,7 @@ export type SkalpEvent = Envelope<'order.created' | 'order.updated' | 'order.can
914
949
  }> | Envelope<'subscription.canceled', SubscriptionEventData & {
915
950
  endedAt: string | null;
916
951
  cancelReason: 'requested' | 'payment_failure' | 'dispute' | 'stripe';
952
+ revokeCommand?: string;
917
953
  }> | Envelope<'subscription.refunded', SubscriptionEventData & {
918
954
  invoiceId: string;
919
955
  amountRefunded: number;
@@ -1,6 +1,6 @@
1
1
  import type { AccessRule, AddAccessRuleParams, AddSerialsParams, AddSerialsResult, AppealFeedbackParams, ChangeSubscriptionPlanParams, CheckoutSession, CheckoutSessionStatus, CoinCheckout, CoinCheckoutStatus, Coupon, CreateCheckoutParams, CreateCoinCheckoutParams, CreateCouponParams, CreateCustomerParams, CreateProductGroupParams, CreateProductParams, CreateStoreCategoryParams, CreateSubscriptionCheckoutParams, CreateUsageChargeParams, Customer, CustomerDetails, CustomerList, Deleted, Feedback, FeedbackList, HeartbeatParams, Invoice, LicenseKeyInfo, LicenseValidation, LicenseVerification, ListAccessRulesParams, ListCustomersParams, ListFeedbackParams, ListOrdersParams, ListProductsParams, ListQueriesParams, ListSerialsParams, ListSubscriptionsParams, Order, OrderKeysReplaced, OrderList, Product, ProductGroup, ProductStock, ReplaceOrderKeysParams, ReplyParams, SerialList, SkalpEvent, StoreCategory, StoreCategoryWithCount, Subscription, SubscriptionCheckout, SubscriptionCheckoutStatus, SubscriptionList, SupportQueryDetails, SupportQueryList, ResetLicenseDevicesParams, UpdateCouponParams, UpdateCustomerParams, UpdateProductGroupParams, UpdateProductParams, UpdateStoreCategoryParams, UsageCharge, ValidateLicenseParams, VerifyLicenseParams } from './types.js';
2
2
  export type * from './types.js';
3
- export declare const VERSION = "1.4.0";
3
+ export declare const VERSION = "1.5.0";
4
4
  export interface SkalpOptions {
5
5
  apiKey?: string;
6
6
  webhookSecret?: string;
@@ -42,7 +42,7 @@ export declare class Skalp {
42
42
  replace: (id: string, { serialIds, values, reason }?: ReplaceOrderKeysParams) => Promise<OrderKeysReplaced>;
43
43
  };
44
44
  readonly products: {
45
- list: ({ status }?: ListProductsParams) => Promise<Product[]>;
45
+ list: ({ status, limit, offset }?: ListProductsParams) => Promise<Array<Product>>;
46
46
  get: (idOrSlug: string) => Promise<Product>;
47
47
  create: (params: CreateProductParams) => Promise<Product>;
48
48
  update: (idOrSlug: string, params: UpdateProductParams) => Promise<Product>;
package/dist/esm/index.js CHANGED
@@ -1,6 +1,7 @@
1
- export const VERSION = '1.4.0';
1
+ export const VERSION = '1.5.0';
2
2
  const SIGNATURE_HEADER = 'skalp-signature';
3
3
  const SIGNATURE_TOLERANCE_SECONDS = 300;
4
+ const PRODUCT_PAGE = 100;
4
5
  const USER_AGENT = `skalp-node/${VERSION}`;
5
6
  const DEFAULT_TIMEOUT_MS = 30_000;
6
7
  const MAX_RETRY_WAIT_SECONDS = 10;
@@ -122,7 +123,21 @@ export class Skalp {
122
123
  }),
123
124
  };
124
125
  products = {
125
- list: ({ status } = {}) => this.#request('GET', '/v1/products', { query: { status } }),
126
+ list: async ({ status, limit, offset } = {}) => {
127
+ if (limit !== undefined || offset !== undefined) {
128
+ return (await this.#send('GET', '/v1/products', { query: { status, limit, offset } })).data;
129
+ }
130
+ const products = [];
131
+ for (;;) {
132
+ const page = await this.#send('GET', '/v1/products', {
133
+ query: { status, limit: PRODUCT_PAGE, offset: products.length },
134
+ });
135
+ products.push(...page.data);
136
+ const total = Number(page.headers.get('x-total-count'));
137
+ if (page.data.length < PRODUCT_PAGE || products.length >= total)
138
+ return products;
139
+ }
140
+ },
126
141
  get: (idOrSlug) => this.#request('GET', `/v1/products/${segment(idOrSlug)}`),
127
142
  create: (params) => this.#request('POST', '/v1/products', {
128
143
  body: pick(params, [...PRODUCT_FIELDS, 'productType', 'submit']),
@@ -288,7 +303,10 @@ export class Skalp {
288
303
  const hmac = await crypto.subtle.importKey('raw', encoder.encode(key), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']);
289
304
  return (await crypto.subtle.verify('HMAC', hmac, parsed.signature, signed)) ? undefined : 'invalid';
290
305
  }
291
- async #request(method, path, { body, query, auth = true } = {}) {
306
+ async #request(method, path, options = {}) {
307
+ return (await this.#send(method, path, options)).data;
308
+ }
309
+ async #send(method, path, { body, query, auth = true } = {}) {
292
310
  if (auth && !this.#apiKey) {
293
311
  throw new Error('Skalp: missing API key. Pass it to new Skalp(apiKey) or set SKALP_API_KEY.');
294
312
  }
@@ -333,7 +351,7 @@ export class Skalp {
333
351
  }
334
352
  if (!res.ok)
335
353
  throw toError(res.status, data);
336
- return data;
354
+ return { data: data, headers: res.headers };
337
355
  }
338
356
  }
339
357
  }
@@ -205,6 +205,13 @@ export interface Product {
205
205
  deliveryType: DeliveryType | null;
206
206
  /** Server stores: what the game server runs after payment. */
207
207
  deliveryPayload: string | null;
208
+ revokePayload: string | null;
209
+ coinConfig: CoinConfig | null;
210
+ pendingChanges: {
211
+ submittedAt: string;
212
+ changes: Partial<Record<ReviewedProductField, unknown>>;
213
+ } | null;
214
+ pendingChangesRejection: string | null;
208
215
  visibility: ProductVisibility;
209
216
  /** Higher comes first on the store page. */
210
217
  sortPriority: number;
@@ -232,8 +239,24 @@ export interface Product {
232
239
  createdAt: string;
233
240
  updatedAt: string;
234
241
  }
242
+ export interface CoinBonusTier {
243
+ thresholdCoins: number;
244
+ bonusPercent: number;
245
+ }
246
+ export interface CoinConfig {
247
+ unitPriceCents: number;
248
+ minQty: number;
249
+ maxQty: number;
250
+ step: number;
251
+ currency: string;
252
+ bonusTiers: Array<CoinBonusTier>;
253
+ }
254
+ export type CoinConfigInput = Omit<CoinConfig, 'currency'>;
255
+ export type ReviewedProductField = 'name' | 'shortDescription' | 'description' | 'images' | 'videoUrl' | 'features' | 'requirements' | 'customFields' | 'deliveryNote' | 'termsOfService' | 'warrantyText' | 'deliveryType' | 'deliveryPayload' | 'revokePayload';
235
256
  export interface ListProductsParams {
236
257
  status?: ProductStatus | 'all';
258
+ limit?: number;
259
+ offset?: number;
237
260
  }
238
261
  interface ProductFields {
239
262
  slug?: string;
@@ -255,6 +278,8 @@ interface ProductFields {
255
278
  deliveryType?: DeliveryType;
256
279
  /** Required for a server store; needs a placeholder such as {steamhex}. */
257
280
  deliveryPayload?: string;
281
+ revokePayload?: string;
282
+ coinConfig?: CoinConfigInput;
258
283
  visibility?: ProductVisibility;
259
284
  sortPriority?: number;
260
285
  /** null or an empty string clears it. */
@@ -279,7 +304,7 @@ export interface CreateProductParams extends ProductFields {
279
304
  name: string;
280
305
  price: number;
281
306
  comparePrice?: number;
282
- productType?: 'ONE_TIME' | 'RECURRING';
307
+ productType?: 'ONE_TIME' | 'RECURRING' | 'COINS';
283
308
  submit?: boolean;
284
309
  }
285
310
  export interface UpdateProductParams extends ProductFields {
@@ -799,6 +824,16 @@ export interface OrderRefundedData {
799
824
  granted?: boolean;
800
825
  fullRefund: boolean;
801
826
  licenses: Array<WebhookLicense>;
827
+ revocations: Array<Revocation>;
828
+ }
829
+ export interface Revocation {
830
+ productId: string;
831
+ productSlug: string;
832
+ productName: string;
833
+ deliveryKey: string;
834
+ delivered: boolean;
835
+ deliveryType: DeliveryType;
836
+ revokeCommand: string;
802
837
  }
803
838
  export interface OrderDisputedData {
804
839
  orderId: string;
@@ -914,6 +949,7 @@ export type SkalpEvent = Envelope<'order.created' | 'order.updated' | 'order.can
914
949
  }> | Envelope<'subscription.canceled', SubscriptionEventData & {
915
950
  endedAt: string | null;
916
951
  cancelReason: 'requested' | 'payment_failure' | 'dispute' | 'stripe';
952
+ revokeCommand?: string;
917
953
  }> | Envelope<'subscription.refunded', SubscriptionEventData & {
918
954
  invoiceId: string;
919
955
  amountRefunded: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skalpgg/sdk",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Official Skalp SDK for Node.js and edge runtimes: checkout, subscriptions, catalog and key stock, webhooks and license verification.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://skalp.gg/docs/sdk",