dgc-core 2.0.5 → 2.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -3
- package/dist/constants/revenuecat.d.ts +7 -0
- package/dist/constants/revenuecat.js +25 -0
- package/dist/enums/giveaway.d.ts +4 -2
- package/dist/enums/giveaway.js +2 -0
- package/dist/giveaways/domain.d.ts +2 -2
- package/dist/giveaways/domain.js +14 -1
- package/dist/giveaways/eligibility.d.ts +5 -0
- package/dist/giveaways/eligibility.js +24 -0
- package/dist/giveaways/giveaway-repository.d.ts +20 -5
- package/dist/giveaways/giveaway-repository.js +253 -17
- package/dist/giveaways/index.d.ts +1 -0
- package/dist/giveaways/index.js +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/pub.js +3 -2
- package/dist/revenuecat.d.ts +16 -0
- package/dist/revenuecat.js +206 -0
- package/dist/task.js +10 -3
- package/dist/types/giveaway.d.ts +22 -2
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/revenuecat.d.ts +19 -0
- package/dist/types/revenuecat.js +2 -0
- package/package.json +1 -1
- package/dist/tw/utils.d.ts +0 -1
- package/dist/tw/utils.js +0 -19
- package/dist/tw/wallet.d.ts +0 -1
- package/dist/tw/wallet.js +0 -57
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.RevenueCatLookupError = void 0;
|
|
7
|
+
exports.createRevenueCatClient = createRevenueCatClient;
|
|
8
|
+
exports.verifyCreditGiveawayEntitlement = verifyCreditGiveawayEntitlement;
|
|
9
|
+
const axios_1 = __importDefault(require("axios"));
|
|
10
|
+
const revenuecat_1 = require("./constants/revenuecat");
|
|
11
|
+
const user_1 = require("./enums/user");
|
|
12
|
+
const http_error_1 = require("./http-error");
|
|
13
|
+
/** Deliberately excludes the original HTTP error, which can contain the API key. */
|
|
14
|
+
class RevenueCatLookupError extends http_error_1.HttpError {
|
|
15
|
+
constructor() {
|
|
16
|
+
super('Unable to verify RevenueCat entitlement access. Please try again.', 503);
|
|
17
|
+
this.name = 'RevenueCatLookupError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.RevenueCatLookupError = RevenueCatLookupError;
|
|
21
|
+
function positiveInteger(value, maximum) {
|
|
22
|
+
return Number.isInteger(value) && value > 0 && value <= maximum;
|
|
23
|
+
}
|
|
24
|
+
function validUserId(value) {
|
|
25
|
+
return (typeof value === 'string' &&
|
|
26
|
+
value.length > 0 &&
|
|
27
|
+
value.length <= 128 &&
|
|
28
|
+
value.trim() === value &&
|
|
29
|
+
!/[.#$[\]/]/.test(value) &&
|
|
30
|
+
!Array.from(value).some(character => {
|
|
31
|
+
const code = character.charCodeAt(0);
|
|
32
|
+
return code < 32 || code === 127;
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
function parsePage(value) {
|
|
36
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
37
|
+
throw new RevenueCatLookupError();
|
|
38
|
+
const page = value;
|
|
39
|
+
if (!Array.isArray(page.items) ||
|
|
40
|
+
(page.next_page != null && (typeof page.next_page !== 'string' || !page.next_page)))
|
|
41
|
+
throw new RevenueCatLookupError();
|
|
42
|
+
const items = page.items.map((raw) => {
|
|
43
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
44
|
+
throw new RevenueCatLookupError();
|
|
45
|
+
const entitlement = raw;
|
|
46
|
+
if (typeof entitlement.entitlement_id !== 'string' ||
|
|
47
|
+
!/^entl[a-zA-Z0-9]+$/.test(entitlement.entitlement_id) ||
|
|
48
|
+
(entitlement.expires_at !== null &&
|
|
49
|
+
(typeof entitlement.expires_at !== 'number' ||
|
|
50
|
+
!Number.isSafeInteger(entitlement.expires_at) ||
|
|
51
|
+
entitlement.expires_at < 0)))
|
|
52
|
+
throw new RevenueCatLookupError();
|
|
53
|
+
return {
|
|
54
|
+
entitlementId: entitlement.entitlement_id,
|
|
55
|
+
expiresAt: entitlement.expires_at
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
return { items, nextPage: page.next_page ?? null };
|
|
59
|
+
}
|
|
60
|
+
function retryAfterMs(value) {
|
|
61
|
+
if (typeof value === 'number')
|
|
62
|
+
return Number.isFinite(value) && value >= 0 ? value * 1000 : null;
|
|
63
|
+
if (typeof value !== 'string' || !value.trim())
|
|
64
|
+
return null;
|
|
65
|
+
const text = value.trim();
|
|
66
|
+
if (/^\d+$/.test(text)) {
|
|
67
|
+
const seconds = Number(text);
|
|
68
|
+
return Number.isFinite(seconds) ? seconds * 1000 : null;
|
|
69
|
+
}
|
|
70
|
+
const date = Date.parse(text);
|
|
71
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : null;
|
|
72
|
+
}
|
|
73
|
+
/** Inspect only retry metadata; never retain or log the original transport error. */
|
|
74
|
+
function requestRetryDelay(error, attempt) {
|
|
75
|
+
if (!error || typeof error !== 'object')
|
|
76
|
+
return null;
|
|
77
|
+
const failure = error;
|
|
78
|
+
const response = failure.response && typeof failure.response === 'object'
|
|
79
|
+
? failure.response
|
|
80
|
+
: undefined;
|
|
81
|
+
if (response) {
|
|
82
|
+
if (typeof response.status !== 'number' || ![429, 500, 502, 503, 504].includes(response.status))
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
else if (typeof failure.code !== 'string' ||
|
|
86
|
+
![
|
|
87
|
+
'ECONNRESET',
|
|
88
|
+
'ETIMEDOUT',
|
|
89
|
+
'ECONNABORTED',
|
|
90
|
+
'EAI_AGAIN',
|
|
91
|
+
'ENOTFOUND',
|
|
92
|
+
'ERR_NETWORK',
|
|
93
|
+
'EPIPE',
|
|
94
|
+
'ECONNREFUSED'
|
|
95
|
+
].includes(failure.code))
|
|
96
|
+
return null;
|
|
97
|
+
let headerDelay = null;
|
|
98
|
+
if (response?.headers && typeof response.headers === 'object') {
|
|
99
|
+
const headers = response.headers;
|
|
100
|
+
headerDelay = retryAfterMs(typeof headers.get === 'function'
|
|
101
|
+
? headers.get('retry-after')
|
|
102
|
+
: (headers['retry-after'] ?? headers['Retry-After']));
|
|
103
|
+
}
|
|
104
|
+
const body = response?.data && typeof response.data === 'object'
|
|
105
|
+
? response.data
|
|
106
|
+
: undefined;
|
|
107
|
+
const bodyDelay = typeof body?.backoff_ms === 'number' && Number.isFinite(body.backoff_ms) && body.backoff_ms >= 0
|
|
108
|
+
? body.backoff_ms
|
|
109
|
+
: 0;
|
|
110
|
+
return Math.max(100 * 2 ** attempt, headerDelay ?? 0, bodyDelay);
|
|
111
|
+
}
|
|
112
|
+
async function requestPage(url, apiKey, timeoutMs, deadline) {
|
|
113
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
114
|
+
const remainingMs = deadline - Date.now();
|
|
115
|
+
if (remainingMs <= 0)
|
|
116
|
+
throw new RevenueCatLookupError();
|
|
117
|
+
try {
|
|
118
|
+
return await axios_1.default.get(url.href, {
|
|
119
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
120
|
+
timeout: Math.min(timeoutMs, remainingMs),
|
|
121
|
+
signal: AbortSignal.timeout(remainingMs),
|
|
122
|
+
maxRedirects: 0,
|
|
123
|
+
maxContentLength: 1024 * 1024
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
const delay = attempt < 2 ? requestRetryDelay(error, attempt) : null;
|
|
128
|
+
if (delay === null || delay >= deadline - Date.now())
|
|
129
|
+
throw new RevenueCatLookupError();
|
|
130
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
throw new RevenueCatLookupError();
|
|
134
|
+
}
|
|
135
|
+
function createRevenueCatClient(options) {
|
|
136
|
+
const apiKey = options.apiKey?.trim();
|
|
137
|
+
const projectId = options.projectId?.trim();
|
|
138
|
+
const timeoutMs = options.timeoutMs ?? 15000;
|
|
139
|
+
const totalTimeoutMs = options.totalTimeoutMs ?? 15000;
|
|
140
|
+
const maxPages = options.maxPages ?? 20;
|
|
141
|
+
if (!apiKey || /[\r\n]/.test(apiKey))
|
|
142
|
+
throw new Error('Configure the RevenueCat API key');
|
|
143
|
+
if (!projectId || projectId.length > 255 || !/^[a-zA-Z0-9_-]+$/.test(projectId))
|
|
144
|
+
throw new Error('Configure a valid RevenueCat project ID');
|
|
145
|
+
if (!positiveInteger(timeoutMs, 60000) ||
|
|
146
|
+
!positiveInteger(totalTimeoutMs, 120000) ||
|
|
147
|
+
!positiveInteger(maxPages, 100))
|
|
148
|
+
throw new Error('Configure valid RevenueCat request limits');
|
|
149
|
+
return {
|
|
150
|
+
async getActiveEntitlements(userId) {
|
|
151
|
+
if (!validUserId(userId))
|
|
152
|
+
throw new http_error_1.HttpError('Invalid DGC user ID', 400);
|
|
153
|
+
const endpoint = new URL(`${revenuecat_1.REVENUECAT_API_V2}/projects/${encodeURIComponent(projectId)}/customers/${encodeURIComponent(userId)}/active_entitlements`);
|
|
154
|
+
endpoint.searchParams.set('limit', '100');
|
|
155
|
+
const deadline = Date.now() + totalTimeoutMs;
|
|
156
|
+
const visited = new Set();
|
|
157
|
+
const entitlements = [];
|
|
158
|
+
let url = endpoint;
|
|
159
|
+
try {
|
|
160
|
+
while (url) {
|
|
161
|
+
const remainingMs = deadline - Date.now();
|
|
162
|
+
if (remainingMs <= 0 ||
|
|
163
|
+
visited.size >= maxPages ||
|
|
164
|
+
url.origin !== endpoint.origin ||
|
|
165
|
+
url.pathname !== endpoint.pathname ||
|
|
166
|
+
url.username ||
|
|
167
|
+
url.password ||
|
|
168
|
+
url.hash ||
|
|
169
|
+
visited.has(url.href))
|
|
170
|
+
throw new RevenueCatLookupError();
|
|
171
|
+
visited.add(url.href);
|
|
172
|
+
const response = await requestPage(url, apiKey, timeoutMs, deadline);
|
|
173
|
+
const page = parsePage(response.data);
|
|
174
|
+
entitlements.push(...page.items);
|
|
175
|
+
url = page.nextPage ? new URL(page.nextPage, endpoint) : null;
|
|
176
|
+
}
|
|
177
|
+
const now = Date.now();
|
|
178
|
+
if (now >= deadline)
|
|
179
|
+
throw new RevenueCatLookupError();
|
|
180
|
+
return entitlements.filter(item => item.expiresAt === null || item.expiresAt > now);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
// Do not attach request/config/cause or log the customer payload.
|
|
184
|
+
throw new RevenueCatLookupError();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/** Resolve any eligible entitlement from one customer lookup; never infer Free from API failures. */
|
|
190
|
+
async function verifyCreditGiveawayEntitlement(userId, client, entitlementIds) {
|
|
191
|
+
const ids = (0, revenuecat_1.createRevenueCatEntitlementIds)(entitlementIds);
|
|
192
|
+
const active = await client.getActiveEntitlements(userId);
|
|
193
|
+
const checkedAt = new Date().toISOString();
|
|
194
|
+
const now = Date.parse(checkedAt);
|
|
195
|
+
const tiers = [
|
|
196
|
+
[user_1.UserCollectorType.Elite, ids.elite],
|
|
197
|
+
[user_1.UserCollectorType.Collector, ids.collector],
|
|
198
|
+
[user_1.UserCollectorType.Rookie, ids.rookie]
|
|
199
|
+
];
|
|
200
|
+
for (const [collectorType, entitlementId] of tiers) {
|
|
201
|
+
const match = active.find(item => item.entitlementId === entitlementId && (item.expiresAt === null || item.expiresAt > now));
|
|
202
|
+
if (match)
|
|
203
|
+
return { userId, checkedAt, collectorType, ...match };
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
package/dist/task.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.isRetryableCloudTasks = isRetryableCloudTasks;
|
|
|
4
4
|
exports.createTask = createTask;
|
|
5
5
|
// src/cloud-tasks.ts
|
|
6
6
|
const tasks_1 = require("@google-cloud/tasks");
|
|
7
|
+
const logger_1 = require("./logger");
|
|
7
8
|
const retry_1 = require("./retry");
|
|
8
9
|
const tasksClient = new tasks_1.CloudTasksClient();
|
|
9
10
|
// cloud-tasks errors can be gRPC-style (numeric code) or an Error with code string.
|
|
@@ -67,7 +68,11 @@ async function createTask(projectId, region, queueId, data, serviceAccount, audi
|
|
|
67
68
|
timeoutMs: 15000,
|
|
68
69
|
isRetryable: isRetryableCloudTasks,
|
|
69
70
|
onRetry: (err, attempt, delay) => {
|
|
70
|
-
|
|
71
|
+
(0, logger_1.log)(logger_1.LogLevel.WARNING, 'createTask retry', {
|
|
72
|
+
attempt,
|
|
73
|
+
delayMs: delay,
|
|
74
|
+
error: String(err?.message ?? err)
|
|
75
|
+
});
|
|
71
76
|
},
|
|
72
77
|
...opts.retryOptions // allow overrides
|
|
73
78
|
};
|
|
@@ -93,11 +98,13 @@ async function createTask(projectId, region, queueId, data, serviceAccount, audi
|
|
|
93
98
|
if (err?.original?.code === 6 ||
|
|
94
99
|
err?.code === 6 ||
|
|
95
100
|
String(err?.original?.code).toUpperCase().includes('ALREADY_EXISTS')) {
|
|
96
|
-
|
|
101
|
+
(0, logger_1.log)(logger_1.LogLevel.WARNING, 'createTask — task already exists, returning existing name', {
|
|
102
|
+
taskName: expectedName
|
|
103
|
+
});
|
|
97
104
|
return expectedName;
|
|
98
105
|
}
|
|
99
106
|
}
|
|
100
|
-
|
|
107
|
+
(0, logger_1.log)(logger_1.LogLevel.ERROR, 'createTask — final failure after retries', { error: err?.message ?? err });
|
|
101
108
|
throw err; // rethrow (or return null if you prefer)
|
|
102
109
|
}
|
|
103
110
|
}
|
package/dist/types/giveaway.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { GiveawayStatus, GiveawayCategory, GiveawaySelectionMethod, GiveawayWinnerStatus, GiveawayNotificationKind, GiveawayNotificationAudience, GiveawayNotificationStatus, GiveawayTicketResult, GiveawayAction, GiveawayNotificationChannel } from '../enums/giveaway';
|
|
2
2
|
import type { FbUser } from './user';
|
|
3
3
|
import type { UserCollectorType } from '../enums/user';
|
|
4
|
+
import type { GiveawayCreditEligibilityEvidence } from './revenuecat';
|
|
4
5
|
/** API identities are strings. MongoDB ObjectIds serialize as 24 lowercase hexadecimal characters. */
|
|
5
6
|
export type GiveawayObjectId = string;
|
|
6
7
|
/** New tickets use DGC- followed by twelve uppercase letters or digits. Existing numeric IDs remain readable. */
|
|
@@ -16,6 +17,7 @@ export type GiveawaySelection = {
|
|
|
16
17
|
reason: string;
|
|
17
18
|
selectedBy: string;
|
|
18
19
|
selectedAt: string;
|
|
20
|
+
creditEligibility?: GiveawayCreditEligibilityEvidence;
|
|
19
21
|
};
|
|
20
22
|
export type GiveawayPrize = {
|
|
21
23
|
_id: GiveawayObjectId;
|
|
@@ -78,7 +80,7 @@ export type GiveawayEntry = {
|
|
|
78
80
|
contactNumber?: string;
|
|
79
81
|
/** Retained only for historical entry records; new entries collect country at claim. */
|
|
80
82
|
country?: string;
|
|
81
|
-
/** Null
|
|
83
|
+
/** Null when the account tier is missing or unsupported; product entries remain eligible. */
|
|
82
84
|
_collectorType: UserCollectorType | null;
|
|
83
85
|
enteredAt: string;
|
|
84
86
|
rulesAcceptedAt: string;
|
|
@@ -96,6 +98,7 @@ export type GiveawayWinner = {
|
|
|
96
98
|
status: GiveawayWinnerStatus;
|
|
97
99
|
userId?: string;
|
|
98
100
|
selection?: GiveawaySelection;
|
|
101
|
+
creditEligibility?: GiveawayCreditEligibilityEvidence;
|
|
99
102
|
category: GiveawayCategory;
|
|
100
103
|
creditAmount?: number;
|
|
101
104
|
claimDeadline?: string;
|
|
@@ -127,6 +130,14 @@ export type GiveawayNotificationJob = {
|
|
|
127
130
|
/** Stable event key supports replacements without resending a prior event. */
|
|
128
131
|
eventKey?: string;
|
|
129
132
|
audience?: GiveawayNotificationAudience;
|
|
133
|
+
/** Snapshots keep targeting and expiry stable throughout a broadcast. */
|
|
134
|
+
category?: GiveawayCategory;
|
|
135
|
+
expiresAt?: string;
|
|
136
|
+
dispatchAfter?: string;
|
|
137
|
+
dispatchGeneration?: number;
|
|
138
|
+
recipientsQueued?: number;
|
|
139
|
+
recipientsSkipped?: number;
|
|
140
|
+
dispatchCompletedAt?: string;
|
|
130
141
|
status: GiveawayNotificationStatus;
|
|
131
142
|
attempts: number;
|
|
132
143
|
error?: string;
|
|
@@ -173,6 +184,12 @@ export type GiveawayWinnerRecord = GiveawayWinner & {
|
|
|
173
184
|
};
|
|
174
185
|
export type GiveawayNotificationRecord = Omit<GiveawayNotificationJob, 'claimToken'> & {
|
|
175
186
|
giveawayTitle: string;
|
|
187
|
+
/** Queue acceptance counts, not confirmation of device delivery. */
|
|
188
|
+
deliveryCounts?: {
|
|
189
|
+
queued: number;
|
|
190
|
+
skipped: number;
|
|
191
|
+
pending: number;
|
|
192
|
+
};
|
|
176
193
|
};
|
|
177
194
|
export type GiveawayOverviewData = {
|
|
178
195
|
days: {
|
|
@@ -182,8 +199,9 @@ export type GiveawayOverviewData = {
|
|
|
182
199
|
liveGiveaways: GiveawaySummary[];
|
|
183
200
|
needsSelection: Giveaway[];
|
|
184
201
|
};
|
|
185
|
-
export type GiveawayUserProfile = Pick<FbUser, 'email' | 'firstName' | 'lastName'
|
|
202
|
+
export type GiveawayUserProfile = Pick<FbUser, 'email' | 'firstName' | 'lastName'> & {
|
|
186
203
|
userId: string;
|
|
204
|
+
_collectorType?: UserCollectorType | null;
|
|
187
205
|
};
|
|
188
206
|
export type GiveawayPhysicalClaim = {
|
|
189
207
|
fullName: string;
|
|
@@ -279,6 +297,8 @@ export type GiveawayWinnerPreview = {
|
|
|
279
297
|
};
|
|
280
298
|
/** Public app response; excludes admin settings, prize values and personal data. */
|
|
281
299
|
export type GiveawayPublic = Pick<Giveaway, '_id' | 'title' | 'description' | 'image' | 'status' | 'startsAt' | 'closesAt' | 'countries' | 'rules' | 'category' | 'creditAmount' | 'deliveryInformation'> & {
|
|
300
|
+
/** Null permits all authenticated accounts regardless of their stored tier. */
|
|
301
|
+
eligibleCollectorTypes: UserCollectorType[] | null;
|
|
282
302
|
entryCount: number;
|
|
283
303
|
images: string[];
|
|
284
304
|
prizes: Pick<GiveawayPrize, 'name' | 'quantity'>[];
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
|
@@ -35,3 +35,5 @@ __exportStar(require("./user"), exports);
|
|
|
35
35
|
__exportStar(require("./x-credit"), exports);
|
|
36
36
|
__exportStar(require("./giveaway"), exports);
|
|
37
37
|
__exportStar(require("../enums/giveaway"), exports);
|
|
38
|
+
__exportStar(require("./revenuecat"), exports);
|
|
39
|
+
__exportStar(require("../constants/revenuecat"), exports);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { UserCollectorType } from '../enums/user';
|
|
2
|
+
export type RevenueCatActiveEntitlement = {
|
|
3
|
+
entitlementId: string;
|
|
4
|
+
/** RevenueCat uses milliseconds since epoch; null is non-expiring access. */
|
|
5
|
+
expiresAt: number | null;
|
|
6
|
+
};
|
|
7
|
+
export type RevenueCatEntitlementIds = Readonly<{
|
|
8
|
+
rookie: string;
|
|
9
|
+
collector: string;
|
|
10
|
+
elite: string;
|
|
11
|
+
}>;
|
|
12
|
+
export type RevenueCatClient = {
|
|
13
|
+
getActiveEntitlements(userId: string): Promise<RevenueCatActiveEntitlement[]>;
|
|
14
|
+
};
|
|
15
|
+
export type GiveawayCreditEligibilityEvidence = RevenueCatActiveEntitlement & {
|
|
16
|
+
userId: string;
|
|
17
|
+
checkedAt: string;
|
|
18
|
+
collectorType: UserCollectorType.Rookie | UserCollectorType.Collector | UserCollectorType.Elite;
|
|
19
|
+
};
|
package/package.json
CHANGED
package/dist/tw/utils.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function isValidWebhookSignature(secret: string, body: string, signature: string): boolean;
|
package/dist/tw/utils.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.isValidWebhookSignature = isValidWebhookSignature;
|
|
7
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
-
const generateSignature = (body, secret) => {
|
|
9
|
-
return crypto_1.default.createHmac('sha256', secret).update(body).digest('hex');
|
|
10
|
-
};
|
|
11
|
-
function isValidWebhookSignature(secret, body, signature) {
|
|
12
|
-
const expectedSignature = generateSignature(body, secret);
|
|
13
|
-
const expectedBuffer = Buffer.from(expectedSignature);
|
|
14
|
-
const signatureBuffer = Buffer.from(signature);
|
|
15
|
-
if (expectedBuffer.length !== signatureBuffer.length) {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
return crypto_1.default.timingSafeEqual(expectedBuffer, signatureBuffer);
|
|
19
|
-
}
|
package/dist/tw/wallet.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function pregenerateInAppWallet(twClientId: string, twSecretKey: string, email: string): Promise<string | null>;
|
package/dist/tw/wallet.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.pregenerateInAppWallet = pregenerateInAppWallet;
|
|
7
|
-
// src/tw/wallet.ts
|
|
8
|
-
const axios_1 = __importDefault(require("axios"));
|
|
9
|
-
const retry_1 = require("../retry");
|
|
10
|
-
async function pregenerateInAppWallet(twClientId, twSecretKey, email) {
|
|
11
|
-
if (!twClientId || !twSecretKey || !email) {
|
|
12
|
-
console.error('Missing client id or secret or email');
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
const doRequest = async () => {
|
|
16
|
-
const res = await axios_1.default.post('https://in-app-wallet.thirdweb.com/api/v1/pregenerate', { strategy: 'email', email }, {
|
|
17
|
-
headers: {
|
|
18
|
-
'Content-Type': 'application/json',
|
|
19
|
-
'x-client-id': twClientId,
|
|
20
|
-
'x-secret-key': twSecretKey
|
|
21
|
-
},
|
|
22
|
-
timeout: 10000
|
|
23
|
-
});
|
|
24
|
-
const wallet = res.data?.wallet;
|
|
25
|
-
if (!wallet || !wallet.address) {
|
|
26
|
-
// server returned 2xx but unexpected shape — don't retry, treat as final failure
|
|
27
|
-
console.error('Invalid wallet response shape', { status: res.status, data: res.data });
|
|
28
|
-
throw new Error('Invalid wallet response');
|
|
29
|
-
}
|
|
30
|
-
return wallet.address;
|
|
31
|
-
};
|
|
32
|
-
const opts = {
|
|
33
|
-
retries: 3,
|
|
34
|
-
baseDelayMs: 300,
|
|
35
|
-
maxDelayMs: 2000,
|
|
36
|
-
timeoutMs: 15000,
|
|
37
|
-
isRetryable: retry_1.isRetryableAxios,
|
|
38
|
-
onRetry: (err, attempt, delay) => {
|
|
39
|
-
console.warn(`pregenerateInAppWallet: retry #${attempt} in ${delay}ms — ${String(err?.message)}`);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
try {
|
|
43
|
-
const address = await (0, retry_1.retry)(() => doRequest(), opts);
|
|
44
|
-
return address;
|
|
45
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
46
|
-
}
|
|
47
|
-
catch (err) {
|
|
48
|
-
// final failure after retries
|
|
49
|
-
console.error('pregenerateInAppWallet: final failure', {
|
|
50
|
-
message: err?.message,
|
|
51
|
-
// include axios response data when possible
|
|
52
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
|
-
responseData: err?.response?.data ?? err?.cause ?? err.original ?? null
|
|
54
|
-
});
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
}
|