@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 +87 -0
- package/README.md +386 -0
- package/dist/drop.js +1557 -0
- package/dist/drop.js.map +1 -0
- package/dist/drop.umd.cjs +6 -0
- package/dist/drop.umd.cjs.map +1 -0
- package/dist/index.d.ts +49 -0
- package/package.json +56 -0
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
|
+
[](https://www.npmjs.com/package/@drop-africa/drop-js)
|
|
6
|
+
[](https://bundlephobia.com/package/@drop-africa/drop-js)
|
|
7
|
+
[](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
|