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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 SwiftShopr
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,339 @@
1
+ # swiftshopr-payments
2
+
3
+ Official Node.js SDK for SwiftShopr Payments API. Accept USDC payments with zero chargebacks, instant settlement, and lower fees than card networks.
4
+
5
+ ## Features
6
+
7
+ - **Zero Dependencies** - Uses native `fetch` (Node.js 18+)
8
+ - **TypeScript Support** - Full type definitions included
9
+ - **Automatic Retries** - Exponential backoff for failed requests
10
+ - **Idempotency** - Safe retries for payment operations
11
+ - **Webhook Verification** - HMAC signature validation
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install swiftshopr-payments
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```javascript
22
+ const { SwiftShoprClient } = require('swiftshopr-payments');
23
+
24
+ const client = new SwiftShoprClient({
25
+ apiKey: 'sk_live_your_api_key',
26
+ });
27
+
28
+ // Create an onramp session (Add Funds)
29
+ const session = await client.payments.createSession({
30
+ amount: 25.00,
31
+ orderId: 'ORDER-123',
32
+ storeId: 'STORE001',
33
+ });
34
+
35
+ console.log('Redirect user to:', session.onrampUrl);
36
+ ```
37
+
38
+ ## Payment Flows
39
+
40
+ ### Two-Button Pattern
41
+
42
+ SwiftShopr supports two payment methods:
43
+
44
+ | Button | Method | When to Use | Fees |
45
+ |--------|--------|-------------|------|
46
+ | **Add Funds** | `createSession()` | User has no USDC | ~$0.30 |
47
+ | **Purchase** | `createTransfer()` | User has USDC balance | $0 |
48
+
49
+ ```javascript
50
+ // Check user's USDC balance first
51
+ const balance = await getUserBalance(); // Your wallet SDK
52
+
53
+ if (balance >= purchaseAmount) {
54
+ // Direct transfer - no fees!
55
+ const transfer = await client.payments.createTransfer({
56
+ amount: 25.00,
57
+ userWalletAddress: '0xUserWallet...',
58
+ storeId: 'STORE001',
59
+ });
60
+ } else {
61
+ // Onramp - small fee for ACH conversion
62
+ const session = await client.payments.createSession({
63
+ amount: 25.00,
64
+ storeId: 'STORE001',
65
+ });
66
+ }
67
+ ```
68
+
69
+ ## API Reference
70
+
71
+ ### Payments
72
+
73
+ #### Create Onramp Session
74
+
75
+ ```javascript
76
+ const session = await client.payments.createSession({
77
+ amount: 25.00, // Required: Amount in USD
78
+ orderId: 'ORDER-123', // Optional: Your order reference
79
+ storeId: 'STORE001', // Optional: Store ID (resolves wallet)
80
+ destinationAddress: '0x...', // Optional: Explicit wallet address
81
+ paymentMethod: 'ACH_BANK_ACCOUNT', // Optional: ACH_BANK_ACCOUNT, CARD
82
+ });
83
+
84
+ // Response
85
+ {
86
+ intentId: 'uuid',
87
+ sessionId: 'sess_xxx',
88
+ orderId: 'ORDER-123',
89
+ onrampUrl: 'https://pay.coinbase.com/...',
90
+ expiresAt: '2024-12-23T10:30:00Z',
91
+ branding: { ... }
92
+ }
93
+ ```
94
+
95
+ #### Create Direct Transfer
96
+
97
+ ```javascript
98
+ const transfer = await client.payments.createTransfer({
99
+ amount: 25.00,
100
+ userWalletAddress: '0xUserWallet...',
101
+ storeId: 'STORE001',
102
+ });
103
+
104
+ // Response includes transfer instructions
105
+ {
106
+ intentId: 'uuid',
107
+ transfer: {
108
+ to: '0xRetailerWallet...',
109
+ amount: '25.00',
110
+ asset: 'USDC',
111
+ network: 'base',
112
+ chainId: 8453,
113
+ }
114
+ }
115
+ ```
116
+
117
+ #### Get Payment Status
118
+
119
+ ```javascript
120
+ // By intent ID
121
+ const status = await client.payments.getStatus('intent-uuid');
122
+
123
+ // By your order ID
124
+ const status = await client.payments.getStatusByOrderId('ORDER-123');
125
+
126
+ // Poll until complete
127
+ const finalStatus = await client.payments.waitForCompletion('intent-uuid', {
128
+ timeout: 300000, // 5 minutes
129
+ interval: 2000, // Check every 2 seconds
130
+ onStatusChange: (status) => console.log('Status:', status.status),
131
+ });
132
+ ```
133
+
134
+ ### Refunds
135
+
136
+ #### Request a Refund
137
+
138
+ ```javascript
139
+ const refund = await client.refunds.create({
140
+ intentId: 'original-payment-intent-uuid',
141
+ amount: 25.00, // Optional: Defaults to full refund
142
+ reason: 'Customer request',
143
+ });
144
+
145
+ // Response includes transfer instructions
146
+ {
147
+ id: 'refund-uuid',
148
+ status: 'requested',
149
+ instructions: {
150
+ message: 'Send USDC to complete the refund...',
151
+ transfer: {
152
+ to: '0xUserWallet...',
153
+ amount: '25.00',
154
+ asset: 'USDC',
155
+ },
156
+ fromWallet: '0xYourStoreWallet...',
157
+ }
158
+ }
159
+ ```
160
+
161
+ #### Check Refund Status
162
+
163
+ ```javascript
164
+ const refund = await client.refunds.get('refund-uuid');
165
+
166
+ // List all refunds for a payment
167
+ const refunds = await client.refunds.listForPayment('intent-uuid');
168
+
169
+ // Wait for completion
170
+ const completedRefund = await client.refunds.waitForCompletion('refund-uuid');
171
+ ```
172
+
173
+ ### Dashboard
174
+
175
+ ```javascript
176
+ // Summary metrics
177
+ const summary = await client.dashboard.getSummary();
178
+
179
+ // Transaction list with filters
180
+ const { transactions, pagination } = await client.dashboard.getTransactions({
181
+ status: 'completed',
182
+ storeId: 'STORE001',
183
+ startDate: '2024-01-01',
184
+ endDate: '2024-12-31',
185
+ limit: 50,
186
+ });
187
+
188
+ // Store breakdown
189
+ const stores = await client.dashboard.getStores();
190
+
191
+ // Daily data for charts
192
+ const daily = await client.dashboard.getDaily({ days: 30 });
193
+
194
+ // Export CSV
195
+ const csv = await client.dashboard.export({
196
+ startDate: '2024-01-01',
197
+ endDate: '2024-12-31',
198
+ });
199
+ ```
200
+
201
+ ### Branding
202
+
203
+ ```javascript
204
+ // Get branding for white-label UI
205
+ const branding = await client.branding.get('STORE001');
206
+
207
+ // Generate CSS variables
208
+ const css = client.branding.toCssVariables(branding);
209
+ // --swiftshopr-primary: #E31837;
210
+ // --swiftshopr-background: #FFFFFF;
211
+
212
+ // Get CDP/OnchainKit theme tokens
213
+ const cdpTheme = client.branding.toCdpTheme(branding);
214
+ ```
215
+
216
+ ## Webhooks
217
+
218
+ ### Verify Webhook Signatures
219
+
220
+ ```javascript
221
+ const { SwiftShoprClient, webhookMiddleware } = require('swiftshopr-payments');
222
+
223
+ // Option 1: Express middleware
224
+ app.post('/webhooks/swiftshopr',
225
+ express.raw({ type: 'application/json' }),
226
+ webhookMiddleware('whsec_your_secret'),
227
+ (req, res) => {
228
+ const event = req.webhookEvent;
229
+
230
+ switch (event.type) {
231
+ case 'payment.completed':
232
+ console.log('Payment completed:', event.data.orderId);
233
+ break;
234
+ case 'refund.completed':
235
+ console.log('Refund completed:', event.data.refundId);
236
+ break;
237
+ }
238
+
239
+ res.json({ received: true });
240
+ }
241
+ );
242
+
243
+ // Option 2: Manual verification
244
+ const client = new SwiftShoprClient({
245
+ apiKey: 'sk_live_...',
246
+ webhookSecret: 'whsec_your_secret',
247
+ });
248
+
249
+ const event = client.webhooks.constructEvent(
250
+ req.body, // Raw body
251
+ req.headers // { 'x-webhook-signature': '...', 'x-webhook-timestamp': '...' }
252
+ );
253
+ ```
254
+
255
+ ### Webhook Events
256
+
257
+ | Event | Description |
258
+ |-------|-------------|
259
+ | `payment.completed` | Payment confirmed on-chain |
260
+ | `payment.failed` | Payment failed |
261
+ | `payment.expired` | Payment intent expired |
262
+ | `refund.requested` | Refund initiated |
263
+ | `refund.completed` | Refund confirmed on-chain |
264
+ | `refund.failed` | Refund failed |
265
+
266
+ ## Error Handling
267
+
268
+ ```javascript
269
+ const { SwiftShoprError, HttpError, TimeoutError } = require('swiftshopr-payments');
270
+
271
+ try {
272
+ await client.payments.createSession({ amount: 25.00 });
273
+ } catch (error) {
274
+ if (error instanceof SwiftShoprError) {
275
+ // Validation or business logic error
276
+ console.log('Code:', error.code);
277
+ console.log('Message:', error.message);
278
+ console.log('Details:', error.details);
279
+ } else if (error instanceof HttpError) {
280
+ // HTTP error from API
281
+ console.log('Status:', error.statusCode);
282
+ console.log('Response:', error.response);
283
+ } else if (error instanceof TimeoutError) {
284
+ // Request timed out
285
+ console.log('Timeout after:', error.timeout, 'ms');
286
+ }
287
+ }
288
+ ```
289
+
290
+ ## Configuration
291
+
292
+ ```javascript
293
+ const client = new SwiftShoprClient({
294
+ apiKey: 'sk_live_...', // Required
295
+ webhookSecret: 'whsec_...', // Optional: For webhook verification
296
+ baseUrl: 'https://...', // Optional: Custom API URL
297
+ timeout: 30000, // Optional: Request timeout (ms)
298
+ retries: 3, // Optional: Retry attempts
299
+
300
+ // Hooks for logging/monitoring
301
+ onRequest: ({ method, url }) => {
302
+ console.log(`[API] ${method} ${url}`);
303
+ },
304
+ onResponse: ({ status, data }) => {
305
+ console.log(`[API] Response: ${status}`);
306
+ },
307
+ onError: (error) => {
308
+ console.error('[API] Error:', error.message);
309
+ },
310
+ });
311
+ ```
312
+
313
+ ## TypeScript
314
+
315
+ Full TypeScript support with type definitions:
316
+
317
+ ```typescript
318
+ import { SwiftShoprClient, PaymentStatus, RefundResult } from 'swiftshopr-payments';
319
+
320
+ const client = new SwiftShoprClient({ apiKey: 'sk_live_...' });
321
+
322
+ const status: PaymentStatus = await client.payments.getStatus('uuid');
323
+ const refund: RefundResult = await client.refunds.create({ intentId: 'uuid' });
324
+ ```
325
+
326
+ ## Requirements
327
+
328
+ - Node.js 18+ (uses native `fetch`)
329
+ - SwiftShopr API key
330
+
331
+ ## Support
332
+
333
+ - Documentation: https://docs.swiftshopr.com
334
+ - Issues: https://github.com/swiftshopr/payments-sdk/issues
335
+ - Email: sdk@swiftshopr.com
336
+
337
+ ## License
338
+
339
+ MIT
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "swiftshopr-payments",
3
+ "version": "1.0.0",
4
+ "description": "Official Node.js SDK for SwiftShopr Payments - Accept USDC payments with zero chargebacks",
5
+ "main": "src/index.js",
6
+ "types": "src/types/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./src/index.js",
10
+ "import": "./src/index.js",
11
+ "types": "./src/types/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "src/**/*",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "test": "jest --passWithNoTests",
21
+ "lint": "echo 'Linting skipped - no eslint config'",
22
+ "prepublishOnly": "npm test"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/swiftshopr/payments-sdk.git"
27
+ },
28
+ "keywords": [
29
+ "swiftshopr",
30
+ "payments",
31
+ "usdc",
32
+ "crypto",
33
+ "stablecoin",
34
+ "point-of-sale",
35
+ "pos",
36
+ "retail",
37
+ "sdk",
38
+ "api",
39
+ "base",
40
+ "coinbase",
41
+ "web3"
42
+ ],
43
+ "author": "SwiftShopr <sdk@swiftshopr.com>",
44
+ "license": "MIT",
45
+ "bugs": {
46
+ "url": "https://github.com/swiftshopr/payments-sdk/issues"
47
+ },
48
+ "homepage": "https://docs.swiftshopr.com/sdk",
49
+ "engines": {
50
+ "node": ">=18.0.0"
51
+ },
52
+ "devDependencies": {},
53
+ "dependencies": {}
54
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Branding API Module
3
+ * Fetch retailer branding for white-label UI
4
+ */
5
+
6
+ const { SwiftShoprError } = require('./utils/http');
7
+
8
+ /**
9
+ * Create Branding API instance
10
+ */
11
+ function createBrandingAPI(http) {
12
+ return {
13
+ /**
14
+ * Get branding for a store
15
+ *
16
+ * @param {string} storeId - Store ID
17
+ * @returns {Promise<Object>} Branding configuration
18
+ */
19
+ async get(storeId) {
20
+ if (!storeId) {
21
+ throw new SwiftShoprError('VALIDATION_ERROR', 'storeId is required');
22
+ }
23
+
24
+ const response = await http.get(
25
+ `/api/v1/sdk/onramp/branding/${encodeURIComponent(storeId)}`,
26
+ );
27
+
28
+ return {
29
+ storeId: response.store_id,
30
+ branding: {
31
+ name: response.branding?.name || null,
32
+ logoUrl: response.branding?.logo_url || null,
33
+ theme: {
34
+ primaryColor: response.branding?.theme?.primary_color || '#000000',
35
+ secondaryColor:
36
+ response.branding?.theme?.secondary_color || '#FFFFFF',
37
+ backgroundColor:
38
+ response.branding?.theme?.background_color || '#FFFFFF',
39
+ textColor: response.branding?.theme?.text_color || '#333333',
40
+ accentColor: response.branding?.theme?.accent_color || '#007AFF',
41
+ mode: response.branding?.theme?.mode || 'light',
42
+ fontFamily: response.branding?.theme?.font_family || null,
43
+ },
44
+ // CDP/OnchainKit compatible theme tokens
45
+ cdpTheme: response.branding?.cdp_theme || null,
46
+ },
47
+ };
48
+ },
49
+
50
+ /**
51
+ * Generate CSS variables from branding
52
+ *
53
+ * @param {Object} branding - Branding object from get()
54
+ * @returns {string} CSS custom properties
55
+ */
56
+ toCssVariables(branding) {
57
+ if (!branding?.branding?.theme) {
58
+ return '';
59
+ }
60
+
61
+ const { theme } = branding.branding;
62
+ const vars = [
63
+ `--swiftshopr-primary: ${theme.primaryColor};`,
64
+ `--swiftshopr-secondary: ${theme.secondaryColor};`,
65
+ `--swiftshopr-background: ${theme.backgroundColor};`,
66
+ `--swiftshopr-text: ${theme.textColor};`,
67
+ `--swiftshopr-accent: ${theme.accentColor};`,
68
+ ];
69
+
70
+ if (theme.fontFamily) {
71
+ vars.push(`--swiftshopr-font: ${theme.fontFamily};`);
72
+ }
73
+
74
+ return vars.join('\n');
75
+ },
76
+
77
+ /**
78
+ * Generate style object for React inline styles
79
+ *
80
+ * @param {Object} branding - Branding object from get()
81
+ * @returns {Object} Style object
82
+ */
83
+ toStyleObject(branding) {
84
+ if (!branding?.branding?.theme) {
85
+ return {};
86
+ }
87
+
88
+ const { theme } = branding.branding;
89
+ return {
90
+ '--swiftshopr-primary': theme.primaryColor,
91
+ '--swiftshopr-secondary': theme.secondaryColor,
92
+ '--swiftshopr-background': theme.backgroundColor,
93
+ '--swiftshopr-text': theme.textColor,
94
+ '--swiftshopr-accent': theme.accentColor,
95
+ ...(theme.fontFamily && { '--swiftshopr-font': theme.fontFamily }),
96
+ };
97
+ },
98
+
99
+ /**
100
+ * Get CDP/OnchainKit theme tokens
101
+ *
102
+ * @param {Object} branding - Branding object from get()
103
+ * @returns {Object} CDP theme tokens for OnchainKit
104
+ */
105
+ toCdpTheme(branding) {
106
+ if (branding?.branding?.cdpTheme) {
107
+ return branding.branding.cdpTheme;
108
+ }
109
+
110
+ // Generate from standard theme
111
+ const theme = branding?.branding?.theme;
112
+ if (!theme) {
113
+ return null;
114
+ }
115
+
116
+ return {
117
+ 'colors-bg-primary': theme.backgroundColor,
118
+ 'colors-bg-secondary': theme.mode === 'dark' ? '#1a1a1a' : '#F5F5F5',
119
+ 'colors-fg-default': theme.textColor,
120
+ 'colors-accent': theme.accentColor || theme.primaryColor,
121
+ 'colors-accent-foreground': '#FFFFFF',
122
+ };
123
+ },
124
+ };
125
+ }
126
+
127
+ module.exports = { createBrandingAPI };
package/src/client.js ADDED
@@ -0,0 +1,111 @@
1
+ /**
2
+ * SwiftShopr Payments Client
3
+ * Main entry point for the SDK
4
+ */
5
+
6
+ const { createHttpClient, SwiftShoprError } = require('./utils/http');
7
+ const { createPaymentsAPI } = require('./payments');
8
+ const { createRefundsAPI } = require('./refunds');
9
+ const { createDashboardAPI } = require('./dashboard');
10
+ const { createBrandingAPI } = require('./branding');
11
+ const { createConfigAPI } = require('./config');
12
+ const { createWebhooksHelper } = require('./webhooks');
13
+
14
+ /**
15
+ * SwiftShopr Payments Client
16
+ *
17
+ * @example
18
+ * const client = new SwiftShoprClient({
19
+ * apiKey: 'sk_live_...',
20
+ * webhookSecret: 'whsec_...', // optional
21
+ * });
22
+ *
23
+ * // Create payment
24
+ * const session = await client.payments.createSession({
25
+ * amount: 25.00,
26
+ * orderId: 'ORDER-123',
27
+ * storeId: 'STORE001',
28
+ * });
29
+ *
30
+ * // Request refund
31
+ * const refund = await client.refunds.create({
32
+ * intentId: session.intentId,
33
+ * reason: 'Customer request',
34
+ * });
35
+ *
36
+ * // Get dashboard metrics
37
+ * const summary = await client.dashboard.getSummary();
38
+ */
39
+ class SwiftShoprClient {
40
+ /**
41
+ * Create a new SwiftShopr client
42
+ *
43
+ * @param {Object} config
44
+ * @param {string} config.apiKey - Your SwiftShopr API key (required)
45
+ * @param {string} [config.webhookSecret] - Webhook secret for signature verification
46
+ * @param {string} [config.baseUrl] - API base URL (default: production)
47
+ * @param {number} [config.timeout=30000] - Request timeout in ms
48
+ * @param {number} [config.retries=3] - Number of retries for failed requests
49
+ * @param {Function} [config.onRequest] - Hook called before each request
50
+ * @param {Function} [config.onResponse] - Hook called after each response
51
+ * @param {Function} [config.onError] - Hook called on errors
52
+ */
53
+ constructor(config) {
54
+ if (!config || !config.apiKey) {
55
+ throw new SwiftShoprError('CONFIG_ERROR', 'API key is required');
56
+ }
57
+
58
+ this._config = {
59
+ baseUrl: config.baseUrl || 'https://shopr-scanner-backend.onrender.com',
60
+ apiKey: config.apiKey,
61
+ webhookSecret: config.webhookSecret || null,
62
+ timeout: config.timeout || 30000,
63
+ retries: config.retries ?? 3,
64
+ onRequest: config.onRequest || null,
65
+ onResponse: config.onResponse || null,
66
+ onError: config.onError || null,
67
+ };
68
+
69
+ // Create HTTP client
70
+ this._http = createHttpClient(this._config);
71
+
72
+ // Initialize API modules
73
+ this.payments = createPaymentsAPI(this._http);
74
+ this.refunds = createRefundsAPI(this._http);
75
+ this.dashboard = createDashboardAPI(this._http);
76
+ this.branding = createBrandingAPI(this._http);
77
+ this.config = createConfigAPI(this._http);
78
+
79
+ // Webhooks helper (if secret provided)
80
+ if (this._config.webhookSecret) {
81
+ this.webhooks = createWebhooksHelper(this._config.webhookSecret);
82
+ } else {
83
+ // Provide method to create webhooks helper later
84
+ this.webhooks = {
85
+ configure: (secret) => createWebhooksHelper(secret),
86
+ };
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Get SDK version
92
+ */
93
+ static get VERSION() {
94
+ return '1.0.0';
95
+ }
96
+
97
+ /**
98
+ * Get current configuration (without sensitive data)
99
+ */
100
+ getConfig() {
101
+ return {
102
+ baseUrl: this._config.baseUrl,
103
+ timeout: this._config.timeout,
104
+ retries: this._config.retries,
105
+ hasApiKey: !!this._config.apiKey,
106
+ hasWebhookSecret: !!this._config.webhookSecret,
107
+ };
108
+ }
109
+ }
110
+
111
+ module.exports = { SwiftShoprClient };