@explorins/pers-sdk 1.1.2 → 1.1.3
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/analytics/api/analytics-api.d.ts +17 -0
- package/dist/analytics/api/analytics-api.d.ts.map +1 -0
- package/{src/analytics/index.ts → dist/analytics/index.d.ts} +28 -52
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/models/index.d.ts +61 -0
- package/dist/analytics/models/index.d.ts.map +1 -0
- package/dist/analytics/services/analytics-service.d.ts +19 -0
- package/dist/analytics/services/analytics-service.d.ts.map +1 -0
- package/dist/analytics.cjs +76 -0
- package/dist/analytics.cjs.map +1 -0
- package/dist/analytics.d.ts +28 -0
- package/dist/analytics.js +72 -0
- package/dist/analytics.js.map +1 -0
- package/dist/auth-admin/api/auth-admin-api.d.ts +27 -0
- package/dist/auth-admin/api/auth-admin-api.d.ts.map +1 -0
- package/dist/auth-admin/index.d.ts +26 -0
- package/dist/auth-admin/index.d.ts.map +1 -0
- package/dist/auth-admin/services/auth-admin-service.d.ts +23 -0
- package/dist/auth-admin/services/auth-admin-service.d.ts.map +1 -0
- package/dist/auth-admin.cjs +96 -0
- package/dist/auth-admin.cjs.map +1 -0
- package/dist/auth-admin.d.ts +26 -0
- package/dist/auth-admin.js +92 -0
- package/dist/auth-admin.js.map +1 -0
- package/dist/business/api/business-api.d.ts +133 -0
- package/dist/business/api/business-api.d.ts.map +1 -0
- package/dist/business/index.d.ts +34 -0
- package/dist/business/index.d.ts.map +1 -0
- package/dist/business/models/index.d.ts +8 -0
- package/dist/business/models/index.d.ts.map +1 -0
- package/dist/business/services/business-service.d.ts +51 -0
- package/dist/business/services/business-service.d.ts.map +1 -0
- package/dist/business.cjs +303 -0
- package/dist/business.cjs.map +1 -0
- package/dist/business.d.ts +34 -0
- package/dist/business.js +299 -0
- package/dist/business.js.map +1 -0
- package/dist/campaign/api/campaign-api.d.ts +213 -0
- package/dist/campaign/api/campaign-api.d.ts.map +1 -0
- package/dist/campaign/index.d.ts +44 -0
- package/dist/campaign/index.d.ts.map +1 -0
- package/dist/campaign/services/campaign-service.d.ts +88 -0
- package/dist/campaign/services/campaign-service.d.ts.map +1 -0
- package/dist/campaign.cjs +506 -0
- package/dist/campaign.cjs.map +1 -0
- package/dist/campaign.d.ts +44 -0
- package/dist/campaign.js +502 -0
- package/dist/campaign.js.map +1 -0
- package/dist/core/abstractions/http-client.d.ts +22 -0
- package/dist/core/abstractions/http-client.d.ts.map +1 -0
- package/dist/core/auth/auth-provider.interface.d.ts +12 -0
- package/dist/core/auth/auth-provider.interface.d.ts.map +1 -0
- package/dist/core/auth/create-auth-provider.d.ts +27 -0
- package/dist/core/auth/create-auth-provider.d.ts.map +1 -0
- package/dist/core/auth/simple-auth-config.interface.d.ts +15 -0
- package/dist/core/auth/simple-auth-config.interface.d.ts.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/pers-api-client.d.ts +54 -0
- package/dist/core/pers-api-client.d.ts.map +1 -0
- package/dist/core/pers-config.d.ts +38 -0
- package/dist/core/pers-config.d.ts.map +1 -0
- package/dist/core/utils/jwt.function.d.ts +2 -0
- package/dist/core/utils/jwt.function.d.ts.map +1 -0
- package/dist/core.cjs +506 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.ts +13 -0
- package/dist/core.js +497 -0
- package/dist/core.js.map +1 -0
- package/dist/donation/api/donation-api.d.ts +18 -0
- package/dist/donation/api/donation-api.d.ts.map +1 -0
- package/dist/donation/index.d.ts +25 -0
- package/dist/donation/index.d.ts.map +1 -0
- package/{src/donation/models/index.ts → dist/donation/models/index.d.ts} +8 -11
- package/dist/donation/models/index.d.ts.map +1 -0
- package/dist/donation/services/donation-service.d.ts +19 -0
- package/dist/donation/services/donation-service.d.ts.map +1 -0
- package/dist/donation.cjs +78 -0
- package/dist/donation.cjs.map +1 -0
- package/dist/donation.d.ts +25 -0
- package/dist/donation.js +74 -0
- package/dist/donation.js.map +1 -0
- package/dist/index.cjs +4217 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4164 -0
- package/dist/index.js.map +1 -0
- package/dist/package.json +148 -0
- package/dist/payment/api/payment-api.d.ts +105 -0
- package/dist/payment/api/payment-api.d.ts.map +1 -0
- package/dist/payment/index.d.ts +36 -0
- package/dist/payment/index.d.ts.map +1 -0
- package/dist/payment/models/index.d.ts +12 -0
- package/dist/payment/models/index.d.ts.map +1 -0
- package/dist/payment/services/payment-service.d.ts +40 -0
- package/dist/payment/services/payment-service.d.ts.map +1 -0
- package/dist/payment.cjs +258 -0
- package/dist/payment.cjs.map +1 -0
- package/dist/payment.d.ts +36 -0
- package/dist/payment.js +254 -0
- package/dist/payment.js.map +1 -0
- package/dist/pers-sdk.d.ts +29 -0
- package/dist/pers-sdk.d.ts.map +1 -0
- package/dist/redemption/api/redemption-api.d.ts +147 -0
- package/dist/redemption/api/redemption-api.d.ts.map +1 -0
- package/dist/redemption/index.d.ts +35 -0
- package/dist/redemption/index.d.ts.map +1 -0
- package/dist/redemption/models/index.d.ts +8 -0
- package/dist/redemption/models/index.d.ts.map +1 -0
- package/dist/redemption/services/redemption-service.d.ts +56 -0
- package/dist/redemption/services/redemption-service.d.ts.map +1 -0
- package/dist/redemption.cjs +333 -0
- package/dist/redemption.cjs.map +1 -0
- package/dist/redemption.d.ts +35 -0
- package/dist/redemption.js +329 -0
- package/dist/redemption.js.map +1 -0
- package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts +18 -0
- package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts.map +1 -0
- package/dist/tenant/api/tenant-api.d.ts +56 -0
- package/dist/tenant/api/tenant-api.d.ts.map +1 -0
- package/dist/tenant/index.d.ts +34 -0
- package/dist/tenant/index.d.ts.map +1 -0
- package/dist/tenant/models/index.d.ts +11 -0
- package/dist/tenant/models/index.d.ts.map +1 -0
- package/dist/tenant/services/tenant-service.d.ts +46 -0
- package/dist/tenant/services/tenant-service.d.ts.map +1 -0
- package/dist/tenant.cjs +177 -0
- package/dist/tenant.cjs.map +1 -0
- package/dist/tenant.d.ts +34 -0
- package/dist/tenant.js +173 -0
- package/dist/tenant.js.map +1 -0
- package/dist/token/api/token-api.d.ts +64 -0
- package/dist/token/api/token-api.d.ts.map +1 -0
- package/dist/token/base/base-token-service.d.ts +87 -0
- package/dist/token/base/base-token-service.d.ts.map +1 -0
- package/dist/token/index.d.ts +13 -0
- package/dist/token/index.d.ts.map +1 -0
- package/dist/token/models/index.d.ts +18 -0
- package/dist/token/models/index.d.ts.map +1 -0
- package/dist/token/services/token-service.d.ts +68 -0
- package/dist/token/services/token-service.d.ts.map +1 -0
- package/dist/token/token-sdk.d.ts +140 -0
- package/dist/token/token-sdk.d.ts.map +1 -0
- package/dist/token.cjs +537 -0
- package/dist/token.cjs.map +1 -0
- package/dist/token.d.ts +13 -0
- package/dist/token.js +532 -0
- package/dist/token.js.map +1 -0
- package/dist/transaction/api/transaction-api.d.ts +133 -0
- package/dist/transaction/api/transaction-api.d.ts.map +1 -0
- package/dist/transaction/index.d.ts +38 -0
- package/dist/transaction/index.d.ts.map +1 -0
- package/dist/transaction/models/index.d.ts +42 -0
- package/dist/transaction/models/index.d.ts.map +1 -0
- package/dist/transaction/services/transaction-service.d.ts +56 -0
- package/dist/transaction/services/transaction-service.d.ts.map +1 -0
- package/dist/transaction.cjs +394 -0
- package/dist/transaction.cjs.map +1 -0
- package/dist/transaction.d.ts +38 -0
- package/dist/transaction.js +390 -0
- package/dist/transaction.js.map +1 -0
- package/dist/user/api/user-api.d.ts +56 -0
- package/dist/user/api/user-api.d.ts.map +1 -0
- package/dist/user/index.d.ts +36 -0
- package/dist/user/index.d.ts.map +1 -0
- package/{src/user/models/index.ts → dist/user/models/index.d.ts} +12 -10
- package/dist/user/models/index.d.ts.map +1 -0
- package/dist/user/services/user-service.d.ts +46 -0
- package/dist/user/services/user-service.d.ts.map +1 -0
- package/dist/user-status/api/user-status-api.d.ts +37 -0
- package/dist/user-status/api/user-status-api.d.ts.map +1 -0
- package/dist/user-status/index.d.ts +28 -0
- package/dist/user-status/index.d.ts.map +1 -0
- package/{src/user-status/models/index.ts → dist/user-status/models/index.d.ts} +8 -11
- package/dist/user-status/models/index.d.ts.map +1 -0
- package/dist/user-status/services/user-status-service.d.ts +26 -0
- package/dist/user-status/services/user-status-service.d.ts.map +1 -0
- package/dist/user-status.cjs +147 -0
- package/dist/user-status.cjs.map +1 -0
- package/dist/user-status.d.ts +28 -0
- package/dist/user-status.js +143 -0
- package/dist/user-status.js.map +1 -0
- package/dist/user.cjs +188 -0
- package/dist/user.cjs.map +1 -0
- package/dist/user.d.ts +36 -0
- package/dist/user.js +184 -0
- package/dist/user.js.map +1 -0
- package/dist/web3/api/web3-api.d.ts +27 -0
- package/dist/web3/api/web3-api.d.ts.map +1 -0
- package/dist/web3/index.d.ts +16 -0
- package/dist/web3/index.d.ts.map +1 -0
- package/dist/web3/models/index.d.ts +92 -0
- package/dist/web3/models/index.d.ts.map +1 -0
- package/dist/web3/services/web3-service.d.ts +21 -0
- package/dist/web3/services/web3-service.d.ts.map +1 -0
- package/dist/web3-chain/api/web3-chain-api.d.ts +19 -0
- package/dist/web3-chain/api/web3-chain-api.d.ts.map +1 -0
- package/dist/web3-chain/index.d.ts +17 -0
- package/dist/web3-chain/index.d.ts.map +1 -0
- package/{src/web3-chain/models/index.ts → dist/web3-chain/models/index.d.ts} +38 -45
- package/dist/web3-chain/models/index.d.ts.map +1 -0
- package/dist/web3-chain/services/getWeb3FCD.service.d.ts +8 -0
- package/dist/web3-chain/services/getWeb3FCD.service.d.ts.map +1 -0
- package/dist/web3-chain/services/provider.service.d.ts +15 -0
- package/dist/web3-chain/services/provider.service.d.ts.map +1 -0
- package/dist/web3-chain/services/public-http-provider.service.d.ts +8 -0
- package/dist/web3-chain/services/public-http-provider.service.d.ts.map +1 -0
- package/dist/web3-chain/services/web3-chain-service.d.ts +17 -0
- package/dist/web3-chain/services/web3-chain-service.d.ts.map +1 -0
- package/dist/web3-chain.cjs +316 -0
- package/dist/web3-chain.cjs.map +1 -0
- package/dist/web3-chain.d.ts +17 -0
- package/dist/web3-chain.js +310 -0
- package/dist/web3-chain.js.map +1 -0
- package/dist/web3.cjs +516 -0
- package/dist/web3.cjs.map +1 -0
- package/dist/web3.d.ts +16 -0
- package/dist/web3.js +513 -0
- package/dist/web3.js.map +1 -0
- package/package.json +70 -67
- package/config/domains.js +0 -22
- package/explorins-pers-sdk-1.0.0-alpha.1.tgz +0 -0
- package/rollup.config.js +0 -74
- package/scripts/copy-declarations.js +0 -147
- package/src/analytics/api/analytics-api.ts +0 -24
- package/src/analytics/models/index.ts +0 -74
- package/src/analytics/services/analytics-service.ts +0 -28
- package/src/auth-admin/api/auth-admin-api.ts +0 -42
- package/src/auth-admin/index.ts +0 -47
- package/src/auth-admin/services/auth-admin-service.ts +0 -36
- package/src/business/api/business-api.ts +0 -234
- package/src/business/index.ts +0 -53
- package/src/business/models/index.ts +0 -13
- package/src/business/services/business-service.ts +0 -88
- package/src/campaign/api/campaign-api.ts +0 -376
- package/src/campaign/index.ts +0 -67
- package/src/campaign/services/campaign-service.ts +0 -164
- package/src/core/abstractions/http-client.ts +0 -24
- package/src/core/auth/auth-provider.interface.ts +0 -16
- package/src/core/auth/create-auth-provider.ts +0 -136
- package/src/core/auth/simple-auth-config.interface.ts +0 -15
- package/src/core/index.ts +0 -33
- package/src/core/pers-api-client.ts +0 -343
- package/src/core/pers-config.ts +0 -65
- package/src/core/utils/jwt.function.ts +0 -24
- package/src/donation/api/donation-api.ts +0 -24
- package/src/donation/index.ts +0 -47
- package/src/donation/services/donation-service.ts +0 -25
- package/src/index.ts +0 -55
- package/src/payment/api/payment-api.ts +0 -185
- package/src/payment/index.ts +0 -64
- package/src/payment/models/index.ts +0 -29
- package/src/payment/services/payment-service.ts +0 -70
- package/src/pers-sdk.ts +0 -45
- package/src/redemption/api/redemption-api.ts +0 -241
- package/src/redemption/index.ts +0 -60
- package/src/redemption/models/index.ts +0 -17
- package/src/redemption/services/redemption-service.ts +0 -103
- package/src/shared/interfaces/pers-shared-lib.interfaces.ts +0 -99
- package/src/tenant/api/tenant-api.ts +0 -92
- package/src/tenant/index.ts +0 -61
- package/src/tenant/models/index.ts +0 -20
- package/src/tenant/services/tenant-service.ts +0 -78
- package/src/token/api/token-api.ts +0 -129
- package/src/token/base/base-token-service.ts +0 -167
- package/src/token/index.ts +0 -38
- package/src/token/models/index.ts +0 -30
- package/src/token/services/token-service.ts +0 -125
- package/src/token/token-sdk.ts +0 -231
- package/src/transaction/api/transaction-api.ts +0 -296
- package/src/transaction/index.ts +0 -65
- package/src/transaction/models/index.ts +0 -60
- package/src/transaction/services/transaction-service.ts +0 -104
- package/src/user/api/user-api.ts +0 -98
- package/src/user/index.ts +0 -62
- package/src/user/services/user-service.ts +0 -75
- package/src/user-status/api/user-status-api.ts +0 -78
- package/src/user-status/index.ts +0 -55
- package/src/user-status/services/user-status-service.ts +0 -51
- package/src/web3/api/web3-api.ts +0 -68
- package/src/web3/index.ts +0 -38
- package/src/web3/models/index.ts +0 -150
- package/src/web3/services/web3-service.ts +0 -338
- package/src/web3-chain/api/web3-chain-api.ts +0 -42
- package/src/web3-chain/index.ts +0 -27
- package/src/web3-chain/services/getWeb3FCD.service.ts +0 -47
- package/src/web3-chain/services/provider.service.ts +0 -123
- package/src/web3-chain/services/public-http-provider.service.ts +0 -26
- package/src/web3-chain/services/web3-chain-service.ts +0 -131
- package/tsconfig.json +0 -28
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,4217 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jwtDecode = require('jwt-decode');
|
|
4
|
+
var Web3 = require('web3');
|
|
5
|
+
var web3Ts = require('@explorins/web3-ts');
|
|
6
|
+
var ethers = require('ethers');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* PERS SDK Configuration interfaces
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Default configuration values
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_PERS_CONFIG = {
|
|
15
|
+
environment: 'production',
|
|
16
|
+
apiVersion: 'v2',
|
|
17
|
+
timeout: 30000,
|
|
18
|
+
retries: 3
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Internal function to construct API root from environment
|
|
22
|
+
* Now defaults to production and v2
|
|
23
|
+
*/
|
|
24
|
+
function buildApiRoot(environment = 'production', version = 'v2') {
|
|
25
|
+
const baseUrls = {
|
|
26
|
+
development: 'https://explorins-loyalty.ngrok.io',
|
|
27
|
+
staging: `https://dev.api.pers.ninja/${version}`,
|
|
28
|
+
production: `https://api.pers.ninja/${version}`
|
|
29
|
+
};
|
|
30
|
+
return `${baseUrls[environment]}`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Merge user config with defaults
|
|
34
|
+
*/
|
|
35
|
+
function mergeWithDefaults(config) {
|
|
36
|
+
return {
|
|
37
|
+
...DEFAULT_PERS_CONFIG,
|
|
38
|
+
...config,
|
|
39
|
+
environment: config.environment ?? DEFAULT_PERS_CONFIG.environment,
|
|
40
|
+
apiVersion: config.apiVersion ?? DEFAULT_PERS_CONFIG.apiVersion,
|
|
41
|
+
timeout: config.timeout ?? DEFAULT_PERS_CONFIG.timeout,
|
|
42
|
+
retries: config.retries ?? DEFAULT_PERS_CONFIG.retries
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// packages/pers-sdk/src/core/pers-api-client.ts
|
|
47
|
+
/**
|
|
48
|
+
* PERS API Client - Core platform-agnostic client for PERS backend
|
|
49
|
+
*/
|
|
50
|
+
class PersApiClient {
|
|
51
|
+
constructor(httpClient, config) {
|
|
52
|
+
this.httpClient = httpClient;
|
|
53
|
+
this.config = config;
|
|
54
|
+
// Merge user config with defaults (production + v2)
|
|
55
|
+
this.mergedConfig = mergeWithDefaults(config);
|
|
56
|
+
// Build API root from merged environment and version
|
|
57
|
+
this.apiRoot = buildApiRoot(this.mergedConfig.environment, this.mergedConfig.apiVersion);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get request headers including auth token and project key
|
|
61
|
+
*/
|
|
62
|
+
async getHeaders() {
|
|
63
|
+
const headers = {
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
};
|
|
66
|
+
// Add authentication token
|
|
67
|
+
if (this.mergedConfig.authProvider) {
|
|
68
|
+
const token = await this.mergedConfig.authProvider.getToken();
|
|
69
|
+
if (token) {
|
|
70
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Add project key
|
|
74
|
+
if (this.mergedConfig.authProvider) {
|
|
75
|
+
const projectKey = await this.mergedConfig.authProvider.getProjectKey();
|
|
76
|
+
if (projectKey) {
|
|
77
|
+
headers['x-project-key'] = projectKey;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (this.mergedConfig.apiProjectKey) {
|
|
81
|
+
// Fallback to config project key if no auth provider
|
|
82
|
+
headers['x-project-key'] = this.mergedConfig.apiProjectKey;
|
|
83
|
+
}
|
|
84
|
+
return headers;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Make a request with proper headers, auth, and error handling
|
|
88
|
+
*/
|
|
89
|
+
async request(method, endpoint, body, options) {
|
|
90
|
+
const { retryCount = 0, responseType = 'json' } = options || {};
|
|
91
|
+
const url = `${this.apiRoot}${endpoint}`;
|
|
92
|
+
// ✅ DEBUGGING: Add extensive logging for CSV endpoint
|
|
93
|
+
const isCSVEndpoint = endpoint.includes('/export/csv');
|
|
94
|
+
const requestOptions = {
|
|
95
|
+
headers: await this.getHeaders(),
|
|
96
|
+
timeout: this.mergedConfig.timeout,
|
|
97
|
+
responseType
|
|
98
|
+
};
|
|
99
|
+
try {
|
|
100
|
+
let result;
|
|
101
|
+
switch (method) {
|
|
102
|
+
case 'GET':
|
|
103
|
+
result = await this.httpClient.get(url, requestOptions);
|
|
104
|
+
break;
|
|
105
|
+
case 'POST':
|
|
106
|
+
result = await this.httpClient.post(url, body, requestOptions);
|
|
107
|
+
break;
|
|
108
|
+
case 'PUT':
|
|
109
|
+
result = await this.httpClient.put(url, body, requestOptions);
|
|
110
|
+
break;
|
|
111
|
+
case 'DELETE':
|
|
112
|
+
result = await this.httpClient.delete(url, requestOptions);
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
throw new Error(`Unsupported HTTP method: ${method}`);
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (isCSVEndpoint) {
|
|
121
|
+
console.error('❌ [PERS API CLIENT] CSV Request failed:', error);
|
|
122
|
+
}
|
|
123
|
+
// Handle 401 errors with automatic token refresh
|
|
124
|
+
const apiError = error;
|
|
125
|
+
if (apiError.status === 401 && retryCount === 0 && this.mergedConfig.authProvider?.onTokenExpired) {
|
|
126
|
+
try {
|
|
127
|
+
await this.mergedConfig.authProvider.onTokenExpired();
|
|
128
|
+
// Retry once with refreshed token
|
|
129
|
+
return this.request(method, endpoint, body, { ...options, retryCount: 1 });
|
|
130
|
+
}
|
|
131
|
+
catch (refreshError) {
|
|
132
|
+
throw new PersApiError(`Authentication refresh failed: ${refreshError}`, endpoint, method, 401);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
throw new PersApiError(`PERS API request failed: ${apiError.message || error}`, endpoint, method, apiError.status);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Generic GET request
|
|
140
|
+
*/
|
|
141
|
+
async get(endpoint, responseType) {
|
|
142
|
+
return this.request('GET', endpoint, undefined, { responseType });
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Generic POST request
|
|
146
|
+
*/
|
|
147
|
+
async post(endpoint, body) {
|
|
148
|
+
return this.request('POST', endpoint, body);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Generic PUT request
|
|
152
|
+
*/
|
|
153
|
+
async put(endpoint, body) {
|
|
154
|
+
return this.request('PUT', endpoint, body);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Generic DELETE request
|
|
158
|
+
*/
|
|
159
|
+
async delete(endpoint) {
|
|
160
|
+
return this.request('DELETE', endpoint);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get current configuration (returns merged config)
|
|
164
|
+
*/
|
|
165
|
+
getConfig() {
|
|
166
|
+
return this.mergedConfig;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get original user configuration
|
|
170
|
+
*/
|
|
171
|
+
getOriginalConfig() {
|
|
172
|
+
return this.config;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
class PersApiError extends Error {
|
|
176
|
+
constructor(message, endpoint, method, statusCode) {
|
|
177
|
+
super(message);
|
|
178
|
+
this.endpoint = endpoint;
|
|
179
|
+
this.method = method;
|
|
180
|
+
this.statusCode = statusCode;
|
|
181
|
+
this.name = 'PersApiError';
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* PERS API Client - Core platform-agnostic client for PERS backend
|
|
186
|
+
*/
|
|
187
|
+
/*import { HttpClient, RequestOptions } from './abstractions/http-client';
|
|
188
|
+
import { PersConfig, buildApiRoot, mergeWithDefaults } from './pers-config';
|
|
189
|
+
|
|
190
|
+
export class PersApiClient {
|
|
191
|
+
private readonly apiRoot: string;
|
|
192
|
+
private readonly mergedConfig: ReturnType<typeof mergeWithDefaults>;
|
|
193
|
+
|
|
194
|
+
constructor(
|
|
195
|
+
private httpClient: HttpClient,
|
|
196
|
+
private config: PersConfig
|
|
197
|
+
) {
|
|
198
|
+
// Merge user config with defaults (production + v2)
|
|
199
|
+
this.mergedConfig = mergeWithDefaults(config);
|
|
200
|
+
|
|
201
|
+
// Build API root from merged environment and version
|
|
202
|
+
this.apiRoot = buildApiRoot(this.mergedConfig.environment, this.mergedConfig.apiVersion);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get request headers including auth token and project key
|
|
207
|
+
*/
|
|
208
|
+
/*private async getHeaders(): Promise<Record<string, string>> {
|
|
209
|
+
const headers: Record<string, string> = {
|
|
210
|
+
'Content-Type': 'application/json',
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Add authentication token
|
|
214
|
+
if (this.mergedConfig.authProvider) {
|
|
215
|
+
const token = await this.mergedConfig.authProvider.getToken();
|
|
216
|
+
if (token) {
|
|
217
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Add project key
|
|
222
|
+
if (this.mergedConfig.authProvider) {
|
|
223
|
+
const projectKey = await this.mergedConfig.authProvider.getProjectKey();
|
|
224
|
+
if (projectKey) {
|
|
225
|
+
headers['x-project-key'] = projectKey;
|
|
226
|
+
}
|
|
227
|
+
} else if(this.mergedConfig.apiProjectKey) {
|
|
228
|
+
// Fallback to config project key if no auth provider
|
|
229
|
+
headers['x-project-key'] = this.mergedConfig.apiProjectKey;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return headers;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Make a request with proper headers, auth, and error handling
|
|
237
|
+
*/
|
|
238
|
+
/*private async request<T>(
|
|
239
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
|
240
|
+
endpoint: string,
|
|
241
|
+
body?: any,
|
|
242
|
+
options?: { retryCount?: number }
|
|
243
|
+
): Promise<T> {
|
|
244
|
+
const { retryCount = 0 } = options || {};
|
|
245
|
+
const url = `${this.apiRoot}${endpoint}`;
|
|
246
|
+
|
|
247
|
+
const requestOptions: RequestOptions = {
|
|
248
|
+
headers: await this.getHeaders(),
|
|
249
|
+
timeout: this.mergedConfig.timeout,
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
switch (method) {
|
|
254
|
+
case 'GET':
|
|
255
|
+
return await this.httpClient.get<T>(url, requestOptions);
|
|
256
|
+
case 'POST':
|
|
257
|
+
return await this.httpClient.post<T>(url, body, requestOptions);
|
|
258
|
+
case 'PUT':
|
|
259
|
+
return await this.httpClient.put<T>(url, body, requestOptions);
|
|
260
|
+
case 'DELETE':
|
|
261
|
+
return await this.httpClient.delete<T>(url, requestOptions);
|
|
262
|
+
default:
|
|
263
|
+
throw new Error(`Unsupported HTTP method: ${method}`);
|
|
264
|
+
}
|
|
265
|
+
} catch (error: any) {
|
|
266
|
+
// Handle 401 errors with automatic token refresh
|
|
267
|
+
if (error.status === 401 && retryCount === 0 && this.mergedConfig.authProvider?.onTokenExpired) {
|
|
268
|
+
try {
|
|
269
|
+
await this.mergedConfig.authProvider.onTokenExpired();
|
|
270
|
+
// Retry once with refreshed token
|
|
271
|
+
return this.request<T>(method, endpoint, body, { ...options, retryCount: 1 });
|
|
272
|
+
} catch (refreshError) {
|
|
273
|
+
throw new PersApiError(
|
|
274
|
+
`Authentication refresh failed: ${refreshError}`,
|
|
275
|
+
endpoint,
|
|
276
|
+
method,
|
|
277
|
+
401
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
throw new PersApiError(
|
|
283
|
+
`PERS API request failed: ${error.message || error}`,
|
|
284
|
+
endpoint,
|
|
285
|
+
method,
|
|
286
|
+
error.status
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Generic GET request
|
|
293
|
+
*/
|
|
294
|
+
/*async get<T>(endpoint: string): Promise<T> {
|
|
295
|
+
return this.request<T>('GET', endpoint);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Generic POST request
|
|
300
|
+
*/
|
|
301
|
+
/*async post<T>(endpoint: string, body?: any): Promise<T> {
|
|
302
|
+
return this.request<T>('POST', endpoint, body);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Generic PUT request
|
|
307
|
+
*/
|
|
308
|
+
/*async put<T>(endpoint: string, body?: any): Promise<T> {
|
|
309
|
+
return this.request<T>('PUT', endpoint, body);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Generic DELETE request
|
|
314
|
+
*/
|
|
315
|
+
/*async delete<T>(endpoint: string): Promise<T> {
|
|
316
|
+
return this.request<T>('DELETE', endpoint);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Get current configuration (returns merged config)
|
|
321
|
+
*/
|
|
322
|
+
/*getConfig(): ReturnType<typeof mergeWithDefaults> {
|
|
323
|
+
return this.mergedConfig;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Get original user configuration
|
|
328
|
+
*/
|
|
329
|
+
/*getOriginalConfig(): PersConfig {
|
|
330
|
+
return this.config;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export class PersApiError extends Error {
|
|
335
|
+
constructor(
|
|
336
|
+
message: string,
|
|
337
|
+
public endpoint: string,
|
|
338
|
+
public method: string,
|
|
339
|
+
public statusCode?: number
|
|
340
|
+
) {
|
|
341
|
+
super(message);
|
|
342
|
+
this.name = 'PersApiError';
|
|
343
|
+
}
|
|
344
|
+
}*/
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Creates a platform-agnostic AuthProvider from simple configuration
|
|
348
|
+
*
|
|
349
|
+
* This factory function is completely platform-agnostic and can be used
|
|
350
|
+
* across Angular, React, Vue, Node.js, or any other JavaScript environment.
|
|
351
|
+
*
|
|
352
|
+
* Features:
|
|
353
|
+
* - Token caching with refresh support
|
|
354
|
+
* - Automatic token refresh on expiration
|
|
355
|
+
* - Configurable token providers
|
|
356
|
+
* - Platform-independent (no localStorage assumptions)
|
|
357
|
+
*
|
|
358
|
+
* @param config - Simple auth configuration
|
|
359
|
+
* @returns AuthProvider implementation
|
|
360
|
+
*/
|
|
361
|
+
function createAuthProvider(config) {
|
|
362
|
+
// Store current token for refresh scenarios and caching
|
|
363
|
+
let currentToken = config.token || null;
|
|
364
|
+
let isRefreshing = false; // Prevent concurrent refresh attempts
|
|
365
|
+
let refreshPromise = null;
|
|
366
|
+
return {
|
|
367
|
+
authType: config.authType || 'user',
|
|
368
|
+
async getToken() {
|
|
369
|
+
// If currently refreshing, wait for it to complete
|
|
370
|
+
if (isRefreshing && refreshPromise) {
|
|
371
|
+
await refreshPromise;
|
|
372
|
+
return currentToken;
|
|
373
|
+
}
|
|
374
|
+
// Use cached current token (updated after refresh)
|
|
375
|
+
if (currentToken) {
|
|
376
|
+
return currentToken;
|
|
377
|
+
}
|
|
378
|
+
// Custom token provider function (always fresh)
|
|
379
|
+
if (config.tokenProvider) {
|
|
380
|
+
const token = await config.tokenProvider();
|
|
381
|
+
currentToken = token; // Cache for future calls
|
|
382
|
+
return token;
|
|
383
|
+
}
|
|
384
|
+
// No token available
|
|
385
|
+
return null;
|
|
386
|
+
},
|
|
387
|
+
async getProjectKey() {
|
|
388
|
+
return config.projectKey || null;
|
|
389
|
+
},
|
|
390
|
+
async onTokenExpired() {
|
|
391
|
+
// Prevent concurrent refresh attempts
|
|
392
|
+
if (isRefreshing) {
|
|
393
|
+
if (refreshPromise) {
|
|
394
|
+
await refreshPromise;
|
|
395
|
+
}
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
// No refresh logic provided
|
|
399
|
+
if (!config.onTokenExpired) {
|
|
400
|
+
console.warn('Token expired but no refresh logic provided');
|
|
401
|
+
currentToken = null; // Clear expired token
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
// Start refresh process
|
|
405
|
+
isRefreshing = true;
|
|
406
|
+
refreshPromise = (async () => {
|
|
407
|
+
try {
|
|
408
|
+
// Execute refresh logic (should update token source)
|
|
409
|
+
await config.onTokenExpired();
|
|
410
|
+
// After refresh, get the new token
|
|
411
|
+
if (config.tokenProvider) {
|
|
412
|
+
const newToken = await config.tokenProvider();
|
|
413
|
+
if (newToken && newToken !== currentToken) {
|
|
414
|
+
currentToken = newToken;
|
|
415
|
+
// Notify about successful token refresh
|
|
416
|
+
if (config.onTokenRefreshed) {
|
|
417
|
+
config.onTokenRefreshed(newToken);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
console.warn('Token refresh completed but no new token received');
|
|
422
|
+
currentToken = null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
// For static token configs, clear the token since we can't refresh
|
|
427
|
+
console.warn('Token expired for static token config - clearing token');
|
|
428
|
+
currentToken = null;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
console.error('Token refresh failed:', error);
|
|
433
|
+
currentToken = null; // Clear token on refresh failure
|
|
434
|
+
throw error; // Re-throw to let SDK handle the error
|
|
435
|
+
}
|
|
436
|
+
finally {
|
|
437
|
+
isRefreshing = false;
|
|
438
|
+
refreshPromise = null;
|
|
439
|
+
}
|
|
440
|
+
})();
|
|
441
|
+
await refreshPromise;
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Platform-specific localStorage token provider for browsers
|
|
447
|
+
* This is a convenience function for browser environments
|
|
448
|
+
*/
|
|
449
|
+
/* export function createBrowserTokenProvider(tokenKey: string = 'userJwt'): () => Promise<string | null> {
|
|
450
|
+
return async () => {
|
|
451
|
+
if (typeof localStorage !== 'undefined') {
|
|
452
|
+
return localStorage.getItem(tokenKey);
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
};
|
|
456
|
+
} */
|
|
457
|
+
/**
|
|
458
|
+
* Platform-specific environment variable token provider for Node.js
|
|
459
|
+
* This is a convenience function for Node.js environments
|
|
460
|
+
*/
|
|
461
|
+
/* export function createNodeTokenProvider(envVar: string = 'JWT_TOKEN'): () => Promise<string | null> {
|
|
462
|
+
return async () => {
|
|
463
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
464
|
+
return process.env[envVar] || null;
|
|
465
|
+
}
|
|
466
|
+
return null;
|
|
467
|
+
};
|
|
468
|
+
} */
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* PERS SDK - Minimal platform-agnostic client with built-in authentication
|
|
472
|
+
* Authentication is now handled at the SDK core level for better scalability
|
|
473
|
+
*/
|
|
474
|
+
/**
|
|
475
|
+
* Minimal PERS SDK - API client with authentication built-in
|
|
476
|
+
* Platform adapters provide auth providers and HTTP clients
|
|
477
|
+
*/
|
|
478
|
+
class PersSDK {
|
|
479
|
+
constructor(httpClient, config) {
|
|
480
|
+
this.apiClient = new PersApiClient(httpClient, config);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Get the API client for direct PERS API calls
|
|
484
|
+
* This is the main interface - keep it simple!
|
|
485
|
+
*/
|
|
486
|
+
api() {
|
|
487
|
+
return this.apiClient;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Quick config check
|
|
491
|
+
*/
|
|
492
|
+
isProduction() {
|
|
493
|
+
return this.apiClient.getConfig().environment === 'production';
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Simple factory function
|
|
498
|
+
*/
|
|
499
|
+
function createPersSDK(httpClient, config) {
|
|
500
|
+
return new PersSDK(httpClient, config);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Platform-Agnostic Business API Client
|
|
505
|
+
*
|
|
506
|
+
* Updated to match the new RESTful /businesses endpoints.
|
|
507
|
+
* Uses @explorins/pers-shared DTOs for full type safety and consistency with backend.
|
|
508
|
+
*/
|
|
509
|
+
class BusinessApi {
|
|
510
|
+
constructor(apiClient) {
|
|
511
|
+
this.apiClient = apiClient;
|
|
512
|
+
this.basePath = '/businesses';
|
|
513
|
+
}
|
|
514
|
+
// ==========================================
|
|
515
|
+
// 🌐 BUSINESS TYPES MANAGEMENT
|
|
516
|
+
// ==========================================
|
|
517
|
+
/**
|
|
518
|
+
* Get all business types (project key required)
|
|
519
|
+
*
|
|
520
|
+
* Endpoint: GET /businesses/types
|
|
521
|
+
* Auth: @ApiSecurity('projectKey')
|
|
522
|
+
*/
|
|
523
|
+
async getAllBusinessTypes() {
|
|
524
|
+
return this.apiClient.get(`${this.basePath}/types`);
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* ADMIN: Create business type
|
|
528
|
+
*
|
|
529
|
+
* Endpoint: POST /businesses/types
|
|
530
|
+
* Auth: @TenantAdmin()
|
|
531
|
+
*/
|
|
532
|
+
async createBusinessType(dto) {
|
|
533
|
+
return this.apiClient.post(`${this.basePath}/types`, dto);
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* ADMIN: Update business type
|
|
537
|
+
*
|
|
538
|
+
* Endpoint: PUT /businesses/types
|
|
539
|
+
* Auth: @TenantAdmin()
|
|
540
|
+
*/
|
|
541
|
+
async updateBusinessType(dto) {
|
|
542
|
+
return this.apiClient.put(`${this.basePath}/types`, dto);
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* ADMIN: Delete business type
|
|
546
|
+
*
|
|
547
|
+
* Endpoint: DELETE /businesses/types/{id}
|
|
548
|
+
* Auth: @TenantAdmin()
|
|
549
|
+
*/
|
|
550
|
+
async deleteBusinessType(id) {
|
|
551
|
+
return this.apiClient.delete(`${this.basePath}/types/${id}`);
|
|
552
|
+
}
|
|
553
|
+
// ==========================================
|
|
554
|
+
// 🏢 BUSINESS MANAGEMENT
|
|
555
|
+
// ==========================================
|
|
556
|
+
/**
|
|
557
|
+
* Get current business info (business authentication required)
|
|
558
|
+
*
|
|
559
|
+
* Endpoint: GET /businesses/me
|
|
560
|
+
* Auth: @Business()
|
|
561
|
+
*/
|
|
562
|
+
async getCurrentBusiness() {
|
|
563
|
+
return this.apiClient.get(`${this.basePath}/me`);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Get all active businesses (project key required)
|
|
567
|
+
*
|
|
568
|
+
* Endpoint: GET /businesses
|
|
569
|
+
* Auth: @ApiSecurity('projectKey')
|
|
570
|
+
* Note: Regular users automatically get active businesses only
|
|
571
|
+
*/
|
|
572
|
+
async getActiveBusinesses() {
|
|
573
|
+
return this.apiClient.get(`${this.basePath}`);
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Get all businesses with filtering (admin users can access inactive)
|
|
577
|
+
*
|
|
578
|
+
* Endpoint: GET /businesses?active={boolean}&sanitize={mode}
|
|
579
|
+
* Auth: @ApiSecurity('projectKey') (enhanced with role-based filtering)
|
|
580
|
+
*/
|
|
581
|
+
async getAllBusinesses(options) {
|
|
582
|
+
const params = new URLSearchParams();
|
|
583
|
+
if (options?.active !== undefined) {
|
|
584
|
+
params.append('active', String(options.active));
|
|
585
|
+
}
|
|
586
|
+
if (options?.sanitize) {
|
|
587
|
+
params.append('sanitize', options.sanitize);
|
|
588
|
+
}
|
|
589
|
+
const queryString = params.toString();
|
|
590
|
+
const url = queryString ? `${this.basePath}?${queryString}` : this.basePath;
|
|
591
|
+
return this.apiClient.get(url);
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* ADMIN: Get all businesses (admin endpoint with full access)
|
|
595
|
+
*
|
|
596
|
+
* Endpoint: GET /businesses/admin
|
|
597
|
+
* Auth: @TenantAdmin()
|
|
598
|
+
*/
|
|
599
|
+
async getAllBusinessesAdmin(options) {
|
|
600
|
+
const params = new URLSearchParams();
|
|
601
|
+
if (options?.active !== undefined) {
|
|
602
|
+
params.append('active', String(options.active));
|
|
603
|
+
}
|
|
604
|
+
if (options?.sanitize) {
|
|
605
|
+
params.append('sanitize', options.sanitize);
|
|
606
|
+
}
|
|
607
|
+
const queryString = params.toString();
|
|
608
|
+
const url = queryString ? `${this.basePath}?${queryString}` : this.basePath;
|
|
609
|
+
return this.apiClient.get(url);
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Get business by ID
|
|
613
|
+
*
|
|
614
|
+
* Endpoint: GET /businesses/{id}
|
|
615
|
+
* Auth: @ApiSecurity('projectKey')
|
|
616
|
+
*/
|
|
617
|
+
async getBusinessById(businessId) {
|
|
618
|
+
return this.apiClient.get(`${this.basePath}/${businessId}`);
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Get business by account address
|
|
622
|
+
*
|
|
623
|
+
* Endpoint: GET /businesses/account/{accountAddress}
|
|
624
|
+
* Auth: @ApiSecurity('projectKey')
|
|
625
|
+
*/
|
|
626
|
+
async getBusinessByAccount(accountAddress) {
|
|
627
|
+
return this.apiClient.get(`${this.basePath}/account/${accountAddress}`);
|
|
628
|
+
}
|
|
629
|
+
// ==========================================
|
|
630
|
+
// 🔧 ADMIN OPERATIONS
|
|
631
|
+
// ==========================================
|
|
632
|
+
/**
|
|
633
|
+
* ADMIN: Create business
|
|
634
|
+
*
|
|
635
|
+
* Endpoint: POST /businesses
|
|
636
|
+
* Auth: @TenantAdmin()
|
|
637
|
+
* Returns: BusinessApiKeyDTO | BusinessTokenBalancesDTO
|
|
638
|
+
*/
|
|
639
|
+
async createBusiness(dto) {
|
|
640
|
+
return this.apiClient.post(`${this.basePath}`, dto);
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* ADMIN: Create business by display name (convenience method)
|
|
644
|
+
*
|
|
645
|
+
* This is a convenience wrapper that creates the proper DTO structure
|
|
646
|
+
*/
|
|
647
|
+
async createBusinessByDisplayName(displayName) {
|
|
648
|
+
const dto = {
|
|
649
|
+
displayName,
|
|
650
|
+
// Add other required fields based on BusinessCreateRequestDTO structure
|
|
651
|
+
// You may need to check the DTO definition for required fields
|
|
652
|
+
};
|
|
653
|
+
return this.createBusiness(dto);
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* ADMIN: Create businesses from URL
|
|
657
|
+
*
|
|
658
|
+
* Endpoint: POST /businesses/bulk/url
|
|
659
|
+
* Auth: @TenantAdmin()
|
|
660
|
+
*/
|
|
661
|
+
async createBusinessesFromUrl(url) {
|
|
662
|
+
return this.apiClient.post(`${this.basePath}/bulk/url`, { url });
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* ADMIN: Update business
|
|
666
|
+
*
|
|
667
|
+
* Endpoint: PUT /businesses/{id}
|
|
668
|
+
* Auth: @TenantAdmin()
|
|
669
|
+
*/
|
|
670
|
+
async updateBusiness(id, businessData) {
|
|
671
|
+
return this.apiClient.put(`${this.basePath}/${id}`, businessData);
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* ADMIN: Toggle business active status
|
|
675
|
+
*
|
|
676
|
+
* Endpoint: PUT /businesses/{id}/activate
|
|
677
|
+
* Auth: @TenantAdmin()
|
|
678
|
+
*/
|
|
679
|
+
async toggleBusinessActive(id, isActive) {
|
|
680
|
+
const dto = { isActive };
|
|
681
|
+
return this.apiClient.put(`${this.basePath}/${id}/status`, dto);
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* ADMIN: Generate new business API key
|
|
685
|
+
*
|
|
686
|
+
* Endpoint: PUT /businesses/{id}/api-key
|
|
687
|
+
* Auth: @TenantAdmin()
|
|
688
|
+
*/
|
|
689
|
+
async generateNewBusinessApiKey(id) {
|
|
690
|
+
return this.apiClient.put(`${this.basePath}/${id}/api-key`, {});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Platform-Agnostic Business Service
|
|
696
|
+
*
|
|
697
|
+
* Contains business logic and operations that work across platforms.
|
|
698
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
699
|
+
*
|
|
700
|
+
* Focuses only on actual backend capabilities.
|
|
701
|
+
*/
|
|
702
|
+
class BusinessService {
|
|
703
|
+
constructor(businessApi) {
|
|
704
|
+
this.businessApi = businessApi;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Get all active businesses
|
|
708
|
+
*/
|
|
709
|
+
async getActiveBusinesses() {
|
|
710
|
+
return this.businessApi.getActiveBusinesses();
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Get all business types
|
|
714
|
+
*/
|
|
715
|
+
async getAllBusinessTypes() {
|
|
716
|
+
return this.businessApi.getAllBusinessTypes();
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Get business by ID
|
|
720
|
+
*/
|
|
721
|
+
async getBusinessById(businessId) {
|
|
722
|
+
return this.businessApi.getBusinessById(businessId);
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Get business by account address
|
|
726
|
+
*/
|
|
727
|
+
async getBusinessByAccount(accountAddress) {
|
|
728
|
+
return this.businessApi.getBusinessByAccount(accountAddress);
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Get businesses by type (client-side filtering)
|
|
732
|
+
*/
|
|
733
|
+
async getBusinessesByType(typeId) {
|
|
734
|
+
const businesses = await this.getActiveBusinesses();
|
|
735
|
+
return businesses.filter(business => business.businessType && business.businessType.id === parseInt(typeId));
|
|
736
|
+
}
|
|
737
|
+
// ==========================================
|
|
738
|
+
// ADMIN OPERATIONS
|
|
739
|
+
// ==========================================
|
|
740
|
+
/**
|
|
741
|
+
* ADMIN: Get all businesses (active and inactive)
|
|
742
|
+
*/
|
|
743
|
+
async getAllBusinesses() {
|
|
744
|
+
return this.businessApi.getAllBusinesses();
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* ADMIN: Create business by display name
|
|
748
|
+
*/
|
|
749
|
+
async createBusinessByDisplayName(displayName) {
|
|
750
|
+
return this.businessApi.createBusinessByDisplayName(displayName);
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* ADMIN: Update business
|
|
754
|
+
*/
|
|
755
|
+
async updateBusiness(id, businessData) {
|
|
756
|
+
return this.businessApi.updateBusiness(id, businessData);
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* ADMIN: Toggle business active status
|
|
760
|
+
*/
|
|
761
|
+
async toggleBusinessActive(id, isActive) {
|
|
762
|
+
return this.businessApi.toggleBusinessActive(id, isActive);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* @explorins/pers-sdk-business
|
|
768
|
+
*
|
|
769
|
+
* Platform-agnostic Business Domain SDK for PERS ecosystem
|
|
770
|
+
* Focuses on non-admin business operations
|
|
771
|
+
*/
|
|
772
|
+
// API Layer
|
|
773
|
+
/**
|
|
774
|
+
* Create a complete Business SDK instance
|
|
775
|
+
*
|
|
776
|
+
* @param apiClient - Configured PERS API client
|
|
777
|
+
* @returns Business SDK with flattened structure for better DX
|
|
778
|
+
*/
|
|
779
|
+
function createBusinessSDK(apiClient) {
|
|
780
|
+
const businessApi = new BusinessApi(apiClient);
|
|
781
|
+
const businessService = new BusinessService(businessApi);
|
|
782
|
+
return {
|
|
783
|
+
// Direct access to service methods (primary interface)
|
|
784
|
+
getActiveBusinesses: () => businessService.getActiveBusinesses(),
|
|
785
|
+
getAllBusinessTypes: () => businessService.getAllBusinessTypes(),
|
|
786
|
+
getBusinessById: (businessId) => businessService.getBusinessById(businessId),
|
|
787
|
+
getBusinessByAccount: (accountAddress) => businessService.getBusinessByAccount(accountAddress),
|
|
788
|
+
getBusinessesByType: (typeId) => businessService.getBusinessesByType(typeId),
|
|
789
|
+
// Admin methods
|
|
790
|
+
getAllBusinesses: () => businessService.getAllBusinesses(),
|
|
791
|
+
createBusinessByDisplayName: (displayName) => businessService.createBusinessByDisplayName(displayName),
|
|
792
|
+
updateBusiness: (id, businessData) => businessService.updateBusiness(id, businessData),
|
|
793
|
+
toggleBusinessActive: (id, isActive) => businessService.toggleBusinessActive(id, isActive),
|
|
794
|
+
// Advanced access for edge cases
|
|
795
|
+
api: businessApi,
|
|
796
|
+
service: businessService
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Platform-Agnostic Transaction API Client (UPDATED FOR NEW RESTful ENDPOINTS)
|
|
802
|
+
*
|
|
803
|
+
* Handles transaction operations using the PERS backend.
|
|
804
|
+
* Uses @explorins/pers-shared DTOs for consistency with backend.
|
|
805
|
+
*
|
|
806
|
+
* MIGRATION NOTES:
|
|
807
|
+
* - All endpoints changed from /transaction to /transactions
|
|
808
|
+
* - Role-based paths removed (no more /auth, /admin, /business in URLs)
|
|
809
|
+
* - New RESTful resource-based structure
|
|
810
|
+
* - Added new client-side transaction flow methods
|
|
811
|
+
* - Enhanced admin query capabilities
|
|
812
|
+
*/
|
|
813
|
+
class TransactionApi {
|
|
814
|
+
constructor(apiClient) {
|
|
815
|
+
this.apiClient = apiClient;
|
|
816
|
+
this.basePath = '/transactions';
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Get transaction by ID (public endpoint)
|
|
820
|
+
*
|
|
821
|
+
* UPDATED: /transaction/{id} → /transactions/{id}
|
|
822
|
+
*/
|
|
823
|
+
async getTransactionById(transactionId) {
|
|
824
|
+
return this.apiClient.get(`${this.basePath}/${transactionId}`);
|
|
825
|
+
}
|
|
826
|
+
// ==========================================
|
|
827
|
+
// AUTHENTICATED USER OPERATIONS
|
|
828
|
+
// ==========================================
|
|
829
|
+
/**
|
|
830
|
+
* AUTH: Create authenticated user transaction
|
|
831
|
+
*
|
|
832
|
+
* UPDATED: /transaction/auth/transaction → /transactions/user
|
|
833
|
+
*/
|
|
834
|
+
async createAuthTransaction(request) {
|
|
835
|
+
return this.apiClient.post(`${this.basePath}/user`, request);
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* AUTH: Get user's sent transactions
|
|
839
|
+
*
|
|
840
|
+
* UPDATED: /transaction/auth/sender → /transactions/me/sent
|
|
841
|
+
*/
|
|
842
|
+
async getUserSentTransactions() {
|
|
843
|
+
const params = new URLSearchParams({
|
|
844
|
+
timestamp: Date.now().toString()
|
|
845
|
+
});
|
|
846
|
+
return this.apiClient.get(`${this.basePath}/me/sent?${params.toString()}`);
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* AUTH: Get user's received transactions
|
|
850
|
+
*
|
|
851
|
+
* UPDATED: /transaction/auth/recipient → /transactions/me/received
|
|
852
|
+
*/
|
|
853
|
+
async getUserReceivedTransactions() {
|
|
854
|
+
const params = new URLSearchParams({
|
|
855
|
+
timestamp: Date.now().toString()
|
|
856
|
+
});
|
|
857
|
+
return this.apiClient.get(`${this.basePath}/me/received?${params.toString()}`);
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* AUTH: Get user transaction history by type (backwards compatibility)
|
|
861
|
+
*
|
|
862
|
+
* UPDATED: Maps to appropriate /transactions/me/* endpoints
|
|
863
|
+
*/
|
|
864
|
+
async getUserTransactionHistory(type) {
|
|
865
|
+
const params = new URLSearchParams({
|
|
866
|
+
timestamp: Date.now().toString()
|
|
867
|
+
});
|
|
868
|
+
// Map legacy type parameter to new endpoints
|
|
869
|
+
switch (type.toLowerCase()) {
|
|
870
|
+
case 'sender':
|
|
871
|
+
case 'sent':
|
|
872
|
+
return this.apiClient.get(`${this.basePath}/me/sent?${params.toString()}`);
|
|
873
|
+
case 'recipient':
|
|
874
|
+
case 'received':
|
|
875
|
+
return this.apiClient.get(`${this.basePath}/me/received?${params.toString()}`);
|
|
876
|
+
default:
|
|
877
|
+
// Default to sent transactions for backwards compatibility
|
|
878
|
+
return this.apiClient.get(`${this.basePath}/me/sent?${params.toString()}`);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* AUTH: Prepare client signed transaction
|
|
883
|
+
*
|
|
884
|
+
* UPDATED: /transaction/auth/prepare-signing → /transactions/prepare
|
|
885
|
+
*/
|
|
886
|
+
async prepareClientSignedTransaction(request) {
|
|
887
|
+
return this.apiClient.post(`${this.basePath}/prepare`, request);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* AUTH: Prepare existing transaction for client-side signing
|
|
891
|
+
*
|
|
892
|
+
* NEW ENDPOINT: GET /transactions/{id}/prepare
|
|
893
|
+
*/
|
|
894
|
+
async prepareExistingTransaction(transactionId) {
|
|
895
|
+
return this.apiClient.get(`${this.basePath}/${transactionId}/prepare`);
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* AUTH: Submit client-side signed transaction
|
|
899
|
+
*
|
|
900
|
+
* NEW ENDPOINT: POST /transactions/{id}/submit
|
|
901
|
+
*/
|
|
902
|
+
async submitSignedTransaction(transactionId, signedData) {
|
|
903
|
+
return this.apiClient.post(`${this.basePath}/${transactionId}/submit`, signedData);
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* AUTH: Burn user tokens
|
|
907
|
+
*
|
|
908
|
+
* UPDATED: Uses new user transaction endpoint with burn-specific parameters
|
|
909
|
+
* Note: This might need backend confirmation on burn functionality implementation
|
|
910
|
+
*/
|
|
911
|
+
async burnUserTokens(request) {
|
|
912
|
+
// Map burn request to TransactionRequestDTO format for new endpoint
|
|
913
|
+
const transactionRequest = {
|
|
914
|
+
...request,
|
|
915
|
+
// Add any specific burn transaction parameters here
|
|
916
|
+
};
|
|
917
|
+
return this.apiClient.post(`${this.basePath}/user`, transactionRequest);
|
|
918
|
+
}
|
|
919
|
+
// ==========================================
|
|
920
|
+
// BUSINESS OPERATIONS
|
|
921
|
+
// ==========================================
|
|
922
|
+
/**
|
|
923
|
+
* BUSINESS: Create business transaction
|
|
924
|
+
*
|
|
925
|
+
* UPDATED: /transaction/business/transaction → /transactions/business
|
|
926
|
+
*/
|
|
927
|
+
async createBusinessTransaction(request) {
|
|
928
|
+
return this.apiClient.post(`${this.basePath}/business`, request);
|
|
929
|
+
}
|
|
930
|
+
// ==========================================
|
|
931
|
+
// ADMIN OPERATIONS
|
|
932
|
+
// ==========================================
|
|
933
|
+
/**
|
|
934
|
+
* ADMIN: Create admin transaction
|
|
935
|
+
*
|
|
936
|
+
* UPDATED: /transaction/admin/transaction → /transactions/admin
|
|
937
|
+
*/
|
|
938
|
+
async createAdminTransaction(request) {
|
|
939
|
+
return this.apiClient.post(`${this.basePath}/system`, request);
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* ADMIN: Get all tenant transactions
|
|
943
|
+
*
|
|
944
|
+
* UPDATED: /transaction/admin → /transactions
|
|
945
|
+
*/
|
|
946
|
+
async getTenantTransactions() {
|
|
947
|
+
const result = await this.apiClient.get(`${this.basePath}`);
|
|
948
|
+
// If the new endpoint returns paginated response, extract the data array
|
|
949
|
+
if ('data' in result) {
|
|
950
|
+
return result.data;
|
|
951
|
+
}
|
|
952
|
+
// Fallback for direct array response
|
|
953
|
+
return result;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* ADMIN: Get paginated transactions with filtering and sorting
|
|
957
|
+
*
|
|
958
|
+
* UPDATED: /transaction/admin → /transactions (same endpoint, better structure)
|
|
959
|
+
*/
|
|
960
|
+
async getPaginatedTransactions(params) {
|
|
961
|
+
// Build query parameters
|
|
962
|
+
const queryParams = {
|
|
963
|
+
page: params.page.toString(),
|
|
964
|
+
limit: params.limit.toString()
|
|
965
|
+
};
|
|
966
|
+
// Add sorting parameters if provided
|
|
967
|
+
if (params.sortBy) {
|
|
968
|
+
queryParams['sortBy'] = params.sortBy;
|
|
969
|
+
}
|
|
970
|
+
if (params.sortOrder) {
|
|
971
|
+
queryParams['sortOrder'] = params.sortOrder;
|
|
972
|
+
}
|
|
973
|
+
// Add user-specific filtering if provided
|
|
974
|
+
if (params.participantId) {
|
|
975
|
+
queryParams['participantId'] = params.participantId;
|
|
976
|
+
}
|
|
977
|
+
// Add status filtering if provided
|
|
978
|
+
if (params.status) {
|
|
979
|
+
queryParams['status'] = params.status;
|
|
980
|
+
}
|
|
981
|
+
// Add additional filters if provided
|
|
982
|
+
if (params.filters) {
|
|
983
|
+
if (params.filters.startDate) {
|
|
984
|
+
queryParams['startDate'] = params.filters.startDate;
|
|
985
|
+
}
|
|
986
|
+
if (params.filters.endDate) {
|
|
987
|
+
queryParams['endDate'] = params.filters.endDate;
|
|
988
|
+
}
|
|
989
|
+
if (params.filters.type && params.filters.type.length > 0) {
|
|
990
|
+
queryParams['type'] = params.filters.type.join(',');
|
|
991
|
+
}
|
|
992
|
+
if (params.filters.tokenType && params.filters.tokenType.length > 0) {
|
|
993
|
+
queryParams['tokenType'] = params.filters.tokenType.join(',');
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
// Build query string
|
|
997
|
+
const queryString = Object.entries(queryParams)
|
|
998
|
+
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
|
999
|
+
.join('&');
|
|
1000
|
+
return this.apiClient.get(`${this.basePath}?${queryString}`);
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* ADMIN: Export transactions to CSV
|
|
1004
|
+
*
|
|
1005
|
+
* UPDATED: /transaction/admin/export/csv → /transactions/export/csv
|
|
1006
|
+
*/
|
|
1007
|
+
async exportTransactionsCSV() {
|
|
1008
|
+
return this.apiClient.get(`${this.basePath}/export/csv`, 'blob');
|
|
1009
|
+
}
|
|
1010
|
+
// ==========================================
|
|
1011
|
+
// NEW ADMIN QUERY METHODS
|
|
1012
|
+
// ==========================================
|
|
1013
|
+
/**
|
|
1014
|
+
* ADMIN: Query transactions by sender
|
|
1015
|
+
*
|
|
1016
|
+
* NEW ENDPOINT: POST /transactions/query-sender
|
|
1017
|
+
*/
|
|
1018
|
+
async queryTransactionsBySender(accountSelector) {
|
|
1019
|
+
return this.apiClient.post(`${this.basePath}/query-sender`, accountSelector);
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* ADMIN: Query transactions by recipient
|
|
1023
|
+
*
|
|
1024
|
+
* NEW ENDPOINT: POST /transactions/query-recipient
|
|
1025
|
+
*/
|
|
1026
|
+
async queryTransactionsByRecipient(accountSelector) {
|
|
1027
|
+
return this.apiClient.post(`${this.basePath}/query-recipient`, accountSelector);
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* ADMIN: Get transaction analytics
|
|
1031
|
+
*
|
|
1032
|
+
* NEW ENDPOINT: POST /transactions/analytics
|
|
1033
|
+
*/
|
|
1034
|
+
async getTransactionAnalytics(analyticsRequest) {
|
|
1035
|
+
return this.apiClient.post(`${this.basePath}/analytics`, analyticsRequest);
|
|
1036
|
+
}
|
|
1037
|
+
// ==========================================
|
|
1038
|
+
// CONVENIENCE METHODS (BACKWARDS COMPATIBILITY)
|
|
1039
|
+
// ==========================================
|
|
1040
|
+
/**
|
|
1041
|
+
* Convenience method: Get user sent transactions (alias)
|
|
1042
|
+
*/
|
|
1043
|
+
async getUserSenderTransactions() {
|
|
1044
|
+
return this.getUserSentTransactions();
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Convenience method: Get user received transactions (alias)
|
|
1048
|
+
*/
|
|
1049
|
+
async getUserRecipientTransactions() {
|
|
1050
|
+
return this.getUserReceivedTransactions();
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* Platform-Agnostic Transaction Service
|
|
1056
|
+
*
|
|
1057
|
+
* Contains transaction business logic and operations that work across platforms.
|
|
1058
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
1059
|
+
*
|
|
1060
|
+
* Focuses only on actual backend capabilities.
|
|
1061
|
+
*/
|
|
1062
|
+
class TransactionService {
|
|
1063
|
+
constructor(transactionApi) {
|
|
1064
|
+
this.transactionApi = transactionApi;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Get transaction by ID
|
|
1068
|
+
*/
|
|
1069
|
+
async getTransactionById(transactionId) {
|
|
1070
|
+
return this.transactionApi.getTransactionById(transactionId);
|
|
1071
|
+
}
|
|
1072
|
+
// ==========================================
|
|
1073
|
+
// AUTHENTICATED OPERATIONS
|
|
1074
|
+
// ==========================================
|
|
1075
|
+
/**
|
|
1076
|
+
* AUTH: Create authenticated transaction
|
|
1077
|
+
*/
|
|
1078
|
+
async createAuthTransaction(request) {
|
|
1079
|
+
return this.transactionApi.createAuthTransaction(request);
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* AUTH: Get user transaction history by type
|
|
1083
|
+
*/
|
|
1084
|
+
async getUserTransactionHistory(type) {
|
|
1085
|
+
return this.transactionApi.getUserTransactionHistory(type);
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* AUTH: Prepare client signed transaction
|
|
1089
|
+
*/
|
|
1090
|
+
async prepareClientSignedTransaction(request) {
|
|
1091
|
+
return this.transactionApi.prepareClientSignedTransaction(request);
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* AUTH: Burn user tokens
|
|
1095
|
+
*/
|
|
1096
|
+
async burnUserTokens(request) {
|
|
1097
|
+
return this.transactionApi.burnUserTokens(request);
|
|
1098
|
+
}
|
|
1099
|
+
// ==========================================
|
|
1100
|
+
// BUSINESS OPERATIONS
|
|
1101
|
+
// ==========================================
|
|
1102
|
+
/**
|
|
1103
|
+
* BUSINESS: Create business transaction
|
|
1104
|
+
*/
|
|
1105
|
+
async createBusinessTransaction(request) {
|
|
1106
|
+
return this.transactionApi.createBusinessTransaction(request);
|
|
1107
|
+
}
|
|
1108
|
+
// ==========================================
|
|
1109
|
+
// ADMIN OPERATIONS
|
|
1110
|
+
// ==========================================
|
|
1111
|
+
/**
|
|
1112
|
+
* ADMIN: Create admin transaction
|
|
1113
|
+
*/
|
|
1114
|
+
async createAdminTransaction(request) {
|
|
1115
|
+
return this.transactionApi.createAdminTransaction(request);
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* ADMIN: Get all tenant transactions
|
|
1119
|
+
*/
|
|
1120
|
+
async getTenantTransactions() {
|
|
1121
|
+
return this.transactionApi.getTenantTransactions();
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* ADMIN: Get paginated transactions with filtering and sorting
|
|
1125
|
+
*/
|
|
1126
|
+
async getPaginatedTransactions(params) {
|
|
1127
|
+
return this.transactionApi.getPaginatedTransactions(params);
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* ADMIN: Export transactions to CSV
|
|
1131
|
+
*/
|
|
1132
|
+
async exportTransactionsCSV() {
|
|
1133
|
+
return this.transactionApi.exportTransactionsCSV();
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Transaction Domain Models
|
|
1139
|
+
*
|
|
1140
|
+
* Re-exports from @explorins/pers-shared for consistency with backend
|
|
1141
|
+
* and to provide a single import source for transaction-related types.
|
|
1142
|
+
*/
|
|
1143
|
+
// Transaction account types (domain-specific enum)
|
|
1144
|
+
exports.TransactionAccountType = void 0;
|
|
1145
|
+
(function (TransactionAccountType) {
|
|
1146
|
+
// Add specific transaction account types as needed
|
|
1147
|
+
// This should match the enum used in the infrastructure layer
|
|
1148
|
+
})(exports.TransactionAccountType || (exports.TransactionAccountType = {}));
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* @explorins/pers-sdk-transaction
|
|
1152
|
+
*
|
|
1153
|
+
* Platform-agnostic Transaction Domain SDK for PERS ecosystem
|
|
1154
|
+
* Handles transaction operations across different authorization levels
|
|
1155
|
+
*/
|
|
1156
|
+
// API Layer
|
|
1157
|
+
/**
|
|
1158
|
+
* Create a complete Transaction SDK instance
|
|
1159
|
+
*
|
|
1160
|
+
* @param apiClient - Configured PERS API client
|
|
1161
|
+
* @returns Transaction SDK with flattened structure for better DX
|
|
1162
|
+
*/
|
|
1163
|
+
function createTransactionSDK(apiClient) {
|
|
1164
|
+
const transactionApi = new TransactionApi(apiClient);
|
|
1165
|
+
const transactionService = new TransactionService(transactionApi);
|
|
1166
|
+
return {
|
|
1167
|
+
// Direct access to service methods (primary interface)
|
|
1168
|
+
// Public methods
|
|
1169
|
+
getTransactionById: (transactionId) => transactionService.getTransactionById(transactionId),
|
|
1170
|
+
// Auth methods
|
|
1171
|
+
createAuthTransaction: (request) => transactionService.createAuthTransaction(request),
|
|
1172
|
+
getUserTransactionHistory: (type) => transactionService.getUserTransactionHistory(type),
|
|
1173
|
+
prepareClientSignedTransaction: (request) => transactionService.prepareClientSignedTransaction(request),
|
|
1174
|
+
burnUserTokens: (request) => transactionService.burnUserTokens(request),
|
|
1175
|
+
// Business methods
|
|
1176
|
+
createBusinessTransaction: (request) => transactionService.createBusinessTransaction(request),
|
|
1177
|
+
// Admin methods
|
|
1178
|
+
createAdminTransaction: (request) => transactionService.createAdminTransaction(request),
|
|
1179
|
+
getTenantTransactions: () => transactionService.getTenantTransactions(),
|
|
1180
|
+
getPaginatedTransactions: (params) => transactionService.getPaginatedTransactions(params),
|
|
1181
|
+
exportTransactionsCSV: () => transactionService.exportTransactionsCSV(),
|
|
1182
|
+
// Advanced access for edge cases
|
|
1183
|
+
api: transactionApi,
|
|
1184
|
+
service: transactionService
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
/**
|
|
1189
|
+
* Platform-Agnostic Analytics API Client
|
|
1190
|
+
*
|
|
1191
|
+
* Handles analytics operations using the PERS backend.
|
|
1192
|
+
* Uses @explorins/pers-shared DTOs for consistency with backend.
|
|
1193
|
+
*/
|
|
1194
|
+
class AnalyticsApi {
|
|
1195
|
+
constructor(apiClient) {
|
|
1196
|
+
this.apiClient = apiClient;
|
|
1197
|
+
}
|
|
1198
|
+
// ==========================================
|
|
1199
|
+
// ADMIN OPERATIONS
|
|
1200
|
+
// ==========================================
|
|
1201
|
+
/**
|
|
1202
|
+
* ADMIN: Get transaction analytics with filtering and aggregation
|
|
1203
|
+
*/
|
|
1204
|
+
async getTransactionAnalytics(request) {
|
|
1205
|
+
return this.apiClient.post('/transactions/analytics', request);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Platform-Agnostic Analytics Service
|
|
1211
|
+
*
|
|
1212
|
+
* Contains analytics business logic and operations that work across platforms.
|
|
1213
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
1214
|
+
*
|
|
1215
|
+
* Focuses only on actual backend capabilities.
|
|
1216
|
+
*/
|
|
1217
|
+
class AnalyticsService {
|
|
1218
|
+
constructor(analyticsApi) {
|
|
1219
|
+
this.analyticsApi = analyticsApi;
|
|
1220
|
+
}
|
|
1221
|
+
// ==========================================
|
|
1222
|
+
// ADMIN OPERATIONS
|
|
1223
|
+
// ==========================================
|
|
1224
|
+
/**
|
|
1225
|
+
* ADMIN: Get transaction analytics with filtering and aggregation
|
|
1226
|
+
*/
|
|
1227
|
+
async getTransactionAnalytics(request) {
|
|
1228
|
+
return this.analyticsApi.getTransactionAnalytics(request);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* @explorins/pers-sdk-analytics
|
|
1234
|
+
*
|
|
1235
|
+
* Platform-agnostic Analytics Domain SDK for PERS ecosystem
|
|
1236
|
+
* Handles analytics operations and data aggregation
|
|
1237
|
+
*/
|
|
1238
|
+
// API Layer
|
|
1239
|
+
/**
|
|
1240
|
+
* Create a complete Analytics SDK instance
|
|
1241
|
+
*
|
|
1242
|
+
* @param apiClient - Configured PERS API client
|
|
1243
|
+
* @returns Analytics SDK with flattened structure for better DX
|
|
1244
|
+
*/
|
|
1245
|
+
function createAnalyticsSDK(apiClient) {
|
|
1246
|
+
const analyticsApi = new AnalyticsApi(apiClient);
|
|
1247
|
+
const analyticsService = new AnalyticsService(analyticsApi);
|
|
1248
|
+
return {
|
|
1249
|
+
// Direct access to service methods (primary interface)
|
|
1250
|
+
// Admin methods
|
|
1251
|
+
getTransactionAnalytics: (request) => analyticsService.getTransactionAnalytics(request),
|
|
1252
|
+
// Advanced access for edge cases
|
|
1253
|
+
api: analyticsApi,
|
|
1254
|
+
service: analyticsService
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
* Platform-Agnostic Auth Admin API Client
|
|
1260
|
+
*
|
|
1261
|
+
* Handles authentication and authorization admin operations using the PERS backend.
|
|
1262
|
+
* Uses @explorins/pers-shared DTOs for consistency with backend.
|
|
1263
|
+
*
|
|
1264
|
+
* Note: Special header handling (bypass-auth-interceptor) may need to be implemented
|
|
1265
|
+
* at the PersApiClient level or through a specialized auth client.
|
|
1266
|
+
*/
|
|
1267
|
+
class AuthAdminApi {
|
|
1268
|
+
constructor(apiClient) {
|
|
1269
|
+
this.apiClient = apiClient;
|
|
1270
|
+
this.basePath = '/auth';
|
|
1271
|
+
}
|
|
1272
|
+
// ==========================================
|
|
1273
|
+
// ADMIN AUTHENTICATION OPERATIONS
|
|
1274
|
+
// ==========================================
|
|
1275
|
+
/**
|
|
1276
|
+
* ADMIN: Login tenant admin with JWT
|
|
1277
|
+
* Note: JWT handling and auth bypass headers may need special implementation
|
|
1278
|
+
*/
|
|
1279
|
+
async loginTenantAdmin(jwt) {
|
|
1280
|
+
// TODO: Implement proper JWT and bypass header handling when PersApiClient supports it
|
|
1281
|
+
return this.apiClient.post(`${this.basePath}/token`, {});
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* ADMIN: Refresh access token
|
|
1285
|
+
* Note: Bypass header handling may need special implementation
|
|
1286
|
+
*/
|
|
1287
|
+
async refreshAccessToken(refreshToken) {
|
|
1288
|
+
return this.apiClient.post(`${this.basePath}/refresh`, { refreshToken });
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* Platform-Agnostic Auth Admin Service
|
|
1294
|
+
*
|
|
1295
|
+
* Contains auth admin business logic and operations that work across platforms.
|
|
1296
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
1297
|
+
*
|
|
1298
|
+
* Focuses only on actual backend capabilities.
|
|
1299
|
+
*/
|
|
1300
|
+
class AuthAdminService {
|
|
1301
|
+
constructor(authAdminApi) {
|
|
1302
|
+
this.authAdminApi = authAdminApi;
|
|
1303
|
+
}
|
|
1304
|
+
// ==========================================
|
|
1305
|
+
// ADMIN AUTHENTICATION OPERATIONS
|
|
1306
|
+
// ==========================================
|
|
1307
|
+
/**
|
|
1308
|
+
* ADMIN: Login tenant admin with JWT
|
|
1309
|
+
*/
|
|
1310
|
+
async loginTenantAdmin(jwt) {
|
|
1311
|
+
return this.authAdminApi.loginTenantAdmin(jwt);
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* ADMIN: Refresh access token
|
|
1315
|
+
*/
|
|
1316
|
+
async refreshAccessToken(refreshToken) {
|
|
1317
|
+
return this.authAdminApi.refreshAccessToken(refreshToken);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
/**
|
|
1322
|
+
* @explorins/pers-sdk-auth-admin
|
|
1323
|
+
*
|
|
1324
|
+
* Platform-agnostic Auth Admin Domain SDK for PERS ecosystem
|
|
1325
|
+
* Handles authentication and authorization admin operations
|
|
1326
|
+
*/
|
|
1327
|
+
// API Layer
|
|
1328
|
+
/**
|
|
1329
|
+
* Create a complete Auth Admin SDK instance
|
|
1330
|
+
*
|
|
1331
|
+
* @param apiClient - Configured PERS API client
|
|
1332
|
+
* @returns Auth Admin SDK with flattened structure for better DX
|
|
1333
|
+
*/
|
|
1334
|
+
function createAuthAdminSDK(apiClient) {
|
|
1335
|
+
const authAdminApi = new AuthAdminApi(apiClient);
|
|
1336
|
+
const authAdminService = new AuthAdminService(authAdminApi);
|
|
1337
|
+
return {
|
|
1338
|
+
// Direct access to service methods (primary interface)
|
|
1339
|
+
// Admin authentication methods
|
|
1340
|
+
loginTenantAdmin: (jwt) => authAdminService.loginTenantAdmin(jwt),
|
|
1341
|
+
refreshAccessToken: (refreshToken) => authAdminService.refreshAccessToken(refreshToken),
|
|
1342
|
+
// Advanced access for edge cases
|
|
1343
|
+
api: authAdminApi,
|
|
1344
|
+
service: authAdminService
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Platform-Agnostic Campaign API Client (NEW - RESTful Design)
|
|
1350
|
+
*
|
|
1351
|
+
* Updated to use the new microservice-ready campaign controllers:
|
|
1352
|
+
* - CampaignsController: Core campaign operations
|
|
1353
|
+
* - CampaignTagsController: Tag management
|
|
1354
|
+
* - CampaignTokensController: Token unit operations
|
|
1355
|
+
* - CampaignTriggersController: Trigger system
|
|
1356
|
+
* - CampaignEngagementsController: Business relationships
|
|
1357
|
+
* - CampaignClaimsController: Claims processing
|
|
1358
|
+
*
|
|
1359
|
+
* Uses @explorins/pers-shared DTOs for consistency with backend.
|
|
1360
|
+
* All endpoints updated to new RESTful patterns without role revelation.
|
|
1361
|
+
*/
|
|
1362
|
+
class CampaignApi {
|
|
1363
|
+
constructor(apiClient) {
|
|
1364
|
+
this.apiClient = apiClient;
|
|
1365
|
+
}
|
|
1366
|
+
// ==========================================
|
|
1367
|
+
// CORE CAMPAIGN OPERATIONS (/campaigns)
|
|
1368
|
+
// ==========================================
|
|
1369
|
+
/**
|
|
1370
|
+
* PUBLIC: Get all active campaigns
|
|
1371
|
+
* NEW: /campaigns (intelligent access detection)
|
|
1372
|
+
*/
|
|
1373
|
+
async getActiveCampaigns() {
|
|
1374
|
+
return this.apiClient.get('/campaigns');
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* ADMIN: Get campaigns with filtering options
|
|
1378
|
+
* NEW: /campaigns with query parameters (admin access detected automatically)
|
|
1379
|
+
*/
|
|
1380
|
+
async getCampaigns(options) {
|
|
1381
|
+
let url = '/campaigns';
|
|
1382
|
+
const params = [];
|
|
1383
|
+
if (options) {
|
|
1384
|
+
if (options.active !== undefined)
|
|
1385
|
+
params.push(`active=${options.active}`);
|
|
1386
|
+
if (options.tag)
|
|
1387
|
+
params.push(`tag=${encodeURIComponent(options.tag)}`);
|
|
1388
|
+
if (options.limit)
|
|
1389
|
+
params.push(`limit=${options.limit}`);
|
|
1390
|
+
if (options.offset)
|
|
1391
|
+
params.push(`offset=${options.offset}`);
|
|
1392
|
+
if (options.sort)
|
|
1393
|
+
params.push(`sort=${options.sort}`);
|
|
1394
|
+
if (options.order)
|
|
1395
|
+
params.push(`order=${options.order}`);
|
|
1396
|
+
}
|
|
1397
|
+
if (params.length > 0) {
|
|
1398
|
+
url += `?${params.join('&')}`;
|
|
1399
|
+
}
|
|
1400
|
+
return this.apiClient.get(url);
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* PUBLIC: Get campaign by ID
|
|
1404
|
+
* NEW: /campaigns/{id}
|
|
1405
|
+
*/
|
|
1406
|
+
async getCampaignById(id) {
|
|
1407
|
+
return this.apiClient.get(`/campaigns/${id}`);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* ADMIN: Create campaign
|
|
1411
|
+
* NEW: POST /campaigns
|
|
1412
|
+
*/
|
|
1413
|
+
async createCampaign(campaign) {
|
|
1414
|
+
return this.apiClient.post('/campaigns', campaign);
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* ADMIN: Update campaign
|
|
1418
|
+
* NEW: PUT /campaigns/{id}
|
|
1419
|
+
*/
|
|
1420
|
+
async updateCampaign(campaignId, campaign) {
|
|
1421
|
+
return this.apiClient.put(`/campaigns/${campaignId}`, campaign);
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* ADMIN: Toggle campaign active status
|
|
1425
|
+
* NEW: PUT /campaigns/{id}/status
|
|
1426
|
+
*/
|
|
1427
|
+
async toggleCampaignActive(campaignId) {
|
|
1428
|
+
return this.apiClient.put(`/campaigns/${campaignId}/status`, {});
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* ADMIN: Toggle campaign testnet environment
|
|
1432
|
+
* NEW: PUT /campaigns/{id}/environment
|
|
1433
|
+
*/
|
|
1434
|
+
async toggleCampaignTestnet(campaignId) {
|
|
1435
|
+
return this.apiClient.put(`/campaigns/${campaignId}/environment`, {});
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* ADMIN: Delete campaign
|
|
1439
|
+
* NEW: DELETE /campaigns/{id}
|
|
1440
|
+
*/
|
|
1441
|
+
async deleteCampaign(campaignId) {
|
|
1442
|
+
return this.apiClient.delete(`/campaigns/${campaignId}`);
|
|
1443
|
+
}
|
|
1444
|
+
// ==========================================
|
|
1445
|
+
// TAG MANAGEMENT (/campaign-tags)
|
|
1446
|
+
// ==========================================
|
|
1447
|
+
/**
|
|
1448
|
+
* ADMIN: Get all unique campaign tags
|
|
1449
|
+
* NEW: GET /campaign-tags
|
|
1450
|
+
*/
|
|
1451
|
+
async getAllUniqueTags() {
|
|
1452
|
+
return this.apiClient.get('/campaign-tags');
|
|
1453
|
+
}
|
|
1454
|
+
/**
|
|
1455
|
+
* ADMIN: Update campaign tags (replace all)
|
|
1456
|
+
* NEW: PUT /campaign-tags/{id}
|
|
1457
|
+
*/
|
|
1458
|
+
async updateCampaignTags(campaignId, tags) {
|
|
1459
|
+
return this.apiClient.put(`/campaign-tags/${campaignId}`, { tags });
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* ADMIN: Add tags to campaign
|
|
1463
|
+
* NEW: POST /campaign-tags/{id}
|
|
1464
|
+
*/
|
|
1465
|
+
async addTagsToCampaign(campaignId, tags) {
|
|
1466
|
+
return this.apiClient.post(`/campaign-tags/${campaignId}`, { tags });
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* ADMIN: Remove tag from campaign
|
|
1470
|
+
* NEW: DELETE /campaign-tags/{id}/{tag}
|
|
1471
|
+
*/
|
|
1472
|
+
async removeTagFromCampaign(campaignId, tag) {
|
|
1473
|
+
return this.apiClient.delete(`/campaign-tags/${campaignId}/${encodeURIComponent(tag)}`);
|
|
1474
|
+
}
|
|
1475
|
+
// ==========================================
|
|
1476
|
+
// TOKEN MANAGEMENT (/campaign-tokens)
|
|
1477
|
+
// ==========================================
|
|
1478
|
+
/**
|
|
1479
|
+
* ADMIN: Create campaign token unit
|
|
1480
|
+
* NEW: POST /campaign-tokens/{id}
|
|
1481
|
+
*/
|
|
1482
|
+
async createCampaignTokenUnit(campaignId, campaignTokenUnit) {
|
|
1483
|
+
return this.apiClient.post(`/campaign-tokens/${campaignId}`, campaignTokenUnit);
|
|
1484
|
+
}
|
|
1485
|
+
/**
|
|
1486
|
+
* ADMIN: Update campaign token unit
|
|
1487
|
+
* NEW: PUT /campaign-tokens/{id}/{tokenUnitId}
|
|
1488
|
+
*/
|
|
1489
|
+
async updateCampaignTokenUnit(campaignId, tokenUnitId, campaignTokenUnit) {
|
|
1490
|
+
return this.apiClient.put(`/campaign-tokens/${campaignId}/${tokenUnitId}`, campaignTokenUnit);
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* ADMIN: Delete campaign token unit
|
|
1494
|
+
* NEW: DELETE /campaign-tokens/{id}/{tokenUnitId}
|
|
1495
|
+
*/
|
|
1496
|
+
async deleteCampaignTokenUnit(campaignId, campaignTokenUnitId) {
|
|
1497
|
+
return this.apiClient.delete(`/campaign-tokens/${campaignId}/${campaignTokenUnitId}`);
|
|
1498
|
+
}
|
|
1499
|
+
// ==========================================
|
|
1500
|
+
// TRIGGER SYSTEM (/campaign-triggers)
|
|
1501
|
+
// ==========================================
|
|
1502
|
+
/**
|
|
1503
|
+
* PUBLIC: Get campaign triggers catalog
|
|
1504
|
+
* NEW: GET /campaign-triggers
|
|
1505
|
+
*/
|
|
1506
|
+
async getCampaignTriggers() {
|
|
1507
|
+
return this.apiClient.get('/campaign-triggers');
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* ADMIN: Create campaign trigger
|
|
1511
|
+
* NEW: POST /campaign-triggers
|
|
1512
|
+
*/
|
|
1513
|
+
async createCampaignTrigger(trigger) {
|
|
1514
|
+
return this.apiClient.post('/campaign-triggers', trigger);
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* ADMIN: Update campaign trigger
|
|
1518
|
+
* NEW: PUT /campaign-triggers/{id}
|
|
1519
|
+
*/
|
|
1520
|
+
async updateCampaignTrigger(triggerId, trigger) {
|
|
1521
|
+
return this.apiClient.put(`/campaign-triggers/${triggerId}`, trigger);
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* ADMIN: Delete campaign trigger
|
|
1525
|
+
* NEW: DELETE /campaign-triggers/{id}
|
|
1526
|
+
*/
|
|
1527
|
+
async deleteCampaignTrigger(triggerId) {
|
|
1528
|
+
return this.apiClient.delete(`/campaign-triggers/${triggerId}`);
|
|
1529
|
+
}
|
|
1530
|
+
/**
|
|
1531
|
+
* ADMIN: Set campaign trigger
|
|
1532
|
+
* NEW: PUT /campaign-triggers/campaigns/{id}/trigger/{triggerId}
|
|
1533
|
+
*/
|
|
1534
|
+
async setCampaignTrigger(campaignId, triggerId) {
|
|
1535
|
+
return this.apiClient.put(`/campaign-triggers/campaigns/${campaignId}/trigger/${triggerId}`, {});
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* ADMIN: Create trigger condition
|
|
1539
|
+
* NEW: POST /campaign-triggers/conditions
|
|
1540
|
+
*/
|
|
1541
|
+
async createTriggerCondition(condition) {
|
|
1542
|
+
return this.apiClient.post('/campaign-triggers/conditions', condition);
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* ADMIN: Update trigger condition
|
|
1546
|
+
* NEW: PUT /campaign-triggers/conditions/{id}
|
|
1547
|
+
*/
|
|
1548
|
+
async updateTriggerCondition(conditionId, condition) {
|
|
1549
|
+
return this.apiClient.put(`/campaign-triggers/conditions/${conditionId}`, condition);
|
|
1550
|
+
}
|
|
1551
|
+
/**
|
|
1552
|
+
* ADMIN: Add/Remove condition to trigger
|
|
1553
|
+
* NEW: PUT /campaign-triggers/{triggerId}/condition/{conditionId}
|
|
1554
|
+
*/
|
|
1555
|
+
async addOrRemoveConditionToTrigger(triggerId, conditionId) {
|
|
1556
|
+
return this.apiClient.put(`/campaign-triggers/${triggerId}/condition/${conditionId}`, {});
|
|
1557
|
+
}
|
|
1558
|
+
// ==========================================
|
|
1559
|
+
// BUSINESS ENGAGEMENTS (/campaign-engagements)
|
|
1560
|
+
// ==========================================
|
|
1561
|
+
/**
|
|
1562
|
+
* ADMIN: Add business engagement to campaign
|
|
1563
|
+
* NEW: POST /campaign-engagements/{id}
|
|
1564
|
+
*/
|
|
1565
|
+
async addBusinessEngagementToCampaign(campaignId, campaignBusinessEngagement) {
|
|
1566
|
+
return this.apiClient.post(`/campaign-engagements/${campaignId}`, campaignBusinessEngagement);
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* ADMIN: Update campaign business engagement
|
|
1570
|
+
* NEW: PUT /campaign-engagements/{id}/{businessEngagementId}
|
|
1571
|
+
*/
|
|
1572
|
+
async updateCampaignBusinessEngagement(campaignId, businessEngagementId, campaignBusinessEngagement) {
|
|
1573
|
+
return this.apiClient.put(`/campaign-engagements/${campaignId}/${businessEngagementId}`, campaignBusinessEngagement);
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* ADMIN: Delete campaign business engagement
|
|
1577
|
+
* NEW: DELETE /campaign-engagements/{id}/{businessEngagementId}
|
|
1578
|
+
*/
|
|
1579
|
+
async deleteCampaignBusinessEngagement(campaignId, businessEngagementId) {
|
|
1580
|
+
return this.apiClient.delete(`/campaign-engagements/${campaignId}/${businessEngagementId}`);
|
|
1581
|
+
}
|
|
1582
|
+
// ==========================================
|
|
1583
|
+
// CLAIMS PROCESSING (/campaign-claims)
|
|
1584
|
+
// ==========================================
|
|
1585
|
+
/**
|
|
1586
|
+
* USER: Claim campaign reward
|
|
1587
|
+
* NEW: POST /campaign-claims/user
|
|
1588
|
+
*/
|
|
1589
|
+
async claimCampaign(request) {
|
|
1590
|
+
return this.apiClient.post('/campaign-claims/user', request);
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* USER: Get claims for logged user
|
|
1594
|
+
* NEW: GET /campaign-claims/users/me
|
|
1595
|
+
*/
|
|
1596
|
+
async getClaimsForLoggedUser() {
|
|
1597
|
+
return this.apiClient.get('/campaign-claims/users/me');
|
|
1598
|
+
}
|
|
1599
|
+
/**
|
|
1600
|
+
* BUSINESS: Claim campaign reward for customer
|
|
1601
|
+
* NEW: POST /campaign-claims/business
|
|
1602
|
+
*/
|
|
1603
|
+
async businessClaimCampaign(request) {
|
|
1604
|
+
return this.apiClient.post('/campaign-claims/business', request);
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* SYSTEM: Process automated claim
|
|
1608
|
+
* NEW: POST /campaign-claims/system
|
|
1609
|
+
*/
|
|
1610
|
+
async systemClaimCampaign(request) {
|
|
1611
|
+
return this.apiClient.post('/campaign-claims/system', request);
|
|
1612
|
+
}
|
|
1613
|
+
/**
|
|
1614
|
+
* ADMIN: Manual claim processing
|
|
1615
|
+
* NEW: POST /campaign-claims/admin
|
|
1616
|
+
*/
|
|
1617
|
+
async adminClaimCampaign(request) {
|
|
1618
|
+
return this.apiClient.post('/campaign-claims/admin', request);
|
|
1619
|
+
}
|
|
1620
|
+
/**
|
|
1621
|
+
* ADMIN: Get all campaign claims
|
|
1622
|
+
* NEW: GET /campaign-claims/admin
|
|
1623
|
+
*/
|
|
1624
|
+
async getCampaignClaims() {
|
|
1625
|
+
return this.apiClient.get('/campaign-claims/admin');
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* ADMIN: Get campaign claims by campaign ID
|
|
1629
|
+
* NEW: GET /campaign-claims/admin/{campaignId}
|
|
1630
|
+
*/
|
|
1631
|
+
async getCampaignClaimsByCampaignId(campaignId) {
|
|
1632
|
+
return this.apiClient.get(`/campaign-claims/admin/${campaignId}`);
|
|
1633
|
+
}
|
|
1634
|
+
/**
|
|
1635
|
+
* ADMIN: Get campaign claims by user ID
|
|
1636
|
+
* NEW: GET /campaign-claims/admin/users/{userId}
|
|
1637
|
+
*/
|
|
1638
|
+
async getCampaignClaimsByUserId(userId) {
|
|
1639
|
+
return this.apiClient.get(`/campaign-claims/admin/users/${userId}`);
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* ADMIN: Get campaign claims by business ID
|
|
1643
|
+
* NEW: GET /campaign-claims/admin/businesses/{businessId}
|
|
1644
|
+
*/
|
|
1645
|
+
async getCampaignClaimsByBusinessId(businessId) {
|
|
1646
|
+
return this.apiClient.get(`/campaign-claims/admin/businesses/${businessId}`);
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* USER: Get user's claims for specific campaign
|
|
1650
|
+
* NEW: GET /campaign-claims/campaigns/{campaignId}/users/me
|
|
1651
|
+
*/
|
|
1652
|
+
async getUserClaimsForCampaign(campaignId) {
|
|
1653
|
+
return this.apiClient.get(`/campaign-claims/campaigns/${campaignId}/users/me`);
|
|
1654
|
+
}
|
|
1655
|
+
// ==========================================
|
|
1656
|
+
// BACKWARD COMPATIBILITY (DEPRECATED)
|
|
1657
|
+
// ==========================================
|
|
1658
|
+
/**
|
|
1659
|
+
* @deprecated Use getCampaigns() instead
|
|
1660
|
+
* LEGACY: Get campaigns with active filter
|
|
1661
|
+
*/
|
|
1662
|
+
async getCampaignsLegacy(active) {
|
|
1663
|
+
console.warn('CampaignApi.getCampaignsLegacy() is deprecated. Use getCampaigns() instead.');
|
|
1664
|
+
return this.getCampaigns(active !== undefined ? { active } : undefined);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
/**
|
|
1669
|
+
* Platform-Agnostic Campaign Service
|
|
1670
|
+
*
|
|
1671
|
+
* Contains campaign business logic and operations that work across platforms.
|
|
1672
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
1673
|
+
*
|
|
1674
|
+
* Focuses only on actual backend capabilities.
|
|
1675
|
+
*/
|
|
1676
|
+
class CampaignService {
|
|
1677
|
+
constructor(campaignApi) {
|
|
1678
|
+
this.campaignApi = campaignApi;
|
|
1679
|
+
}
|
|
1680
|
+
// ==========================================
|
|
1681
|
+
// PUBLIC OPERATIONS
|
|
1682
|
+
// ==========================================
|
|
1683
|
+
/**
|
|
1684
|
+
* PUBLIC: Get all active campaigns
|
|
1685
|
+
*/
|
|
1686
|
+
async getActiveCampaigns() {
|
|
1687
|
+
return this.campaignApi.getActiveCampaigns();
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* PUBLIC: Get campaign by ID
|
|
1691
|
+
*/
|
|
1692
|
+
async getCampaignById(id) {
|
|
1693
|
+
return this.campaignApi.getCampaignById(id);
|
|
1694
|
+
}
|
|
1695
|
+
// ==========================================
|
|
1696
|
+
// AUTHENTICATED OPERATIONS
|
|
1697
|
+
// ==========================================
|
|
1698
|
+
/**
|
|
1699
|
+
* AUTH: Claim campaign
|
|
1700
|
+
*/
|
|
1701
|
+
async claimCampaign(request) {
|
|
1702
|
+
return this.campaignApi.claimCampaign(request);
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* AUTH: Get claims for logged user
|
|
1706
|
+
*/
|
|
1707
|
+
async getClaimsForLoggedUser() {
|
|
1708
|
+
return this.campaignApi.getClaimsForLoggedUser();
|
|
1709
|
+
}
|
|
1710
|
+
// ==========================================
|
|
1711
|
+
// ADMIN OPERATIONS
|
|
1712
|
+
// ==========================================
|
|
1713
|
+
/**
|
|
1714
|
+
* ADMIN: Get campaigns with optional active filter
|
|
1715
|
+
*/
|
|
1716
|
+
async getCampaigns(active) {
|
|
1717
|
+
return this.campaignApi.getCampaigns(active !== undefined ? { active } : undefined);
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* ADMIN: Get campaign triggers
|
|
1721
|
+
*/
|
|
1722
|
+
async getCampaignTriggers() {
|
|
1723
|
+
return this.campaignApi.getCampaignTriggers();
|
|
1724
|
+
}
|
|
1725
|
+
/**
|
|
1726
|
+
* ADMIN: Toggle campaign active status
|
|
1727
|
+
*/
|
|
1728
|
+
async toggleCampaignActive(campaignId) {
|
|
1729
|
+
return this.campaignApi.toggleCampaignActive(campaignId);
|
|
1730
|
+
}
|
|
1731
|
+
/**
|
|
1732
|
+
* ADMIN: Toggle campaign testnet environment
|
|
1733
|
+
*/
|
|
1734
|
+
async toggleCampaignTestnet(campaignId) {
|
|
1735
|
+
return this.campaignApi.toggleCampaignTestnet(campaignId);
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* ADMIN: Create campaign
|
|
1739
|
+
*/
|
|
1740
|
+
async createCampaign(campaign) {
|
|
1741
|
+
return this.campaignApi.createCampaign(campaign);
|
|
1742
|
+
}
|
|
1743
|
+
/**
|
|
1744
|
+
* ADMIN: Set campaign trigger
|
|
1745
|
+
*/
|
|
1746
|
+
async setCampaignTrigger(campaignId, triggerId) {
|
|
1747
|
+
return this.campaignApi.setCampaignTrigger(campaignId, triggerId);
|
|
1748
|
+
}
|
|
1749
|
+
/**
|
|
1750
|
+
* ADMIN: Update campaign
|
|
1751
|
+
*/
|
|
1752
|
+
async updateCampaign(campaignId, campaign) {
|
|
1753
|
+
return this.campaignApi.updateCampaign(campaignId, campaign);
|
|
1754
|
+
}
|
|
1755
|
+
/**
|
|
1756
|
+
* ADMIN: Create campaign token unit
|
|
1757
|
+
*/
|
|
1758
|
+
async createCampaignTokenUnit(campaignId, campaignTokenUnit) {
|
|
1759
|
+
return this.campaignApi.createCampaignTokenUnit(campaignId, campaignTokenUnit);
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* ADMIN: Delete campaign token unit
|
|
1763
|
+
*/
|
|
1764
|
+
async deleteCampaignTokenUnit(campaignId, campaignTokenUnitId) {
|
|
1765
|
+
return this.campaignApi.deleteCampaignTokenUnit(campaignId, campaignTokenUnitId);
|
|
1766
|
+
}
|
|
1767
|
+
/**
|
|
1768
|
+
* ADMIN: Add business engagement to campaign
|
|
1769
|
+
*/
|
|
1770
|
+
async addBusinessEngagementToCampaign(campaignId, campaignBusinessEngagement) {
|
|
1771
|
+
return this.campaignApi.addBusinessEngagementToCampaign(campaignId, campaignBusinessEngagement);
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* ADMIN: Update campaign business engagement
|
|
1775
|
+
*/
|
|
1776
|
+
async updateCampaignBusinessEngagement(campaignId, businessEngagementId, campaignBusinessEngagement) {
|
|
1777
|
+
return this.campaignApi.updateCampaignBusinessEngagement(campaignId, businessEngagementId, campaignBusinessEngagement);
|
|
1778
|
+
}
|
|
1779
|
+
async deleteCampaignBusinessEngagement(campaignId, businessEngagementId) {
|
|
1780
|
+
return this.campaignApi.deleteCampaignBusinessEngagement(campaignId, businessEngagementId);
|
|
1781
|
+
}
|
|
1782
|
+
/**
|
|
1783
|
+
* ADMIN: Get all campaign claims
|
|
1784
|
+
*/
|
|
1785
|
+
async getCampaignClaims() {
|
|
1786
|
+
return this.campaignApi.getCampaignClaims();
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* ADMIN: Get campaign claims by user ID
|
|
1790
|
+
*/
|
|
1791
|
+
async getCampaignClaimsByUserId(userId) {
|
|
1792
|
+
return this.campaignApi.getCampaignClaimsByUserId(userId);
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* ADMIN: Get campaign claims by business ID
|
|
1796
|
+
*/
|
|
1797
|
+
async getCampaignClaimsByBusinessId(businessId) {
|
|
1798
|
+
return this.campaignApi.getCampaignClaimsByBusinessId(businessId);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
/**
|
|
1803
|
+
* @explorins/pers-sdk-campaign
|
|
1804
|
+
*
|
|
1805
|
+
* Platform-agnostic Campaign Domain SDK for PERS ecosystem
|
|
1806
|
+
* Handles campaign operations across different authorization levels
|
|
1807
|
+
*/
|
|
1808
|
+
// API Layer
|
|
1809
|
+
/**
|
|
1810
|
+
* Create a complete Campaign SDK instance
|
|
1811
|
+
*
|
|
1812
|
+
* @param apiClient - Configured PERS API client
|
|
1813
|
+
* @returns Campaign SDK with flattened structure for better DX
|
|
1814
|
+
*/
|
|
1815
|
+
function createCampaignSDK(apiClient) {
|
|
1816
|
+
const campaignApi = new CampaignApi(apiClient);
|
|
1817
|
+
const campaignService = new CampaignService(campaignApi);
|
|
1818
|
+
return {
|
|
1819
|
+
// Direct access to service methods (primary interface)
|
|
1820
|
+
// Public methods
|
|
1821
|
+
getActiveCampaigns: () => campaignService.getActiveCampaigns(),
|
|
1822
|
+
getCampaignById: (id) => campaignService.getCampaignById(id),
|
|
1823
|
+
// Auth methods
|
|
1824
|
+
claimCampaign: (request) => campaignService.claimCampaign(request),
|
|
1825
|
+
getClaimsForLoggedUser: () => campaignService.getClaimsForLoggedUser(),
|
|
1826
|
+
// Admin methods
|
|
1827
|
+
getCampaigns: (active) => campaignService.getCampaigns(active),
|
|
1828
|
+
getCampaignTriggers: () => campaignService.getCampaignTriggers(),
|
|
1829
|
+
toggleCampaignActive: (campaignId) => campaignService.toggleCampaignActive(campaignId),
|
|
1830
|
+
toggleCampaignTestnet: (campaignId) => campaignService.toggleCampaignTestnet(campaignId),
|
|
1831
|
+
createCampaign: (campaign) => campaignService.createCampaign(campaign),
|
|
1832
|
+
setCampaignTrigger: (campaignId, triggerId) => campaignService.setCampaignTrigger(campaignId, triggerId),
|
|
1833
|
+
updateCampaign: (campaignId, campaign) => campaignService.updateCampaign(campaignId, campaign),
|
|
1834
|
+
createCampaignTokenUnit: (campaignId, campaignTokenUnit) => campaignService.createCampaignTokenUnit(campaignId, campaignTokenUnit),
|
|
1835
|
+
deleteCampaignTokenUnit: (campaignId, campaignTokenUnitId) => campaignService.deleteCampaignTokenUnit(campaignId, campaignTokenUnitId),
|
|
1836
|
+
addBusinessEngagementToCampaign: (campaignId, campaignBusinessEngagement) => campaignService.addBusinessEngagementToCampaign(campaignId, campaignBusinessEngagement),
|
|
1837
|
+
updateCampaignBusinessEngagement: (campaignId, businessEngagementId, campaignBusinessEngagement) => campaignService.updateCampaignBusinessEngagement(campaignId, businessEngagementId, campaignBusinessEngagement),
|
|
1838
|
+
deleteCampaignBusinessEngagement: (campaignId, businessEngagementId) => campaignService.deleteCampaignBusinessEngagement(campaignId, businessEngagementId),
|
|
1839
|
+
getCampaignClaims: () => campaignService.getCampaignClaims(),
|
|
1840
|
+
getCampaignClaimsByUserId: (userId) => campaignService.getCampaignClaimsByUserId(userId),
|
|
1841
|
+
getCampaignClaimsByBusinessId: (businessId) => campaignService.getCampaignClaimsByBusinessId(businessId),
|
|
1842
|
+
// Advanced access for edge cases
|
|
1843
|
+
api: campaignApi,
|
|
1844
|
+
service: campaignService
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
/**
|
|
1849
|
+
* Platform-Agnostic Donation API Client
|
|
1850
|
+
*
|
|
1851
|
+
* Handles donation operations using the PERS backend.
|
|
1852
|
+
* Matches framework DonationApiService methods exactly.
|
|
1853
|
+
*/
|
|
1854
|
+
class DonationApi {
|
|
1855
|
+
constructor(apiClient) {
|
|
1856
|
+
this.apiClient = apiClient;
|
|
1857
|
+
}
|
|
1858
|
+
// ==========================================
|
|
1859
|
+
// PUBLIC OPERATIONS
|
|
1860
|
+
// ==========================================
|
|
1861
|
+
/**
|
|
1862
|
+
* PUBLIC: Get all donation types
|
|
1863
|
+
* ✅ ONLY method actually used by framework
|
|
1864
|
+
*/
|
|
1865
|
+
async getAllDonationTypes() {
|
|
1866
|
+
return this.apiClient.get('/purchase/donation/type');
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
/**
|
|
1871
|
+
* Platform-Agnostic Donation Service
|
|
1872
|
+
*
|
|
1873
|
+
* Contains donation business logic and operations that work across platforms.
|
|
1874
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
1875
|
+
* Matches framework DonationApiService capabilities exactly.
|
|
1876
|
+
*/
|
|
1877
|
+
class DonationService {
|
|
1878
|
+
constructor(donationApi) {
|
|
1879
|
+
this.donationApi = donationApi;
|
|
1880
|
+
}
|
|
1881
|
+
// ==========================================
|
|
1882
|
+
// PUBLIC OPERATIONS
|
|
1883
|
+
// ==========================================
|
|
1884
|
+
/**
|
|
1885
|
+
* PUBLIC: Get all donation types
|
|
1886
|
+
* ✅ ONLY method actually used by framework
|
|
1887
|
+
*/
|
|
1888
|
+
async getAllDonationTypes() {
|
|
1889
|
+
return this.donationApi.getAllDonationTypes();
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
/**
|
|
1894
|
+
* @explorins/pers-sdk-donation
|
|
1895
|
+
*
|
|
1896
|
+
* Platform-agnostic Donation Domain SDK for PERS ecosystem
|
|
1897
|
+
* Handles donation type retrieval for purchase flow integration
|
|
1898
|
+
*/
|
|
1899
|
+
// API Layer
|
|
1900
|
+
/**
|
|
1901
|
+
* Create a complete Donation SDK instance
|
|
1902
|
+
*
|
|
1903
|
+
* @param apiClient - Configured PERS API client
|
|
1904
|
+
* @returns Donation SDK with flattened structure for better DX
|
|
1905
|
+
*/
|
|
1906
|
+
function createDonationSDK(apiClient) {
|
|
1907
|
+
const donationApi = new DonationApi(apiClient);
|
|
1908
|
+
const donationService = new DonationService(donationApi);
|
|
1909
|
+
return {
|
|
1910
|
+
// Direct access to service methods (primary interface)
|
|
1911
|
+
// ✅ FRAMEWORK ALIGNED: Only method actually used by framework
|
|
1912
|
+
// Public methods
|
|
1913
|
+
getAllDonationTypes: () => donationService.getAllDonationTypes(),
|
|
1914
|
+
// Advanced access for edge cases
|
|
1915
|
+
api: donationApi,
|
|
1916
|
+
service: donationService
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
/**
|
|
1921
|
+
* Platform-Agnostic Purchase API Client (RESTful Architecture)
|
|
1922
|
+
*
|
|
1923
|
+
* Handles purchase and payment operations using the PERS backend's new RESTful endpoints.
|
|
1924
|
+
* Uses @explorins/pers-shared DTOs for consistency with backend.
|
|
1925
|
+
*
|
|
1926
|
+
* Migration Status: Updated to match /purchases controller (replaces /purchase endpoints)
|
|
1927
|
+
*
|
|
1928
|
+
* Available Access Levels:
|
|
1929
|
+
* - PUBLIC: Project key authentication for catalog browsing and payment operations
|
|
1930
|
+
* - USER: Requires user authentication JWT (purchase creation, history access)
|
|
1931
|
+
* - ADMIN: Requires tenant admin privileges (not implemented in this client)
|
|
1932
|
+
*
|
|
1933
|
+
* Note: This SDK focuses on backend purchase operations only.
|
|
1934
|
+
* Payment provider integrations (Stripe, etc.) should remain in infrastructure layer.
|
|
1935
|
+
*/
|
|
1936
|
+
class PurchaseApi {
|
|
1937
|
+
constructor(apiClient) {
|
|
1938
|
+
this.apiClient = apiClient;
|
|
1939
|
+
this.basePath = '/purchases';
|
|
1940
|
+
}
|
|
1941
|
+
// ==========================================
|
|
1942
|
+
// PUBLIC OPERATIONS (Project Key)
|
|
1943
|
+
// ==========================================
|
|
1944
|
+
/**
|
|
1945
|
+
* PUBLIC: Get purchase tokens (Intelligent Access)
|
|
1946
|
+
*
|
|
1947
|
+
* RESTful endpoint: GET /purchases/tokens
|
|
1948
|
+
* Replaces: GET /purchase/token
|
|
1949
|
+
*
|
|
1950
|
+
* INTELLIGENT ACCESS:
|
|
1951
|
+
* - PUBLIC (Project Key): Returns active tokens only (active parameter ignored)
|
|
1952
|
+
* - ADMIN (Tenant Admin JWT): Returns filtered results based on active parameter
|
|
1953
|
+
*/
|
|
1954
|
+
async getPurchaseTokens(active) {
|
|
1955
|
+
let url = `${this.basePath}/tokens`;
|
|
1956
|
+
if (active !== undefined) {
|
|
1957
|
+
url += `?active=${active}`;
|
|
1958
|
+
}
|
|
1959
|
+
return this.apiClient.get(url);
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* PUBLIC: Get donation types
|
|
1963
|
+
*
|
|
1964
|
+
* RESTful endpoint: GET /purchases/donation-types
|
|
1965
|
+
* Replaces: GET /purchase/donation/type
|
|
1966
|
+
*/
|
|
1967
|
+
async getDonationTypes() {
|
|
1968
|
+
return this.apiClient.get(`${this.basePath}/donation-types`);
|
|
1969
|
+
}
|
|
1970
|
+
// ==========================================
|
|
1971
|
+
// PAYMENT OPERATIONS (FINANCIAL - CRITICAL)
|
|
1972
|
+
// ==========================================
|
|
1973
|
+
/**
|
|
1974
|
+
* PUBLIC: Create payment intent (FINANCIAL OPERATION)
|
|
1975
|
+
*
|
|
1976
|
+
* RESTful endpoint: POST /purchases/payment-intents
|
|
1977
|
+
* Replaces: POST /purchase/payment-intent
|
|
1978
|
+
*
|
|
1979
|
+
* CRITICAL: Handles real money operations - tenant context required
|
|
1980
|
+
*/
|
|
1981
|
+
async createPaymentIntent(amount, currency, receiptEmail, description) {
|
|
1982
|
+
const body = {
|
|
1983
|
+
amount,
|
|
1984
|
+
currency,
|
|
1985
|
+
receiptEmail,
|
|
1986
|
+
description
|
|
1987
|
+
};
|
|
1988
|
+
return this.apiClient.post(`${this.basePath}/payment-intents`, body);
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* PUBLIC: Update payment intent (FINANCIAL OPERATION)
|
|
1992
|
+
*
|
|
1993
|
+
* RESTful endpoint: PUT /purchases/payment-intents/{paymentIntentId}
|
|
1994
|
+
* Replaces: PUT /purchase/payment-intent/{paymentIntentId}
|
|
1995
|
+
*
|
|
1996
|
+
* CRITICAL: Handles real money operations - tenant context required
|
|
1997
|
+
*/
|
|
1998
|
+
async updatePaymentIntent(paymentIntentId, amount, currency, receiptEmail, description) {
|
|
1999
|
+
const body = {
|
|
2000
|
+
amount,
|
|
2001
|
+
currency,
|
|
2002
|
+
receiptEmail,
|
|
2003
|
+
description
|
|
2004
|
+
};
|
|
2005
|
+
return this.apiClient.put(`${this.basePath}/payment-intents/${paymentIntentId}`, body);
|
|
2006
|
+
}
|
|
2007
|
+
/**
|
|
2008
|
+
* PUBLIC: Cancel payment intent (FINANCIAL OPERATION)
|
|
2009
|
+
*
|
|
2010
|
+
* RESTful endpoint: DELETE /purchases/payment-intents/{paymentIntentId}
|
|
2011
|
+
* Replaces: DELETE /purchase/payment-intent/{paymentIntentId}
|
|
2012
|
+
*
|
|
2013
|
+
* CRITICAL: Handles real money operations - tenant context required
|
|
2014
|
+
*/
|
|
2015
|
+
async cancelPaymentIntent(paymentIntentId) {
|
|
2016
|
+
return this.apiClient.delete(`${this.basePath}/payment-intents/${paymentIntentId}`);
|
|
2017
|
+
}
|
|
2018
|
+
// ==========================================
|
|
2019
|
+
// USER OPERATIONS (JWT + Project Key)
|
|
2020
|
+
// ==========================================
|
|
2021
|
+
/**
|
|
2022
|
+
* USER: Create purchase (BUSINESS CRITICAL - FINANCIAL TRANSACTION)
|
|
2023
|
+
*
|
|
2024
|
+
* RESTful endpoint: POST /purchases
|
|
2025
|
+
* Replaces: POST /purchase/auth
|
|
2026
|
+
*
|
|
2027
|
+
* USER-ONLY: Requires user authentication JWT for purchase creation
|
|
2028
|
+
* CRITICAL: Real financial transaction with Stripe integration
|
|
2029
|
+
*/
|
|
2030
|
+
async createUserPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress) {
|
|
2031
|
+
const body = {
|
|
2032
|
+
quantity: amount,
|
|
2033
|
+
purchaseTokenId: purchaseTokenId || '',
|
|
2034
|
+
donationTypeId,
|
|
2035
|
+
donationAccountAddress,
|
|
2036
|
+
paymentIntentId
|
|
2037
|
+
};
|
|
2038
|
+
return this.apiClient.post(`${this.basePath}`, body);
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* USER: Get user purchase history
|
|
2042
|
+
*
|
|
2043
|
+
* RESTful endpoint: GET /purchases/me/history
|
|
2044
|
+
* Replaces: GET /purchase/auth
|
|
2045
|
+
*
|
|
2046
|
+
* USER-ONLY: Get authenticated user's purchase history
|
|
2047
|
+
* FINANCIAL RECORDS: User attribution critical for compliance
|
|
2048
|
+
*/
|
|
2049
|
+
async getUserPurchaseHistory() {
|
|
2050
|
+
return this.apiClient.get(`${this.basePath}/me/history`);
|
|
2051
|
+
}
|
|
2052
|
+
// ==========================================
|
|
2053
|
+
// CONVENIENCE METHODS (Backward Compatibility)
|
|
2054
|
+
// ==========================================
|
|
2055
|
+
/**
|
|
2056
|
+
* @deprecated Use getPurchaseTokens() instead
|
|
2057
|
+
* Backward compatibility alias for getActivePurchaseTokens
|
|
2058
|
+
*/
|
|
2059
|
+
async getActivePurchaseTokens(active = true) {
|
|
2060
|
+
return this.getPurchaseTokens(active);
|
|
2061
|
+
}
|
|
2062
|
+
/**
|
|
2063
|
+
* @deprecated Use createUserPurchase() instead
|
|
2064
|
+
* Backward compatibility alias for createPurchase
|
|
2065
|
+
*/
|
|
2066
|
+
async createPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress) {
|
|
2067
|
+
return this.createUserPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress);
|
|
2068
|
+
}
|
|
2069
|
+
/**
|
|
2070
|
+
* @deprecated Use getUserPurchaseHistory() instead
|
|
2071
|
+
* Backward compatibility alias for getAllUserPurchases
|
|
2072
|
+
*/
|
|
2073
|
+
async getAllUserPurchases() {
|
|
2074
|
+
return this.getUserPurchaseHistory();
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
/**
|
|
2079
|
+
* Platform-Agnostic Payment Service
|
|
2080
|
+
*
|
|
2081
|
+
* Contains payment business logic and operations that work across platforms.
|
|
2082
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
2083
|
+
*
|
|
2084
|
+
* Focuses only on actual backend capabilities.
|
|
2085
|
+
* Payment provider logic (Stripe, etc.) should remain in infrastructure layer.
|
|
2086
|
+
*/
|
|
2087
|
+
class PaymentService {
|
|
2088
|
+
constructor(paymentApi) {
|
|
2089
|
+
this.paymentApi = paymentApi;
|
|
2090
|
+
}
|
|
2091
|
+
// ==========================================
|
|
2092
|
+
// PUBLIC OPERATIONS
|
|
2093
|
+
// ==========================================
|
|
2094
|
+
/**
|
|
2095
|
+
* PUBLIC: Get active purchase tokens
|
|
2096
|
+
*/
|
|
2097
|
+
async getActivePurchaseTokens(active = true) {
|
|
2098
|
+
return this.paymentApi.getActivePurchaseTokens(active);
|
|
2099
|
+
}
|
|
2100
|
+
/**
|
|
2101
|
+
* PUBLIC: Create payment intent
|
|
2102
|
+
*/
|
|
2103
|
+
async createPaymentIntent(amount, currency, receiptEmail, description) {
|
|
2104
|
+
return this.paymentApi.createPaymentIntent(amount, currency, receiptEmail, description);
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* PUBLIC: Update payment intent
|
|
2108
|
+
*/
|
|
2109
|
+
async updatePaymentIntent(paymentIntentId, amount, currency, receiptEmail, description) {
|
|
2110
|
+
return this.paymentApi.updatePaymentIntent(paymentIntentId, amount, currency, receiptEmail, description);
|
|
2111
|
+
}
|
|
2112
|
+
/**
|
|
2113
|
+
* PUBLIC: Cancel payment intent
|
|
2114
|
+
*/
|
|
2115
|
+
async cancelPaymentIntent(paymentIntentId) {
|
|
2116
|
+
return this.paymentApi.cancelPaymentIntent(paymentIntentId);
|
|
2117
|
+
}
|
|
2118
|
+
// ==========================================
|
|
2119
|
+
// AUTHENTICATED OPERATIONS
|
|
2120
|
+
// ==========================================
|
|
2121
|
+
/**
|
|
2122
|
+
* AUTH: Create purchase
|
|
2123
|
+
*/
|
|
2124
|
+
async createPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress) {
|
|
2125
|
+
return this.paymentApi.createPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress);
|
|
2126
|
+
}
|
|
2127
|
+
/**
|
|
2128
|
+
* AUTH: Get all user purchases
|
|
2129
|
+
*/
|
|
2130
|
+
async getAllUserPurchases() {
|
|
2131
|
+
return this.paymentApi.getAllUserPurchases();
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
/**
|
|
2136
|
+
* @explorins/pers-sdk-payment
|
|
2137
|
+
*
|
|
2138
|
+
* Platform-agnostic Payment Domain SDK for PERS ecosystem
|
|
2139
|
+
* Handles payment intents, purchases, and purchase tokens
|
|
2140
|
+
*
|
|
2141
|
+
* Note: Payment provider integrations (Stripe, etc.) are kept separate
|
|
2142
|
+
* in the infrastructure layer to maintain platform-agnostic principles.
|
|
2143
|
+
*/
|
|
2144
|
+
// API Layer
|
|
2145
|
+
/**
|
|
2146
|
+
* Create a complete Payment SDK instance
|
|
2147
|
+
*
|
|
2148
|
+
* @param apiClient - Configured PERS API client
|
|
2149
|
+
* @returns Payment SDK with flattened structure for better DX
|
|
2150
|
+
*/
|
|
2151
|
+
function createPaymentSDK(apiClient) {
|
|
2152
|
+
const paymentApi = new PurchaseApi(apiClient);
|
|
2153
|
+
const paymentService = new PaymentService(paymentApi);
|
|
2154
|
+
return {
|
|
2155
|
+
// Direct access to service methods (primary interface)
|
|
2156
|
+
// Public methods
|
|
2157
|
+
getActivePurchaseTokens: (active) => paymentService.getActivePurchaseTokens(active),
|
|
2158
|
+
// ✅ FIXED: Proper type instead of any
|
|
2159
|
+
createPaymentIntent: (amount, currency, receiptEmail, description) => paymentService.createPaymentIntent(amount, currency, receiptEmail, description),
|
|
2160
|
+
// ✅ FIXED: Proper type instead of any
|
|
2161
|
+
updatePaymentIntent: (paymentIntentId, amount, currency, receiptEmail, description) => paymentService.updatePaymentIntent(paymentIntentId, amount, currency, receiptEmail, description),
|
|
2162
|
+
cancelPaymentIntent: (paymentIntentId) => paymentService.cancelPaymentIntent(paymentIntentId),
|
|
2163
|
+
// Auth methods
|
|
2164
|
+
createPurchase: (paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress) => paymentService.createPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress),
|
|
2165
|
+
getAllUserPurchases: () => paymentService.getAllUserPurchases(),
|
|
2166
|
+
// Advanced access for edge cases
|
|
2167
|
+
api: paymentApi,
|
|
2168
|
+
service: paymentService
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
/**
|
|
2173
|
+
* Platform-Agnostic Redemption API Client (UPDATED - RESTful Design)
|
|
2174
|
+
*
|
|
2175
|
+
* Updated to work with the new RESTful /redemptions endpoints.
|
|
2176
|
+
* Handles redemption operations using the PERS backend with intelligent access detection.
|
|
2177
|
+
* Uses @explorins/pers-shared DTOs for consistency with backend.
|
|
2178
|
+
*
|
|
2179
|
+
* Migration Update: Updated all endpoints from /redemption to /redemptions
|
|
2180
|
+
* - Removed role revelation from URLs (no more /admin, /auth paths)
|
|
2181
|
+
* - Added intelligent access detection for unified endpoints
|
|
2182
|
+
* - Updated toggle endpoint to follow /status pattern
|
|
2183
|
+
* - Enhanced redemption process with path-based IDs
|
|
2184
|
+
*/
|
|
2185
|
+
class RedemptionApi {
|
|
2186
|
+
constructor(apiClient) {
|
|
2187
|
+
this.apiClient = apiClient;
|
|
2188
|
+
this.basePath = '/redemptions';
|
|
2189
|
+
}
|
|
2190
|
+
// ==========================================
|
|
2191
|
+
// PUBLIC OPERATIONS (Project Key)
|
|
2192
|
+
// ==========================================
|
|
2193
|
+
/**
|
|
2194
|
+
* PUBLIC: Get redemptions (intelligent access)
|
|
2195
|
+
*
|
|
2196
|
+
* NEW: Intelligent endpoint that adapts based on authentication
|
|
2197
|
+
* - Public users: Get active redemptions only
|
|
2198
|
+
* - Admin users: Get all redemptions with optional filtering
|
|
2199
|
+
*
|
|
2200
|
+
* Replaces: getActiveRedemptions() + getRedemptionsAsAdmin()
|
|
2201
|
+
*/
|
|
2202
|
+
async getRedemptions(active) {
|
|
2203
|
+
let url = `${this.basePath}`;
|
|
2204
|
+
if (active !== undefined) {
|
|
2205
|
+
url += `?active=${active}`;
|
|
2206
|
+
}
|
|
2207
|
+
return this.apiClient.get(url);
|
|
2208
|
+
}
|
|
2209
|
+
/**
|
|
2210
|
+
* PUBLIC: Get active redemptions
|
|
2211
|
+
*
|
|
2212
|
+
* Updated: Now uses unified endpoint (backward compatibility)
|
|
2213
|
+
*/
|
|
2214
|
+
async getActiveRedemptions() {
|
|
2215
|
+
return this.getRedemptions(); // Will return active only for public access
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* PUBLIC: Get redemption types
|
|
2219
|
+
*
|
|
2220
|
+
* Updated: /redemption/type → /redemptions/types
|
|
2221
|
+
*/
|
|
2222
|
+
async getRedemptionTypes() {
|
|
2223
|
+
return this.apiClient.get(`${this.basePath}/types`);
|
|
2224
|
+
}
|
|
2225
|
+
/**
|
|
2226
|
+
* PUBLIC: Get redemption by ID
|
|
2227
|
+
*
|
|
2228
|
+
* Updated: /redemption/:id → /redemptions/:id
|
|
2229
|
+
*/
|
|
2230
|
+
async getRedemptionById(id) {
|
|
2231
|
+
return this.apiClient.get(`${this.basePath}/${id}`);
|
|
2232
|
+
}
|
|
2233
|
+
/**
|
|
2234
|
+
* PUBLIC: Get available supply for redemption
|
|
2235
|
+
*
|
|
2236
|
+
* Updated: /redemption/:id/available-supply → /redemptions/:id/available-supply
|
|
2237
|
+
*/
|
|
2238
|
+
async getRedemptionAvailableSupply(id) {
|
|
2239
|
+
return this.apiClient.get(`${this.basePath}/${id}/available-supply`);
|
|
2240
|
+
}
|
|
2241
|
+
// ==========================================
|
|
2242
|
+
// USER OPERATIONS (JWT + Project Key)
|
|
2243
|
+
// ==========================================
|
|
2244
|
+
/**
|
|
2245
|
+
* USER: Redeem a redemption
|
|
2246
|
+
*
|
|
2247
|
+
* Updated: /redemption/auth/redeem → /redemptions/:id/redeem
|
|
2248
|
+
* Enhanced: Path-based redemption ID for better RESTful design
|
|
2249
|
+
*/
|
|
2250
|
+
async redeemRedemption(redemptionId) {
|
|
2251
|
+
const body = {
|
|
2252
|
+
redemptionId: redemptionId,
|
|
2253
|
+
};
|
|
2254
|
+
return this.apiClient.post(`${this.basePath}/${redemptionId}/redeem`, body);
|
|
2255
|
+
}
|
|
2256
|
+
/**
|
|
2257
|
+
* USER: Get user redemption history
|
|
2258
|
+
*
|
|
2259
|
+
* Updated: /redemption/auth/redeem → /redemptions/me/history
|
|
2260
|
+
*/
|
|
2261
|
+
async getUserRedemptionHistory() {
|
|
2262
|
+
return this.apiClient.get(`${this.basePath}/me/history`);
|
|
2263
|
+
}
|
|
2264
|
+
/**
|
|
2265
|
+
* USER: Get user redemptions (backward compatibility)
|
|
2266
|
+
*
|
|
2267
|
+
* Deprecated: Use getUserRedemptionHistory() instead
|
|
2268
|
+
*/
|
|
2269
|
+
async getUserRedeems() {
|
|
2270
|
+
return this.getUserRedemptionHistory();
|
|
2271
|
+
}
|
|
2272
|
+
// ==========================================
|
|
2273
|
+
// ADMIN OPERATIONS (Tenant Admin JWT)
|
|
2274
|
+
// ==========================================
|
|
2275
|
+
/**
|
|
2276
|
+
* ADMIN: Get redemptions with filtering (using intelligent endpoint)
|
|
2277
|
+
*
|
|
2278
|
+
* Updated: /redemption/admin → /redemptions (intelligent access detection)
|
|
2279
|
+
* The unified endpoint will detect admin privileges and allow filtering
|
|
2280
|
+
*/
|
|
2281
|
+
async getRedemptionsAsAdmin(active) {
|
|
2282
|
+
return this.getRedemptions(active); // Uses intelligent endpoint
|
|
2283
|
+
}
|
|
2284
|
+
/**
|
|
2285
|
+
* ADMIN: Create redemption
|
|
2286
|
+
*
|
|
2287
|
+
* Updated: /redemption/admin → /redemptions
|
|
2288
|
+
*/
|
|
2289
|
+
async createRedemption(redemption) {
|
|
2290
|
+
return this.apiClient.post(`${this.basePath}`, redemption);
|
|
2291
|
+
}
|
|
2292
|
+
/**
|
|
2293
|
+
* ADMIN: Update redemption
|
|
2294
|
+
*
|
|
2295
|
+
* Updated: /redemption/admin/:id → /redemptions/:id
|
|
2296
|
+
*/
|
|
2297
|
+
async updateRedemption(id, redemptionCreateRequest) {
|
|
2298
|
+
return this.apiClient.put(`${this.basePath}/${id}`, redemptionCreateRequest);
|
|
2299
|
+
}
|
|
2300
|
+
/**
|
|
2301
|
+
* ADMIN: Toggle redemption status
|
|
2302
|
+
*
|
|
2303
|
+
* Updated: /redemption/admin/:id/toggle-active → /redemptions/:id/status
|
|
2304
|
+
* Following standard /status pattern used across domains
|
|
2305
|
+
*/
|
|
2306
|
+
async toggleRedemptionStatus(redemptionId) {
|
|
2307
|
+
return this.apiClient.put(`${this.basePath}/${redemptionId}/status`, {});
|
|
2308
|
+
}
|
|
2309
|
+
/**
|
|
2310
|
+
* ADMIN: Toggle redemption active (backward compatibility)
|
|
2311
|
+
*
|
|
2312
|
+
* Deprecated: Use toggleRedemptionStatus() instead
|
|
2313
|
+
*/
|
|
2314
|
+
async toggleRedemptionActive(redemptionId) {
|
|
2315
|
+
return this.toggleRedemptionStatus(redemptionId);
|
|
2316
|
+
}
|
|
2317
|
+
/**
|
|
2318
|
+
* ADMIN: Delete redemption
|
|
2319
|
+
*
|
|
2320
|
+
* Updated: /redemption/admin/:id → /redemptions/:id
|
|
2321
|
+
*/
|
|
2322
|
+
async deleteRedemption(id) {
|
|
2323
|
+
return this.apiClient.delete(`${this.basePath}/${id}`);
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* ADMIN: Create redemption type
|
|
2327
|
+
*
|
|
2328
|
+
* Updated: /redemption/admin/type → /redemptions/types
|
|
2329
|
+
*/
|
|
2330
|
+
async createRedemptionType(redemptionType) {
|
|
2331
|
+
return this.apiClient.post(`${this.basePath}/types`, redemptionType);
|
|
2332
|
+
}
|
|
2333
|
+
// ==========================================
|
|
2334
|
+
// TOKEN UNIT MANAGEMENT (Admin)
|
|
2335
|
+
// ==========================================
|
|
2336
|
+
/**
|
|
2337
|
+
* ADMIN: Create redemption token unit
|
|
2338
|
+
*
|
|
2339
|
+
* Updated: /redemption/admin/:id/token-units → /redemptions/:id/token-units
|
|
2340
|
+
*/
|
|
2341
|
+
async createRedemptionTokenUnit(redemptionId, redemptionTokenUnit) {
|
|
2342
|
+
return this.apiClient.post(`${this.basePath}/${redemptionId}/token-units`, redemptionTokenUnit);
|
|
2343
|
+
}
|
|
2344
|
+
/**
|
|
2345
|
+
* ADMIN: Update redemption token unit
|
|
2346
|
+
*
|
|
2347
|
+
* Updated: /redemption/admin/:id/token-units/:tokenUnitId → /redemptions/:id/token-units/:tokenUnitId
|
|
2348
|
+
*/
|
|
2349
|
+
async updateRedemptionTokenUnit(redemptionId, tokenUnitId, redemptionTokenUnit) {
|
|
2350
|
+
return this.apiClient.put(`${this.basePath}/${redemptionId}/token-units/${tokenUnitId}`, redemptionTokenUnit);
|
|
2351
|
+
}
|
|
2352
|
+
/**
|
|
2353
|
+
* ADMIN: Delete redemption token unit
|
|
2354
|
+
*
|
|
2355
|
+
* Updated: /redemption/admin/:id/token-units/:tokenUnitId → /redemptions/:id/token-units/:tokenUnitId
|
|
2356
|
+
*/
|
|
2357
|
+
async deleteRedemptionTokenUnit(redemptionId, redemptionTokenUnitId) {
|
|
2358
|
+
return this.apiClient.delete(`${this.basePath}/${redemptionId}/token-units/${redemptionTokenUnitId}`);
|
|
2359
|
+
}
|
|
2360
|
+
// ==========================================
|
|
2361
|
+
// BACKWARD COMPATIBILITY METHODS
|
|
2362
|
+
// ==========================================
|
|
2363
|
+
/**
|
|
2364
|
+
* @deprecated Use getRedemptions() instead
|
|
2365
|
+
* Backward compatibility for old admin endpoint
|
|
2366
|
+
*/
|
|
2367
|
+
async getRedemptionsAdmin(active) {
|
|
2368
|
+
return this.getRedemptionsAsAdmin(active);
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
* @deprecated Use redeemRedemption() instead
|
|
2372
|
+
* Backward compatibility for old redeem method
|
|
2373
|
+
*/
|
|
2374
|
+
async redeem(redemptionId) {
|
|
2375
|
+
return this.redeemRedemption(redemptionId);
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
/**
|
|
2380
|
+
* Platform-Agnostic Redemption Service
|
|
2381
|
+
*
|
|
2382
|
+
* Contains redemption business logic and operations that work across platforms.
|
|
2383
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
2384
|
+
*
|
|
2385
|
+
* Focuses only on actual backend capabilities.
|
|
2386
|
+
*/
|
|
2387
|
+
class RedemptionService {
|
|
2388
|
+
constructor(redemptionApi) {
|
|
2389
|
+
this.redemptionApi = redemptionApi;
|
|
2390
|
+
}
|
|
2391
|
+
// ==========================================
|
|
2392
|
+
// PUBLIC OPERATIONS
|
|
2393
|
+
// ==========================================
|
|
2394
|
+
/**
|
|
2395
|
+
* PUBLIC: Get active redemptions
|
|
2396
|
+
*/
|
|
2397
|
+
async getActiveRedemptions() {
|
|
2398
|
+
return this.redemptionApi.getActiveRedemptions();
|
|
2399
|
+
}
|
|
2400
|
+
/**
|
|
2401
|
+
* PUBLIC: Get redemption types
|
|
2402
|
+
*/
|
|
2403
|
+
async getRedemptionTypes() {
|
|
2404
|
+
return this.redemptionApi.getRedemptionTypes();
|
|
2405
|
+
}
|
|
2406
|
+
// ==========================================
|
|
2407
|
+
// AUTHENTICATED OPERATIONS
|
|
2408
|
+
// ==========================================
|
|
2409
|
+
/**
|
|
2410
|
+
* AUTH: Redeem a redemption
|
|
2411
|
+
*/
|
|
2412
|
+
async redeemRedemption(redemptionId) {
|
|
2413
|
+
return this.redemptionApi.redeemRedemption(redemptionId);
|
|
2414
|
+
}
|
|
2415
|
+
/**
|
|
2416
|
+
* AUTH: Get user redemptions
|
|
2417
|
+
*/
|
|
2418
|
+
async getUserRedeems() {
|
|
2419
|
+
return this.redemptionApi.getUserRedeems();
|
|
2420
|
+
}
|
|
2421
|
+
// ==========================================
|
|
2422
|
+
// ADMIN OPERATIONS
|
|
2423
|
+
// ==========================================
|
|
2424
|
+
/**
|
|
2425
|
+
* ADMIN: Get redemptions with optional active filter
|
|
2426
|
+
*/
|
|
2427
|
+
async getRedemptionsAsAdmin(active) {
|
|
2428
|
+
return this.redemptionApi.getRedemptionsAsAdmin(active);
|
|
2429
|
+
}
|
|
2430
|
+
/**
|
|
2431
|
+
* ADMIN: Create redemption
|
|
2432
|
+
*/
|
|
2433
|
+
async createRedemption(redemption) {
|
|
2434
|
+
return this.redemptionApi.createRedemption(redemption);
|
|
2435
|
+
}
|
|
2436
|
+
/**
|
|
2437
|
+
* ADMIN: Update redemption
|
|
2438
|
+
*/
|
|
2439
|
+
async updateRedemption(id, redemptionCreateRequest) {
|
|
2440
|
+
return this.redemptionApi.updateRedemption(id, redemptionCreateRequest); // ✅ CORRECTED: Fixed parameter
|
|
2441
|
+
}
|
|
2442
|
+
/**
|
|
2443
|
+
* ADMIN: Toggle redemption active status
|
|
2444
|
+
*/
|
|
2445
|
+
async toggleRedemptionActive(redemptionId) {
|
|
2446
|
+
return this.redemptionApi.toggleRedemptionActive(redemptionId);
|
|
2447
|
+
}
|
|
2448
|
+
/**
|
|
2449
|
+
* ADMIN: Create redemption token unit
|
|
2450
|
+
*/
|
|
2451
|
+
async createRedemptionTokenUnit(redemptionId, redemptionTokenUnit) {
|
|
2452
|
+
return this.redemptionApi.createRedemptionTokenUnit(redemptionId, redemptionTokenUnit);
|
|
2453
|
+
}
|
|
2454
|
+
/**
|
|
2455
|
+
* ADMIN: Delete redemption token unit
|
|
2456
|
+
*/
|
|
2457
|
+
async deleteRedemptionTokenUnit(redemptionId, redemptionTokenUnitId) {
|
|
2458
|
+
return this.redemptionApi.deleteRedemptionTokenUnit(redemptionId, redemptionTokenUnitId);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
/**
|
|
2463
|
+
* @explorins/pers-sdk-redemption
|
|
2464
|
+
*
|
|
2465
|
+
* Platform-agnostic Redemption Domain SDK for PERS ecosystem
|
|
2466
|
+
* Handles redemption operations across different authorization levels
|
|
2467
|
+
*/
|
|
2468
|
+
// API Layer
|
|
2469
|
+
/**
|
|
2470
|
+
* Create a complete Redemption SDK instance
|
|
2471
|
+
*
|
|
2472
|
+
* @param apiClient - Configured PERS API client
|
|
2473
|
+
* @returns Redemption SDK with flattened structure for better DX
|
|
2474
|
+
*/
|
|
2475
|
+
function createRedemptionSDK(apiClient) {
|
|
2476
|
+
const redemptionApi = new RedemptionApi(apiClient);
|
|
2477
|
+
const redemptionService = new RedemptionService(redemptionApi);
|
|
2478
|
+
return {
|
|
2479
|
+
// Direct access to service methods (primary interface)
|
|
2480
|
+
// Public methods
|
|
2481
|
+
getActiveRedemptions: () => redemptionService.getActiveRedemptions(),
|
|
2482
|
+
getRedemptionTypes: () => redemptionService.getRedemptionTypes(),
|
|
2483
|
+
// Auth methods
|
|
2484
|
+
redeemRedemption: (redemptionId) => redemptionService.redeemRedemption(redemptionId),
|
|
2485
|
+
getUserRedeems: () => redemptionService.getUserRedeems(),
|
|
2486
|
+
// Admin methods
|
|
2487
|
+
getRedemptionsAsAdmin: (active) => redemptionService.getRedemptionsAsAdmin(active),
|
|
2488
|
+
createRedemption: (redemption) => redemptionService.createRedemption(redemption),
|
|
2489
|
+
updateRedemption: (id, redemptionCreateRequest) => redemptionService.updateRedemption(id, redemptionCreateRequest),
|
|
2490
|
+
toggleRedemptionActive: (redemptionId) => redemptionService.toggleRedemptionActive(redemptionId),
|
|
2491
|
+
createRedemptionTokenUnit: (redemptionId, redemptionTokenUnit) => redemptionService.createRedemptionTokenUnit(redemptionId, redemptionTokenUnit),
|
|
2492
|
+
deleteRedemptionTokenUnit: (redemptionId, redemptionTokenUnitId) => redemptionService.deleteRedemptionTokenUnit(redemptionId, redemptionTokenUnitId),
|
|
2493
|
+
// Advanced access for edge cases
|
|
2494
|
+
api: redemptionApi,
|
|
2495
|
+
service: redemptionService
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
/**
|
|
2500
|
+
* Platform-Agnostic Tenant API Client
|
|
2501
|
+
*
|
|
2502
|
+
* Handles tenant and admin operations using the PERS backend.
|
|
2503
|
+
* Matches framework TenantApiService methods exactly.
|
|
2504
|
+
*
|
|
2505
|
+
* Note: Special header handling (bypass-auth-interceptor) should be handled by PersApiClient internally
|
|
2506
|
+
* or through endpoint-specific configuration.
|
|
2507
|
+
*/
|
|
2508
|
+
class TenantApi {
|
|
2509
|
+
constructor(apiClient) {
|
|
2510
|
+
this.apiClient = apiClient;
|
|
2511
|
+
this.basePath = '/tenants';
|
|
2512
|
+
this.adminPath = '/admins';
|
|
2513
|
+
}
|
|
2514
|
+
// ==========================================
|
|
2515
|
+
// PUBLIC OPERATIONS
|
|
2516
|
+
// ==========================================
|
|
2517
|
+
/**
|
|
2518
|
+
* PUBLIC: Get tenant public information
|
|
2519
|
+
* ✅ FIXED: Matches framework cache busting pattern exactly
|
|
2520
|
+
*/
|
|
2521
|
+
async getRemoteTenant() {
|
|
2522
|
+
const timestamp = Date.now().toString();
|
|
2523
|
+
const url = `${this.basePath}/public?date=${timestamp}`;
|
|
2524
|
+
return this.apiClient.get(url);
|
|
2525
|
+
}
|
|
2526
|
+
/**
|
|
2527
|
+
* PUBLIC: Get remote login token
|
|
2528
|
+
*/
|
|
2529
|
+
async getRemoteLoginToken() {
|
|
2530
|
+
return this.apiClient.get(`${this.basePath}/login-token`);
|
|
2531
|
+
}
|
|
2532
|
+
/**
|
|
2533
|
+
* PUBLIC: Get remote client configuration
|
|
2534
|
+
* ✅ FIXED: Removed second parameter - PersApiClient handles bypass auth internally
|
|
2535
|
+
* Note: The /tenants/client-config endpoint should be configured to bypass auth at the API client level
|
|
2536
|
+
*/
|
|
2537
|
+
async getRemoteClientConfig() {
|
|
2538
|
+
return this.apiClient.get(`${this.basePath}/client-config`);
|
|
2539
|
+
}
|
|
2540
|
+
// ==========================================
|
|
2541
|
+
// ADMIN OPERATIONS
|
|
2542
|
+
// ==========================================
|
|
2543
|
+
/**
|
|
2544
|
+
* ADMIN: Update tenant information
|
|
2545
|
+
* ✅ FIXED: Uses TenantPublicDTO directly like framework
|
|
2546
|
+
*/
|
|
2547
|
+
async updateRemoteTenant(tenantData) {
|
|
2548
|
+
return this.apiClient.put(`${this.basePath}`, tenantData);
|
|
2549
|
+
}
|
|
2550
|
+
/**
|
|
2551
|
+
* ADMIN: Get all tenant admins
|
|
2552
|
+
*/
|
|
2553
|
+
async getAdmins() {
|
|
2554
|
+
return this.apiClient.get(`${this.adminPath}`);
|
|
2555
|
+
}
|
|
2556
|
+
/**
|
|
2557
|
+
* ADMIN: Create new admin
|
|
2558
|
+
* ✅ FIXED: Renamed to match framework postAdmin method
|
|
2559
|
+
*/
|
|
2560
|
+
async postAdmin(adminData) {
|
|
2561
|
+
return this.apiClient.post(`${this.adminPath}`, adminData);
|
|
2562
|
+
}
|
|
2563
|
+
/**
|
|
2564
|
+
* ADMIN: Update admin (toggle tenant association)
|
|
2565
|
+
* ✅ FIXED: Renamed to match framework putAdmin method
|
|
2566
|
+
*/
|
|
2567
|
+
async putAdmin(adminId, adminData) {
|
|
2568
|
+
return this.apiClient.put(`${this.adminPath}/${adminId}/tenant`, adminData);
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
/**
|
|
2573
|
+
* Platform-Agnostic Tenant Service
|
|
2574
|
+
*
|
|
2575
|
+
* Contains tenant business logic and operations that work across platforms.
|
|
2576
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
2577
|
+
* Matches framework TenantApiService capabilities exactly.
|
|
2578
|
+
*/
|
|
2579
|
+
class TenantService {
|
|
2580
|
+
constructor(tenantApi) {
|
|
2581
|
+
this.tenantApi = tenantApi;
|
|
2582
|
+
}
|
|
2583
|
+
// ==========================================
|
|
2584
|
+
// PUBLIC OPERATIONS
|
|
2585
|
+
// ==========================================
|
|
2586
|
+
/**
|
|
2587
|
+
* PUBLIC: Get tenant public information
|
|
2588
|
+
*/
|
|
2589
|
+
async getRemoteTenant() {
|
|
2590
|
+
return this.tenantApi.getRemoteTenant();
|
|
2591
|
+
}
|
|
2592
|
+
/**
|
|
2593
|
+
* PUBLIC: Get remote login token
|
|
2594
|
+
*/
|
|
2595
|
+
async getRemoteLoginToken() {
|
|
2596
|
+
return this.tenantApi.getRemoteLoginToken();
|
|
2597
|
+
}
|
|
2598
|
+
/**
|
|
2599
|
+
* PUBLIC: Get remote client configuration
|
|
2600
|
+
*/
|
|
2601
|
+
async getRemoteClientConfig() {
|
|
2602
|
+
return this.tenantApi.getRemoteClientConfig();
|
|
2603
|
+
}
|
|
2604
|
+
// ==========================================
|
|
2605
|
+
// ADMIN OPERATIONS
|
|
2606
|
+
// ==========================================
|
|
2607
|
+
/**
|
|
2608
|
+
* ADMIN: Update tenant information
|
|
2609
|
+
* ✅ FIXED: Uses TenantPublicDTO directly like framework
|
|
2610
|
+
*/
|
|
2611
|
+
async updateRemoteTenant(tenantData) {
|
|
2612
|
+
return this.tenantApi.updateRemoteTenant(tenantData);
|
|
2613
|
+
}
|
|
2614
|
+
/**
|
|
2615
|
+
* ADMIN: Get all tenant admins
|
|
2616
|
+
*/
|
|
2617
|
+
async getAdmins() {
|
|
2618
|
+
return this.tenantApi.getAdmins();
|
|
2619
|
+
}
|
|
2620
|
+
/**
|
|
2621
|
+
* ADMIN: Create new admin
|
|
2622
|
+
* ✅ FIXED: Renamed to match framework postAdmin method
|
|
2623
|
+
*/
|
|
2624
|
+
async postAdmin(adminData) {
|
|
2625
|
+
return this.tenantApi.postAdmin(adminData);
|
|
2626
|
+
}
|
|
2627
|
+
/**
|
|
2628
|
+
* ADMIN: Update admin (toggle tenant association)
|
|
2629
|
+
* ✅ FIXED: Renamed to match framework putAdmin method
|
|
2630
|
+
*/
|
|
2631
|
+
async putAdmin(adminId, adminData) {
|
|
2632
|
+
return this.tenantApi.putAdmin(adminId, adminData);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
/**
|
|
2637
|
+
* @explorins/pers-sdk-tenant
|
|
2638
|
+
*
|
|
2639
|
+
* Platform-agnostic Tenant Domain SDK for PERS ecosystem
|
|
2640
|
+
* Handles tenant management and admin operations for multi-tenant architecture
|
|
2641
|
+
*/
|
|
2642
|
+
// API Layer
|
|
2643
|
+
/**
|
|
2644
|
+
* Create a complete Tenant SDK instance
|
|
2645
|
+
*
|
|
2646
|
+
* @param apiClient - Configured PERS API client
|
|
2647
|
+
* @returns Tenant SDK with flattened structure for better DX
|
|
2648
|
+
*/
|
|
2649
|
+
function createTenantSDK(apiClient) {
|
|
2650
|
+
const tenantApi = new TenantApi(apiClient);
|
|
2651
|
+
const tenantService = new TenantService(tenantApi);
|
|
2652
|
+
return {
|
|
2653
|
+
// Direct access to service methods (primary interface)
|
|
2654
|
+
// ✅ FRAMEWORK ALIGNED: Only methods actually used by framework
|
|
2655
|
+
// Public methods
|
|
2656
|
+
getRemoteTenant: () => tenantService.getRemoteTenant(),
|
|
2657
|
+
getRemoteLoginToken: () => tenantService.getRemoteLoginToken(),
|
|
2658
|
+
getRemoteClientConfig: () => tenantService.getRemoteClientConfig(),
|
|
2659
|
+
// Admin methods - ✅ FIXED: Matches framework method names exactly
|
|
2660
|
+
updateRemoteTenant: (tenantData) => tenantService.updateRemoteTenant(tenantData),
|
|
2661
|
+
getAdmins: () => tenantService.getAdmins(),
|
|
2662
|
+
postAdmin: (adminData) => tenantService.postAdmin(adminData),
|
|
2663
|
+
putAdmin: (adminId, adminData) => tenantService.putAdmin(adminId, adminData),
|
|
2664
|
+
// Advanced access for edge cases
|
|
2665
|
+
api: tenantApi,
|
|
2666
|
+
service: tenantService
|
|
2667
|
+
};
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
class TokenApi {
|
|
2671
|
+
constructor(apiClient) {
|
|
2672
|
+
this.apiClient = apiClient;
|
|
2673
|
+
this.basePath = '/tokens';
|
|
2674
|
+
}
|
|
2675
|
+
// ==========================================
|
|
2676
|
+
// PUBLIC OPERATIONS
|
|
2677
|
+
// ==========================================
|
|
2678
|
+
/**
|
|
2679
|
+
* PUBLIC: Get all remote tokens
|
|
2680
|
+
* ENHANCED: Added admin filtering capability
|
|
2681
|
+
*/
|
|
2682
|
+
async getRemoteTokens(includeInactive = false) {
|
|
2683
|
+
const url = includeInactive ? `${this.basePath}?active=false` : `${this.basePath}`;
|
|
2684
|
+
return this.apiClient.get(url);
|
|
2685
|
+
}
|
|
2686
|
+
/**
|
|
2687
|
+
* PUBLIC: Get all remote token types
|
|
2688
|
+
*/
|
|
2689
|
+
async getRemoteTokenTypes() {
|
|
2690
|
+
return this.apiClient.get(`${this.basePath}/types`);
|
|
2691
|
+
}
|
|
2692
|
+
/**
|
|
2693
|
+
* PUBLIC: Get active point token (was credit token)
|
|
2694
|
+
*/
|
|
2695
|
+
async getRemoteActiveCreditToken() {
|
|
2696
|
+
return this.apiClient.get(`${this.basePath}/points`);
|
|
2697
|
+
}
|
|
2698
|
+
/**
|
|
2699
|
+
* PUBLIC: Get reward tokens
|
|
2700
|
+
* ENHANCED: Added admin filtering capability
|
|
2701
|
+
*/
|
|
2702
|
+
async getRemoteRewardTokens(includeInactive = false) {
|
|
2703
|
+
const url = includeInactive ? `${this.basePath}/rewards?active=false` : `${this.basePath}/rewards`;
|
|
2704
|
+
return this.apiClient.get(url);
|
|
2705
|
+
}
|
|
2706
|
+
/**
|
|
2707
|
+
* PUBLIC: Get stamp tokens (was status tokens)
|
|
2708
|
+
* ENHANCED: Added admin filtering capability
|
|
2709
|
+
*/
|
|
2710
|
+
async getRemoteStatusTokens(includeInactive = false) {
|
|
2711
|
+
const url = includeInactive ? `${this.basePath}/stamps?active=false` : `${this.basePath}/stamps`;
|
|
2712
|
+
return this.apiClient.get(url);
|
|
2713
|
+
}
|
|
2714
|
+
/**
|
|
2715
|
+
* PUBLIC: Get token by contract address
|
|
2716
|
+
*/
|
|
2717
|
+
async getTokenByContractAddress(contractAddress, contractTokenId) {
|
|
2718
|
+
let url = `${this.basePath}/address/${contractAddress}`;
|
|
2719
|
+
if (contractTokenId) {
|
|
2720
|
+
url += `?contractTokenId=${contractTokenId}`;
|
|
2721
|
+
}
|
|
2722
|
+
return this.apiClient.get(url);
|
|
2723
|
+
}
|
|
2724
|
+
// ==========================================
|
|
2725
|
+
// ADMIN OPERATIONS
|
|
2726
|
+
// ==========================================
|
|
2727
|
+
/**
|
|
2728
|
+
* ADMIN: Create new token
|
|
2729
|
+
*/
|
|
2730
|
+
async createToken(tokenData) {
|
|
2731
|
+
return this.apiClient.post(`${this.basePath}`, tokenData);
|
|
2732
|
+
}
|
|
2733
|
+
/**
|
|
2734
|
+
* ADMIN: Update token
|
|
2735
|
+
*/
|
|
2736
|
+
async updateToken(tokenId, tokenData) {
|
|
2737
|
+
return this.apiClient.put(`${this.basePath}/${tokenId}`, tokenData);
|
|
2738
|
+
}
|
|
2739
|
+
/**
|
|
2740
|
+
* ADMIN: Toggle token active status
|
|
2741
|
+
* FIXED: Now calls correct endpoint
|
|
2742
|
+
*/
|
|
2743
|
+
async toggleTokenActive(tokenId) {
|
|
2744
|
+
return this.apiClient.put(`${this.basePath}/${tokenId}/status`, {});
|
|
2745
|
+
}
|
|
2746
|
+
/**
|
|
2747
|
+
* ADMIN: Set mainnet contract address
|
|
2748
|
+
*/
|
|
2749
|
+
async setMainnetContract(tokenId, contractAddress, chainId) {
|
|
2750
|
+
return this.apiClient.put(`${this.basePath}/${tokenId}/mainnet`, {
|
|
2751
|
+
contractAddress,
|
|
2752
|
+
chainId
|
|
2753
|
+
});
|
|
2754
|
+
}
|
|
2755
|
+
/**
|
|
2756
|
+
* ADMIN: Create token metadata
|
|
2757
|
+
*/
|
|
2758
|
+
async createTokenMetadata(tokenId, tokenData) {
|
|
2759
|
+
return this.apiClient.post(`${this.basePath}/${tokenId}/metadata`, tokenData);
|
|
2760
|
+
}
|
|
2761
|
+
/**
|
|
2762
|
+
* ADMIN: Toggle token metadata status (separate from token status)
|
|
2763
|
+
*/
|
|
2764
|
+
async toggleTokenMetadataStatus(metadataId) {
|
|
2765
|
+
return this.apiClient.put(`${this.basePath}/metadata/${metadataId}/status`, {});
|
|
2766
|
+
}
|
|
2767
|
+
/**
|
|
2768
|
+
* ADMIN: Create token type
|
|
2769
|
+
*/
|
|
2770
|
+
async createTokenType(tokenType) {
|
|
2771
|
+
return this.apiClient.post(`${this.basePath}/types`, tokenType);
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
/**
|
|
2776
|
+
* Platform-Agnostic Token Service
|
|
2777
|
+
*
|
|
2778
|
+
* Contains token business logic and operations that work across platforms.
|
|
2779
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
2780
|
+
* Matches framework TokenApiService capabilities exactly.
|
|
2781
|
+
*/
|
|
2782
|
+
class TokenService {
|
|
2783
|
+
constructor(tokenApi) {
|
|
2784
|
+
this.tokenApi = tokenApi;
|
|
2785
|
+
}
|
|
2786
|
+
// ==========================================
|
|
2787
|
+
// PUBLIC OPERATIONS
|
|
2788
|
+
// ==========================================
|
|
2789
|
+
/**
|
|
2790
|
+
* PUBLIC: Get all remote tokens
|
|
2791
|
+
*/
|
|
2792
|
+
async getRemoteTokens() {
|
|
2793
|
+
return this.tokenApi.getRemoteTokens();
|
|
2794
|
+
}
|
|
2795
|
+
/**
|
|
2796
|
+
* PUBLIC: Get all remote token types
|
|
2797
|
+
*/
|
|
2798
|
+
async getRemoteTokenTypes() {
|
|
2799
|
+
return this.tokenApi.getRemoteTokenTypes();
|
|
2800
|
+
}
|
|
2801
|
+
/**
|
|
2802
|
+
* PUBLIC: Get active credit token
|
|
2803
|
+
*/
|
|
2804
|
+
async getRemoteActiveCreditToken() {
|
|
2805
|
+
return this.tokenApi.getRemoteActiveCreditToken();
|
|
2806
|
+
}
|
|
2807
|
+
/**
|
|
2808
|
+
* PUBLIC: Get reward tokens
|
|
2809
|
+
*/
|
|
2810
|
+
async getRemoteRewardTokens() {
|
|
2811
|
+
return this.tokenApi.getRemoteRewardTokens();
|
|
2812
|
+
}
|
|
2813
|
+
/**
|
|
2814
|
+
* PUBLIC: Get status tokens
|
|
2815
|
+
*/
|
|
2816
|
+
async getRemoteStatusTokens() {
|
|
2817
|
+
return this.tokenApi.getRemoteStatusTokens();
|
|
2818
|
+
}
|
|
2819
|
+
/**
|
|
2820
|
+
* PUBLIC: Get token by contract address
|
|
2821
|
+
* ✅ FIXED: Matches framework parameter types exactly
|
|
2822
|
+
*/
|
|
2823
|
+
async getTokenByContractAddress(contractAddress, contractTokenId) {
|
|
2824
|
+
return this.tokenApi.getTokenByContractAddress(contractAddress, contractTokenId);
|
|
2825
|
+
}
|
|
2826
|
+
// ==========================================
|
|
2827
|
+
// ADMIN OPERATIONS
|
|
2828
|
+
// ==========================================
|
|
2829
|
+
/**
|
|
2830
|
+
* ADMIN: Create token metadata
|
|
2831
|
+
*/
|
|
2832
|
+
async createTokenMetadata(tokenId, tokenData) {
|
|
2833
|
+
return this.tokenApi.createTokenMetadata(tokenId, tokenData);
|
|
2834
|
+
}
|
|
2835
|
+
/**
|
|
2836
|
+
* ADMIN: Toggle token active status
|
|
2837
|
+
*/
|
|
2838
|
+
async toggleTokenActive(tokenId) {
|
|
2839
|
+
return this.tokenApi.toggleTokenActive(tokenId);
|
|
2840
|
+
}
|
|
2841
|
+
/**
|
|
2842
|
+
* ADMIN: Create new token
|
|
2843
|
+
*/
|
|
2844
|
+
async createToken(tokenData) {
|
|
2845
|
+
return this.tokenApi.createToken(tokenData);
|
|
2846
|
+
}
|
|
2847
|
+
/**
|
|
2848
|
+
* ADMIN: Update token
|
|
2849
|
+
*/
|
|
2850
|
+
async updateToken(tokenId, tokenData) {
|
|
2851
|
+
return this.tokenApi.updateToken(tokenId, tokenData);
|
|
2852
|
+
}
|
|
2853
|
+
/**
|
|
2854
|
+
* ADMIN: Set mainnet contract address
|
|
2855
|
+
*/
|
|
2856
|
+
async setMainnetContract(tokenId, contractAddress, chainId) {
|
|
2857
|
+
return this.tokenApi.setMainnetContract(tokenId, contractAddress, chainId);
|
|
2858
|
+
}
|
|
2859
|
+
/**
|
|
2860
|
+
* ADMIN: Toggle token metadata status
|
|
2861
|
+
*/
|
|
2862
|
+
async toggleTokenMetadataStatus(metadataId) {
|
|
2863
|
+
return this.tokenApi.toggleTokenMetadataStatus(metadataId);
|
|
2864
|
+
}
|
|
2865
|
+
/**
|
|
2866
|
+
* ADMIN: Create token type
|
|
2867
|
+
*/
|
|
2868
|
+
async createTokenType(tokenType) {
|
|
2869
|
+
return this.tokenApi.createTokenType(tokenType);
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
/**
|
|
2874
|
+
* Token SDK - Class-based Promise SDK for Token Operations
|
|
2875
|
+
*
|
|
2876
|
+
* Modern, performant SDK with direct method access and excellent TypeScript support.
|
|
2877
|
+
* Optimized for bundle size, performance, and developer experience.
|
|
2878
|
+
*
|
|
2879
|
+
* Usage:
|
|
2880
|
+
* const tokenSDK = new TokenSDK(apiClient);
|
|
2881
|
+
* const tokens = await tokenSDK.getTokens();
|
|
2882
|
+
* const creditToken = await tokenSDK.getActiveCreditToken();
|
|
2883
|
+
*/
|
|
2884
|
+
class TokenSDK {
|
|
2885
|
+
constructor(apiClient) {
|
|
2886
|
+
this.tokenApi = new TokenApi(apiClient);
|
|
2887
|
+
this.tokenService = new TokenService(this.tokenApi);
|
|
2888
|
+
}
|
|
2889
|
+
// ==========================================
|
|
2890
|
+
// CONVENIENCE METHODS - Direct Promise API
|
|
2891
|
+
// ==========================================
|
|
2892
|
+
// Simplified method names for better developer experience
|
|
2893
|
+
/**
|
|
2894
|
+
* Get all tokens
|
|
2895
|
+
* @returns Promise with all available tokens
|
|
2896
|
+
*/
|
|
2897
|
+
async getTokens() {
|
|
2898
|
+
return this.tokenService.getRemoteTokens();
|
|
2899
|
+
}
|
|
2900
|
+
/**
|
|
2901
|
+
* Get all token types
|
|
2902
|
+
* @returns Promise with all available token types
|
|
2903
|
+
*/
|
|
2904
|
+
async getTokenTypes() {
|
|
2905
|
+
return this.tokenService.getRemoteTokenTypes();
|
|
2906
|
+
}
|
|
2907
|
+
/**
|
|
2908
|
+
* Get the active credit token
|
|
2909
|
+
* @returns Promise with the current active credit token
|
|
2910
|
+
*/
|
|
2911
|
+
async getActiveCreditToken() {
|
|
2912
|
+
return this.tokenService.getRemoteActiveCreditToken();
|
|
2913
|
+
}
|
|
2914
|
+
/**
|
|
2915
|
+
* Get all reward tokens
|
|
2916
|
+
* @returns Promise with all reward tokens
|
|
2917
|
+
*/
|
|
2918
|
+
async getRewardTokens() {
|
|
2919
|
+
return this.tokenService.getRemoteRewardTokens();
|
|
2920
|
+
}
|
|
2921
|
+
/**
|
|
2922
|
+
* Get all status tokens
|
|
2923
|
+
* @returns Promise with all status tokens
|
|
2924
|
+
*/
|
|
2925
|
+
async getStatusTokens() {
|
|
2926
|
+
return this.tokenService.getRemoteStatusTokens();
|
|
2927
|
+
}
|
|
2928
|
+
/**
|
|
2929
|
+
* Get token by contract address
|
|
2930
|
+
* @param contractAddress - The contract address to search for
|
|
2931
|
+
* @param contractTokenId - Optional contract token ID
|
|
2932
|
+
* @returns Promise with the matching token
|
|
2933
|
+
*/
|
|
2934
|
+
async getTokenByContract(contractAddress, contractTokenId = null) {
|
|
2935
|
+
return this.tokenService.getTokenByContractAddress(contractAddress, contractTokenId);
|
|
2936
|
+
}
|
|
2937
|
+
// ==========================================
|
|
2938
|
+
// ADMIN METHODS - Token Management
|
|
2939
|
+
// ==========================================
|
|
2940
|
+
/**
|
|
2941
|
+
* Create token metadata
|
|
2942
|
+
* @param tokenId - The token ID
|
|
2943
|
+
* @param tokenData - The token storage data
|
|
2944
|
+
* @returns Promise with the updated token
|
|
2945
|
+
*/
|
|
2946
|
+
async createTokenMetadata(tokenId, tokenData) {
|
|
2947
|
+
return this.tokenService.createTokenMetadata(tokenId, tokenData);
|
|
2948
|
+
}
|
|
2949
|
+
/**
|
|
2950
|
+
* Toggle token active status
|
|
2951
|
+
* @param tokenId - The token ID to toggle
|
|
2952
|
+
* @returns Promise with the updated token
|
|
2953
|
+
*/
|
|
2954
|
+
async toggleTokenActive(tokenId) {
|
|
2955
|
+
return this.tokenService.toggleTokenActive(tokenId);
|
|
2956
|
+
}
|
|
2957
|
+
/**
|
|
2958
|
+
* Create a new token
|
|
2959
|
+
* @param tokenData - The token creation data
|
|
2960
|
+
* @returns Promise with the created token
|
|
2961
|
+
*/
|
|
2962
|
+
async createToken(tokenData) {
|
|
2963
|
+
return this.tokenService.createToken(tokenData);
|
|
2964
|
+
}
|
|
2965
|
+
/**
|
|
2966
|
+
* Update an existing token
|
|
2967
|
+
* @param tokenId - The token ID to update
|
|
2968
|
+
* @param tokenData - The token update data
|
|
2969
|
+
* @returns Promise with the updated token
|
|
2970
|
+
*/
|
|
2971
|
+
async updateToken(tokenId, tokenData) {
|
|
2972
|
+
return this.tokenService.updateToken(tokenId, tokenData);
|
|
2973
|
+
}
|
|
2974
|
+
/**
|
|
2975
|
+
* Set mainnet contract address for a token
|
|
2976
|
+
* @param tokenId - The token ID
|
|
2977
|
+
* @param contractAddress - The contract address
|
|
2978
|
+
* @param chainId - The blockchain chain ID
|
|
2979
|
+
* @returns Promise with the updated token
|
|
2980
|
+
*/
|
|
2981
|
+
async setMainnetContract(tokenId, contractAddress, chainId) {
|
|
2982
|
+
return this.tokenService.setMainnetContract(tokenId, contractAddress, chainId);
|
|
2983
|
+
}
|
|
2984
|
+
/**
|
|
2985
|
+
* Toggle token metadata status
|
|
2986
|
+
* @param metadataId - The metadata ID to toggle
|
|
2987
|
+
* @returns Promise with the updated metadata
|
|
2988
|
+
*/
|
|
2989
|
+
async toggleTokenMetadataStatus(metadataId) {
|
|
2990
|
+
return this.tokenService.toggleTokenMetadataStatus(metadataId);
|
|
2991
|
+
}
|
|
2992
|
+
/**
|
|
2993
|
+
* Create a new token type
|
|
2994
|
+
* @param tokenType - The token type data
|
|
2995
|
+
* @returns Promise with the created token type
|
|
2996
|
+
*/
|
|
2997
|
+
async createTokenType(tokenType) {
|
|
2998
|
+
return this.tokenService.createTokenType(tokenType);
|
|
2999
|
+
}
|
|
3000
|
+
// ==========================================
|
|
3001
|
+
// ADVANCED ACCESS - For Complex Operations
|
|
3002
|
+
// ==========================================
|
|
3003
|
+
/**
|
|
3004
|
+
* Get direct access to the token service for advanced operations
|
|
3005
|
+
* @returns The underlying TokenService instance
|
|
3006
|
+
*/
|
|
3007
|
+
getTokenService() {
|
|
3008
|
+
return this.tokenService;
|
|
3009
|
+
}
|
|
3010
|
+
/**
|
|
3011
|
+
* Get direct access to the token API for low-level operations
|
|
3012
|
+
* @returns The underlying TokenApi instance
|
|
3013
|
+
*/
|
|
3014
|
+
getTokenApi() {
|
|
3015
|
+
return this.tokenApi;
|
|
3016
|
+
}
|
|
3017
|
+
// ==========================================
|
|
3018
|
+
// FRAMEWORK COMPATIBILITY METHODS
|
|
3019
|
+
// ==========================================
|
|
3020
|
+
// These maintain compatibility with existing framework method names
|
|
3021
|
+
/**
|
|
3022
|
+
* @deprecated Use getTokens() instead
|
|
3023
|
+
* Framework compatibility method
|
|
3024
|
+
*/
|
|
3025
|
+
async getRemoteTokens() {
|
|
3026
|
+
return this.getTokens();
|
|
3027
|
+
}
|
|
3028
|
+
/**
|
|
3029
|
+
* @deprecated Use getTokenTypes() instead
|
|
3030
|
+
* Framework compatibility method
|
|
3031
|
+
*/
|
|
3032
|
+
async getRemoteTokenTypes() {
|
|
3033
|
+
return this.getTokenTypes();
|
|
3034
|
+
}
|
|
3035
|
+
/**
|
|
3036
|
+
* @deprecated Use getActiveCreditToken() instead
|
|
3037
|
+
* Framework compatibility method
|
|
3038
|
+
*/
|
|
3039
|
+
async getRemoteActiveCreditToken() {
|
|
3040
|
+
return this.getActiveCreditToken();
|
|
3041
|
+
}
|
|
3042
|
+
/**
|
|
3043
|
+
* @deprecated Use getRewardTokens() instead
|
|
3044
|
+
* Framework compatibility method
|
|
3045
|
+
*/
|
|
3046
|
+
async getRemoteRewardTokens() {
|
|
3047
|
+
return this.getRewardTokens();
|
|
3048
|
+
}
|
|
3049
|
+
/**
|
|
3050
|
+
* @deprecated Use getStatusTokens() instead
|
|
3051
|
+
* Framework compatibility method
|
|
3052
|
+
*/
|
|
3053
|
+
async getRemoteStatusTokens() {
|
|
3054
|
+
return this.getStatusTokens();
|
|
3055
|
+
}
|
|
3056
|
+
/**
|
|
3057
|
+
* @deprecated Use getTokenByContract() instead
|
|
3058
|
+
* Framework compatibility method
|
|
3059
|
+
*/
|
|
3060
|
+
async getTokenByContractAddress(contractAddress, contractTokenId) {
|
|
3061
|
+
return this.getTokenByContract(contractAddress, contractTokenId);
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
/**
|
|
3066
|
+
* Abstract Base Token Service - Explicit Initialization Pattern
|
|
3067
|
+
*
|
|
3068
|
+
* Platform-agnostic token operations with Promise-based API.
|
|
3069
|
+
* Framework services extend this and control initialization lifecycle.
|
|
3070
|
+
*
|
|
3071
|
+
* Benefits:
|
|
3072
|
+
* - Explicit initialization control
|
|
3073
|
+
* - Better error boundaries
|
|
3074
|
+
* - Clear lifecycle management
|
|
3075
|
+
* - Testable initialization state
|
|
3076
|
+
* - Zero framework dependencies
|
|
3077
|
+
*/
|
|
3078
|
+
class BaseTokenService {
|
|
3079
|
+
constructor() {
|
|
3080
|
+
this._isInitialized = false;
|
|
3081
|
+
}
|
|
3082
|
+
// ==========================================
|
|
3083
|
+
// INITIALIZATION LIFECYCLE
|
|
3084
|
+
// ==========================================
|
|
3085
|
+
/**
|
|
3086
|
+
* LIFECYCLE: Initialize token service with API client
|
|
3087
|
+
* Must be called before using any token operations
|
|
3088
|
+
*/
|
|
3089
|
+
initializeTokenService(apiClient) {
|
|
3090
|
+
if (!apiClient) {
|
|
3091
|
+
throw new Error('Cannot initialize TokenService: apiClient is null or undefined');
|
|
3092
|
+
}
|
|
3093
|
+
if (!this._isInitialized) {
|
|
3094
|
+
this._tokenApi = new TokenApi(apiClient);
|
|
3095
|
+
this._tokenBusinessService = new TokenService(this._tokenApi);
|
|
3096
|
+
this._isInitialized = true;
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
/**
|
|
3100
|
+
* LIFECYCLE: Check if token service is initialized
|
|
3101
|
+
*/
|
|
3102
|
+
get isTokenServiceInitialized() {
|
|
3103
|
+
return this._isInitialized;
|
|
3104
|
+
}
|
|
3105
|
+
/**
|
|
3106
|
+
* INTERNAL: Get token business service (throws if not initialized)
|
|
3107
|
+
*/
|
|
3108
|
+
get tokenBusinessService() {
|
|
3109
|
+
if (!this._tokenBusinessService) {
|
|
3110
|
+
throw new Error('TokenService not initialized. Call initializeTokenService(apiClient) first.');
|
|
3111
|
+
}
|
|
3112
|
+
return this._tokenBusinessService;
|
|
3113
|
+
}
|
|
3114
|
+
// ==========================================
|
|
3115
|
+
// PUBLIC OPERATIONS
|
|
3116
|
+
// ==========================================
|
|
3117
|
+
/**
|
|
3118
|
+
* PUBLIC: Get all remote tokens
|
|
3119
|
+
*/
|
|
3120
|
+
getRemoteTokens() {
|
|
3121
|
+
return this.tokenBusinessService.getRemoteTokens();
|
|
3122
|
+
}
|
|
3123
|
+
/**
|
|
3124
|
+
* PUBLIC: Get all remote token types
|
|
3125
|
+
*/
|
|
3126
|
+
getRemoteTokenTypes() {
|
|
3127
|
+
return this.tokenBusinessService.getRemoteTokenTypes();
|
|
3128
|
+
}
|
|
3129
|
+
/**
|
|
3130
|
+
* PUBLIC: Get active credit token
|
|
3131
|
+
*/
|
|
3132
|
+
getRemoteActiveCreditToken() {
|
|
3133
|
+
return this.tokenBusinessService.getRemoteActiveCreditToken();
|
|
3134
|
+
}
|
|
3135
|
+
/**
|
|
3136
|
+
* PUBLIC: Get reward tokens
|
|
3137
|
+
*/
|
|
3138
|
+
getRemoteRewardTokens() {
|
|
3139
|
+
return this.tokenBusinessService.getRemoteRewardTokens();
|
|
3140
|
+
}
|
|
3141
|
+
/**
|
|
3142
|
+
* PUBLIC: Get status tokens
|
|
3143
|
+
*/
|
|
3144
|
+
getRemoteStatusTokens() {
|
|
3145
|
+
return this.tokenBusinessService.getRemoteStatusTokens();
|
|
3146
|
+
}
|
|
3147
|
+
/**
|
|
3148
|
+
* PUBLIC: Get token by contract address
|
|
3149
|
+
*/
|
|
3150
|
+
getTokenByContractAddress(contractAddress, contractTokenId) {
|
|
3151
|
+
return this.tokenBusinessService.getTokenByContractAddress(contractAddress, contractTokenId);
|
|
3152
|
+
}
|
|
3153
|
+
// ==========================================
|
|
3154
|
+
// ADMIN OPERATIONS
|
|
3155
|
+
// ==========================================
|
|
3156
|
+
/**
|
|
3157
|
+
* ADMIN: Create token metadata
|
|
3158
|
+
*/
|
|
3159
|
+
createTokenMetadata(tokenId, tokenData) {
|
|
3160
|
+
return this.tokenBusinessService.createTokenMetadata(tokenId, tokenData);
|
|
3161
|
+
}
|
|
3162
|
+
/**
|
|
3163
|
+
* ADMIN: Toggle token active status
|
|
3164
|
+
*/
|
|
3165
|
+
toggleTokenActive(tokenId) {
|
|
3166
|
+
return this.tokenBusinessService.toggleTokenActive(tokenId);
|
|
3167
|
+
}
|
|
3168
|
+
/**
|
|
3169
|
+
* ADMIN: Create new token
|
|
3170
|
+
*/
|
|
3171
|
+
createToken(tokenData) {
|
|
3172
|
+
return this.tokenBusinessService.createToken(tokenData);
|
|
3173
|
+
}
|
|
3174
|
+
/**
|
|
3175
|
+
* ADMIN: Update token
|
|
3176
|
+
*/
|
|
3177
|
+
updateToken(tokenId, tokenData) {
|
|
3178
|
+
return this.tokenBusinessService.updateToken(tokenId, tokenData);
|
|
3179
|
+
}
|
|
3180
|
+
/**
|
|
3181
|
+
* ADMIN: Set mainnet contract address
|
|
3182
|
+
*/
|
|
3183
|
+
setMainnetContract(tokenId, contractAddress, chainId) {
|
|
3184
|
+
return this.tokenBusinessService.setMainnetContract(tokenId, contractAddress, chainId);
|
|
3185
|
+
}
|
|
3186
|
+
/**
|
|
3187
|
+
* ADMIN: Toggle token metadata status
|
|
3188
|
+
*/
|
|
3189
|
+
toggleTokenMetadataStatus(metadataId) {
|
|
3190
|
+
return this.tokenBusinessService.toggleTokenMetadataStatus(metadataId);
|
|
3191
|
+
}
|
|
3192
|
+
/**
|
|
3193
|
+
* ADMIN: Create token type
|
|
3194
|
+
*/
|
|
3195
|
+
createTokenType(tokenType) {
|
|
3196
|
+
return this.tokenBusinessService.createTokenType(tokenType);
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
|
|
3200
|
+
/**
|
|
3201
|
+
* Platform-Agnostic User API Client
|
|
3202
|
+
*
|
|
3203
|
+
* Handles user operations using the PERS backend RESTful API.
|
|
3204
|
+
* Updated to use new /users endpoints with enhanced security and consistency.
|
|
3205
|
+
* Maintains framework UserApiService method compatibility.
|
|
3206
|
+
*/
|
|
3207
|
+
class UserApi {
|
|
3208
|
+
constructor(apiClient) {
|
|
3209
|
+
this.apiClient = apiClient;
|
|
3210
|
+
this.basePath = '/users';
|
|
3211
|
+
}
|
|
3212
|
+
// ==========================================
|
|
3213
|
+
// PUBLIC OPERATIONS
|
|
3214
|
+
// ==========================================
|
|
3215
|
+
/**
|
|
3216
|
+
* PUBLIC: Get all users public profiles with optional filtering
|
|
3217
|
+
* ✅ UPDATED: Uses new RESTful /users/public endpoint
|
|
3218
|
+
*/
|
|
3219
|
+
async getAllUsersPublicProfiles(filter = null) {
|
|
3220
|
+
let url = `${this.basePath}/public`;
|
|
3221
|
+
if (filter) {
|
|
3222
|
+
// ✅ MAINTAINED: Same parameter pattern for compatibility
|
|
3223
|
+
const params = new URLSearchParams();
|
|
3224
|
+
params.set('filterKey', filter.key);
|
|
3225
|
+
params.set('filterValue', filter.value);
|
|
3226
|
+
url += `?${params.toString()}`;
|
|
3227
|
+
}
|
|
3228
|
+
return this.apiClient.get(url);
|
|
3229
|
+
}
|
|
3230
|
+
// ==========================================
|
|
3231
|
+
// AUTHENTICATED OPERATIONS
|
|
3232
|
+
// ==========================================
|
|
3233
|
+
/**
|
|
3234
|
+
* AUTH: Get current authenticated user
|
|
3235
|
+
* ✅ UPDATED: Uses new RESTful /users/me endpoint
|
|
3236
|
+
*/
|
|
3237
|
+
async getRemoteUser() {
|
|
3238
|
+
return this.apiClient.get(`${this.basePath}/me`);
|
|
3239
|
+
}
|
|
3240
|
+
/**
|
|
3241
|
+
* AUTH: Update current authenticated user
|
|
3242
|
+
* ✅ UPDATED: Uses new RESTful /users/me endpoint
|
|
3243
|
+
*/
|
|
3244
|
+
async updateRemoteUser(updateRequest) {
|
|
3245
|
+
return this.apiClient.put(`${this.basePath}/me`, updateRequest);
|
|
3246
|
+
}
|
|
3247
|
+
// ==========================================
|
|
3248
|
+
// ADMIN OPERATIONS
|
|
3249
|
+
// ==========================================
|
|
3250
|
+
/**
|
|
3251
|
+
* ADMIN: Get all remote users with query parameters
|
|
3252
|
+
* ✅ UPDATED: Uses new RESTful /users endpoint with role-based access
|
|
3253
|
+
* Note: Admin users get full data, non-admin users get public profiles only
|
|
3254
|
+
*/
|
|
3255
|
+
async getAllRemoteUsers() {
|
|
3256
|
+
// ✅ MAINTAINED: Same merge=soft parameter for compatibility
|
|
3257
|
+
const url = `${this.basePath}?merge=soft`;
|
|
3258
|
+
return this.apiClient.get(url);
|
|
3259
|
+
}
|
|
3260
|
+
/**
|
|
3261
|
+
* ADMIN: Update user as admin
|
|
3262
|
+
* ✅ UPDATED: Uses new RESTful /users/{id} endpoint
|
|
3263
|
+
*/
|
|
3264
|
+
async updateUserAsAdmin(id, userData) {
|
|
3265
|
+
return this.apiClient.put(`${this.basePath}/${id}`, userData);
|
|
3266
|
+
}
|
|
3267
|
+
/**
|
|
3268
|
+
* ADMIN: Toggle user active status
|
|
3269
|
+
* ✅ UPDATED: Uses new consistent /users/{id}/status endpoint
|
|
3270
|
+
* Enhanced: Follows RESTful status management pattern across all domains
|
|
3271
|
+
*/
|
|
3272
|
+
async toggleUserActiveStatusByUser(user) {
|
|
3273
|
+
return this.apiClient.put(`${this.basePath}/${user.id}/status`, {});
|
|
3274
|
+
}
|
|
3275
|
+
/**
|
|
3276
|
+
* ADMIN: Get user by unique identifier
|
|
3277
|
+
* ✅ UPDATED: Uses new RESTful /users/{id} endpoint
|
|
3278
|
+
*/
|
|
3279
|
+
async getUserByUniqueIdentifier(id) {
|
|
3280
|
+
return this.apiClient.get(`${this.basePath}/${id}`);
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
/**
|
|
3285
|
+
* Platform-Agnostic User Service
|
|
3286
|
+
*
|
|
3287
|
+
* Contains user business logic and operations that work across platforms.
|
|
3288
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
3289
|
+
* Matches framework UserApiService capabilities exactly.
|
|
3290
|
+
*/
|
|
3291
|
+
class UserService {
|
|
3292
|
+
constructor(userApi) {
|
|
3293
|
+
this.userApi = userApi;
|
|
3294
|
+
}
|
|
3295
|
+
// ==========================================
|
|
3296
|
+
// PUBLIC OPERATIONS
|
|
3297
|
+
// ==========================================
|
|
3298
|
+
/**
|
|
3299
|
+
* PUBLIC: Get all users public profiles with optional filtering
|
|
3300
|
+
* ✅ FIXED: Uses framework-compatible inline filter type
|
|
3301
|
+
*/
|
|
3302
|
+
async getAllUsersPublicProfiles(filter = null) {
|
|
3303
|
+
return this.userApi.getAllUsersPublicProfiles(filter);
|
|
3304
|
+
}
|
|
3305
|
+
// ==========================================
|
|
3306
|
+
// AUTHENTICATED OPERATIONS
|
|
3307
|
+
// ==========================================
|
|
3308
|
+
/**
|
|
3309
|
+
* AUTH: Get current authenticated user
|
|
3310
|
+
*/
|
|
3311
|
+
async getRemoteUser() {
|
|
3312
|
+
return this.userApi.getRemoteUser();
|
|
3313
|
+
}
|
|
3314
|
+
/**
|
|
3315
|
+
* AUTH: Update current authenticated user
|
|
3316
|
+
*/
|
|
3317
|
+
async updateRemoteUser(updateRequest) {
|
|
3318
|
+
return this.userApi.updateRemoteUser(updateRequest);
|
|
3319
|
+
}
|
|
3320
|
+
// ==========================================
|
|
3321
|
+
// ADMIN OPERATIONS
|
|
3322
|
+
// ==========================================
|
|
3323
|
+
/**
|
|
3324
|
+
* ADMIN: Get all remote users
|
|
3325
|
+
* ✅ FIXED: Matches API method signature (no parameters needed)
|
|
3326
|
+
*/
|
|
3327
|
+
async getAllRemoteUsers() {
|
|
3328
|
+
return this.userApi.getAllRemoteUsers();
|
|
3329
|
+
}
|
|
3330
|
+
/**
|
|
3331
|
+
* ADMIN: Update user as admin
|
|
3332
|
+
*/
|
|
3333
|
+
async updateUserAsAdmin(id, userData) {
|
|
3334
|
+
return this.userApi.updateUserAsAdmin(id, userData);
|
|
3335
|
+
}
|
|
3336
|
+
async getUserByUniqueIdentifier(id) {
|
|
3337
|
+
return this.userApi.getUserByUniqueIdentifier(id);
|
|
3338
|
+
}
|
|
3339
|
+
/**
|
|
3340
|
+
* ADMIN: Toggle user active status by user object
|
|
3341
|
+
* ✅ FIXED: Matches API method signature exactly
|
|
3342
|
+
*/
|
|
3343
|
+
async toggleUserActiveStatusByUser(user) {
|
|
3344
|
+
return this.userApi.toggleUserActiveStatusByUser(user);
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
/**
|
|
3349
|
+
* @explorins/pers-sdk-user
|
|
3350
|
+
*
|
|
3351
|
+
* Platform-agnostic User Domain SDK for PERS ecosystem
|
|
3352
|
+
* Handles user management, profiles, and authentication operations
|
|
3353
|
+
*/
|
|
3354
|
+
// API Layer
|
|
3355
|
+
/**
|
|
3356
|
+
* Create a complete User SDK instance
|
|
3357
|
+
*
|
|
3358
|
+
* @param apiClient - Configured PERS API client
|
|
3359
|
+
* @returns User SDK with flattened structure for better DX
|
|
3360
|
+
*/
|
|
3361
|
+
function createUserSDK(apiClient) {
|
|
3362
|
+
const userApi = new UserApi(apiClient);
|
|
3363
|
+
const userService = new UserService(userApi);
|
|
3364
|
+
return {
|
|
3365
|
+
// Direct access to service methods (primary interface)
|
|
3366
|
+
// Public methods - matches framework exactly
|
|
3367
|
+
getAllUsersPublicProfiles: (filter = null) => userService.getAllUsersPublicProfiles(filter),
|
|
3368
|
+
// Auth methods - matches framework exactly
|
|
3369
|
+
getRemoteUser: () => userService.getRemoteUser(),
|
|
3370
|
+
updateRemoteUser: (updateRequest) => userService.updateRemoteUser(updateRequest),
|
|
3371
|
+
// Admin methods - matches framework exactly
|
|
3372
|
+
getAllRemoteUsers: () => userService.getAllRemoteUsers(),
|
|
3373
|
+
updateUserAsAdmin: (id, userData) => userService.updateUserAsAdmin(id, userData),
|
|
3374
|
+
toggleUserActiveStatusByUser: (user) => userService.toggleUserActiveStatusByUser(user),
|
|
3375
|
+
getUserByUniqueIdentifier: (id) => userService.getUserByUniqueIdentifier(id),
|
|
3376
|
+
// Advanced access for edge cases
|
|
3377
|
+
api: userApi,
|
|
3378
|
+
service: userService
|
|
3379
|
+
};
|
|
3380
|
+
}
|
|
3381
|
+
|
|
3382
|
+
/**
|
|
3383
|
+
* Platform-Agnostic User Status API Client
|
|
3384
|
+
*
|
|
3385
|
+
* Handles user status operations using the PERS backend.
|
|
3386
|
+
* Matches framework UserStatusApiService methods exactly.
|
|
3387
|
+
*
|
|
3388
|
+
* ✅ UPDATED: All endpoints updated to new RESTful /users patterns
|
|
3389
|
+
*
|
|
3390
|
+
* Error handling patterns follow framework implementation:
|
|
3391
|
+
* - getRemoteEarnedUserStatus() uses silent fallback (empty array)
|
|
3392
|
+
* - Other operations throw errors for proper error handling
|
|
3393
|
+
*/
|
|
3394
|
+
class UserStatusApi {
|
|
3395
|
+
constructor(apiClient) {
|
|
3396
|
+
this.apiClient = apiClient;
|
|
3397
|
+
this.basePath = '/users';
|
|
3398
|
+
}
|
|
3399
|
+
// ==========================================
|
|
3400
|
+
// PUBLIC OPERATIONS
|
|
3401
|
+
// ==========================================
|
|
3402
|
+
/**
|
|
3403
|
+
* PUBLIC: Get remote user status types
|
|
3404
|
+
* ✅ UPDATED: /user/status-type → /users/status-types
|
|
3405
|
+
*/
|
|
3406
|
+
async getRemoteUserStatusTypes() {
|
|
3407
|
+
try {
|
|
3408
|
+
return await this.apiClient.get(`${this.basePath}/status-types`);
|
|
3409
|
+
}
|
|
3410
|
+
catch (error) {
|
|
3411
|
+
console.error('Error getting user status types', error);
|
|
3412
|
+
throw error;
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
// ==========================================
|
|
3416
|
+
// AUTHENTICATED OPERATIONS
|
|
3417
|
+
// ==========================================
|
|
3418
|
+
/**
|
|
3419
|
+
* AUTH: Get earned user status for authenticated user
|
|
3420
|
+
* ✅ UPDATED: /user/auth/status → /users/me/status
|
|
3421
|
+
* ✅ FIXED: Returns UserStatusTypeDTO[] to match framework exactly
|
|
3422
|
+
* Note: Uses silent fallback pattern from framework - returns empty array on error
|
|
3423
|
+
*/
|
|
3424
|
+
async getRemoteEarnedUserStatus() {
|
|
3425
|
+
try {
|
|
3426
|
+
return await this.apiClient.get(`${this.basePath}/me/status`);
|
|
3427
|
+
}
|
|
3428
|
+
catch (error) {
|
|
3429
|
+
console.error('Error getting user status', error);
|
|
3430
|
+
// ✅ FIXED: Silent fallback pattern from framework implementation
|
|
3431
|
+
return [];
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
// ==========================================
|
|
3435
|
+
// ADMIN OPERATIONS
|
|
3436
|
+
// ==========================================
|
|
3437
|
+
/**
|
|
3438
|
+
* ADMIN: Create user status type
|
|
3439
|
+
* ✅ UPDATED: /user/admin/status-type → /users/status-types
|
|
3440
|
+
*/
|
|
3441
|
+
async createUserStatusType(userStatusType) {
|
|
3442
|
+
try {
|
|
3443
|
+
return await this.apiClient.post(`${this.basePath}/status-types`, userStatusType);
|
|
3444
|
+
}
|
|
3445
|
+
catch (error) {
|
|
3446
|
+
console.error('Error creating user status type', error);
|
|
3447
|
+
throw error;
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
/**
|
|
3453
|
+
* Platform-Agnostic User Status Service
|
|
3454
|
+
*
|
|
3455
|
+
* Contains user status business logic and operations that work across platforms.
|
|
3456
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
3457
|
+
* Matches framework UserStatusApiService capabilities exactly.
|
|
3458
|
+
*/
|
|
3459
|
+
class UserStatusService {
|
|
3460
|
+
constructor(userStatusApi) {
|
|
3461
|
+
this.userStatusApi = userStatusApi;
|
|
3462
|
+
}
|
|
3463
|
+
// ==========================================
|
|
3464
|
+
// PUBLIC OPERATIONS
|
|
3465
|
+
// ==========================================
|
|
3466
|
+
/**
|
|
3467
|
+
* PUBLIC: Get remote user status types
|
|
3468
|
+
*/
|
|
3469
|
+
async getRemoteUserStatusTypes() {
|
|
3470
|
+
return this.userStatusApi.getRemoteUserStatusTypes();
|
|
3471
|
+
}
|
|
3472
|
+
// ==========================================
|
|
3473
|
+
// AUTHENTICATED OPERATIONS
|
|
3474
|
+
// ==========================================
|
|
3475
|
+
/**
|
|
3476
|
+
* AUTH: Get earned user status for authenticated user
|
|
3477
|
+
*/
|
|
3478
|
+
async getRemoteEarnedUserStatus() {
|
|
3479
|
+
return this.userStatusApi.getRemoteEarnedUserStatus();
|
|
3480
|
+
}
|
|
3481
|
+
// ==========================================
|
|
3482
|
+
// ADMIN OPERATIONS
|
|
3483
|
+
// ==========================================
|
|
3484
|
+
/**
|
|
3485
|
+
* ADMIN: Create user status type
|
|
3486
|
+
*/
|
|
3487
|
+
async createUserStatusType(userStatusType) {
|
|
3488
|
+
return this.userStatusApi.createUserStatusType(userStatusType);
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
/**
|
|
3493
|
+
* @explorins/pers-sdk-user-status
|
|
3494
|
+
*
|
|
3495
|
+
* Platform-agnostic User Status Domain SDK for PERS ecosystem
|
|
3496
|
+
* Handles user status management and type operations
|
|
3497
|
+
*/
|
|
3498
|
+
// API Layer
|
|
3499
|
+
/**
|
|
3500
|
+
* Create a complete User Status SDK instance
|
|
3501
|
+
*
|
|
3502
|
+
* @param apiClient - Configured PERS API client
|
|
3503
|
+
* @returns User Status SDK with flattened structure for better DX
|
|
3504
|
+
*/
|
|
3505
|
+
function createUserStatusSDK(apiClient) {
|
|
3506
|
+
const userStatusApi = new UserStatusApi(apiClient);
|
|
3507
|
+
const userStatusService = new UserStatusService(userStatusApi);
|
|
3508
|
+
return {
|
|
3509
|
+
// Direct access to service methods (primary interface)
|
|
3510
|
+
// ✅ FRAMEWORK ALIGNED: Only methods actually used by framework
|
|
3511
|
+
// Public methods
|
|
3512
|
+
getRemoteUserStatusTypes: () => userStatusService.getRemoteUserStatusTypes(),
|
|
3513
|
+
// Auth methods
|
|
3514
|
+
getRemoteEarnedUserStatus: () => userStatusService.getRemoteEarnedUserStatus(),
|
|
3515
|
+
// Admin methods
|
|
3516
|
+
createUserStatusType: (userStatusType) => userStatusService.createUserStatusType(userStatusType),
|
|
3517
|
+
// Advanced access for edge cases
|
|
3518
|
+
api: userStatusApi,
|
|
3519
|
+
service: userStatusService
|
|
3520
|
+
};
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3523
|
+
/**
|
|
3524
|
+
* Platform-Agnostic Web3 Chain API Client
|
|
3525
|
+
*
|
|
3526
|
+
* Handles blockchain chain operations using the PERS backend.
|
|
3527
|
+
* Uses @explorins/web3-ts types for perfect framework compatibility.
|
|
3528
|
+
*/
|
|
3529
|
+
class Web3ChainApi {
|
|
3530
|
+
constructor(apiClient) {
|
|
3531
|
+
this.apiClient = apiClient;
|
|
3532
|
+
this.basePath = '/chains';
|
|
3533
|
+
}
|
|
3534
|
+
// ==========================================
|
|
3535
|
+
// PUBLIC OPERATIONS
|
|
3536
|
+
// ==========================================
|
|
3537
|
+
/**
|
|
3538
|
+
* PUBLIC: Get chain data by chain ID
|
|
3539
|
+
* ✅ Returns ChainData exactly as framework expects from @explorins/web3-ts
|
|
3540
|
+
*/
|
|
3541
|
+
async getChainData(chainId) {
|
|
3542
|
+
try {
|
|
3543
|
+
console.log('🔍 [Web3ChainApi] Fetching chain data for chainId:', chainId);
|
|
3544
|
+
const response = await this.apiClient.get(`${this.basePath}/${chainId}`);
|
|
3545
|
+
if (!response) {
|
|
3546
|
+
throw new Error(`No chain data received for chainId: ${chainId}`);
|
|
3547
|
+
}
|
|
3548
|
+
return response;
|
|
3549
|
+
}
|
|
3550
|
+
catch (error) {
|
|
3551
|
+
console.error('❌ [Web3ChainApi] Failed to get chain data:', {
|
|
3552
|
+
chainId,
|
|
3553
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3554
|
+
});
|
|
3555
|
+
throw error;
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
|
|
3560
|
+
const isTokenExpired = (token, margin = 60) => {
|
|
3561
|
+
// ✅ SANITIZE: Remove any words (Bearer etc) if exists and get the token
|
|
3562
|
+
const cleanToken = token.split(' ')[1] || token;
|
|
3563
|
+
// ✅ VALIDATION: Check if token is jwt
|
|
3564
|
+
if (!cleanToken || cleanToken.split('.').length !== 3) {
|
|
3565
|
+
return true; // Consider invalid tokens as expired
|
|
3566
|
+
}
|
|
3567
|
+
try {
|
|
3568
|
+
// ✅ TYPE-SAFE: Decode with proper typing
|
|
3569
|
+
const decoded = jwtDecode.jwtDecode(cleanToken);
|
|
3570
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
3571
|
+
// ✅ CONFIGURABLE: Use provided margin (default 60 seconds)
|
|
3572
|
+
return decoded.exp < (currentTime + margin);
|
|
3573
|
+
}
|
|
3574
|
+
catch (error) {
|
|
3575
|
+
console.error('[SDK JWT Utils] Error decoding JWT token:', error);
|
|
3576
|
+
return true; // Consider unparseable tokens as expired
|
|
3577
|
+
}
|
|
3578
|
+
};
|
|
3579
|
+
|
|
3580
|
+
class Web3ChainService {
|
|
3581
|
+
constructor(web3ChainApi, providerService) {
|
|
3582
|
+
this.web3ChainApi = web3ChainApi;
|
|
3583
|
+
this.providerService = providerService;
|
|
3584
|
+
this.web3InstanceCache = new Map();
|
|
3585
|
+
}
|
|
3586
|
+
async getWeb3ByChainId(chainId) {
|
|
3587
|
+
const cached = this.web3InstanceCache.get(chainId);
|
|
3588
|
+
// ✅ SMART CACHE: Check if cache is valid based on token expiration
|
|
3589
|
+
if (cached && !this.checkIsTokenExpired(cached.chainData.authHeader)) {
|
|
3590
|
+
console.log(`♻️ [Web3ChainService] Using cached Web3 instance for chain ${chainId}`);
|
|
3591
|
+
return cached.web3Instance;
|
|
3592
|
+
}
|
|
3593
|
+
// ✅ CREATE WITH ERROR RECOVERY: Handle auth errors gracefully
|
|
3594
|
+
try {
|
|
3595
|
+
console.log(`🔧 [Web3ChainService] Creating new Web3 instance for chain ${chainId}`);
|
|
3596
|
+
const { web3, chainData } = await this.createWeb3Instance(chainId);
|
|
3597
|
+
// ✅ CACHE NEW INSTANCE
|
|
3598
|
+
this.web3InstanceCache.set(chainId, {
|
|
3599
|
+
web3Instance: web3,
|
|
3600
|
+
chainData: chainData,
|
|
3601
|
+
createdAt: Date.now(),
|
|
3602
|
+
chainId
|
|
3603
|
+
});
|
|
3604
|
+
return web3;
|
|
3605
|
+
}
|
|
3606
|
+
catch (error) {
|
|
3607
|
+
// ✅ AUTH ERROR RECOVERY: Clear cache and retry once
|
|
3608
|
+
if (this.isAuthError(error)) {
|
|
3609
|
+
console.warn(`🔄 [Web3ChainService] Auth error detected, clearing cache and retrying...`);
|
|
3610
|
+
this.web3InstanceCache.delete(chainId);
|
|
3611
|
+
// Retry once with fresh instance
|
|
3612
|
+
const { web3, chainData } = await this.createWeb3Instance(chainId);
|
|
3613
|
+
this.web3InstanceCache.set(chainId, {
|
|
3614
|
+
web3Instance: web3,
|
|
3615
|
+
chainData: chainData,
|
|
3616
|
+
createdAt: Date.now(),
|
|
3617
|
+
chainId
|
|
3618
|
+
});
|
|
3619
|
+
return web3;
|
|
3620
|
+
}
|
|
3621
|
+
throw error;
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
async getChainDataWithCache(chainId) {
|
|
3625
|
+
const cached = this.web3InstanceCache.get(chainId);
|
|
3626
|
+
//const now = Date.now();
|
|
3627
|
+
if (cached && !this.checkIsTokenExpired(cached.chainData.authHeader)) {
|
|
3628
|
+
console.log(`♻️ [Web3ChainService] Using cached ChainData for chain ${chainId}`);
|
|
3629
|
+
return cached.chainData;
|
|
3630
|
+
}
|
|
3631
|
+
// If not cached, fetch fresh data
|
|
3632
|
+
const chainData = await this.getChainDataById(chainId);
|
|
3633
|
+
if (!chainData) {
|
|
3634
|
+
throw new Error(`Chain data not found for chainId: ${chainId}`);
|
|
3635
|
+
}
|
|
3636
|
+
return chainData;
|
|
3637
|
+
}
|
|
3638
|
+
checkIsTokenExpired(authHeader) {
|
|
3639
|
+
// ✅ NULL CHECK: Handle undefined case
|
|
3640
|
+
if (!authHeader) {
|
|
3641
|
+
return false; // No token means no expiration (public chains)
|
|
3642
|
+
}
|
|
3643
|
+
// ✅ FUNCTION CALL: Use imported function, not method
|
|
3644
|
+
return isTokenExpired(authHeader);
|
|
3645
|
+
}
|
|
3646
|
+
async createWeb3Instance(chainId) {
|
|
3647
|
+
const chainData = await this.getChainDataById(chainId);
|
|
3648
|
+
if (!chainData) {
|
|
3649
|
+
throw new Error(`Chain data not found for chainId: ${chainId}`);
|
|
3650
|
+
}
|
|
3651
|
+
// ✅ CRITICAL CHECK: Ensure authHeader is present for private chains
|
|
3652
|
+
if (chainData.chainType === 'PRIVATE' && !chainData.authHeader) {
|
|
3653
|
+
console.error('❌ [Web3ChainService] CRITICAL: Private chain missing authHeader!');
|
|
3654
|
+
throw new Error(`Private chain ${chainId} missing authentication header`);
|
|
3655
|
+
}
|
|
3656
|
+
const web3 = await this.providerService.getWeb3(chainId, chainData.chainType || 'PUBLIC', chainData);
|
|
3657
|
+
return { web3, chainData };
|
|
3658
|
+
}
|
|
3659
|
+
// ✅ HELPER: Type-safe error checking
|
|
3660
|
+
isAuthError(error) {
|
|
3661
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
3662
|
+
return (message.includes('unauthorized') ||
|
|
3663
|
+
message.includes('401') ||
|
|
3664
|
+
message.includes('token expired') ||
|
|
3665
|
+
message.includes('invalid token'));
|
|
3666
|
+
}
|
|
3667
|
+
async getChainDataById(chainId) {
|
|
3668
|
+
try {
|
|
3669
|
+
return await this.web3ChainApi.getChainData(chainId);
|
|
3670
|
+
}
|
|
3671
|
+
catch (error) {
|
|
3672
|
+
console.error('❌ [SDK Web3ChainService] Error getting chain data for chainId:', chainId, error);
|
|
3673
|
+
return null;
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
|
|
3678
|
+
//IMPORTANT//
|
|
3679
|
+
//This function is temporary so we install ethers just to make it work, once we delete this function we must uninstall Ethers
|
|
3680
|
+
const getWeb3ProviderFromChainData = (chainData, timeout = 15000, customUserAgentName = '', tokenRefresher) => {
|
|
3681
|
+
// Fixed ethers provider setup for authenticated requests
|
|
3682
|
+
let ethersProvider;
|
|
3683
|
+
if (chainData.authHeader) {
|
|
3684
|
+
// For authenticated requests, create a custom FetchRequest
|
|
3685
|
+
const fetchRequest = new ethers.FetchRequest(chainData.rpcUrl);
|
|
3686
|
+
fetchRequest.timeout = timeout;
|
|
3687
|
+
fetchRequest.setHeader('Authorization', chainData.authHeader);
|
|
3688
|
+
fetchRequest.setHeader('Content-Type', 'application/json');
|
|
3689
|
+
if (customUserAgentName) {
|
|
3690
|
+
fetchRequest.setHeader('User-Agent', customUserAgentName);
|
|
3691
|
+
}
|
|
3692
|
+
// Create provider with the configured FetchRequest
|
|
3693
|
+
ethersProvider = new ethers.JsonRpcProvider(fetchRequest, undefined, {
|
|
3694
|
+
staticNetwork: false,
|
|
3695
|
+
polling: false, // Disable polling for better Lambda performance
|
|
3696
|
+
});
|
|
3697
|
+
}
|
|
3698
|
+
else {
|
|
3699
|
+
// For public chains, use simple URL-based provider
|
|
3700
|
+
ethersProvider = new ethers.JsonRpcProvider(chainData.rpcUrl, undefined, {
|
|
3701
|
+
staticNetwork: false,
|
|
3702
|
+
polling: false,
|
|
3703
|
+
});
|
|
3704
|
+
}
|
|
3705
|
+
return {
|
|
3706
|
+
web3Provider: null,
|
|
3707
|
+
ethersProvider: ethersProvider,
|
|
3708
|
+
isAuthenticated: !!chainData.authHeader,
|
|
3709
|
+
};
|
|
3710
|
+
};
|
|
3711
|
+
|
|
3712
|
+
class Web3ProviderService {
|
|
3713
|
+
constructor(publicHttpProviderService) {
|
|
3714
|
+
this.publicHttpProviderService = publicHttpProviderService;
|
|
3715
|
+
this._web3 = null;
|
|
3716
|
+
this._currentChainId = null;
|
|
3717
|
+
}
|
|
3718
|
+
async getWeb3(chainId, chainType, privateChainData = null) {
|
|
3719
|
+
if (!this._web3 || this._currentChainId !== chainId) {
|
|
3720
|
+
if (!chainId)
|
|
3721
|
+
throw new Error('ChainId not found');
|
|
3722
|
+
try {
|
|
3723
|
+
this._currentChainId = chainId;
|
|
3724
|
+
const provider = await this.getWeb3ByChainId(chainId, chainType, privateChainData);
|
|
3725
|
+
this._web3 = this.convertToWeb3(provider);
|
|
3726
|
+
}
|
|
3727
|
+
catch (error) {
|
|
3728
|
+
console.error('Error getting web3 connection from chain id ' + chainId, error);
|
|
3729
|
+
throw new Error('Error getting web3 connection from chain id ' + chainId);
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
return this._web3;
|
|
3733
|
+
}
|
|
3734
|
+
// Keep return type as 'any' to avoid TypeScript errors while still being adapted later
|
|
3735
|
+
getWeb3ByChainId(chainId, chainType, privateChainData = null) {
|
|
3736
|
+
// Rest of the method remains the same
|
|
3737
|
+
if (chainType === web3Ts.ChainTypes.PRIVATE && privateChainData) {
|
|
3738
|
+
//const privateProvider = this.privateChainProviderService.getProviderFromChainData(privateChainData)
|
|
3739
|
+
const privateProvider = getWeb3ProviderFromChainData(privateChainData);
|
|
3740
|
+
if (!privateProvider || privateProvider instanceof Error)
|
|
3741
|
+
throw new Error('Error getting web3 provider');
|
|
3742
|
+
return privateProvider;
|
|
3743
|
+
}
|
|
3744
|
+
else {
|
|
3745
|
+
const publicProvider = this.publicHttpProviderService.getProvider(chainId);
|
|
3746
|
+
if (!publicProvider || publicProvider instanceof Error)
|
|
3747
|
+
throw new Error('Error getting web3 provider');
|
|
3748
|
+
return publicProvider;
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
convertToWeb3(provider) {
|
|
3752
|
+
if (provider instanceof Web3) {
|
|
3753
|
+
return provider;
|
|
3754
|
+
}
|
|
3755
|
+
if (provider && typeof provider === 'object' && 'web3Provider' in provider) {
|
|
3756
|
+
const providerObj = provider;
|
|
3757
|
+
// If we want to user the web3Provider directly:
|
|
3758
|
+
/*if (providerObj.web3Provider) {
|
|
3759
|
+
return new Web3(providerObj.web3Provider as never);
|
|
3760
|
+
}*/
|
|
3761
|
+
if (providerObj.ethersProvider) {
|
|
3762
|
+
const url = this.extractUrlFromEthersProvider(providerObj.ethersProvider);
|
|
3763
|
+
const headers = this.extractHeadersFromEthersProvider(providerObj.ethersProvider);
|
|
3764
|
+
const web3 = new Web3(url);
|
|
3765
|
+
const currentProvider = web3.currentProvider;
|
|
3766
|
+
if (currentProvider) {
|
|
3767
|
+
currentProvider['url'] = url;
|
|
3768
|
+
if (headers && Object.keys(headers).length > 0) {
|
|
3769
|
+
currentProvider['request'] = async (payload) => {
|
|
3770
|
+
const response = await fetch(url, {
|
|
3771
|
+
method: 'POST',
|
|
3772
|
+
headers: {
|
|
3773
|
+
'Content-Type': 'application/json',
|
|
3774
|
+
...headers
|
|
3775
|
+
},
|
|
3776
|
+
body: JSON.stringify(payload)
|
|
3777
|
+
});
|
|
3778
|
+
if (!response.ok) {
|
|
3779
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
3780
|
+
}
|
|
3781
|
+
return await response.json();
|
|
3782
|
+
};
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
return web3;
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
return new Web3(provider);
|
|
3789
|
+
}
|
|
3790
|
+
extractUrlFromEthersProvider(ethersProvider) {
|
|
3791
|
+
return ethersProvider.connection?.url ||
|
|
3792
|
+
ethersProvider._getConnection?.()?.url ||
|
|
3793
|
+
ethersProvider.url ||
|
|
3794
|
+
'';
|
|
3795
|
+
}
|
|
3796
|
+
extractHeadersFromEthersProvider(ethersProvider) {
|
|
3797
|
+
return ethersProvider.connection?.headers ||
|
|
3798
|
+
ethersProvider._getConnection?.()?.headers ||
|
|
3799
|
+
{};
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3803
|
+
/**
|
|
3804
|
+
* Web3 Chain Domain Models
|
|
3805
|
+
*
|
|
3806
|
+
* Minimal interface definitions matching framework usage exactly.
|
|
3807
|
+
* Local ChainData interface to avoid external dependency issues.
|
|
3808
|
+
*/
|
|
3809
|
+
const ChainTypes = {
|
|
3810
|
+
PUBLIC: 'PUBLIC',
|
|
3811
|
+
PRIVATE: 'PRIVATE'
|
|
3812
|
+
};
|
|
3813
|
+
|
|
3814
|
+
function createWeb3ChainSDK(apiClient, providerService) {
|
|
3815
|
+
const web3ChainApi = new Web3ChainApi(apiClient);
|
|
3816
|
+
const web3ChainService = new Web3ChainService(web3ChainApi, providerService); // ✅ DIRECT INJECTION
|
|
3817
|
+
return {
|
|
3818
|
+
// ✅ REPLICA: Same methods as framework
|
|
3819
|
+
getChainDataById: (chainId) => web3ChainService.getChainDataById(chainId),
|
|
3820
|
+
getWeb3ByChainId: (chainId) => web3ChainService.getWeb3ByChainId(chainId),
|
|
3821
|
+
api: web3ChainApi,
|
|
3822
|
+
service: web3ChainService
|
|
3823
|
+
};
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
class Web3Api {
|
|
3827
|
+
constructor(web3ChainService) {
|
|
3828
|
+
this.web3ChainService = web3ChainService;
|
|
3829
|
+
}
|
|
3830
|
+
async getTokenBalance(request) {
|
|
3831
|
+
const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
|
|
3832
|
+
const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
|
|
3833
|
+
const balance = await web3Ts.getAddressTokenBalanceByContract(tokenContract, request.accountAddress, request.tokenId);
|
|
3834
|
+
return Number(balance);
|
|
3835
|
+
}
|
|
3836
|
+
async getTokenUri(request) {
|
|
3837
|
+
const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
|
|
3838
|
+
// ✅ DIRECT: Use web3-ts functions directly
|
|
3839
|
+
const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
|
|
3840
|
+
const tokenId = Number(request.tokenId);
|
|
3841
|
+
const tokenUri = await web3Ts.getTokenUri(tokenContract, tokenId);
|
|
3842
|
+
return String(tokenUri);
|
|
3843
|
+
}
|
|
3844
|
+
async getTokenOfOwnerByIndex(request) {
|
|
3845
|
+
const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
|
|
3846
|
+
// ✅ DIRECT: Use web3-ts functions directly
|
|
3847
|
+
const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
|
|
3848
|
+
const tokenId = await web3Ts.getTokenOfOwnerByIndex(tokenContract, request.accountAddress, request.tokenIndex);
|
|
3849
|
+
return String(tokenId);
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
class SimpleCache {
|
|
3854
|
+
constructor() {
|
|
3855
|
+
this.storage = {};
|
|
3856
|
+
this.defaultTTL = 10 * 1000; // 10 seconds
|
|
3857
|
+
}
|
|
3858
|
+
set(key, data, ttl) {
|
|
3859
|
+
this.storage[key] = {
|
|
3860
|
+
data,
|
|
3861
|
+
timestamp: Date.now(),
|
|
3862
|
+
ttl: ttl ?? this.defaultTTL
|
|
3863
|
+
};
|
|
3864
|
+
}
|
|
3865
|
+
get(key) {
|
|
3866
|
+
const entry = this.storage[key];
|
|
3867
|
+
if (!entry) {
|
|
3868
|
+
return null;
|
|
3869
|
+
}
|
|
3870
|
+
const now = Date.now();
|
|
3871
|
+
const isExpired = (now - entry.timestamp) > entry.ttl;
|
|
3872
|
+
if (isExpired) {
|
|
3873
|
+
delete this.storage[key];
|
|
3874
|
+
return null;
|
|
3875
|
+
}
|
|
3876
|
+
return entry.data;
|
|
3877
|
+
}
|
|
3878
|
+
clear() {
|
|
3879
|
+
this.storage = {};
|
|
3880
|
+
}
|
|
3881
|
+
cleanup() {
|
|
3882
|
+
const now = Date.now();
|
|
3883
|
+
Object.keys(this.storage).forEach(key => {
|
|
3884
|
+
const entry = this.storage[key];
|
|
3885
|
+
if ((now - entry.timestamp) > entry.ttl) {
|
|
3886
|
+
delete this.storage[key];
|
|
3887
|
+
}
|
|
3888
|
+
});
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
|
|
3892
|
+
class Web3Service {
|
|
3893
|
+
constructor(web3Api, web3ChainService) {
|
|
3894
|
+
this.web3Api = web3Api;
|
|
3895
|
+
this.web3ChainService = web3ChainService;
|
|
3896
|
+
//temporary fix, remove when the backend supports custom gateways
|
|
3897
|
+
this.defaultIpfsGatewayDomain = 'pers.mypinata.cloud';
|
|
3898
|
+
// ✅ CACHE: Simple 10-second cache instance
|
|
3899
|
+
this.cache = new SimpleCache();
|
|
3900
|
+
this.cleanupInterval = null;
|
|
3901
|
+
this.cleanupInterval = setInterval(() => {
|
|
3902
|
+
this.cache.cleanup();
|
|
3903
|
+
}, 30 * 1000);
|
|
3904
|
+
}
|
|
3905
|
+
destroy() {
|
|
3906
|
+
if (this.cleanupInterval) {
|
|
3907
|
+
clearInterval(this.cleanupInterval);
|
|
3908
|
+
this.cleanupInterval = null;
|
|
3909
|
+
}
|
|
3910
|
+
this.cache.clear();
|
|
3911
|
+
}
|
|
3912
|
+
async getERC20Balance(request) {
|
|
3913
|
+
const cacheKey = `erc20_balance_${request.accountAddress}_${request.token.contractAddress}_${request.token.chainId}`;
|
|
3914
|
+
// ✅ CACHE CHECK: Try to get from cache first
|
|
3915
|
+
const cached = this.cache.get(cacheKey);
|
|
3916
|
+
if (cached) {
|
|
3917
|
+
console.debug(`💾 [Web3Service] Using cached ERC20 balance for ${request.token.symbol}`);
|
|
3918
|
+
return cached;
|
|
3919
|
+
}
|
|
3920
|
+
console.debug(`🔄 [Web3Service] Fetching fresh ERC20 balance for ${request.token.symbol}`);
|
|
3921
|
+
const rawBalance = await this.web3Api.getTokenBalance({
|
|
3922
|
+
accountAddress: request.accountAddress,
|
|
3923
|
+
contractAddress: request.token.contractAddress,
|
|
3924
|
+
abi: request.token.abi,
|
|
3925
|
+
tokenId: null, // Always null for ERC20
|
|
3926
|
+
chainId: request.token.chainId
|
|
3927
|
+
});
|
|
3928
|
+
const decimals = request.token.decimals ?? 18;
|
|
3929
|
+
const symbol = request.token.symbol ?? 'UNKNOWN';
|
|
3930
|
+
const response = {
|
|
3931
|
+
rawBalance,
|
|
3932
|
+
formattedBalance: this.formatBalance(rawBalance, decimals),
|
|
3933
|
+
decimals,
|
|
3934
|
+
symbol,
|
|
3935
|
+
hasBalance: rawBalance > 0
|
|
3936
|
+
};
|
|
3937
|
+
// ✅ CACHE SET: Store result in cache
|
|
3938
|
+
this.cache.set(cacheKey, response);
|
|
3939
|
+
return response;
|
|
3940
|
+
}
|
|
3941
|
+
async getERC1155Collection(request) {
|
|
3942
|
+
// ✅ CACHE KEY: Create unique cache key for collection request
|
|
3943
|
+
const contractAddresses = request.tokens.map(t => t.contractAddress).sort().join(',');
|
|
3944
|
+
const cacheKey = `erc1155_collection_${request.accountAddress}_${contractAddresses}`;
|
|
3945
|
+
// ✅ CACHE CHECK: Try to get from cache first
|
|
3946
|
+
const cached = this.cache.get(cacheKey);
|
|
3947
|
+
if (cached) {
|
|
3948
|
+
console.debug(`💾 [Web3Service] Using cached ERC1155 collection for ${request.accountAddress}`);
|
|
3949
|
+
return cached;
|
|
3950
|
+
}
|
|
3951
|
+
console.debug(`🔄 [Web3Service] Fetching fresh ERC1155 collection for ${request.accountAddress}`);
|
|
3952
|
+
const tokenResults = await Promise.all(request.tokens.map(async (token) => {
|
|
3953
|
+
// ✅ FIXED: Handle null metadata properly
|
|
3954
|
+
const tokenIds = token.metadata?.map(m => m.tokenMetadataIncrementalId?.toString()).filter((id) => id !== undefined) ?? [];
|
|
3955
|
+
// Check balance for each known tokenId
|
|
3956
|
+
const balanceResults = await Promise.allSettled(tokenIds.map(async (tokenId) => {
|
|
3957
|
+
try {
|
|
3958
|
+
const rawBalance = await this.web3Api.getTokenBalance({
|
|
3959
|
+
accountAddress: request.accountAddress,
|
|
3960
|
+
contractAddress: token.contractAddress,
|
|
3961
|
+
abi: token.abi,
|
|
3962
|
+
tokenId,
|
|
3963
|
+
chainId: token.chainId
|
|
3964
|
+
});
|
|
3965
|
+
const decimals = token.decimals ?? 0; // ERC1155 usually no decimals
|
|
3966
|
+
return {
|
|
3967
|
+
tokenId,
|
|
3968
|
+
balance: rawBalance,
|
|
3969
|
+
formattedBalance: this.formatBalance(rawBalance, decimals),
|
|
3970
|
+
hasBalance: rawBalance > 0,
|
|
3971
|
+
// ✅ FIXED: Convert null to undefined for findMetadata
|
|
3972
|
+
metadata: this.findMetadata(token.metadata ?? undefined, tokenId)
|
|
3973
|
+
};
|
|
3974
|
+
}
|
|
3975
|
+
catch (error) {
|
|
3976
|
+
console.warn(`Failed to get balance for token ${token.contractAddress}:${tokenId}`, error);
|
|
3977
|
+
return null; // Skip failed tokens
|
|
3978
|
+
}
|
|
3979
|
+
}));
|
|
3980
|
+
// Filter successful results with balance > 0
|
|
3981
|
+
const successfulResults = [];
|
|
3982
|
+
for (const result of balanceResults) {
|
|
3983
|
+
if (result.status === 'fulfilled' && result.value !== null && result.value.hasBalance) {
|
|
3984
|
+
successfulResults.push(result.value);
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
return {
|
|
3988
|
+
token,
|
|
3989
|
+
results: successfulResults
|
|
3990
|
+
};
|
|
3991
|
+
}));
|
|
3992
|
+
const response = {
|
|
3993
|
+
accountAddress: request.accountAddress,
|
|
3994
|
+
tokens: tokenResults.filter(t => t.results.length > 0)
|
|
3995
|
+
};
|
|
3996
|
+
// ✅ CACHE SET: Store complete collection result
|
|
3997
|
+
this.cache.set(cacheKey, response);
|
|
3998
|
+
return response;
|
|
3999
|
+
}
|
|
4000
|
+
async getERC721Collection(request) {
|
|
4001
|
+
// ✅ CACHE KEY: Create unique cache key for NFT collection
|
|
4002
|
+
const contractAddresses = request.nftContracts.map(t => t.contractAddress).sort().join(',');
|
|
4003
|
+
const maxNFTs = request.maxNFTsPerContract || 50;
|
|
4004
|
+
const cacheKey = `erc721_collection_${request.accountAddress}_${contractAddresses}_${maxNFTs}`;
|
|
4005
|
+
// ✅ CACHE CHECK: Try to get from cache first
|
|
4006
|
+
const cached = this.cache.get(cacheKey);
|
|
4007
|
+
if (cached) {
|
|
4008
|
+
console.debug(`💾 [Web3Service] Using cached ERC721 collection for ${request.accountAddress}`);
|
|
4009
|
+
return cached;
|
|
4010
|
+
}
|
|
4011
|
+
console.debug(`🔄 [Web3Service] Fetching fresh ERC721 collection for ${request.accountAddress}`);
|
|
4012
|
+
const startTime = Date.now();
|
|
4013
|
+
const contractResults = await Promise.all(request.nftContracts.map(async (token) => {
|
|
4014
|
+
try {
|
|
4015
|
+
const totalBalance = await this.web3Api.getTokenBalance({
|
|
4016
|
+
accountAddress: request.accountAddress,
|
|
4017
|
+
contractAddress: token.contractAddress,
|
|
4018
|
+
abi: token.abi,
|
|
4019
|
+
tokenId: null,
|
|
4020
|
+
chainId: token.chainId
|
|
4021
|
+
});
|
|
4022
|
+
if (totalBalance === 0) {
|
|
4023
|
+
return {
|
|
4024
|
+
token,
|
|
4025
|
+
totalNFTs: 0,
|
|
4026
|
+
nfts: [],
|
|
4027
|
+
hasMore: false
|
|
4028
|
+
};
|
|
4029
|
+
}
|
|
4030
|
+
const nftsToLoad = Math.min(totalBalance, maxNFTs);
|
|
4031
|
+
const nftResults = await Promise.allSettled(Array.from({ length: nftsToLoad }, async (_, index) => {
|
|
4032
|
+
try {
|
|
4033
|
+
const tokenId = await this.web3Api.getTokenOfOwnerByIndex({
|
|
4034
|
+
contractAddress: token.contractAddress,
|
|
4035
|
+
abi: token.abi,
|
|
4036
|
+
accountAddress: request.accountAddress,
|
|
4037
|
+
tokenIndex: index,
|
|
4038
|
+
chainId: token.chainId
|
|
4039
|
+
});
|
|
4040
|
+
const tokenUri = await this.web3Api.getTokenUri({
|
|
4041
|
+
contractAddress: token.contractAddress,
|
|
4042
|
+
abi: token.abi,
|
|
4043
|
+
tokenId,
|
|
4044
|
+
chainId: token.chainId
|
|
4045
|
+
});
|
|
4046
|
+
const metadata = await this.fetchMetadata(tokenUri, token.chainId);
|
|
4047
|
+
const nftItem = {
|
|
4048
|
+
tokenId,
|
|
4049
|
+
name: metadata?.name || `Token #${tokenId}`,
|
|
4050
|
+
description: metadata?.description || '',
|
|
4051
|
+
imageUrl: await this.resolveIPFSUrl(metadata?.image || '', token.chainId),
|
|
4052
|
+
rawBalance: 1,
|
|
4053
|
+
formattedBalance: '1',
|
|
4054
|
+
hasBalance: true,
|
|
4055
|
+
metadata,
|
|
4056
|
+
tokenIndex: index
|
|
4057
|
+
};
|
|
4058
|
+
return nftItem;
|
|
4059
|
+
}
|
|
4060
|
+
catch (error) {
|
|
4061
|
+
console.warn(`Failed to load NFT at index ${index} for ${token.symbol}:`, error);
|
|
4062
|
+
return null;
|
|
4063
|
+
}
|
|
4064
|
+
}));
|
|
4065
|
+
// ✅ FIXED: Usar tipo específico NFTItem
|
|
4066
|
+
const successfulNFTs = nftResults
|
|
4067
|
+
.filter((result) => result.status === 'fulfilled' && result.value !== null)
|
|
4068
|
+
.map(result => result.value);
|
|
4069
|
+
return {
|
|
4070
|
+
token,
|
|
4071
|
+
totalNFTs: totalBalance,
|
|
4072
|
+
nfts: successfulNFTs,
|
|
4073
|
+
hasMore: totalBalance > maxNFTs
|
|
4074
|
+
};
|
|
4075
|
+
}
|
|
4076
|
+
catch (error) {
|
|
4077
|
+
console.error(`Failed to load NFT collection for ${token.symbol}:`, error);
|
|
4078
|
+
return {
|
|
4079
|
+
token,
|
|
4080
|
+
totalNFTs: 0,
|
|
4081
|
+
nfts: [],
|
|
4082
|
+
hasMore: false
|
|
4083
|
+
};
|
|
4084
|
+
}
|
|
4085
|
+
}));
|
|
4086
|
+
const totalNFTs = contractResults.reduce((sum, contract) => sum + contract.nfts.length, 0);
|
|
4087
|
+
const loadingTime = Date.now() - startTime;
|
|
4088
|
+
const response = {
|
|
4089
|
+
accountAddress: request.accountAddress,
|
|
4090
|
+
contracts: contractResults,
|
|
4091
|
+
summary: {
|
|
4092
|
+
totalContracts: request.nftContracts.length,
|
|
4093
|
+
totalNFTs,
|
|
4094
|
+
loadingTime
|
|
4095
|
+
}
|
|
4096
|
+
};
|
|
4097
|
+
// ✅ CACHE SET: Store complete collection response
|
|
4098
|
+
this.cache.set(cacheKey, response);
|
|
4099
|
+
return response;
|
|
4100
|
+
}
|
|
4101
|
+
// ==========================================
|
|
4102
|
+
// HELPER METHODS
|
|
4103
|
+
// ==========================================
|
|
4104
|
+
formatBalance(rawBalance, decimals) {
|
|
4105
|
+
const balance = rawBalance / Math.pow(10, decimals);
|
|
4106
|
+
return balance.toLocaleString('en-US', {
|
|
4107
|
+
minimumFractionDigits: 0,
|
|
4108
|
+
maximumFractionDigits: decimals > 0 ? 2 : 0
|
|
4109
|
+
});
|
|
4110
|
+
}
|
|
4111
|
+
// ✅ FIXED: Update method signature to handle null properly
|
|
4112
|
+
findMetadata(metadata, tokenId) {
|
|
4113
|
+
if (!metadata || tokenId === null)
|
|
4114
|
+
return null;
|
|
4115
|
+
return metadata.find(m => m.tokenMetadataIncrementalId?.toString() === tokenId) || null;
|
|
4116
|
+
}
|
|
4117
|
+
async fetchMetadata(uri, chainId) {
|
|
4118
|
+
try {
|
|
4119
|
+
const httpUrl = await this.resolveIPFSUrl(uri, chainId);
|
|
4120
|
+
const response = await fetch(httpUrl);
|
|
4121
|
+
if (!response.ok) {
|
|
4122
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
4123
|
+
}
|
|
4124
|
+
return await response.json();
|
|
4125
|
+
}
|
|
4126
|
+
catch (error) {
|
|
4127
|
+
console.warn('Failed to fetch NFT metadata:', error);
|
|
4128
|
+
return null;
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4131
|
+
async getIpfsGatewayDomain(chainId) {
|
|
4132
|
+
try {
|
|
4133
|
+
const chainData = await this.web3ChainService.getChainDataWithCache(chainId);
|
|
4134
|
+
return chainData.ipfsGatewayDomain || this.defaultIpfsGatewayDomain;
|
|
4135
|
+
}
|
|
4136
|
+
catch (error) {
|
|
4137
|
+
console.warn(`Failed to get chain data for chainId ${chainId}, using default IPFS gateway:`, error);
|
|
4138
|
+
return this.defaultIpfsGatewayDomain;
|
|
4139
|
+
}
|
|
4140
|
+
}
|
|
4141
|
+
async resolveIPFSUrl(url, chainId) {
|
|
4142
|
+
if (!url)
|
|
4143
|
+
return '';
|
|
4144
|
+
if (url.startsWith('ipfs://')) {
|
|
4145
|
+
const gatewayDomain = await this.getIpfsGatewayDomain(chainId);
|
|
4146
|
+
return `https://${gatewayDomain}/ipfs/${url.slice(7)}`;
|
|
4147
|
+
}
|
|
4148
|
+
return url;
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
|
|
4152
|
+
function createWeb3SDK(apiClient, web3ProviderService) {
|
|
4153
|
+
const web3ChainSDK = createWeb3ChainSDK(apiClient, web3ProviderService);
|
|
4154
|
+
const web3Api = new Web3Api(web3ChainSDK.service);
|
|
4155
|
+
const web3Service = new Web3Service(web3Api, web3ChainSDK.service);
|
|
4156
|
+
return {
|
|
4157
|
+
getCreditsBalance: (request) => web3Service.getERC20Balance(request),
|
|
4158
|
+
getRewardsCollection: (request) => web3Service.getERC1155Collection(request),
|
|
4159
|
+
getStampsCollection: (request) => web3Service.getERC721Collection(request),
|
|
4160
|
+
api: web3Api,
|
|
4161
|
+
service: web3Service
|
|
4162
|
+
};
|
|
4163
|
+
}
|
|
4164
|
+
|
|
4165
|
+
exports.AnalyticsApi = AnalyticsApi;
|
|
4166
|
+
exports.AnalyticsService = AnalyticsService;
|
|
4167
|
+
exports.AuthAdminApi = AuthAdminApi;
|
|
4168
|
+
exports.AuthAdminService = AuthAdminService;
|
|
4169
|
+
exports.BaseTokenService = BaseTokenService;
|
|
4170
|
+
exports.BusinessApi = BusinessApi;
|
|
4171
|
+
exports.BusinessService = BusinessService;
|
|
4172
|
+
exports.CampaignApi = CampaignApi;
|
|
4173
|
+
exports.CampaignService = CampaignService;
|
|
4174
|
+
exports.ChainTypes = ChainTypes;
|
|
4175
|
+
exports.DEFAULT_PERS_CONFIG = DEFAULT_PERS_CONFIG;
|
|
4176
|
+
exports.DonationApi = DonationApi;
|
|
4177
|
+
exports.DonationService = DonationService;
|
|
4178
|
+
exports.PaymentApi = PurchaseApi;
|
|
4179
|
+
exports.PaymentService = PaymentService;
|
|
4180
|
+
exports.PersApiClient = PersApiClient;
|
|
4181
|
+
exports.PersApiError = PersApiError;
|
|
4182
|
+
exports.PersSDK = PersSDK;
|
|
4183
|
+
exports.RedemptionApi = RedemptionApi;
|
|
4184
|
+
exports.RedemptionService = RedemptionService;
|
|
4185
|
+
exports.SimpleCache = SimpleCache;
|
|
4186
|
+
exports.TenantApi = TenantApi;
|
|
4187
|
+
exports.TenantService = TenantService;
|
|
4188
|
+
exports.TokenApi = TokenApi;
|
|
4189
|
+
exports.TokenSDK = TokenSDK;
|
|
4190
|
+
exports.TokenService = TokenService;
|
|
4191
|
+
exports.TransactionApi = TransactionApi;
|
|
4192
|
+
exports.TransactionService = TransactionService;
|
|
4193
|
+
exports.UserApi = UserApi;
|
|
4194
|
+
exports.UserService = UserService;
|
|
4195
|
+
exports.UserStatusApi = UserStatusApi;
|
|
4196
|
+
exports.UserStatusService = UserStatusService;
|
|
4197
|
+
exports.Web3ChainApi = Web3ChainApi;
|
|
4198
|
+
exports.Web3ChainService = Web3ChainService;
|
|
4199
|
+
exports.Web3ProviderService = Web3ProviderService;
|
|
4200
|
+
exports.buildApiRoot = buildApiRoot;
|
|
4201
|
+
exports.createAnalyticsSDK = createAnalyticsSDK;
|
|
4202
|
+
exports.createAuthAdminSDK = createAuthAdminSDK;
|
|
4203
|
+
exports.createAuthProvider = createAuthProvider;
|
|
4204
|
+
exports.createBusinessSDK = createBusinessSDK;
|
|
4205
|
+
exports.createCampaignSDK = createCampaignSDK;
|
|
4206
|
+
exports.createDonationSDK = createDonationSDK;
|
|
4207
|
+
exports.createPaymentSDK = createPaymentSDK;
|
|
4208
|
+
exports.createPersSDK = createPersSDK;
|
|
4209
|
+
exports.createRedemptionSDK = createRedemptionSDK;
|
|
4210
|
+
exports.createTenantSDK = createTenantSDK;
|
|
4211
|
+
exports.createTransactionSDK = createTransactionSDK;
|
|
4212
|
+
exports.createUserSDK = createUserSDK;
|
|
4213
|
+
exports.createUserStatusSDK = createUserStatusSDK;
|
|
4214
|
+
exports.createWeb3ChainSDK = createWeb3ChainSDK;
|
|
4215
|
+
exports.createWeb3SDK = createWeb3SDK;
|
|
4216
|
+
exports.mergeWithDefaults = mergeWithDefaults;
|
|
4217
|
+
//# sourceMappingURL=index.cjs.map
|