@drop-africa/drop-js 0.1.1

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 ADDED
@@ -0,0 +1,87 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-01-31
9
+
10
+ ### Added
11
+
12
+ - **QR Code Rendering**: Embed high-quality, scannable payment QR codes with customizable styling
13
+ - Configurable size (128-1024 pixels)
14
+ - Custom colors (hex, rgb, rgba, hsl, hsla formats)
15
+ - Adjustable corner radius (0-1 range)
16
+ - Drop Africa branding with centered logo
17
+ - **Real-time Polling**: Automatic payment status updates
18
+ - Configurable polling interval (1000-60000ms)
19
+ - Smart polling lifecycle management
20
+ - Automatic cleanup on completion or error
21
+ - **Copyable Payment Links**: Optional fallback for users who cannot scan QR codes
22
+ - Toggle visibility with `showCopyableLink`
23
+ - Customizable link text
24
+ - One-click copy functionality
25
+ - **Status Management**: Track payment lifecycle through 6 states
26
+ - `initiated`, `pending`, `succeeded`, `failed`, `cancelled`, `expired`
27
+ - `onStatusChange` callback for real-time updates
28
+ - `getStatus()` method for current state
29
+ - **Comprehensive Validation**: Input validation with clear error messages
30
+ - Client secret format validation (min 10 chars, `pi_secret_` prefix)
31
+ - QR size bounds checking (128-1024px)
32
+ - Polling interval limits (1000-60000ms)
33
+ - Color format validation (hex, rgb, rgba, hsl, hsla)
34
+ - Corner radius range checking (0-1)
35
+ - Link text length validation (max 100 chars)
36
+ - Callback type validation
37
+ - **Error Handling**: Structured error reporting with retry logic
38
+ - Error codes: `NETWORK_ERROR`, `AUTH_ERROR`, `RATE_LIMITED`, `NOT_FOUND`, `VALIDATION_ERROR`, `UNKNOWN`
39
+ - Retryable error detection
40
+ - `onError` callback for custom error handling
41
+ - **TypeScript Support**: Full type definitions for all APIs
42
+ - `DropConfig`, `DropInstance`, `DropPaymentStatus`, `DropError`, `QROptions`
43
+ - Exported `ValidationError` class
44
+ - **Browser Compatibility**: Works in all modern browsers
45
+ - Chrome/Edge 90+, Firefox 88+, Safari 14+, Opera 76+
46
+ - Lightweight bundles: ESM (21.73 KB gzipped), UMD (19.33 KB gzipped)
47
+ - **Multiple Distribution Formats**:
48
+ - ESM for modern bundlers (Vite, Webpack, Rollup)
49
+ - UMD for CDN usage and legacy browsers
50
+ - TypeScript declaration files
51
+ - **Lifecycle Management**: Clean resource cleanup
52
+ - `destroy()` method stops polling and removes DOM elements
53
+ - Automatic cleanup on terminal statuses
54
+ - **Developer Experience**:
55
+ - `onPoll` callback for debugging and analytics
56
+ - Clear validation error messages
57
+ - Comprehensive test coverage (105 tests)
58
+
59
+ ### Security
60
+
61
+ - **Client Secret Validation**: Ensures secrets start with `pi_secret_` to catch authentication errors early
62
+ - **Rate Limiting Protection**: Enforces minimum 1000ms polling interval to prevent server DOS
63
+ - **Input Sanitization**: Validates all user inputs to prevent injection attacks
64
+ - **Secure Defaults**: Conservative default values for all configuration parameters
65
+
66
+ ### Changed
67
+
68
+ N/A (initial release)
69
+
70
+ ### Deprecated
71
+
72
+ N/A (initial release)
73
+
74
+ ### Removed
75
+
76
+ N/A (initial release)
77
+
78
+ ### Fixed
79
+
80
+ N/A (initial release)
81
+
82
+ ## Links
83
+
84
+ - [GitHub Releases](https://github.com/Fabaladibbasey/drop/releases)
85
+ - [npm Package](https://www.npmjs.com/package/@drop-africa/drop-js)
86
+
87
+ [0.1.0]: https://github.com/Fabaladibbasey/drop/releases/tag/sdk-v0.1.0
package/README.md ADDED
@@ -0,0 +1,386 @@
1
+ # Drop.js SDK
2
+
3
+ Embed payment QR codes in your web app and poll for payment status changes in real-time.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@drop-africa/drop-js.svg)](https://www.npmjs.com/package/@drop-africa/drop-js)
6
+ [![npm bundle size](https://img.shields.io/bundlephobia/minzip/@drop-africa/drop-js)](https://bundlephobia.com/package/@drop-africa/drop-js)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
8
+
9
+ ## Features
10
+
11
+ - **QR Code Rendering**: High-quality, scannable QR codes with customizable styling
12
+ - **Real-time Polling**: Automatic status updates with configurable intervals
13
+ - **Copyable Links**: Optional fallback for users who can't scan QR codes
14
+ - **TypeScript Support**: Full type definitions included
15
+ - **Validation**: Comprehensive input validation with clear error messages
16
+ - **Browser Compatible**: Works in all modern browsers (Chrome, Firefox, Safari, Edge)
17
+ - **Lightweight**: ~22KB gzipped (ESM), ~19KB gzipped (UMD)
18
+
19
+ ## Installation
20
+
21
+ ### NPM
22
+
23
+ ```bash
24
+ npm install @drop-africa/drop-js
25
+ ```
26
+
27
+ ### CDN (Browser)
28
+
29
+ ```html
30
+ <script src="https://cdn.drop.africa/sdk/drop.js"></script>
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ### CDN Usage (6 lines)
36
+
37
+ ```html
38
+ <div id="payment-qr"></div>
39
+
40
+ <script src="https://cdn.drop.africa/sdk/drop.js"></script>
41
+ <script>
42
+ Drop.create({
43
+ clientSecret: 'pi_secret_your_payment_intent_secret',
44
+ instanceUrl: 'https://drop.africa/api/v1/payment-accounts/acc_123/payment-intents/pi_456',
45
+ containerId: 'payment-qr'
46
+ });
47
+ </script>
48
+ ```
49
+
50
+ ### NPM/React Usage
51
+
52
+ ```typescript
53
+ import { Drop } from '@drop-africa/drop-js';
54
+
55
+ function PaymentPage() {
56
+ useEffect(() => {
57
+ const instance = await Drop.create({
58
+ clientSecret: 'pi_secret_your_payment_intent_secret',
59
+ instanceUrl: 'https://drop.africa/api/v1/payment-accounts/acc_123/payment-intents/pi_456',
60
+ containerId: 'payment-qr',
61
+ onStatusChange: (status) => {
62
+ if (status === 'succeeded') {
63
+ console.log('Payment successful!');
64
+ }
65
+ }
66
+ });
67
+
68
+ return () => instance.destroy();
69
+ }, []);
70
+
71
+ return <div id="payment-qr"></div>;
72
+ }
73
+ ```
74
+
75
+ ## API Reference
76
+
77
+ ### `Drop.create(config: DropConfig): Promise<DropInstance>`
78
+
79
+ Creates a Drop instance that renders a QR code and polls for payment status.
80
+
81
+ #### Parameters
82
+
83
+ | Parameter | Type | Required | Default | Description |
84
+ |-----------|------|----------|---------|-------------|
85
+ | `clientSecret` | `string` | ✅ | - | Payment intent client secret (starts with `pi_secret_`) |
86
+ | `instanceUrl` | `string` | ✅ | - | Full URL to the payment intent resource |
87
+ | `containerId` | `string` | ✅ | - | DOM element ID where QR code will be rendered |
88
+ | `apiBaseUrl` | `string` | ❌ | `https://drop.africa` | Base URL for API requests |
89
+ | `qrSize` | `number` | ❌ | `256` | QR code size in pixels (128-1024) |
90
+ | `pollingInterval` | `number` | ❌ | `3000` | Polling interval in milliseconds (1000-60000) |
91
+ | `showCopyableLink` | `boolean` | ❌ | `true` | Show copyable payment link below QR code |
92
+ | `linkText` | `string` | ❌ | `"Or paste this link:"` | Text above copyable link (max 100 chars) |
93
+ | `qrOptions.moduleColor` | `string` | ❌ | `hsl(160, 65%, 25%)` | QR code foreground color |
94
+ | `qrOptions.backgroundColor` | `string` | ❌ | `hsl(0, 0%, 98%)` | QR code background color |
95
+ | `qrOptions.cornerRadius` | `number` | ❌ | `0` | Corner radius for QR modules (0-1) |
96
+ | `onStatusChange` | `(status) => void` | ❌ | - | Callback when payment status changes |
97
+ | `onError` | `(error) => void` | ❌ | - | Callback when errors occur |
98
+ | `onPoll` | `() => void` | ❌ | - | Callback on each polling attempt |
99
+
100
+ #### Returns
101
+
102
+ `Promise<DropInstance>` - Instance with `destroy()` and `getStatus()` methods
103
+
104
+ #### Example
105
+
106
+ ```javascript
107
+ const instance = await Drop.create({
108
+ clientSecret: 'pi_secret_1234567890abcdef',
109
+ instanceUrl: 'https://drop.africa/api/v1/payment-accounts/acc_123/payment-intents/pi_456',
110
+ containerId: 'payment-qr',
111
+ qrSize: 512,
112
+ pollingInterval: 5000,
113
+ qrOptions: {
114
+ cornerRadius: 0.5,
115
+ moduleColor: '#00796B',
116
+ backgroundColor: '#F5F5F5'
117
+ },
118
+ linkText: 'Scan or paste this link',
119
+ onStatusChange: (status) => {
120
+ console.log('Payment status:', status);
121
+ },
122
+ onError: (error) => {
123
+ console.error('Error:', error.message);
124
+ }
125
+ });
126
+ ```
127
+
128
+ ### `DropInstance`
129
+
130
+ #### Methods
131
+
132
+ ##### `destroy(): void`
133
+
134
+ Stops polling and removes the QR code from the DOM.
135
+
136
+ ```javascript
137
+ instance.destroy();
138
+ ```
139
+
140
+ ##### `getStatus(): DropPaymentStatus`
141
+
142
+ Returns the current payment status.
143
+
144
+ ```javascript
145
+ const status = instance.getStatus();
146
+ // Returns: "initiated" | "pending" | "succeeded" | "failed" | "cancelled" | "expired"
147
+ ```
148
+
149
+ ## Configuration
150
+
151
+ ### Payment Statuses
152
+
153
+ | Status | Description |
154
+ |--------|-------------|
155
+ | `initiated` | Payment intent created, waiting for customer action |
156
+ | `pending` | Customer scanned QR code, payment processing |
157
+ | `succeeded` | Payment completed successfully |
158
+ | `failed` | Payment failed (insufficient funds, network error, etc.) |
159
+ | `cancelled` | Payment cancelled by customer or merchant |
160
+ | `expired` | Payment intent expired (typically after 15 minutes) |
161
+
162
+ ### Color Formats
163
+
164
+ Supported color formats for `moduleColor` and `backgroundColor`:
165
+
166
+ - **Hex**: `#000`, `#000000`, `#AbC123`
167
+ - **RGB**: `rgb(0, 0, 0)`, `rgb(255, 128, 64)`
168
+ - **RGBA**: `rgba(0, 0, 0, 1)`, `rgba(255, 128, 64, 0.5)`
169
+ - **HSL**: `hsl(0, 0%, 0%)`, `hsl(240, 100%, 50%)`
170
+ - **HSLA**: `hsla(0, 0%, 0%, 1)`, `hsla(240, 100%, 50%, 0.5)`
171
+
172
+ ### Validation Constraints
173
+
174
+ The SDK validates all configuration parameters and throws `ValidationError` for invalid inputs:
175
+
176
+ | Parameter | Constraint | Rationale |
177
+ |-----------|-----------|-----------|
178
+ | `clientSecret` | Min 10 chars, starts with `pi_secret_` | Catches auth errors early |
179
+ | `qrSize` | 128-1024 pixels | Below 128 = hard to scan; above 1024 = memory issues |
180
+ | `pollingInterval` | 1000-60000 ms | Prevents server DOS; maintains real-time UX |
181
+ | `cornerRadius` | 0-1 | QR libraries use 0-1 range |
182
+ | `moduleColor` | Valid color format | Must be hex, rgb, rgba, hsl, or hsla |
183
+ | `backgroundColor` | Valid color format | Must be hex, rgb, rgba, hsl, or hsla |
184
+ | `linkText` | Max 100 characters | Prevents UI layout breaking |
185
+
186
+ ## Error Handling
187
+
188
+ ### Validation Errors
189
+
190
+ ```javascript
191
+ import { Drop, ValidationError } from '@drop-africa/drop-js';
192
+
193
+ try {
194
+ await Drop.create({
195
+ clientSecret: 'invalid',
196
+ instanceUrl: '...',
197
+ containerId: 'qr'
198
+ });
199
+ } catch (error) {
200
+ if (error instanceof ValidationError) {
201
+ console.error('Configuration error:', error.message);
202
+ // Output: "clientSecret must be at least 10 characters long. Received length: 7"
203
+ }
204
+ }
205
+ ```
206
+
207
+ ### Runtime Errors
208
+
209
+ ```javascript
210
+ Drop.create({
211
+ clientSecret: 'pi_secret_1234567890',
212
+ instanceUrl: '...',
213
+ containerId: 'payment-qr',
214
+ onError: (error) => {
215
+ switch (error.code) {
216
+ case 'NETWORK_ERROR':
217
+ console.error('Network issue:', error.message);
218
+ if (error.retryable) {
219
+ // Polling will automatically retry
220
+ }
221
+ break;
222
+ case 'AUTH_ERROR':
223
+ console.error('Invalid client secret');
224
+ break;
225
+ case 'RATE_LIMITED':
226
+ console.error('Too many requests');
227
+ break;
228
+ case 'NOT_FOUND':
229
+ console.error('Payment intent not found');
230
+ break;
231
+ }
232
+ }
233
+ });
234
+ ```
235
+
236
+ ### Error Codes
237
+
238
+ | Code | Description | Retryable |
239
+ |------|-------------|-----------|
240
+ | `NETWORK_ERROR` | Network connectivity issue | ✅ |
241
+ | `AUTH_ERROR` | Invalid client secret | ❌ |
242
+ | `RATE_LIMITED` | Too many polling requests | ✅ |
243
+ | `NOT_FOUND` | Payment intent not found | ❌ |
244
+ | `VALIDATION_ERROR` | Invalid configuration | ❌ |
245
+ | `UNKNOWN` | Unexpected error | ❌ |
246
+
247
+ ## Browser Support
248
+
249
+ Drop.js works in all modern browsers:
250
+
251
+ - Chrome/Edge 90+
252
+ - Firefox 88+
253
+ - Safari 14+
254
+ - Opera 76+
255
+
256
+ **Not supported**: Internet Explorer (use a polyfill for Promise and fetch if needed)
257
+
258
+ ## TypeScript
259
+
260
+ Full TypeScript definitions are included. Import types directly:
261
+
262
+ ```typescript
263
+ import { Drop, DropConfig, DropInstance, DropPaymentStatus, DropError } from '@drop-africa/drop-js';
264
+
265
+ const config: DropConfig = {
266
+ clientSecret: 'pi_secret_1234567890',
267
+ instanceUrl: 'https://drop.africa/api/v1/payment-accounts/acc_123/payment-intents/pi_456',
268
+ containerId: 'payment-qr'
269
+ };
270
+
271
+ const instance: DropInstance = await Drop.create(config);
272
+ const status: DropPaymentStatus = instance.getStatus();
273
+ ```
274
+
275
+ ## Examples
276
+
277
+ ### React Component
278
+
279
+ ```typescript
280
+ import { Drop, DropPaymentStatus } from '@drop-africa/drop-js';
281
+ import { useEffect, useState } from 'react';
282
+
283
+ export function Payment({ clientSecret, instanceUrl }) {
284
+ const [status, setStatus] = useState<DropPaymentStatus>('initiated');
285
+
286
+ useEffect(() => {
287
+ const instance = await Drop.create({
288
+ clientSecret,
289
+ instanceUrl,
290
+ containerId: 'payment-qr',
291
+ onStatusChange: setStatus
292
+ });
293
+
294
+ return () => instance.destroy();
295
+ }, [clientSecret, instanceUrl]);
296
+
297
+ return (
298
+ <div>
299
+ {status === 'succeeded' ? (
300
+ <p>Payment successful!</p>
301
+ ) : (
302
+ <div id="payment-qr"></div>
303
+ )}
304
+ </div>
305
+ );
306
+ }
307
+ ```
308
+
309
+ ### Vue Component
310
+
311
+ ```vue
312
+ <template>
313
+ <div>
314
+ <div v-if="status === 'succeeded'">Payment successful!</div>
315
+ <div v-else id="payment-qr"></div>
316
+ </div>
317
+ </template>
318
+
319
+ <script setup>
320
+ import { Drop } from '@drop-africa/drop-js';
321
+ import { ref, onMounted, onUnmounted } from 'vue';
322
+
323
+ const props = defineProps(['clientSecret', 'instanceUrl']);
324
+ const status = ref('initiated');
325
+ let instance;
326
+
327
+ onMounted(async () => {
328
+ instance = await Drop.create({
329
+ clientSecret: props.clientSecret,
330
+ instanceUrl: props.instanceUrl,
331
+ containerId: 'payment-qr',
332
+ onStatusChange: (newStatus) => {
333
+ status.value = newStatus;
334
+ }
335
+ });
336
+ });
337
+
338
+ onUnmounted(() => {
339
+ instance?.destroy();
340
+ });
341
+ </script>
342
+ ```
343
+
344
+ ### Vanilla JavaScript
345
+
346
+ ```html
347
+ <!DOCTYPE html>
348
+ <html>
349
+ <head>
350
+ <title>Drop.js Payment</title>
351
+ </head>
352
+ <body>
353
+ <div id="payment-qr"></div>
354
+ <div id="status"></div>
355
+
356
+ <script src="https://cdn.drop.africa/sdk/drop.js"></script>
357
+ <script>
358
+ Drop.create({
359
+ clientSecret: 'pi_secret_1234567890abcdef',
360
+ instanceUrl: 'https://drop.africa/api/v1/payment-accounts/acc_123/payment-intents/pi_456',
361
+ containerId: 'payment-qr',
362
+ onStatusChange: (status) => {
363
+ document.getElementById('status').textContent = `Status: ${status}`;
364
+ if (status === 'succeeded') {
365
+ alert('Payment successful!');
366
+ }
367
+ },
368
+ onError: (error) => {
369
+ console.error('Payment error:', error);
370
+ }
371
+ });
372
+ </script>
373
+ </body>
374
+ </html>
375
+ ```
376
+
377
+ ## Links
378
+
379
+ - [Documentation](https://github.com/Fabaladibbasey/drop/tree/main/sdk/drop-js)
380
+ - [GitHub Repository](https://github.com/Fabaladibbasey/drop)
381
+ - [npm Package](https://www.npmjs.com/package/@drop-africa/drop-js)
382
+ - [Issues](https://github.com/Fabaladibbasey/drop/issues)
383
+
384
+ ## License
385
+
386
+ MIT