paymongo-cli 1.4.3 → 1.4.6
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/CHANGELOG.md +47 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/commands/config.js +14 -0
- package/dist/commands/dev/logs.js +46 -0
- package/dist/commands/dev/status.js +41 -0
- package/dist/commands/dev/stop.js +41 -0
- package/dist/commands/dev.js +9 -283
- package/dist/commands/generate/templates/checkout-page/index.js +539 -0
- package/dist/commands/generate/templates/index.js +5 -0
- package/dist/commands/generate/templates/payment-intent/javascript.js +71 -0
- package/dist/commands/generate/templates/payment-intent/typescript.js +95 -0
- package/dist/commands/generate/templates/webhook-handler/javascript.js +219 -0
- package/dist/commands/generate/templates/webhook-handler/typescript.js +168 -0
- package/dist/commands/generate.js +21 -1007
- package/dist/commands/init.js +22 -5
- package/dist/commands/login.js +53 -16
- package/dist/commands/team/index.js +4 -3
- package/dist/commands/trigger.js +40 -2
- package/dist/commands/webhooks.js +1 -1
- package/dist/services/analytics/service.js +5 -1
- package/dist/services/api/client.js +1 -1
- package/dist/services/config/manager.js +6 -8
- package/dist/services/dev/server.js +167 -0
- package/dist/services/team/service.js +4 -1
- package/dist/types/schemas.js +38 -9
- package/package.json +1 -1
- package/dist/services/api/undici-client.js +0 -288
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
import { request } from 'undici';
|
|
2
|
-
import { NetworkError, ApiKeyError, PayMongoError, withRetry } from '../../utils/errors.js';
|
|
3
|
-
import Cache from '../../utils/cache.js';
|
|
4
|
-
import RateLimiter from './rate-limiter.js';
|
|
5
|
-
const REQUEST_TIMEOUT = 30000;
|
|
6
|
-
export class ApiClient {
|
|
7
|
-
config;
|
|
8
|
-
baseUrl;
|
|
9
|
-
defaultHeaders;
|
|
10
|
-
timeout;
|
|
11
|
-
cache;
|
|
12
|
-
rateLimiter;
|
|
13
|
-
constructor(options) {
|
|
14
|
-
this.config = options.config;
|
|
15
|
-
this.baseUrl = 'https://api.paymongo.com';
|
|
16
|
-
this.timeout = options.timeout || REQUEST_TIMEOUT;
|
|
17
|
-
this.defaultHeaders = {
|
|
18
|
-
'Content-Type': 'application/json',
|
|
19
|
-
'User-Agent': 'paymongo-cli/1.0.0',
|
|
20
|
-
};
|
|
21
|
-
this.cache = new Cache({ ttl: 2 * 60 * 1000 });
|
|
22
|
-
const rateLimitEnabled = options.enableRateLimiting !== false && this.config.rateLimiting?.enabled !== false;
|
|
23
|
-
if (rateLimitEnabled) {
|
|
24
|
-
const rateLimitConfig = options.rateLimitConfig || this.getDefaultRateLimitConfig();
|
|
25
|
-
if (this.config.rateLimiting) {
|
|
26
|
-
rateLimitConfig.default.maxRequests = this.config.rateLimiting.maxRequests;
|
|
27
|
-
rateLimitConfig.default.windowMs = this.config.rateLimiting.windowMs;
|
|
28
|
-
if (this.config.rateLimiting.environmentMultiplier !== undefined) {
|
|
29
|
-
rateLimitConfig.default.environmentMultiplier =
|
|
30
|
-
this.config.rateLimiting.environmentMultiplier;
|
|
31
|
-
}
|
|
32
|
-
if (this.config.rateLimiting.endpoints) {
|
|
33
|
-
rateLimitConfig.endpoints = {
|
|
34
|
-
...rateLimitConfig.endpoints,
|
|
35
|
-
...this.config.rateLimiting.endpoints,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
this.rateLimiter = new RateLimiter(this.config, rateLimitConfig);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
getDefaultRateLimitConfig() {
|
|
43
|
-
return {
|
|
44
|
-
default: {
|
|
45
|
-
maxRequests: 100,
|
|
46
|
-
windowMs: 60 * 1000,
|
|
47
|
-
environmentMultiplier: 0.5,
|
|
48
|
-
},
|
|
49
|
-
endpoints: {
|
|
50
|
-
'/webhooks': {
|
|
51
|
-
maxRequests: 30,
|
|
52
|
-
windowMs: 60 * 1000,
|
|
53
|
-
},
|
|
54
|
-
'/payments': {
|
|
55
|
-
maxRequests: 60,
|
|
56
|
-
windowMs: 60 * 1000,
|
|
57
|
-
},
|
|
58
|
-
'/payment_intents': {
|
|
59
|
-
maxRequests: 60,
|
|
60
|
-
windowMs: 60 * 1000,
|
|
61
|
-
},
|
|
62
|
-
'/refunds': {
|
|
63
|
-
maxRequests: 20,
|
|
64
|
-
windowMs: 60 * 1000,
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
environments: {
|
|
68
|
-
test: {},
|
|
69
|
-
live: {},
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
async makeRequest(method, path, options = {}) {
|
|
74
|
-
const url = new URL(path, this.baseUrl);
|
|
75
|
-
if (options.params) {
|
|
76
|
-
Object.entries(options.params).forEach(([key, value]) => {
|
|
77
|
-
url.searchParams.append(key, value.toString());
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
if (this.rateLimiter) {
|
|
81
|
-
const endpoint = path.replace('/v1', '') || '/unknown';
|
|
82
|
-
const limitCheck = this.rateLimiter.checkLimit(endpoint);
|
|
83
|
-
if (!limitCheck.allowed) {
|
|
84
|
-
const backoffMs = limitCheck.backoffMs;
|
|
85
|
-
if (backoffMs === undefined) {
|
|
86
|
-
throw new PayMongoError('Rate limit exceeded but no backoff time available.', 'RATE_LIMIT_ERROR', 429);
|
|
87
|
-
}
|
|
88
|
-
const waitTime = Math.ceil(backoffMs / 1000);
|
|
89
|
-
throw new PayMongoError(`Rate limit exceeded. Next request available in ${waitTime} seconds. ` +
|
|
90
|
-
`Consider using --rate-limit-max-requests to increase limits or wait before retrying.`, 'RATE_LIMIT_EXCEEDED', 429);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const env = this.config.environment;
|
|
94
|
-
const secretKey = this.config.apiKeys[env]?.secret;
|
|
95
|
-
if (!secretKey) {
|
|
96
|
-
throw new Error('Secret API key not found');
|
|
97
|
-
}
|
|
98
|
-
const headers = {
|
|
99
|
-
...this.defaultHeaders,
|
|
100
|
-
...options.headers,
|
|
101
|
-
Authorization: `Basic ${Buffer.from(`${secretKey}:`).toString('base64')}`,
|
|
102
|
-
};
|
|
103
|
-
let body = null;
|
|
104
|
-
if (options.body) {
|
|
105
|
-
body = JSON.stringify(options.body);
|
|
106
|
-
}
|
|
107
|
-
const controller = new AbortController();
|
|
108
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
109
|
-
try {
|
|
110
|
-
const response = await request(url.toString(), {
|
|
111
|
-
method,
|
|
112
|
-
headers,
|
|
113
|
-
body,
|
|
114
|
-
signal: controller.signal,
|
|
115
|
-
});
|
|
116
|
-
clearTimeout(timeoutId);
|
|
117
|
-
if (this.rateLimiter && path) {
|
|
118
|
-
const endpoint = path.replace('/v1', '');
|
|
119
|
-
this.rateLimiter.recordCall(endpoint);
|
|
120
|
-
}
|
|
121
|
-
let data;
|
|
122
|
-
const contentType = response.headers['content-type'];
|
|
123
|
-
if (contentType && contentType.includes('application/json')) {
|
|
124
|
-
data = await response.body.json();
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
data = await response.body.text();
|
|
128
|
-
}
|
|
129
|
-
if (response.statusCode >= 400) {
|
|
130
|
-
this.handleHttpError(response.statusCode, data);
|
|
131
|
-
}
|
|
132
|
-
return {
|
|
133
|
-
statusCode: response.statusCode,
|
|
134
|
-
data,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
catch (error) {
|
|
138
|
-
clearTimeout(timeoutId);
|
|
139
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
140
|
-
throw new NetworkError(`Request timeout after ${this.timeout}ms`, error);
|
|
141
|
-
}
|
|
142
|
-
if (error instanceof Error && error.code === 'UND_ERR_CONNECT_TIMEOUT') {
|
|
143
|
-
throw new NetworkError(`Connection timeout: ${error.message}`, error);
|
|
144
|
-
}
|
|
145
|
-
if (error instanceof Error && error.code === 'ENOTFOUND') {
|
|
146
|
-
throw new NetworkError(`DNS resolution failed: ${error.message}`, error);
|
|
147
|
-
}
|
|
148
|
-
throw new NetworkError(`Network error: ${error instanceof Error ? error.message : 'Unknown error'}`, error instanceof Error ? error : undefined);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
handleHttpError(statusCode, data) {
|
|
152
|
-
if (statusCode === 401) {
|
|
153
|
-
throw new ApiKeyError('Invalid API key or unauthorized', 'secret');
|
|
154
|
-
}
|
|
155
|
-
if (statusCode === 404) {
|
|
156
|
-
throw new PayMongoError('Resource not found', 'RESOURCE_NOT_FOUND', statusCode);
|
|
157
|
-
}
|
|
158
|
-
if (statusCode >= 500) {
|
|
159
|
-
throw new PayMongoError(`Server error: ${statusCode}`, `SERVER_${statusCode}`, statusCode);
|
|
160
|
-
}
|
|
161
|
-
if (data && typeof data === 'object' && 'errors' in data) {
|
|
162
|
-
const errorResponse = data;
|
|
163
|
-
if (Array.isArray(errorResponse.errors) && errorResponse.errors.length > 0) {
|
|
164
|
-
const error = errorResponse.errors[0];
|
|
165
|
-
if (error) {
|
|
166
|
-
const message = error.detail || error.title || `API error: ${statusCode}`;
|
|
167
|
-
const code = error.code || `API_${statusCode}`;
|
|
168
|
-
throw new PayMongoError(message, code, statusCode);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
throw new PayMongoError(`HTTP ${statusCode}`, `HTTP_${statusCode}`, statusCode);
|
|
173
|
-
}
|
|
174
|
-
async validateApiKey() {
|
|
175
|
-
await withRetry(() => this.makeRequest('GET', '/v1/webhooks'));
|
|
176
|
-
}
|
|
177
|
-
async createWebhook(url, events) {
|
|
178
|
-
const result = await withRetry(() => this.makeRequest('POST', '/v1/webhooks', {
|
|
179
|
-
body: {
|
|
180
|
-
data: {
|
|
181
|
-
attributes: {
|
|
182
|
-
url,
|
|
183
|
-
events,
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
}).then((response) => response.data.data));
|
|
188
|
-
await this.cache.invalidate(`webhooks_${this.config.environment}`);
|
|
189
|
-
return result;
|
|
190
|
-
}
|
|
191
|
-
async listWebhooks() {
|
|
192
|
-
const cacheKey = `webhooks_${this.config.environment}`;
|
|
193
|
-
const cached = await this.cache.get(cacheKey);
|
|
194
|
-
if (cached) {
|
|
195
|
-
return cached;
|
|
196
|
-
}
|
|
197
|
-
const result = await withRetry(() => this.makeRequest('GET', '/v1/webhooks').then((response) => response.data.data));
|
|
198
|
-
await this.cache.set(cacheKey, result);
|
|
199
|
-
return result;
|
|
200
|
-
}
|
|
201
|
-
async getWebhook(id) {
|
|
202
|
-
const cacheKey = `webhook_${id}`;
|
|
203
|
-
const cached = await this.cache.get(cacheKey);
|
|
204
|
-
if (cached) {
|
|
205
|
-
return cached;
|
|
206
|
-
}
|
|
207
|
-
const result = await withRetry(() => this.makeRequest('GET', `/v1/webhooks/${id}`).then((response) => response.data.data));
|
|
208
|
-
await this.cache.set(cacheKey, result);
|
|
209
|
-
return result;
|
|
210
|
-
}
|
|
211
|
-
async updateWebhook(id, updates) {
|
|
212
|
-
await this.cache.invalidate(`webhook_${id}`);
|
|
213
|
-
await this.cache.invalidate(`webhooks_${this.config.environment}`);
|
|
214
|
-
return withRetry(() => this.makeRequest('PUT', `/v1/webhooks/${id}`, {
|
|
215
|
-
body: {
|
|
216
|
-
data: {
|
|
217
|
-
attributes: updates,
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
}).then((response) => response.data.data));
|
|
221
|
-
}
|
|
222
|
-
async deleteWebhook(id) {
|
|
223
|
-
await this.cache.invalidate(`webhook_${id}`);
|
|
224
|
-
await this.cache.invalidate(`webhooks_${this.config.environment}`);
|
|
225
|
-
return withRetry(async () => {
|
|
226
|
-
await this.makeRequest('DELETE', `/v1/webhooks/${id}`);
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
async getPayment(id) {
|
|
230
|
-
return withRetry(() => this.makeRequest('GET', `/v1/payments/${id}`).then((response) => response.data.data));
|
|
231
|
-
}
|
|
232
|
-
async listPayments(limit = 10) {
|
|
233
|
-
const validLimit = Math.max(1, Math.min(100, limit));
|
|
234
|
-
const result = await withRetry(() => this.makeRequest('GET', '/v1/payments', {
|
|
235
|
-
params: { limit: validLimit },
|
|
236
|
-
}).then((response) => response.data.data));
|
|
237
|
-
return result;
|
|
238
|
-
}
|
|
239
|
-
async createPaymentIntent(amount, currency = 'PHP', description, paymentMethods = ['card', 'gcash', 'paymaya']) {
|
|
240
|
-
return withRetry(() => this.makeRequest('POST', '/v1/payment_intents', {
|
|
241
|
-
body: {
|
|
242
|
-
data: {
|
|
243
|
-
attributes: {
|
|
244
|
-
amount,
|
|
245
|
-
payment_method_allowed: paymentMethods,
|
|
246
|
-
currency,
|
|
247
|
-
description,
|
|
248
|
-
},
|
|
249
|
-
},
|
|
250
|
-
},
|
|
251
|
-
}).then((response) => response.data.data));
|
|
252
|
-
}
|
|
253
|
-
async confirmPaymentIntent(id, paymentMethodId, returnUrl) {
|
|
254
|
-
return withRetry(() => this.makeRequest('POST', `/v1/payment_intents/${id}/confirm`, {
|
|
255
|
-
body: {
|
|
256
|
-
data: {
|
|
257
|
-
attributes: {
|
|
258
|
-
payment_method: paymentMethodId,
|
|
259
|
-
return_url: returnUrl,
|
|
260
|
-
},
|
|
261
|
-
},
|
|
262
|
-
},
|
|
263
|
-
}).then((response) => response.data.data));
|
|
264
|
-
}
|
|
265
|
-
async capturePaymentIntent(id) {
|
|
266
|
-
return withRetry(() => this.makeRequest('POST', `/v1/payment_intents/${id}/capture`).then((response) => response.data.data));
|
|
267
|
-
}
|
|
268
|
-
async createRefund(paymentId, amount, reason) {
|
|
269
|
-
const attributes = {};
|
|
270
|
-
if (amount !== undefined) {
|
|
271
|
-
attributes.amount = amount;
|
|
272
|
-
}
|
|
273
|
-
if (reason) {
|
|
274
|
-
attributes.reason = reason;
|
|
275
|
-
}
|
|
276
|
-
return withRetry(() => this.makeRequest('POST', '/v1/refunds', {
|
|
277
|
-
body: {
|
|
278
|
-
data: {
|
|
279
|
-
attributes: {
|
|
280
|
-
payment_id: paymentId,
|
|
281
|
-
...attributes,
|
|
282
|
-
},
|
|
283
|
-
},
|
|
284
|
-
},
|
|
285
|
-
}).then((response) => response.data.data));
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
export default ApiClient;
|