@qelos/sdk 4.1.0 → 4.2.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/dist/administrator/integration-sources.d.ts +3 -1
- package/dist/administrator/integration-sources.js +14 -0
- package/dist/administrator/integration-sources.js.map +1 -1
- package/dist/administrator/payments.d.ts +11 -1
- package/dist/administrator/payments.js +12 -0
- package/dist/administrator/payments.js.map +1 -1
- package/dist/administrator/tests/integration-sources-status.test.d.ts +1 -0
- package/dist/administrator/tests/integration-sources-status.test.js +105 -0
- package/dist/administrator/tests/integration-sources-status.test.js.map +1 -0
- package/dist/authentication.d.ts +11 -0
- package/dist/authentication.js +6 -3
- package/dist/authentication.js.map +1 -1
- package/dist/payments.d.ts +5 -0
- package/dist/payments.js.map +1 -1
- package/dist/services/decode-uri-fields.d.ts +4 -0
- package/dist/services/decode-uri-fields.js +42 -0
- package/dist/services/decode-uri-fields.js.map +1 -0
- package/docs/payments.md +54 -0
- package/package.json +2 -2
- package/src/administrator/integration-sources.ts +30 -1
- package/src/administrator/payments.ts +22 -0
- package/src/administrator/tests/integration-sources-status.test.ts +121 -0
- package/src/authentication.ts +16 -3
- package/src/payments.ts +5 -0
- package/src/services/decode-uri-fields.ts +48 -0
- package/test/authentication-me.test.ts +109 -0
- package/test/authentication-social.test.ts +24 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { QelosSDKOptions, RequestExtra } from '../types';
|
|
2
2
|
import BaseSDK from '../base-sdk';
|
|
3
|
-
import { IIntegrationSource } from '@qelos/global-types';
|
|
3
|
+
import { IIntegrationSource, IIntegrationSourceStatusRequest, IIntegrationSourceStatusResult } from '@qelos/global-types';
|
|
4
4
|
export default class QlIntegrationSources extends BaseSDK {
|
|
5
5
|
private options;
|
|
6
6
|
private relativePath;
|
|
@@ -12,4 +12,6 @@ export default class QlIntegrationSources extends BaseSDK {
|
|
|
12
12
|
remove(sourceId: string): Promise<any>;
|
|
13
13
|
update(sourceId: string, changes: Partial<Pick<IIntegrationSource, 'name' | 'labels' | 'metadata' | 'authentication'>>): Promise<IIntegrationSource>;
|
|
14
14
|
create(source: Pick<IIntegrationSource, 'name' | 'labels' | 'metadata' | 'authentication'>): Promise<IIntegrationSource>;
|
|
15
|
+
checkStatus(sourceId: string, overrides?: Partial<Pick<IIntegrationSource, 'metadata' | 'authentication'>>): Promise<IIntegrationSourceStatusResult>;
|
|
16
|
+
checkDraftStatus(source: IIntegrationSourceStatusRequest): Promise<IIntegrationSourceStatusResult>;
|
|
15
17
|
}
|
|
@@ -29,5 +29,19 @@ export default class QlIntegrationSources extends BaseSDK {
|
|
|
29
29
|
body: JSON.stringify(source)
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
+
checkStatus(sourceId, overrides) {
|
|
33
|
+
return this.callJsonApi(`${this.relativePath}/${sourceId}/status`, {
|
|
34
|
+
method: 'post',
|
|
35
|
+
headers: { 'content-type': 'application/json' },
|
|
36
|
+
body: JSON.stringify(overrides ?? {}),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
checkDraftStatus(source) {
|
|
40
|
+
return this.callJsonApi(`${this.relativePath}/status`, {
|
|
41
|
+
method: 'post',
|
|
42
|
+
headers: { 'content-type': 'application/json' },
|
|
43
|
+
body: JSON.stringify(source),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
32
46
|
}
|
|
33
47
|
//# sourceMappingURL=integration-sources.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration-sources.js","sourceRoot":"","sources":["../../src/administrator/integration-sources.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"integration-sources.js","sourceRoot":"","sources":["../../src/administrator/integration-sources.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,aAAa,CAAC;AAOlC,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,OAAO;IAGnC;IAFZ,YAAY,GAAG,0BAA0B,CAAC;IAElD,YAAoB,OAAwB;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAA;QADI,YAAO,GAAP,OAAO,CAAiB;IAE5C,CAAC;IAED,oBAAoB,CAAC,QAAgB,EAAE,KAAoB;QACzD,OAAO,IAAI,CAAC,WAAW,CAAqB,GAAG,IAAI,CAAC,YAAY,IAAI,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC;IACzF,CAAC;IAED,OAAO,CAAC,KAAyB,EAAE,KAAoB;QACrD,OAAO,IAAI,CAAC,WAAW,CAAuB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IACvG,CAAC;IAED,MAAM,CAAC,QAAgB;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,YAAY,IAAI,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,CAAC,QAAgB,EAAE,OAA6F;QACpH,OAAO,IAAI,CAAC,WAAW,CACrB,GAAG,IAAI,CAAC,YAAY,IAAI,QAAQ,EAAE,EAClC;YACE,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;SAC9B,CACF,CAAA;IACH,CAAC;IAED,MAAM,CAAC,MAAmF;QACxF,OAAO,IAAI,CAAC,WAAW,CAAqB,IAAI,CAAC,YAAY,EAAE;YAC7D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;SAC7B,CAAC,CAAA;IACJ,CAAC;IAED,WAAW,CACT,QAAgB,EAChB,SAA4E;QAE5E,OAAO,IAAI,CAAC,WAAW,CACrB,GAAG,IAAI,CAAC,YAAY,IAAI,QAAQ,SAAS,EACzC;YACE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,IAAI,EAAE,CAAC;SACtC,CACF,CAAC;IACJ,CAAC;IAED,gBAAgB,CAAC,MAAuC;QACtD,OAAO,IAAI,CAAC,WAAW,CACrB,GAAG,IAAI,CAAC,YAAY,SAAS,EAC7B;YACE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;SAC7B,CACF,CAAC;IACJ,CAAC;CAEF"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { QelosSDKOptions } from '../types';
|
|
2
2
|
import BaseSDK from '../base-sdk';
|
|
3
|
-
import { IPlan, ISubscription, IInvoice, ICoupon, BillableEntityType, SubscriptionStatus, InvoiceStatus, BillingCycle } from '@qelos/global-types';
|
|
3
|
+
import { IPlan, ISubscription, IInvoice, ICoupon, BillableEntityType, SubscriptionStatus, InvoiceStatus, BillingCycle, IPaymentsConfigurationMetadata } from '@qelos/global-types';
|
|
4
|
+
export interface PaymentsConfigurationRecord {
|
|
5
|
+
key: string;
|
|
6
|
+
metadata: IPaymentsConfigurationMetadata;
|
|
7
|
+
}
|
|
4
8
|
export interface AdminCheckoutRequest {
|
|
5
9
|
planId: string;
|
|
6
10
|
billingCycle: BillingCycle;
|
|
@@ -10,6 +14,8 @@ export interface AdminCheckoutRequest {
|
|
|
10
14
|
couponCode?: string;
|
|
11
15
|
successUrl?: string;
|
|
12
16
|
cancelUrl?: string;
|
|
17
|
+
/** When true, cancels any existing active/trialing subscription before checkout. */
|
|
18
|
+
reset?: boolean;
|
|
13
19
|
/**
|
|
14
20
|
* Admin-only. For plans with `dynamic: true`, sets the `dynamicAmount` on the
|
|
15
21
|
* pending subscription before initiating checkout. Regular users cannot set this;
|
|
@@ -77,4 +83,8 @@ export default class QlPaymentsAdmin extends BaseSDK {
|
|
|
77
83
|
createCoupon(data: Omit<ICoupon, '_id' | 'tenant' | 'created' | 'currentRedemptions'>): Promise<ICoupon>;
|
|
78
84
|
updateCoupon(couponId: string, data: Partial<ICoupon>): Promise<ICoupon>;
|
|
79
85
|
deleteCoupon(couponId: string): Promise<ICoupon>;
|
|
86
|
+
/** Load tenant payments settings (`payments-configuration`). */
|
|
87
|
+
getPaymentsConfiguration(): Promise<PaymentsConfigurationRecord>;
|
|
88
|
+
/** Update tenant payments settings. Partial metadata is merged with the existing record. */
|
|
89
|
+
updatePaymentsConfiguration(metadata: Partial<IPaymentsConfigurationMetadata>): Promise<PaymentsConfigurationRecord>;
|
|
80
90
|
}
|
|
@@ -108,5 +108,17 @@ export default class QlPaymentsAdmin extends BaseSDK {
|
|
|
108
108
|
deleteCoupon(couponId) {
|
|
109
109
|
return this.callJsonApi(`/api/coupons/${couponId}`, { method: 'delete' });
|
|
110
110
|
}
|
|
111
|
+
/** Load tenant payments settings (`payments-configuration`). */
|
|
112
|
+
getPaymentsConfiguration() {
|
|
113
|
+
return this.callJsonApi('/api/payments/configuration');
|
|
114
|
+
}
|
|
115
|
+
/** Update tenant payments settings. Partial metadata is merged with the existing record. */
|
|
116
|
+
updatePaymentsConfiguration(metadata) {
|
|
117
|
+
return this.callJsonApi('/api/payments/configuration', {
|
|
118
|
+
method: 'put',
|
|
119
|
+
headers: { 'content-type': 'application/json' },
|
|
120
|
+
body: JSON.stringify({ metadata }),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
111
123
|
}
|
|
112
124
|
//# sourceMappingURL=payments.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payments.js","sourceRoot":"","sources":["../../src/administrator/payments.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"payments.js","sourceRoot":"","sources":["../../src/administrator/payments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as assert from 'node:assert/strict';
|
|
2
|
+
import { test } from 'node:test';
|
|
3
|
+
import { IntegrationSourceKind } from '@qelos/global-types';
|
|
4
|
+
import QlIntegrationSources from '../integration-sources';
|
|
5
|
+
function jsonResponse(body) {
|
|
6
|
+
const res = new Response(JSON.stringify(body));
|
|
7
|
+
res.headers.set('Content-Type', 'application/json');
|
|
8
|
+
return res;
|
|
9
|
+
}
|
|
10
|
+
const connectedResult = {
|
|
11
|
+
status: 'connected',
|
|
12
|
+
message: 'Connection verified',
|
|
13
|
+
kind: IntegrationSourceKind.Sumit,
|
|
14
|
+
checkedAt: '2026-07-23T05:00:00.000Z',
|
|
15
|
+
};
|
|
16
|
+
test('QlIntegrationSources status methods', async (t) => {
|
|
17
|
+
await t.test('checkStatus sends POST to saved source status path', async () => {
|
|
18
|
+
let url = '';
|
|
19
|
+
let init;
|
|
20
|
+
const options = {
|
|
21
|
+
appUrl: 'http://localhost:3000',
|
|
22
|
+
fetch: async (requestUrl, requestInit) => {
|
|
23
|
+
url = requestUrl.toString();
|
|
24
|
+
init = requestInit;
|
|
25
|
+
return jsonResponse(connectedResult);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
const sdk = new QlIntegrationSources(options);
|
|
29
|
+
const result = await sdk.checkStatus('src-123');
|
|
30
|
+
assert.equal(url, 'http://localhost:3000/api/integration-sources/src-123/status');
|
|
31
|
+
assert.equal(init?.method, 'post');
|
|
32
|
+
assert.equal((init?.headers)['content-type'], 'application/json');
|
|
33
|
+
assert.deepEqual(JSON.parse(init?.body), {});
|
|
34
|
+
assert.deepEqual(result, connectedResult);
|
|
35
|
+
});
|
|
36
|
+
await t.test('checkStatus sends metadata and authentication overrides', async () => {
|
|
37
|
+
let init;
|
|
38
|
+
const options = {
|
|
39
|
+
appUrl: 'http://localhost:3000',
|
|
40
|
+
fetch: async (_url, requestInit) => {
|
|
41
|
+
init = requestInit;
|
|
42
|
+
return jsonResponse(connectedResult);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const sdk = new QlIntegrationSources(options);
|
|
46
|
+
await sdk.checkStatus('src-123', {
|
|
47
|
+
metadata: { companyId: '42' },
|
|
48
|
+
authentication: { apiKey: 'secret' },
|
|
49
|
+
});
|
|
50
|
+
assert.deepEqual(JSON.parse(init?.body), {
|
|
51
|
+
metadata: { companyId: '42' },
|
|
52
|
+
authentication: { apiKey: 'secret' },
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
await t.test('checkDraftStatus sends POST to draft status path', async () => {
|
|
56
|
+
let url = '';
|
|
57
|
+
let init;
|
|
58
|
+
const options = {
|
|
59
|
+
appUrl: 'http://localhost:3000',
|
|
60
|
+
fetch: async (requestUrl, requestInit) => {
|
|
61
|
+
url = requestUrl.toString();
|
|
62
|
+
init = requestInit;
|
|
63
|
+
return jsonResponse(connectedResult);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
const draft = {
|
|
67
|
+
kind: IntegrationSourceKind.PayPal,
|
|
68
|
+
metadata: { environment: 'sandbox' },
|
|
69
|
+
authentication: { clientId: 'id', clientSecret: 'secret' },
|
|
70
|
+
};
|
|
71
|
+
const sdk = new QlIntegrationSources(options);
|
|
72
|
+
const result = await sdk.checkDraftStatus(draft);
|
|
73
|
+
assert.equal(url, 'http://localhost:3000/api/integration-sources/status');
|
|
74
|
+
assert.equal(init?.method, 'post');
|
|
75
|
+
assert.equal((init?.headers)['content-type'], 'application/json');
|
|
76
|
+
assert.deepEqual(JSON.parse(init?.body), draft);
|
|
77
|
+
assert.deepEqual(result, connectedResult);
|
|
78
|
+
});
|
|
79
|
+
await t.test('checkDraftStatus is kind-agnostic and works for non-payment sources too', async () => {
|
|
80
|
+
let init;
|
|
81
|
+
const emailConnectedResult = {
|
|
82
|
+
status: 'connected',
|
|
83
|
+
message: 'SMTP connection verified',
|
|
84
|
+
kind: IntegrationSourceKind.Email,
|
|
85
|
+
checkedAt: '2026-07-23T05:00:00.000Z',
|
|
86
|
+
};
|
|
87
|
+
const options = {
|
|
88
|
+
appUrl: 'http://localhost:3000',
|
|
89
|
+
fetch: async (_url, requestInit) => {
|
|
90
|
+
init = requestInit;
|
|
91
|
+
return jsonResponse(emailConnectedResult);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
const draft = {
|
|
95
|
+
kind: IntegrationSourceKind.Email,
|
|
96
|
+
metadata: { smtp: 'smtp.example.com', username: 'bot@example.com', email: 'bot@example.com', pop3: 'pop3.example.com', senderName: 'Bot' },
|
|
97
|
+
authentication: { password: 'secret' },
|
|
98
|
+
};
|
|
99
|
+
const sdk = new QlIntegrationSources(options);
|
|
100
|
+
const result = await sdk.checkDraftStatus(draft);
|
|
101
|
+
assert.deepEqual(JSON.parse(init?.body), draft);
|
|
102
|
+
assert.deepEqual(result, emailConnectedResult);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=integration-sources-status.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration-sources-status.test.js","sourceRoot":"","sources":["../../../src/administrator/tests/integration-sources-status.test.ts"],"names":[],"mappings":"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"}
|
package/dist/authentication.d.ts
CHANGED
|
@@ -49,7 +49,18 @@ export interface IApiToken {
|
|
|
49
49
|
export type SocialProvider = 'linkedin' | 'facebook' | 'google' | 'github';
|
|
50
50
|
export interface SocialLoginOptions {
|
|
51
51
|
state?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Where Qelos redirects after OAuth with `?rt=<refreshToken>` (SDK flow).
|
|
54
|
+
* Absolute URLs must use a host listed in app-configuration `metadata.websiteUrls`.
|
|
55
|
+
*/
|
|
52
56
|
returnUrl?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Overrides the OAuth provider callback origin when its host is listed in
|
|
59
|
+
* app-configuration `metadata.websiteUrls`. The callback path stays
|
|
60
|
+
* `/api/auth/<provider>/callback` on that origin. When omitted, Qelos uses
|
|
61
|
+
* the request tenant host (`tenanthost`).
|
|
62
|
+
*/
|
|
63
|
+
redirectUrl?: string;
|
|
53
64
|
}
|
|
54
65
|
export interface SocialAuthCallbackPayload {
|
|
55
66
|
payload: BasicPayload & {
|
package/dist/authentication.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import BaseSDK from './base-sdk';
|
|
2
|
+
import { decodeMeUser } from './services/decode-uri-fields';
|
|
2
3
|
function collectSetCookieHeaders(res) {
|
|
3
4
|
const h = res.headers;
|
|
4
5
|
if (typeof h.getSetCookie === 'function') {
|
|
@@ -166,7 +167,7 @@ export default class QlAuthentication extends BaseSDK {
|
|
|
166
167
|
async apiTokenSignin(apiToken) {
|
|
167
168
|
this.#apiToken = apiToken;
|
|
168
169
|
this.qlOptions.apiToken = apiToken;
|
|
169
|
-
return this.callJsonApi('/api/me');
|
|
170
|
+
return this.callJsonApi('/api/me').then(decodeMeUser);
|
|
170
171
|
}
|
|
171
172
|
listApiTokens() {
|
|
172
173
|
return this.callJsonApi('/api/me/api-tokens');
|
|
@@ -192,6 +193,8 @@ export default class QlAuthentication extends BaseSDK {
|
|
|
192
193
|
params.state = options.state;
|
|
193
194
|
if (options.returnUrl)
|
|
194
195
|
params.returnUrl = options.returnUrl;
|
|
196
|
+
if (options.redirectUrl)
|
|
197
|
+
params.redirectUrl = options.redirectUrl;
|
|
195
198
|
const qs = Object.keys(params).length ? `?${new URLSearchParams(params)}` : '';
|
|
196
199
|
return this.buildUrl(`/api/auth/${provider}${qs}`);
|
|
197
200
|
}
|
|
@@ -249,14 +252,14 @@ export default class QlAuthentication extends BaseSDK {
|
|
|
249
252
|
return this.exchangeAuthCallback(refreshToken);
|
|
250
253
|
}
|
|
251
254
|
getLoggedInUser() {
|
|
252
|
-
return this.callJsonApi('/api/me');
|
|
255
|
+
return this.callJsonApi('/api/me').then(decodeMeUser);
|
|
253
256
|
}
|
|
254
257
|
updateLoggedInUser(changes) {
|
|
255
258
|
return this.callJsonApi('/api/me', {
|
|
256
259
|
method: 'post',
|
|
257
260
|
headers: { 'content-type': 'application/json' },
|
|
258
261
|
body: JSON.stringify(changes)
|
|
259
|
-
});
|
|
262
|
+
}).then(decodeMeUser);
|
|
260
263
|
}
|
|
261
264
|
}
|
|
262
265
|
//# sourceMappingURL=authentication.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authentication.js","sourceRoot":"","sources":["../src/authentication.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"authentication.js","sourceRoot":"","sources":["../src/authentication.ts"],"names":[],"mappings":"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"}
|
package/dist/payments.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export interface CheckoutRequest {
|
|
|
9
9
|
couponCode?: string;
|
|
10
10
|
successUrl?: string;
|
|
11
11
|
cancelUrl?: string;
|
|
12
|
+
/**
|
|
13
|
+
* When true, cancels any existing active or trialing subscription for the
|
|
14
|
+
* authenticated billable entity before starting checkout (e.g. plan change).
|
|
15
|
+
*/
|
|
16
|
+
reset?: boolean;
|
|
12
17
|
/**
|
|
13
18
|
* Pre-created subscription ID. Required for dynamic plans — the admin must set
|
|
14
19
|
* `dynamicAmount` on the subscription before this checkout call will succeed.
|
package/dist/payments.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payments.js","sourceRoot":"","sources":["../src/payments.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"payments.js","sourceRoot":"","sources":["../src/payments.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,YAAY,CAAC;AAgCjC,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,OAAO;IACrC,YAAY,GAAG,MAAM,CAAC;IAE9B,YAAY,OAAwB;QAClC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjB,CAAC;IAED,QAAQ,CAAC,KAA8B;QACrC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,eAAe,CAAC,KAAY,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,OAAO,IAAI,CAAC,WAAW,CAAU,GAAG,IAAI,CAAC,YAAY,gBAAgB,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,CAAC,MAAc;QACpB,OAAO,IAAI,CAAC,WAAW,CAAQ,GAAG,IAAI,CAAC,YAAY,UAAU,MAAM,EAAE,CAAC,CAAC;IACzE,CAAC;IAED;;;;;OAKG;IACH,eAAe,CAAC,MAAc,EAAE,YAA0B,EAAE,UAAmB;QAC7E,OAAO,IAAI,CAAC,WAAW,CACrB,GAAG,IAAI,CAAC,YAAY,gBAAgB,EACpC;YACE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;SAC3D,CACF,CAAC;IACJ,CAAC;IAED;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,MAAuB;QAC9B,OAAO,IAAI,CAAC,WAAW,CACrB,GAAG,IAAI,CAAC,YAAY,WAAW,EAC/B;YACE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;SAC7B,CACF,CAAC;IACJ,CAAC;IAED,iBAAiB;QACf,OAAO,IAAI,CAAC,WAAW,CAAgB,GAAG,IAAI,CAAC,YAAY,mBAAmB,CAAC,CAAC;IAClF,CAAC;IAED,kBAAkB,CAAC,cAAsB;QACvC,OAAO,IAAI,CAAC,WAAW,CACrB,GAAG,IAAI,CAAC,YAAY,kBAAkB,cAAc,SAAS,EAC7D,EAAE,MAAM,EAAE,KAAK,EAAE,CAClB,CAAC;IACJ,CAAC;IAED,WAAW,CAAC,KAA2B;QACrC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,eAAe,CAAC,KAAY,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,OAAO,IAAI,CAAC,WAAW,CAAa,GAAG,IAAI,CAAC,YAAY,YAAY,EAAE,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,WAAW,CAAW,GAAG,IAAI,CAAC,YAAY,aAAa,SAAS,EAAE,CAAC,CAAC;IAClF,CAAC;IAED,cAAc,CAAC,IAAY,EAAE,MAAe;QAC1C,OAAO,IAAI,CAAC,WAAW,CACrB,GAAG,IAAI,CAAC,YAAY,mBAAmB,EACvC;YACE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;SACvC,CACF,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Mirrors auth `encodeIfNeeded` — only decodes values that are URI-encoded. */
|
|
2
|
+
export declare function decodeIfNeeded(value?: string): string | undefined;
|
|
3
|
+
/** Decodes URI-encoded user fields returned by `GET/POST /api/me`. */
|
|
4
|
+
export declare function decodeMeUser<T extends Record<string, unknown>>(user: T): T;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Mirrors auth `encodeIfNeeded` — only decodes values that are URI-encoded. */
|
|
2
|
+
export function decodeIfNeeded(value) {
|
|
3
|
+
if (value === undefined || value === null || value === '') {
|
|
4
|
+
return value;
|
|
5
|
+
}
|
|
6
|
+
try {
|
|
7
|
+
const decoded = decodeURIComponent(value);
|
|
8
|
+
if (encodeURIComponent(decoded) === value) {
|
|
9
|
+
return decoded;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// not a valid encoded URI component
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
const ME_USER_STRING_FIELDS = ['name', 'fullName', 'firstName', 'lastName'];
|
|
18
|
+
/** Decodes URI-encoded user fields returned by `GET/POST /api/me`. */
|
|
19
|
+
export function decodeMeUser(user) {
|
|
20
|
+
if (!user || typeof user !== 'object') {
|
|
21
|
+
return user;
|
|
22
|
+
}
|
|
23
|
+
const result = { ...user };
|
|
24
|
+
for (const field of ME_USER_STRING_FIELDS) {
|
|
25
|
+
const value = result[field];
|
|
26
|
+
if (typeof value === 'string') {
|
|
27
|
+
result[field] = decodeIfNeeded(value);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const workspace = result.workspace;
|
|
31
|
+
if (workspace && typeof workspace === 'object' && !Array.isArray(workspace)) {
|
|
32
|
+
const ws = workspace;
|
|
33
|
+
if (typeof ws.name === 'string') {
|
|
34
|
+
result.workspace = {
|
|
35
|
+
...ws,
|
|
36
|
+
name: decodeIfNeeded(ws.name),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=decode-uri-fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode-uri-fields.js","sourceRoot":"","sources":["../../src/services/decode-uri-fields.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE;QACzD,OAAO,KAAK,CAAC;KACd;IAED,IAAI;QACF,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,kBAAkB,CAAC,OAAO,CAAC,KAAK,KAAK,EAAE;YACzC,OAAO,OAAO,CAAC;SAChB;KACF;IAAC,MAAM;QACN,oCAAoC;KACrC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,CAAU,CAAC;AAErF,sEAAsE;AACtE,MAAM,UAAU,YAAY,CAAoC,IAAO;IACrE,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QACrC,OAAO,IAAI,CAAC;KACb;IAED,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAE3B,KAAK,MAAM,KAAK,IAAI,qBAAqB,EAAE;QACzC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;YAC5B,MAAkC,CAAC,KAAK,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;SACpE;KACF;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IACnC,IAAI,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;QAC3E,MAAM,EAAE,GAAG,SAAoC,CAAC;QAChD,IAAI,OAAO,EAAE,CAAC,IAAI,KAAK,QAAQ,EAAE;YAC9B,MAAkC,CAAC,SAAS,GAAG;gBAC9C,GAAG,EAAE;gBACL,IAAI,EAAE,cAAc,CAAC,EAAE,CAAC,IAAI,CAAC;aAC9B,CAAC;SACH;KACF;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/docs/payments.md
CHANGED
|
@@ -104,6 +104,60 @@ const result = await adminSdk.payments.checkout({
|
|
|
104
104
|
|
|
105
105
|
---
|
|
106
106
|
|
|
107
|
+
## Payments configuration
|
|
108
|
+
|
|
109
|
+
Admin-only tenant settings stored in `payments-configuration`.
|
|
110
|
+
|
|
111
|
+
### `GET /api/payments/configuration`
|
|
112
|
+
|
|
113
|
+
Returns the current payments configuration metadata. **Auth**: privileged.
|
|
114
|
+
|
|
115
|
+
**Response `200`**
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"key": "payments-configuration",
|
|
120
|
+
"metadata": {
|
|
121
|
+
"isEnabled": true,
|
|
122
|
+
"paymentSourceId": "integration-source-id",
|
|
123
|
+
"defaultCurrency": "ILS",
|
|
124
|
+
"gracePeriodDays": 3,
|
|
125
|
+
"successUrl": "https://your-app.com/billing/return?status=success",
|
|
126
|
+
"cancelUrl": "https://your-app.com/billing/return?status=cancel"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### `PUT /api/payments/configuration`
|
|
132
|
+
|
|
133
|
+
Creates or updates payments configuration. Partial `metadata` is merged with the existing record. **Auth**: privileged.
|
|
134
|
+
|
|
135
|
+
**Body**
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"metadata": {
|
|
140
|
+
"isEnabled": true,
|
|
141
|
+
"paymentSourceId": "integration-source-id",
|
|
142
|
+
"successUrl": "https://your-app.com/billing/return?status=success",
|
|
143
|
+
"cancelUrl": "https://your-app.com/billing/return?status=cancel"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| Field | Type | Description |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `isEnabled` | boolean | Enable payments for the tenant |
|
|
151
|
+
| `paymentSourceId` | string | Integration source `_id` for the payment provider |
|
|
152
|
+
| `defaultCurrency` | string | Default ISO 4217 currency code |
|
|
153
|
+
| `gracePeriodDays` | number | Grace period after failed payment |
|
|
154
|
+
| `successUrl` | string | Default redirect URL after successful checkout |
|
|
155
|
+
| `cancelUrl` | string | Default redirect URL when checkout is canceled |
|
|
156
|
+
|
|
157
|
+
> **SDK:** `adminSdk.payments.getPaymentsConfiguration()` · `adminSdk.payments.updatePaymentsConfiguration(metadata)`
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
107
161
|
## Listing Plans and Subscriptions
|
|
108
162
|
|
|
109
163
|
### User SDK
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qelos/sdk",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "SDK for Qelos API",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"access": "public"
|
|
103
103
|
},
|
|
104
104
|
"dependencies": {
|
|
105
|
-
"@qelos/global-types": "^4.
|
|
105
|
+
"@qelos/global-types": "^4.2.0"
|
|
106
106
|
},
|
|
107
107
|
"devDependencies": {
|
|
108
108
|
"tsx": "^4.21.0",
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { QelosSDKOptions, RequestExtra } from '../types';
|
|
2
2
|
import BaseSDK from '../base-sdk';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
IIntegrationSource,
|
|
5
|
+
IIntegrationSourceStatusRequest,
|
|
6
|
+
IIntegrationSourceStatusResult,
|
|
7
|
+
} from '@qelos/global-types';
|
|
4
8
|
|
|
5
9
|
export default class QlIntegrationSources extends BaseSDK {
|
|
6
10
|
private relativePath = '/api/integration-sources';
|
|
@@ -40,4 +44,29 @@ export default class QlIntegrationSources extends BaseSDK {
|
|
|
40
44
|
})
|
|
41
45
|
}
|
|
42
46
|
|
|
47
|
+
checkStatus(
|
|
48
|
+
sourceId: string,
|
|
49
|
+
overrides?: Partial<Pick<IIntegrationSource, 'metadata' | 'authentication'>>,
|
|
50
|
+
): Promise<IIntegrationSourceStatusResult> {
|
|
51
|
+
return this.callJsonApi<IIntegrationSourceStatusResult>(
|
|
52
|
+
`${this.relativePath}/${sourceId}/status`,
|
|
53
|
+
{
|
|
54
|
+
method: 'post',
|
|
55
|
+
headers: { 'content-type': 'application/json' },
|
|
56
|
+
body: JSON.stringify(overrides ?? {}),
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
checkDraftStatus(source: IIntegrationSourceStatusRequest): Promise<IIntegrationSourceStatusResult> {
|
|
62
|
+
return this.callJsonApi<IIntegrationSourceStatusResult>(
|
|
63
|
+
`${this.relativePath}/status`,
|
|
64
|
+
{
|
|
65
|
+
method: 'post',
|
|
66
|
+
headers: { 'content-type': 'application/json' },
|
|
67
|
+
body: JSON.stringify(source),
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
43
72
|
}
|
|
@@ -3,8 +3,14 @@ import BaseSDK from '../base-sdk';
|
|
|
3
3
|
import {
|
|
4
4
|
IPlan, ISubscription, IInvoice, ICoupon,
|
|
5
5
|
BillableEntityType, SubscriptionStatus, InvoiceStatus, BillingCycle,
|
|
6
|
+
IPaymentsConfigurationMetadata,
|
|
6
7
|
} from '@qelos/global-types';
|
|
7
8
|
|
|
9
|
+
export interface PaymentsConfigurationRecord {
|
|
10
|
+
key: string;
|
|
11
|
+
metadata: IPaymentsConfigurationMetadata;
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
export interface AdminCheckoutRequest {
|
|
9
15
|
planId: string;
|
|
10
16
|
billingCycle: BillingCycle;
|
|
@@ -14,6 +20,8 @@ export interface AdminCheckoutRequest {
|
|
|
14
20
|
couponCode?: string;
|
|
15
21
|
successUrl?: string;
|
|
16
22
|
cancelUrl?: string;
|
|
23
|
+
/** When true, cancels any existing active/trialing subscription before checkout. */
|
|
24
|
+
reset?: boolean;
|
|
17
25
|
/**
|
|
18
26
|
* Admin-only. For plans with `dynamic: true`, sets the `dynamicAmount` on the
|
|
19
27
|
* pending subscription before initiating checkout. Regular users cannot set this;
|
|
@@ -181,4 +189,18 @@ export default class QlPaymentsAdmin extends BaseSDK {
|
|
|
181
189
|
deleteCoupon(couponId: string) {
|
|
182
190
|
return this.callJsonApi<ICoupon>(`/api/coupons/${couponId}`, { method: 'delete' });
|
|
183
191
|
}
|
|
192
|
+
|
|
193
|
+
/** Load tenant payments settings (`payments-configuration`). */
|
|
194
|
+
getPaymentsConfiguration() {
|
|
195
|
+
return this.callJsonApi<PaymentsConfigurationRecord>('/api/payments/configuration');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Update tenant payments settings. Partial metadata is merged with the existing record. */
|
|
199
|
+
updatePaymentsConfiguration(metadata: Partial<IPaymentsConfigurationMetadata>) {
|
|
200
|
+
return this.callJsonApi<PaymentsConfigurationRecord>('/api/payments/configuration', {
|
|
201
|
+
method: 'put',
|
|
202
|
+
headers: { 'content-type': 'application/json' },
|
|
203
|
+
body: JSON.stringify({ metadata }),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
184
206
|
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as assert from 'node:assert/strict';
|
|
2
|
+
import { test } from 'node:test';
|
|
3
|
+
import { IntegrationSourceKind } from '@qelos/global-types';
|
|
4
|
+
import { QelosSDKOptions } from '../../types';
|
|
5
|
+
import QlIntegrationSources from '../integration-sources';
|
|
6
|
+
|
|
7
|
+
function jsonResponse(body: unknown) {
|
|
8
|
+
const res = new Response(JSON.stringify(body));
|
|
9
|
+
res.headers.set('Content-Type', 'application/json');
|
|
10
|
+
return res;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const connectedResult = {
|
|
14
|
+
status: 'connected' as const,
|
|
15
|
+
message: 'Connection verified',
|
|
16
|
+
kind: IntegrationSourceKind.Sumit,
|
|
17
|
+
checkedAt: '2026-07-23T05:00:00.000Z',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
test('QlIntegrationSources status methods', async (t) => {
|
|
21
|
+
await t.test('checkStatus sends POST to saved source status path', async () => {
|
|
22
|
+
let url = '';
|
|
23
|
+
let init: RequestInit | undefined;
|
|
24
|
+
const options: QelosSDKOptions = {
|
|
25
|
+
appUrl: 'http://localhost:3000',
|
|
26
|
+
fetch: async (requestUrl, requestInit) => {
|
|
27
|
+
url = requestUrl.toString();
|
|
28
|
+
init = requestInit;
|
|
29
|
+
return jsonResponse(connectedResult);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const sdk = new QlIntegrationSources(options);
|
|
34
|
+
const result = await sdk.checkStatus('src-123');
|
|
35
|
+
|
|
36
|
+
assert.equal(url, 'http://localhost:3000/api/integration-sources/src-123/status');
|
|
37
|
+
assert.equal(init?.method, 'post');
|
|
38
|
+
assert.equal((init?.headers as Record<string, string>)['content-type'], 'application/json');
|
|
39
|
+
assert.deepEqual(JSON.parse(init?.body as string), {});
|
|
40
|
+
assert.deepEqual(result, connectedResult);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
await t.test('checkStatus sends metadata and authentication overrides', async () => {
|
|
44
|
+
let init: RequestInit | undefined;
|
|
45
|
+
const options: QelosSDKOptions = {
|
|
46
|
+
appUrl: 'http://localhost:3000',
|
|
47
|
+
fetch: async (_url, requestInit) => {
|
|
48
|
+
init = requestInit;
|
|
49
|
+
return jsonResponse(connectedResult);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const sdk = new QlIntegrationSources(options);
|
|
54
|
+
await sdk.checkStatus('src-123', {
|
|
55
|
+
metadata: { companyId: '42' },
|
|
56
|
+
authentication: { apiKey: 'secret' },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
assert.deepEqual(JSON.parse(init?.body as string), {
|
|
60
|
+
metadata: { companyId: '42' },
|
|
61
|
+
authentication: { apiKey: 'secret' },
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await t.test('checkDraftStatus sends POST to draft status path', async () => {
|
|
66
|
+
let url = '';
|
|
67
|
+
let init: RequestInit | undefined;
|
|
68
|
+
const options: QelosSDKOptions = {
|
|
69
|
+
appUrl: 'http://localhost:3000',
|
|
70
|
+
fetch: async (requestUrl, requestInit) => {
|
|
71
|
+
url = requestUrl.toString();
|
|
72
|
+
init = requestInit;
|
|
73
|
+
return jsonResponse(connectedResult);
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const draft = {
|
|
78
|
+
kind: IntegrationSourceKind.PayPal,
|
|
79
|
+
metadata: { environment: 'sandbox' },
|
|
80
|
+
authentication: { clientId: 'id', clientSecret: 'secret' },
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const sdk = new QlIntegrationSources(options);
|
|
84
|
+
const result = await sdk.checkDraftStatus(draft);
|
|
85
|
+
|
|
86
|
+
assert.equal(url, 'http://localhost:3000/api/integration-sources/status');
|
|
87
|
+
assert.equal(init?.method, 'post');
|
|
88
|
+
assert.equal((init?.headers as Record<string, string>)['content-type'], 'application/json');
|
|
89
|
+
assert.deepEqual(JSON.parse(init?.body as string), draft);
|
|
90
|
+
assert.deepEqual(result, connectedResult);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await t.test('checkDraftStatus is kind-agnostic and works for non-payment sources too', async () => {
|
|
94
|
+
let init: RequestInit | undefined;
|
|
95
|
+
const emailConnectedResult = {
|
|
96
|
+
status: 'connected' as const,
|
|
97
|
+
message: 'SMTP connection verified',
|
|
98
|
+
kind: IntegrationSourceKind.Email,
|
|
99
|
+
checkedAt: '2026-07-23T05:00:00.000Z',
|
|
100
|
+
};
|
|
101
|
+
const options: QelosSDKOptions = {
|
|
102
|
+
appUrl: 'http://localhost:3000',
|
|
103
|
+
fetch: async (_url, requestInit) => {
|
|
104
|
+
init = requestInit;
|
|
105
|
+
return jsonResponse(emailConnectedResult);
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const draft = {
|
|
110
|
+
kind: IntegrationSourceKind.Email,
|
|
111
|
+
metadata: { smtp: 'smtp.example.com', username: 'bot@example.com', email: 'bot@example.com', pop3: 'pop3.example.com', senderName: 'Bot' },
|
|
112
|
+
authentication: { password: 'secret' },
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const sdk = new QlIntegrationSources(options);
|
|
116
|
+
const result = await sdk.checkDraftStatus(draft);
|
|
117
|
+
|
|
118
|
+
assert.deepEqual(JSON.parse(init?.body as string), draft);
|
|
119
|
+
assert.deepEqual(result, emailConnectedResult);
|
|
120
|
+
});
|
|
121
|
+
});
|
package/src/authentication.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { QelosSDKOptions } from './types';
|
|
2
2
|
import BaseSDK from './base-sdk';
|
|
3
|
+
import { decodeMeUser } from './services/decode-uri-fields';
|
|
3
4
|
|
|
4
5
|
export interface IUser {
|
|
5
6
|
_id: string;
|
|
@@ -54,7 +55,18 @@ export type SocialProvider = 'linkedin' | 'facebook' | 'google' | 'github';
|
|
|
54
55
|
|
|
55
56
|
export interface SocialLoginOptions {
|
|
56
57
|
state?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Where Qelos redirects after OAuth with `?rt=<refreshToken>` (SDK flow).
|
|
60
|
+
* Absolute URLs must use a host listed in app-configuration `metadata.websiteUrls`.
|
|
61
|
+
*/
|
|
57
62
|
returnUrl?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Overrides the OAuth provider callback origin when its host is listed in
|
|
65
|
+
* app-configuration `metadata.websiteUrls`. The callback path stays
|
|
66
|
+
* `/api/auth/<provider>/callback` on that origin. When omitted, Qelos uses
|
|
67
|
+
* the request tenant host (`tenanthost`).
|
|
68
|
+
*/
|
|
69
|
+
redirectUrl?: string;
|
|
58
70
|
}
|
|
59
71
|
|
|
60
72
|
export interface SocialAuthCallbackPayload {
|
|
@@ -271,7 +283,7 @@ export default class QlAuthentication extends BaseSDK {
|
|
|
271
283
|
async apiTokenSignin(apiToken: string): Promise<IUser> {
|
|
272
284
|
this.#apiToken = apiToken;
|
|
273
285
|
this.qlOptions.apiToken = apiToken;
|
|
274
|
-
return this.callJsonApi<IUser>('/api/me');
|
|
286
|
+
return this.callJsonApi<IUser>('/api/me').then(decodeMeUser);
|
|
275
287
|
}
|
|
276
288
|
|
|
277
289
|
listApiTokens(): Promise<IApiToken[]> {
|
|
@@ -300,6 +312,7 @@ export default class QlAuthentication extends BaseSDK {
|
|
|
300
312
|
const params: Record<string, string> = {};
|
|
301
313
|
if (options.state) params.state = options.state;
|
|
302
314
|
if (options.returnUrl) params.returnUrl = options.returnUrl;
|
|
315
|
+
if (options.redirectUrl) params.redirectUrl = options.redirectUrl;
|
|
303
316
|
const qs = Object.keys(params).length ? `?${new URLSearchParams(params)}` : '';
|
|
304
317
|
return this.buildUrl(`/api/auth/${provider}${qs}`);
|
|
305
318
|
}
|
|
@@ -364,7 +377,7 @@ export default class QlAuthentication extends BaseSDK {
|
|
|
364
377
|
}
|
|
365
378
|
|
|
366
379
|
getLoggedInUser() {
|
|
367
|
-
return this.callJsonApi<IUser>('/api/me')
|
|
380
|
+
return this.callJsonApi<IUser>('/api/me').then(decodeMeUser);
|
|
368
381
|
}
|
|
369
382
|
|
|
370
383
|
updateLoggedInUser(changes: Partial<IUser & { password?: string }>): Promise<IUser> {
|
|
@@ -372,6 +385,6 @@ export default class QlAuthentication extends BaseSDK {
|
|
|
372
385
|
method: 'post',
|
|
373
386
|
headers: { 'content-type': 'application/json' },
|
|
374
387
|
body: JSON.stringify(changes)
|
|
375
|
-
})
|
|
388
|
+
}).then(decodeMeUser);
|
|
376
389
|
}
|
|
377
390
|
}
|
package/src/payments.ts
CHANGED
|
@@ -10,6 +10,11 @@ export interface CheckoutRequest {
|
|
|
10
10
|
couponCode?: string;
|
|
11
11
|
successUrl?: string;
|
|
12
12
|
cancelUrl?: string;
|
|
13
|
+
/**
|
|
14
|
+
* When true, cancels any existing active or trialing subscription for the
|
|
15
|
+
* authenticated billable entity before starting checkout (e.g. plan change).
|
|
16
|
+
*/
|
|
17
|
+
reset?: boolean;
|
|
13
18
|
/**
|
|
14
19
|
* Pre-created subscription ID. Required for dynamic plans — the admin must set
|
|
15
20
|
* `dynamicAmount` on the subscription before this checkout call will succeed.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Mirrors auth `encodeIfNeeded` — only decodes values that are URI-encoded. */
|
|
2
|
+
export function decodeIfNeeded(value?: string): string | undefined {
|
|
3
|
+
if (value === undefined || value === null || value === '') {
|
|
4
|
+
return value;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
const decoded = decodeURIComponent(value);
|
|
9
|
+
if (encodeURIComponent(decoded) === value) {
|
|
10
|
+
return decoded;
|
|
11
|
+
}
|
|
12
|
+
} catch {
|
|
13
|
+
// not a valid encoded URI component
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const ME_USER_STRING_FIELDS = ['name', 'fullName', 'firstName', 'lastName'] as const;
|
|
20
|
+
|
|
21
|
+
/** Decodes URI-encoded user fields returned by `GET/POST /api/me`. */
|
|
22
|
+
export function decodeMeUser<T extends Record<string, unknown>>(user: T): T {
|
|
23
|
+
if (!user || typeof user !== 'object') {
|
|
24
|
+
return user;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const result = { ...user };
|
|
28
|
+
|
|
29
|
+
for (const field of ME_USER_STRING_FIELDS) {
|
|
30
|
+
const value = result[field];
|
|
31
|
+
if (typeof value === 'string') {
|
|
32
|
+
(result as Record<string, unknown>)[field] = decodeIfNeeded(value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const workspace = result.workspace;
|
|
37
|
+
if (workspace && typeof workspace === 'object' && !Array.isArray(workspace)) {
|
|
38
|
+
const ws = workspace as Record<string, unknown>;
|
|
39
|
+
if (typeof ws.name === 'string') {
|
|
40
|
+
(result as Record<string, unknown>).workspace = {
|
|
41
|
+
...ws,
|
|
42
|
+
name: decodeIfNeeded(ws.name),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as assert from 'node:assert/strict';
|
|
2
|
+
import { test } from 'node:test';
|
|
3
|
+
import { decodeIfNeeded, decodeMeUser } from '../src/services/decode-uri-fields';
|
|
4
|
+
|
|
5
|
+
test('decodeIfNeeded', async (t) => {
|
|
6
|
+
await t.test('decodes URI-encoded values', () => {
|
|
7
|
+
assert.equal(decodeIfNeeded('David%20Meir-Levy'), 'David Meir-Levy');
|
|
8
|
+
assert.equal(
|
|
9
|
+
decodeIfNeeded(encodeURIComponent('My Workspace / Special & Name')),
|
|
10
|
+
'My Workspace / Special & Name',
|
|
11
|
+
);
|
|
12
|
+
assert.equal(decodeIfNeeded(encodeURIComponent('José García')), 'José García');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
await t.test('leaves plain strings unchanged', () => {
|
|
16
|
+
assert.equal(decodeIfNeeded('David Meir-Levy'), 'David Meir-Levy');
|
|
17
|
+
assert.equal(decodeIfNeeded('user@example.com'), 'user@example.com');
|
|
18
|
+
assert.equal(decodeIfNeeded('100% legit'), '100% legit');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await t.test('passes through empty and undefined values', () => {
|
|
22
|
+
assert.equal(decodeIfNeeded(''), '');
|
|
23
|
+
assert.equal(decodeIfNeeded(undefined), undefined);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('decodeMeUser', async (t) => {
|
|
28
|
+
await t.test('decodes user and workspace name fields', () => {
|
|
29
|
+
const encoded = {
|
|
30
|
+
_id: 'user-1',
|
|
31
|
+
name: 'David%20Meir-Levy',
|
|
32
|
+
fullName: 'David%20Meir-Levy',
|
|
33
|
+
firstName: 'David',
|
|
34
|
+
lastName: 'Meir-Levy',
|
|
35
|
+
workspace: {
|
|
36
|
+
_id: 'ws-1',
|
|
37
|
+
name: encodeURIComponent('My Workspace / Special & Name'),
|
|
38
|
+
roles: ['admin'],
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const decoded = decodeMeUser(encoded);
|
|
43
|
+
|
|
44
|
+
assert.equal(decoded.name, 'David Meir-Levy');
|
|
45
|
+
assert.equal(decoded.fullName, 'David Meir-Levy');
|
|
46
|
+
assert.equal(decoded.firstName, 'David');
|
|
47
|
+
assert.equal(decoded.lastName, 'Meir-Levy');
|
|
48
|
+
assert.equal(decoded.workspace.name, 'My Workspace / Special & Name');
|
|
49
|
+
assert.equal(decoded._id, 'user-1');
|
|
50
|
+
assert.deepEqual(decoded.workspace.roles, ['admin']);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await t.test('leaves non-encoded fields unchanged', () => {
|
|
54
|
+
const user = {
|
|
55
|
+
_id: 'user-1',
|
|
56
|
+
username: 'david@example.com',
|
|
57
|
+
email: 'david@example.com',
|
|
58
|
+
name: 'David Meir-Levy',
|
|
59
|
+
fullName: 'David Meir-Levy',
|
|
60
|
+
firstName: 'David',
|
|
61
|
+
lastName: 'Meir-Levy',
|
|
62
|
+
roles: ['user'],
|
|
63
|
+
metadata: {},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
assert.deepEqual(decodeMeUser(user), user);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('getLoggedInUser decodes /api/me response', async (t) => {
|
|
71
|
+
const { default: QelosSDK } = await import('../src/index');
|
|
72
|
+
|
|
73
|
+
await t.test('returns decoded user fields', async () => {
|
|
74
|
+
const sdk = new QelosSDK({
|
|
75
|
+
appUrl: 'http://localhost:3000',
|
|
76
|
+
accessToken: 'test-token',
|
|
77
|
+
fetch: async () => {
|
|
78
|
+
const res = new Response(
|
|
79
|
+
JSON.stringify({
|
|
80
|
+
_id: '6a33dcba2c798875d68aa8ab',
|
|
81
|
+
tenant: '6a280e991bbd4078edefe097',
|
|
82
|
+
username: 'david@davidlevy.co.il',
|
|
83
|
+
email: 'david@davidlevy.co.il',
|
|
84
|
+
name: 'David%20Meir-Levy',
|
|
85
|
+
fullName: 'David%20Meir-Levy',
|
|
86
|
+
firstName: 'David',
|
|
87
|
+
lastName: 'Meir-Levy',
|
|
88
|
+
roles: ['user'],
|
|
89
|
+
metadata: {},
|
|
90
|
+
workspace: {
|
|
91
|
+
_id: 'ws-1',
|
|
92
|
+
name: encodeURIComponent('Acme / Dev'),
|
|
93
|
+
roles: ['admin'],
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
res.headers.set('Content-Type', 'application/json');
|
|
98
|
+
return res;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const user = await sdk.authentication.getLoggedInUser();
|
|
103
|
+
|
|
104
|
+
assert.equal(user.name, 'David Meir-Levy');
|
|
105
|
+
assert.equal(user.fullName, 'David Meir-Levy');
|
|
106
|
+
assert.equal(user.workspace.name, 'Acme / Dev');
|
|
107
|
+
assert.equal(user.username, 'david@davidlevy.co.il');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -28,6 +28,30 @@ test('parseSocialCallbackRefreshToken', async (t) => {
|
|
|
28
28
|
});
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
+
test('getSocialLoginUrl', async (t) => {
|
|
32
|
+
const { default: QelosSDK } = await import('../src/index');
|
|
33
|
+
|
|
34
|
+
await t.test('builds provider URL with state, returnUrl, and redirectUrl', () => {
|
|
35
|
+
const sdk = new QelosSDK({ appUrl: 'https://gateway.example.com' });
|
|
36
|
+
const url = sdk.authentication.getSocialLoginUrl('google', {
|
|
37
|
+
state: 'csrf-1',
|
|
38
|
+
returnUrl: 'https://app.example.com/auth/finish',
|
|
39
|
+
redirectUrl: 'https://app.example.com',
|
|
40
|
+
});
|
|
41
|
+
const parsed = new URL(url);
|
|
42
|
+
assert.equal(parsed.origin, 'https://gateway.example.com');
|
|
43
|
+
assert.equal(parsed.pathname, '/api/auth/google');
|
|
44
|
+
assert.equal(parsed.searchParams.get('state'), 'csrf-1');
|
|
45
|
+
assert.equal(parsed.searchParams.get('returnUrl'), 'https://app.example.com/auth/finish');
|
|
46
|
+
assert.equal(parsed.searchParams.get('redirectUrl'), 'https://app.example.com');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await t.test('omits query string when no options', () => {
|
|
50
|
+
const sdk = new QelosSDK({ appUrl: 'http://localhost:3000' });
|
|
51
|
+
assert.equal(sdk.authentication.getSocialLoginUrl('github'), 'http://localhost:3000/api/auth/github');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
31
55
|
test('getSocialAuthSetCookieParts', async (t) => {
|
|
32
56
|
await t.test('prefers setCookieHeaders array', () => {
|
|
33
57
|
const r = {
|