swiftshopr-payments 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +339 -0
- package/package.json +54 -0
- package/src/branding.js +127 -0
- package/src/client.js +111 -0
- package/src/config.js +273 -0
- package/src/dashboard.js +265 -0
- package/src/index.js +79 -0
- package/src/payments.js +319 -0
- package/src/refunds.js +209 -0
- package/src/types/index.d.ts +460 -0
- package/src/utils/http.js +217 -0
- package/src/webhooks.js +163 -0
package/src/config.js
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config API Module
|
|
3
|
+
* Retailer self-service configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { SwiftShoprError } = require('./utils/http');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Ethereum address validation regex
|
|
10
|
+
*/
|
|
11
|
+
const ETH_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Validate Ethereum address
|
|
15
|
+
*/
|
|
16
|
+
function isValidEthAddress(address) {
|
|
17
|
+
return ETH_ADDRESS_REGEX.test(address);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create Config API instance
|
|
22
|
+
*/
|
|
23
|
+
function createConfigAPI(http) {
|
|
24
|
+
return {
|
|
25
|
+
/**
|
|
26
|
+
* Get current retailer configuration
|
|
27
|
+
*
|
|
28
|
+
* @returns {Promise<Object>} Current configuration
|
|
29
|
+
*/
|
|
30
|
+
async get() {
|
|
31
|
+
const response = await http.get('/api/v1/sdk/config');
|
|
32
|
+
|
|
33
|
+
if (!response.success) {
|
|
34
|
+
throw new SwiftShoprError(
|
|
35
|
+
'CONFIG_ERROR',
|
|
36
|
+
response.error || 'Failed to get configuration',
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
chainId: response.config.chainId,
|
|
42
|
+
label: response.config.label,
|
|
43
|
+
webhook: response.config.webhook,
|
|
44
|
+
permissions: response.config.permissions,
|
|
45
|
+
rateLimit: response.config.rateLimit,
|
|
46
|
+
allowedIps: response.config.allowedIps,
|
|
47
|
+
createdAt: response.config.createdAt,
|
|
48
|
+
lastUsedAt: response.config.lastUsedAt,
|
|
49
|
+
stores: response.stores,
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Configure webhook URL
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} params
|
|
57
|
+
* @param {string} [params.url] - Webhook URL (HTTPS recommended)
|
|
58
|
+
* @param {boolean} [params.generateSecret] - Generate a new webhook secret
|
|
59
|
+
* @returns {Promise<Object>} Updated webhook config
|
|
60
|
+
*/
|
|
61
|
+
async setWebhook(params = {}) {
|
|
62
|
+
const body = {};
|
|
63
|
+
|
|
64
|
+
if (params.url !== undefined) {
|
|
65
|
+
body.url = params.url;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (params.generateSecret) {
|
|
69
|
+
body.generateSecret = true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const response = await http.put('/api/v1/sdk/config/webhook', body);
|
|
73
|
+
|
|
74
|
+
if (!response.success) {
|
|
75
|
+
throw new SwiftShoprError(
|
|
76
|
+
'CONFIG_ERROR',
|
|
77
|
+
response.error || 'Failed to update webhook',
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
url: response.webhook.url,
|
|
83
|
+
hasSecret: response.webhook.hasSecret,
|
|
84
|
+
secret: response.webhook.secret || null, // Only returned when generated
|
|
85
|
+
message: response.message || null,
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Rotate webhook secret
|
|
91
|
+
*
|
|
92
|
+
* @returns {Promise<Object>} New webhook secret (save it!)
|
|
93
|
+
*/
|
|
94
|
+
async rotateWebhookSecret() {
|
|
95
|
+
const response = await http.post(
|
|
96
|
+
'/api/v1/sdk/config/webhook/rotate-secret',
|
|
97
|
+
{},
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (!response.success) {
|
|
101
|
+
throw new SwiftShoprError(
|
|
102
|
+
'CONFIG_ERROR',
|
|
103
|
+
response.error || 'Failed to rotate webhook secret',
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
url: response.webhook.url,
|
|
109
|
+
secret: response.webhook.secret,
|
|
110
|
+
message: response.message,
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* List all registered stores
|
|
116
|
+
*
|
|
117
|
+
* @returns {Promise<Array>} Store list
|
|
118
|
+
*/
|
|
119
|
+
async getStores() {
|
|
120
|
+
const response = await http.get('/api/v1/sdk/config/stores');
|
|
121
|
+
|
|
122
|
+
if (!response.success) {
|
|
123
|
+
throw new SwiftShoprError(
|
|
124
|
+
'CONFIG_ERROR',
|
|
125
|
+
response.error || 'Failed to get stores',
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return response.stores;
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Register a new store with wallet address
|
|
134
|
+
*
|
|
135
|
+
* @param {Object} params
|
|
136
|
+
* @param {string} params.storeId - Unique store identifier
|
|
137
|
+
* @param {string} params.walletAddress - Ethereum wallet address for payments
|
|
138
|
+
* @param {string} [params.storeName] - Human-readable store name
|
|
139
|
+
* @param {string} [params.webhookUrl] - Store-specific webhook URL (optional)
|
|
140
|
+
* @param {Object} [params.branding] - Store branding configuration
|
|
141
|
+
* @returns {Promise<Object>} Created store
|
|
142
|
+
*/
|
|
143
|
+
async registerStore(params) {
|
|
144
|
+
if (!params.storeId) {
|
|
145
|
+
throw new SwiftShoprError('VALIDATION_ERROR', 'storeId is required');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!params.walletAddress) {
|
|
149
|
+
throw new SwiftShoprError(
|
|
150
|
+
'VALIDATION_ERROR',
|
|
151
|
+
'walletAddress is required',
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!isValidEthAddress(params.walletAddress)) {
|
|
156
|
+
throw new SwiftShoprError(
|
|
157
|
+
'VALIDATION_ERROR',
|
|
158
|
+
'walletAddress must be a valid Ethereum address',
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const body = {
|
|
163
|
+
storeId: params.storeId,
|
|
164
|
+
walletAddress: params.walletAddress,
|
|
165
|
+
...(params.storeName && { storeName: params.storeName }),
|
|
166
|
+
...(params.webhookUrl && { webhookUrl: params.webhookUrl }),
|
|
167
|
+
...(params.branding && { branding: params.branding }),
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const response = await http.post('/api/v1/sdk/config/stores', body);
|
|
171
|
+
|
|
172
|
+
if (!response.success) {
|
|
173
|
+
throw new SwiftShoprError(
|
|
174
|
+
'CONFIG_ERROR',
|
|
175
|
+
response.error || 'Failed to register store',
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return response.store;
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Update a store configuration
|
|
184
|
+
*
|
|
185
|
+
* @param {string} storeId - Store ID to update
|
|
186
|
+
* @param {Object} updates - Fields to update
|
|
187
|
+
* @returns {Promise<Object>} Updated store
|
|
188
|
+
*/
|
|
189
|
+
async updateStore(storeId, updates) {
|
|
190
|
+
if (!storeId) {
|
|
191
|
+
throw new SwiftShoprError('VALIDATION_ERROR', 'storeId is required');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (updates.walletAddress && !isValidEthAddress(updates.walletAddress)) {
|
|
195
|
+
throw new SwiftShoprError(
|
|
196
|
+
'VALIDATION_ERROR',
|
|
197
|
+
'walletAddress must be a valid Ethereum address',
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const response = await http.put(
|
|
202
|
+
`/api/v1/sdk/config/stores/${encodeURIComponent(storeId)}`,
|
|
203
|
+
updates,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
if (!response.success) {
|
|
207
|
+
throw new SwiftShoprError(
|
|
208
|
+
'CONFIG_ERROR',
|
|
209
|
+
response.error || 'Failed to update store',
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return response.store;
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Deactivate a store
|
|
218
|
+
*
|
|
219
|
+
* @param {string} storeId - Store ID to deactivate
|
|
220
|
+
* @returns {Promise<Object>} Confirmation
|
|
221
|
+
*/
|
|
222
|
+
async deactivateStore(storeId) {
|
|
223
|
+
if (!storeId) {
|
|
224
|
+
throw new SwiftShoprError('VALIDATION_ERROR', 'storeId is required');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const response = await http.delete(
|
|
228
|
+
`/api/v1/sdk/config/stores/${encodeURIComponent(storeId)}`,
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
if (!response.success) {
|
|
232
|
+
throw new SwiftShoprError(
|
|
233
|
+
'CONFIG_ERROR',
|
|
234
|
+
response.error || 'Failed to deactivate store',
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return { message: response.message };
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Rotate webhook secret for a specific store
|
|
243
|
+
*
|
|
244
|
+
* @param {string} storeId - Store ID
|
|
245
|
+
* @returns {Promise<Object>} New webhook secret
|
|
246
|
+
*/
|
|
247
|
+
async rotateStoreWebhookSecret(storeId) {
|
|
248
|
+
if (!storeId) {
|
|
249
|
+
throw new SwiftShoprError('VALIDATION_ERROR', 'storeId is required');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const response = await http.post(
|
|
253
|
+
`/api/v1/sdk/config/stores/${encodeURIComponent(storeId)}/webhook/rotate-secret`,
|
|
254
|
+
{},
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
if (!response.success) {
|
|
258
|
+
throw new SwiftShoprError(
|
|
259
|
+
'CONFIG_ERROR',
|
|
260
|
+
response.error || 'Failed to rotate store webhook secret',
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
url: response.webhook.url,
|
|
266
|
+
secret: response.webhook.secret,
|
|
267
|
+
message: response.message,
|
|
268
|
+
};
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
module.exports = { createConfigAPI };
|
package/src/dashboard.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard API Module
|
|
3
|
+
* Retailer analytics and transaction management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { SwiftShoprError } = require('./utils/http');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build query string from params
|
|
10
|
+
*/
|
|
11
|
+
function buildQueryString(params) {
|
|
12
|
+
const searchParams = new URLSearchParams();
|
|
13
|
+
|
|
14
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
15
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
16
|
+
searchParams.append(key, value);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const query = searchParams.toString();
|
|
21
|
+
return query ? `?${query}` : '';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create Dashboard API instance
|
|
26
|
+
*/
|
|
27
|
+
function createDashboardAPI(http) {
|
|
28
|
+
return {
|
|
29
|
+
/**
|
|
30
|
+
* Get dashboard summary metrics
|
|
31
|
+
*
|
|
32
|
+
* @returns {Promise<Object>} Summary with today, week, month metrics
|
|
33
|
+
*/
|
|
34
|
+
async getSummary() {
|
|
35
|
+
const response = await http.get('/api/v1/sdk/dashboard/summary');
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
today: {
|
|
39
|
+
revenue: response.today?.revenue || 0,
|
|
40
|
+
transactionCount: response.today?.transaction_count || 0,
|
|
41
|
+
completed: response.today?.completed || 0,
|
|
42
|
+
pending: response.today?.pending || 0,
|
|
43
|
+
failed: response.today?.failed || 0,
|
|
44
|
+
revenueTrendPercent: response.today?.revenue_trend_percent || 0,
|
|
45
|
+
},
|
|
46
|
+
week: {
|
|
47
|
+
revenue: response.week?.revenue || 0,
|
|
48
|
+
transactionCount: response.week?.transaction_count || 0,
|
|
49
|
+
completed: response.week?.completed || 0,
|
|
50
|
+
},
|
|
51
|
+
month: {
|
|
52
|
+
revenue: response.month?.revenue || 0,
|
|
53
|
+
transactionCount: response.month?.transaction_count || 0,
|
|
54
|
+
completed: response.month?.completed || 0,
|
|
55
|
+
avgTransaction: response.month?.avg_transaction || 0,
|
|
56
|
+
estimatedCardSavings: response.month?.estimated_card_savings || 0,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get paginated transaction list
|
|
63
|
+
*
|
|
64
|
+
* @param {Object} [params]
|
|
65
|
+
* @param {string} [params.status] - Filter by status (completed, pending, failed)
|
|
66
|
+
* @param {string} [params.storeId] - Filter by store
|
|
67
|
+
* @param {string} [params.startDate] - Start date (ISO format)
|
|
68
|
+
* @param {string} [params.endDate] - End date (ISO format)
|
|
69
|
+
* @param {number} [params.limit=50] - Results per page
|
|
70
|
+
* @param {number} [params.offset=0] - Pagination offset
|
|
71
|
+
* @param {string} [params.sort=-created_at] - Sort field
|
|
72
|
+
* @returns {Promise<Object>} Transactions with pagination
|
|
73
|
+
*/
|
|
74
|
+
async getTransactions(params = {}) {
|
|
75
|
+
const query = buildQueryString({
|
|
76
|
+
status: params.status,
|
|
77
|
+
store_id: params.storeId,
|
|
78
|
+
start_date: params.startDate,
|
|
79
|
+
end_date: params.endDate,
|
|
80
|
+
limit: params.limit || 50,
|
|
81
|
+
offset: params.offset || 0,
|
|
82
|
+
sort: params.sort || '-created_at',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const response = await http.get(
|
|
86
|
+
`/api/v1/sdk/dashboard/transactions${query}`,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
transactions: (response.transactions || []).map((tx) => ({
|
|
91
|
+
id: tx.id,
|
|
92
|
+
orderId: tx.order_id,
|
|
93
|
+
status: tx.status,
|
|
94
|
+
amount: tx.amount,
|
|
95
|
+
storeId: tx.store_id,
|
|
96
|
+
txHash: tx.tx_hash,
|
|
97
|
+
explorerUrl: tx.explorer_url,
|
|
98
|
+
createdAt: tx.created_at,
|
|
99
|
+
confirmedAt: tx.confirmed_at,
|
|
100
|
+
refund: tx.refund
|
|
101
|
+
? {
|
|
102
|
+
status: tx.refund.status,
|
|
103
|
+
amount: tx.refund.amount,
|
|
104
|
+
txHash: tx.refund.tx_hash,
|
|
105
|
+
refundedAt: tx.refund.refunded_at,
|
|
106
|
+
}
|
|
107
|
+
: null,
|
|
108
|
+
})),
|
|
109
|
+
pagination: {
|
|
110
|
+
total: response.pagination?.total || 0,
|
|
111
|
+
limit: response.pagination?.limit || 50,
|
|
112
|
+
offset: response.pagination?.offset || 0,
|
|
113
|
+
hasMore: response.pagination?.has_more || false,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get all transactions (auto-paginated)
|
|
120
|
+
*
|
|
121
|
+
* @param {Object} [params] - Same as getTransactions
|
|
122
|
+
* @param {number} [params.maxResults=1000] - Maximum total results
|
|
123
|
+
* @returns {Promise<Array>} All matching transactions
|
|
124
|
+
*/
|
|
125
|
+
async getAllTransactions(params = {}) {
|
|
126
|
+
const maxResults = params.maxResults || 1000;
|
|
127
|
+
const limit = 100;
|
|
128
|
+
let offset = 0;
|
|
129
|
+
const allTransactions = [];
|
|
130
|
+
|
|
131
|
+
while (allTransactions.length < maxResults) {
|
|
132
|
+
const { transactions, pagination } = await this.getTransactions({
|
|
133
|
+
...params,
|
|
134
|
+
limit,
|
|
135
|
+
offset,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
allTransactions.push(...transactions);
|
|
139
|
+
|
|
140
|
+
if (!pagination.hasMore || transactions.length === 0) {
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
offset += limit;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return allTransactions.slice(0, maxResults);
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get store-level breakdown
|
|
152
|
+
*
|
|
153
|
+
* @returns {Promise<Array>} Store metrics
|
|
154
|
+
*/
|
|
155
|
+
async getStores() {
|
|
156
|
+
const response = await http.get('/api/v1/sdk/dashboard/stores');
|
|
157
|
+
|
|
158
|
+
return (response.stores || response || []).map((store) => ({
|
|
159
|
+
storeId: store.store_id,
|
|
160
|
+
storeName: store.store_name,
|
|
161
|
+
revenue: store.revenue || 0,
|
|
162
|
+
transactionCount: store.transaction_count || 0,
|
|
163
|
+
completed: store.completed || 0,
|
|
164
|
+
pending: store.pending || 0,
|
|
165
|
+
failed: store.failed || 0,
|
|
166
|
+
successRate: store.success_rate || 0,
|
|
167
|
+
avgTransaction: store.avg_transaction || 0,
|
|
168
|
+
}));
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get daily metrics for charts
|
|
173
|
+
*
|
|
174
|
+
* @param {Object} [params]
|
|
175
|
+
* @param {number} [params.days=30] - Number of days
|
|
176
|
+
* @param {string} [params.storeId] - Filter by store
|
|
177
|
+
* @returns {Promise<Array>} Daily data points
|
|
178
|
+
*/
|
|
179
|
+
async getDaily(params = {}) {
|
|
180
|
+
const query = buildQueryString({
|
|
181
|
+
days: params.days || 30,
|
|
182
|
+
store_id: params.storeId,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const response = await http.get(`/api/v1/sdk/dashboard/daily${query}`);
|
|
186
|
+
|
|
187
|
+
return (response.daily || response || []).map((day) => ({
|
|
188
|
+
date: day.date,
|
|
189
|
+
revenue: day.revenue || 0,
|
|
190
|
+
transactionCount: day.transaction_count || 0,
|
|
191
|
+
completed: day.completed || 0,
|
|
192
|
+
failed: day.failed || 0,
|
|
193
|
+
avgTransaction: day.avg_transaction || 0,
|
|
194
|
+
}));
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Export transactions as CSV
|
|
199
|
+
*
|
|
200
|
+
* @param {Object} [params]
|
|
201
|
+
* @param {string} [params.startDate] - Start date
|
|
202
|
+
* @param {string} [params.endDate] - End date
|
|
203
|
+
* @param {string} [params.status] - Filter by status
|
|
204
|
+
* @param {string} [params.storeId] - Filter by store
|
|
205
|
+
* @returns {Promise<string>} CSV data
|
|
206
|
+
*/
|
|
207
|
+
async export(params = {}) {
|
|
208
|
+
const query = buildQueryString({
|
|
209
|
+
start_date: params.startDate,
|
|
210
|
+
end_date: params.endDate,
|
|
211
|
+
status: params.status,
|
|
212
|
+
store_id: params.storeId,
|
|
213
|
+
format: 'csv',
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const response = await http.get(`/api/v1/sdk/dashboard/export${query}`);
|
|
217
|
+
|
|
218
|
+
// Response might be CSV string or object with csv property
|
|
219
|
+
return typeof response === 'string' ? response : response.csv || response;
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Get revenue analytics
|
|
224
|
+
*
|
|
225
|
+
* @param {Object} [params]
|
|
226
|
+
* @param {string} [params.period] - 'day', 'week', 'month', 'year'
|
|
227
|
+
* @param {number} [params.periods=12] - Number of periods
|
|
228
|
+
* @returns {Promise<Object>} Revenue analytics
|
|
229
|
+
*/
|
|
230
|
+
async getRevenueAnalytics(params = {}) {
|
|
231
|
+
const period = params.period || 'month';
|
|
232
|
+
const periods = params.periods || 12;
|
|
233
|
+
|
|
234
|
+
// Get daily data and aggregate
|
|
235
|
+
const days =
|
|
236
|
+
period === 'day'
|
|
237
|
+
? periods
|
|
238
|
+
: period === 'week'
|
|
239
|
+
? periods * 7
|
|
240
|
+
: period === 'month'
|
|
241
|
+
? periods * 30
|
|
242
|
+
: periods * 365;
|
|
243
|
+
|
|
244
|
+
const daily = await this.getDaily({ days: Math.min(days, 365) });
|
|
245
|
+
|
|
246
|
+
const totalRevenue = daily.reduce((sum, d) => sum + d.revenue, 0);
|
|
247
|
+
const totalTransactions = daily.reduce(
|
|
248
|
+
(sum, d) => sum + d.transactionCount,
|
|
249
|
+
0,
|
|
250
|
+
);
|
|
251
|
+
const avgDaily = daily.length > 0 ? totalRevenue / daily.length : 0;
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
totalRevenue,
|
|
255
|
+
totalTransactions,
|
|
256
|
+
averageDailyRevenue: avgDaily,
|
|
257
|
+
averageTransactionValue:
|
|
258
|
+
totalTransactions > 0 ? totalRevenue / totalTransactions : 0,
|
|
259
|
+
dataPoints: daily,
|
|
260
|
+
};
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = { createDashboardAPI };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* swiftshopr-payments
|
|
3
|
+
*
|
|
4
|
+
* Official Node.js SDK for SwiftShopr Payments API
|
|
5
|
+
* Accept USDC payments with zero chargebacks
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const { SwiftShoprClient } = require('swiftshopr-payments');
|
|
9
|
+
*
|
|
10
|
+
* const client = new SwiftShoprClient({
|
|
11
|
+
* apiKey: 'sk_live_...',
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* // Create onramp session (Add Funds)
|
|
15
|
+
* const session = await client.payments.createSession({
|
|
16
|
+
* amount: 25.00,
|
|
17
|
+
* orderId: 'ORDER-123',
|
|
18
|
+
* storeId: 'STORE001',
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Create direct transfer (Purchase)
|
|
22
|
+
* const transfer = await client.payments.createTransfer({
|
|
23
|
+
* amount: 25.00,
|
|
24
|
+
* userWalletAddress: '0x...',
|
|
25
|
+
* storeId: 'STORE001',
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Check payment status
|
|
29
|
+
* const status = await client.payments.getStatus(transfer.intentId);
|
|
30
|
+
*
|
|
31
|
+
* // Request refund
|
|
32
|
+
* const refund = await client.refunds.create({
|
|
33
|
+
* intentId: transfer.intentId,
|
|
34
|
+
* reason: 'Customer request',
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* // Dashboard metrics
|
|
38
|
+
* const summary = await client.dashboard.getSummary();
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
// Main client
|
|
42
|
+
const { SwiftShoprClient } = require('./client');
|
|
43
|
+
|
|
44
|
+
// Error types
|
|
45
|
+
const {
|
|
46
|
+
HttpError,
|
|
47
|
+
TimeoutError,
|
|
48
|
+
SwiftShoprError,
|
|
49
|
+
generateIdempotencyKey,
|
|
50
|
+
} = require('./utils/http');
|
|
51
|
+
|
|
52
|
+
// Webhook helpers
|
|
53
|
+
const {
|
|
54
|
+
createWebhooksHelper,
|
|
55
|
+
webhookMiddleware,
|
|
56
|
+
WebhookEvents,
|
|
57
|
+
} = require('./webhooks');
|
|
58
|
+
|
|
59
|
+
// Export everything
|
|
60
|
+
module.exports = {
|
|
61
|
+
// Main client
|
|
62
|
+
SwiftShoprClient,
|
|
63
|
+
|
|
64
|
+
// Error types
|
|
65
|
+
HttpError,
|
|
66
|
+
TimeoutError,
|
|
67
|
+
SwiftShoprError,
|
|
68
|
+
|
|
69
|
+
// Utilities
|
|
70
|
+
generateIdempotencyKey,
|
|
71
|
+
|
|
72
|
+
// Webhook helpers
|
|
73
|
+
createWebhooksHelper,
|
|
74
|
+
webhookMiddleware,
|
|
75
|
+
WebhookEvents,
|
|
76
|
+
|
|
77
|
+
// Default export for ESM compatibility
|
|
78
|
+
default: SwiftShoprClient,
|
|
79
|
+
};
|