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/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 };
@@ -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
+ };