@funnelfox/billing 0.1.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 +548 -0
- package/dist/funnelfox-billing.cjs.d.ts +2 -0
- package/dist/funnelfox-billing.cjs.js +1513 -0
- package/dist/funnelfox-billing.d.ts +204 -0
- package/dist/funnelfox-billing.esm.d.ts +2 -0
- package/dist/funnelfox-billing.esm.js +1492 -0
- package/dist/funnelfox-billing.js +1519 -0
- package/dist/funnelfox-billing.min.js +1 -0
- package/package.json +80 -0
- package/src/types.d.ts +203 -0
- package/types.d.ts +203 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Funnelfox
|
|
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,548 @@
|
|
|
1
|
+
# @funnelfox/billing
|
|
2
|
+
|
|
3
|
+
A modern JavaScript SDK for subscription payments with Primer integration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Modern API**: Clean, Promise-based interface with event-driven architecture
|
|
8
|
+
- 🔄 **Dynamic Pricing**: Update prices without page reload
|
|
9
|
+
- 🛡️ **Type-Safe**: Complete JSDoc coverage with TypeScript definitions
|
|
10
|
+
- 🎯 **Event-Driven**: Handle success, errors, and status changes with ease
|
|
11
|
+
- 🔧 **Robust**: Built-in error handling, retries, and validation
|
|
12
|
+
- 📦 **Lightweight**: Minimal dependencies (15KB minified), browser-optimized
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### Via CDN
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<!-- Include Primer SDK first -->
|
|
20
|
+
<script src="https://sdk.primer.io/web/v2.54.0/Primer.min.js"></script>
|
|
21
|
+
<link rel="stylesheet" href="https://sdk.primer.io/web/v2.0.0/Checkout.css" />
|
|
22
|
+
|
|
23
|
+
<!-- Include Funnelfox Billing SDK -->
|
|
24
|
+
<script src="https://unpkg.com/@funnelfox/billing@latest/dist/funnelfox-billing.min.js"></script>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Via NPM
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @funnelfox/billing @primer-io/checkout-web
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
import { Billing } from '@funnelfox/billing';
|
|
37
|
+
|
|
38
|
+
await Billing.createCheckout({
|
|
39
|
+
orgId: 'your-org-id',
|
|
40
|
+
priceId: 'price_123',
|
|
41
|
+
customer: {
|
|
42
|
+
externalId: 'user_456',
|
|
43
|
+
email: 'user@example.com',
|
|
44
|
+
},
|
|
45
|
+
container: '#checkout-container',
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API Reference
|
|
50
|
+
|
|
51
|
+
### `configure(config)`
|
|
52
|
+
|
|
53
|
+
Configure global SDK settings.
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
import { configure } from '@funnelfox/billing';
|
|
57
|
+
|
|
58
|
+
configure({
|
|
59
|
+
orgId: 'your-org-id', // Required
|
|
60
|
+
baseUrl: 'https://custom.api', // Optional, defaults to https://billing.funnelfox.com
|
|
61
|
+
region: 'us-east-1', // Optional, defaults to 'default'
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Parameters:**
|
|
66
|
+
|
|
67
|
+
- `config.orgId` (string, required) - Your organization identifier
|
|
68
|
+
- `config.baseUrl` (string, optional) - Custom API URL
|
|
69
|
+
- `config.region` (string, optional) - Region, defaults to 'default'
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### `createCheckout(options)`
|
|
74
|
+
|
|
75
|
+
Creates a new checkout instance.
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
const checkout = await createCheckout({
|
|
79
|
+
// Required
|
|
80
|
+
priceId: 'price_123',
|
|
81
|
+
customer: {
|
|
82
|
+
externalId: 'user_456',
|
|
83
|
+
email: 'user@example.com',
|
|
84
|
+
countryCode: 'US', // Optional
|
|
85
|
+
},
|
|
86
|
+
container: '#checkout-container',
|
|
87
|
+
|
|
88
|
+
// Optional
|
|
89
|
+
orgId: 'your-org-id', // If not configured globally
|
|
90
|
+
clientMetadata: { source: 'web' },
|
|
91
|
+
universalCheckoutOptions: {
|
|
92
|
+
// Primer SDK options
|
|
93
|
+
paypal: {
|
|
94
|
+
buttonColor: 'blue',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// Callbacks (alternative to events)
|
|
99
|
+
onSuccess: result => {
|
|
100
|
+
/* ... */
|
|
101
|
+
},
|
|
102
|
+
onError: error => {
|
|
103
|
+
/* ... */
|
|
104
|
+
},
|
|
105
|
+
onStatusChange: (state, oldState) => {
|
|
106
|
+
/* ... */
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Parameters:**
|
|
112
|
+
|
|
113
|
+
- `options.priceId` (string, required) - Price identifier
|
|
114
|
+
- `options.customer` (object, required)
|
|
115
|
+
- `customer.externalId` (string, required) - Your user identifier
|
|
116
|
+
- `customer.email` (string, required) - Customer email
|
|
117
|
+
- `customer.countryCode` (string, optional) - ISO country code
|
|
118
|
+
- `options.container` (string, required) - CSS selector for checkout container
|
|
119
|
+
- `options.orgId` (string, optional) - Org ID (if not configured globally)
|
|
120
|
+
- `options.clientMetadata` (object, optional) - Custom metadata
|
|
121
|
+
- `options.universalCheckoutOptions` (object, optional) - Primer SDK options
|
|
122
|
+
- `options.onSuccess` (function, optional) - Success callback
|
|
123
|
+
- `options.onError` (function, optional) - Error callback
|
|
124
|
+
- `options.onStatusChange` (function, optional) - State change callback
|
|
125
|
+
|
|
126
|
+
**Returns:** `Promise<CheckoutInstance>`
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### `createClientSession(params)`
|
|
131
|
+
|
|
132
|
+
Create a client session manually (for advanced integrations).
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
import { createClientSession } from '@funnelfox/billing';
|
|
136
|
+
|
|
137
|
+
const session = await createClientSession({
|
|
138
|
+
priceId: 'price_123',
|
|
139
|
+
externalId: 'user_456',
|
|
140
|
+
email: 'user@example.com',
|
|
141
|
+
orgId: 'your-org-id', // Optional if configured
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
console.log(session.clientToken); // Use with Primer SDK
|
|
145
|
+
console.log(session.orderId);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Returns:** `Promise<{ clientToken: string, orderId: string, type: string }>`
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### `showUniversalCheckout(clientToken, options)`
|
|
153
|
+
|
|
154
|
+
Direct Primer integration (for advanced use cases).
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
import { showUniversalCheckout } from '@funnelfox/billing';
|
|
158
|
+
|
|
159
|
+
const primerCheckout = await showUniversalCheckout(clientToken, {
|
|
160
|
+
container: '#checkout',
|
|
161
|
+
onTokenizeSuccess: async (data, handler) => {
|
|
162
|
+
// Handle payment...
|
|
163
|
+
handler.handleSuccess();
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### CheckoutInstance
|
|
171
|
+
|
|
172
|
+
#### Properties
|
|
173
|
+
|
|
174
|
+
- `id` (string) - Unique checkout identifier
|
|
175
|
+
- `state` (string) - Current state: `initializing`, `ready`, `processing`, `completed`, `error`
|
|
176
|
+
- `orderId` (string) - Order identifier (available after initialization)
|
|
177
|
+
- `isDestroyed` (boolean) - Whether checkout has been destroyed
|
|
178
|
+
|
|
179
|
+
#### Events
|
|
180
|
+
|
|
181
|
+
##### `'success'`
|
|
182
|
+
|
|
183
|
+
Emitted when payment completes successfully.
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
checkout.on('success', result => {
|
|
187
|
+
console.log('Order ID:', result.orderId);
|
|
188
|
+
console.log('Status:', result.status); // 'succeeded'
|
|
189
|
+
console.log('Transaction:', result.transactionId);
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
##### `'error'`
|
|
194
|
+
|
|
195
|
+
Emitted when payment fails or encounters an error.
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
checkout.on('error', error => {
|
|
199
|
+
console.error('Error:', error.message);
|
|
200
|
+
console.error('Code:', error.code);
|
|
201
|
+
console.error('Request ID:', error.requestId); // For support
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
##### `'status-change'`
|
|
206
|
+
|
|
207
|
+
Emitted when checkout state changes.
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
checkout.on('status-change', (newState, oldState) => {
|
|
211
|
+
console.log(`${oldState} → ${newState}`);
|
|
212
|
+
// States: initializing, ready, processing, action_required, completed, error
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
##### `'destroy'`
|
|
217
|
+
|
|
218
|
+
Emitted when checkout is destroyed.
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
checkout.on('destroy', () => {
|
|
222
|
+
console.log('Checkout cleaned up');
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### Methods
|
|
227
|
+
|
|
228
|
+
##### `updatePrice(priceId)`
|
|
229
|
+
|
|
230
|
+
Updates the checkout to use a different price.
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
await checkout.updatePrice('price_yearly');
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Note:** Cannot update price while payment is processing.
|
|
237
|
+
|
|
238
|
+
##### `getStatus()`
|
|
239
|
+
|
|
240
|
+
Returns current checkout status.
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
const status = checkout.getStatus();
|
|
244
|
+
console.log(status.id); // Checkout ID
|
|
245
|
+
console.log(status.state); // Current state
|
|
246
|
+
console.log(status.orderId); // Order ID
|
|
247
|
+
console.log(status.priceId); // Current price ID
|
|
248
|
+
console.log(status.isDestroyed); // Cleanup status
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
##### `destroy()`
|
|
252
|
+
|
|
253
|
+
Destroys the checkout instance and cleans up resources.
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
await checkout.destroy();
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
##### `isReady()`
|
|
260
|
+
|
|
261
|
+
Check if checkout is ready for payment.
|
|
262
|
+
|
|
263
|
+
```javascript
|
|
264
|
+
if (checkout.isReady()) {
|
|
265
|
+
console.log('Ready to accept payment');
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
##### `isProcessing()`
|
|
270
|
+
|
|
271
|
+
Check if payment is being processed.
|
|
272
|
+
|
|
273
|
+
```javascript
|
|
274
|
+
if (checkout.isProcessing()) {
|
|
275
|
+
console.log('Payment in progress...');
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Complete Example
|
|
282
|
+
|
|
283
|
+
```html
|
|
284
|
+
<!DOCTYPE html>
|
|
285
|
+
<html>
|
|
286
|
+
<head>
|
|
287
|
+
<title>Funnelfox Checkout</title>
|
|
288
|
+
<script src="https://sdk.primer.io/web/v2.54.0/Primer.min.js"></script>
|
|
289
|
+
<link
|
|
290
|
+
rel="stylesheet"
|
|
291
|
+
href="https://sdk.primer.io/web/v2.0.0/Checkout.css"
|
|
292
|
+
/>
|
|
293
|
+
<script src="https://unpkg.com/@funnelfox/billing@latest/dist/funnelfox-billing.min.js"></script>
|
|
294
|
+
</head>
|
|
295
|
+
<body>
|
|
296
|
+
<div id="price-selector">
|
|
297
|
+
<button onclick="selectPrice('price_monthly')">Monthly - $9.99</button>
|
|
298
|
+
<button onclick="selectPrice('price_yearly')">Yearly - $99.99</button>
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
<div id="checkout-container"></div>
|
|
302
|
+
|
|
303
|
+
<script>
|
|
304
|
+
let currentCheckout = null;
|
|
305
|
+
|
|
306
|
+
// Configure SDK once
|
|
307
|
+
Billing.configure({
|
|
308
|
+
orgId: 'your-org-id',
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
async function selectPrice(priceId) {
|
|
312
|
+
try {
|
|
313
|
+
if (currentCheckout && currentCheckout.isReady()) {
|
|
314
|
+
// Update existing checkout
|
|
315
|
+
await currentCheckout.updatePrice(priceId);
|
|
316
|
+
} else {
|
|
317
|
+
// Destroy old checkout if exists
|
|
318
|
+
if (currentCheckout) {
|
|
319
|
+
await currentCheckout.destroy();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Create new checkout
|
|
323
|
+
currentCheckout = await Billing.createCheckout({
|
|
324
|
+
priceId: priceId,
|
|
325
|
+
customer: {
|
|
326
|
+
externalId: generateUserId(),
|
|
327
|
+
email: getUserEmail(),
|
|
328
|
+
},
|
|
329
|
+
container: '#checkout-container',
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Handle success
|
|
333
|
+
currentCheckout.on('success', result => {
|
|
334
|
+
alert('Payment successful!');
|
|
335
|
+
window.location.href = '/success?order=' + result.orderId;
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Handle errors
|
|
339
|
+
currentCheckout.on('error', error => {
|
|
340
|
+
alert('Payment failed: ' + error.message);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Track state changes
|
|
344
|
+
currentCheckout.on('status-change', state => {
|
|
345
|
+
console.log('Checkout state:', state);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
} catch (error) {
|
|
349
|
+
console.error('Checkout error:', error);
|
|
350
|
+
alert('Failed to initialize checkout');
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function generateUserId() {
|
|
355
|
+
return 'user_' + Math.random().toString(36).substr(2, 9);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function getUserEmail() {
|
|
359
|
+
return 'user@example.com'; // Get from your auth system
|
|
360
|
+
}
|
|
361
|
+
</script>
|
|
362
|
+
</body>
|
|
363
|
+
</html>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Error Handling
|
|
367
|
+
|
|
368
|
+
The SDK provides specific error classes for different scenarios:
|
|
369
|
+
|
|
370
|
+
```javascript
|
|
371
|
+
import {
|
|
372
|
+
ValidationError,
|
|
373
|
+
APIError,
|
|
374
|
+
PrimerError,
|
|
375
|
+
CheckoutError,
|
|
376
|
+
NetworkError,
|
|
377
|
+
} from '@funnelfox/billing';
|
|
378
|
+
|
|
379
|
+
try {
|
|
380
|
+
const checkout = await createCheckout(config);
|
|
381
|
+
} catch (error) {
|
|
382
|
+
if (error instanceof ValidationError) {
|
|
383
|
+
// Invalid input
|
|
384
|
+
console.log('Field:', error.field);
|
|
385
|
+
console.log('Value:', error.value);
|
|
386
|
+
console.log('Message:', error.message);
|
|
387
|
+
} else if (error instanceof APIError) {
|
|
388
|
+
// API error
|
|
389
|
+
console.log('Status:', error.statusCode);
|
|
390
|
+
console.log('Error Code:', error.errorCode); // e.g., 'double_purchase'
|
|
391
|
+
console.log('Error Type:', error.errorType); // e.g., 'api_exception'
|
|
392
|
+
console.log('Request ID:', error.requestId); // For support
|
|
393
|
+
console.log('Message:', error.message);
|
|
394
|
+
} else if (error instanceof PrimerError) {
|
|
395
|
+
// Primer SDK error
|
|
396
|
+
console.log('Primer error:', error.message);
|
|
397
|
+
console.log('Original:', error.primerError);
|
|
398
|
+
} else if (error instanceof CheckoutError) {
|
|
399
|
+
// Checkout lifecycle error
|
|
400
|
+
console.log('Phase:', error.phase);
|
|
401
|
+
console.log('Message:', error.message);
|
|
402
|
+
} else if (error instanceof NetworkError) {
|
|
403
|
+
// Network/connectivity error
|
|
404
|
+
console.log('Network error:', error.message);
|
|
405
|
+
console.log('Original:', error.originalError);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Common Error Codes
|
|
411
|
+
|
|
412
|
+
- `double_purchase` - User already has an active subscription
|
|
413
|
+
- `invalid_price` - Price ID not found
|
|
414
|
+
- `invalid_customer` - Customer data validation failed
|
|
415
|
+
- `payment_failed` - Payment processing failed
|
|
416
|
+
|
|
417
|
+
## TypeScript Support
|
|
418
|
+
|
|
419
|
+
The SDK includes comprehensive TypeScript definitions:
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
import {
|
|
423
|
+
configure,
|
|
424
|
+
createCheckout,
|
|
425
|
+
CheckoutInstance,
|
|
426
|
+
PaymentResult,
|
|
427
|
+
CheckoutConfig,
|
|
428
|
+
} from '@funnelfox/billing';
|
|
429
|
+
|
|
430
|
+
// Configure
|
|
431
|
+
configure({
|
|
432
|
+
orgId: 'your-org-id',
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// Create checkout with type safety
|
|
436
|
+
const checkout: CheckoutInstance = await createCheckout({
|
|
437
|
+
priceId: 'price_123',
|
|
438
|
+
customer: {
|
|
439
|
+
externalId: 'user_456',
|
|
440
|
+
email: 'user@example.com',
|
|
441
|
+
countryCode: 'US',
|
|
442
|
+
},
|
|
443
|
+
container: '#checkout',
|
|
444
|
+
clientMetadata: {
|
|
445
|
+
source: 'web',
|
|
446
|
+
campaign: 'summer-sale',
|
|
447
|
+
},
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Type-safe event handlers
|
|
451
|
+
checkout.on('success', (result: PaymentResult) => {
|
|
452
|
+
console.log('Order:', result.orderId);
|
|
453
|
+
console.log('Status:', result.status);
|
|
454
|
+
console.log('Transaction:', result.transactionId);
|
|
455
|
+
});
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Advanced Usage
|
|
459
|
+
|
|
460
|
+
### Using Callbacks Instead of Events
|
|
461
|
+
|
|
462
|
+
```javascript
|
|
463
|
+
const checkout = await createCheckout({
|
|
464
|
+
priceId: 'price_123',
|
|
465
|
+
customer: {
|
|
466
|
+
externalId: 'user_456',
|
|
467
|
+
email: 'user@example.com',
|
|
468
|
+
},
|
|
469
|
+
container: '#checkout',
|
|
470
|
+
|
|
471
|
+
// Callback style (alternative to .on() events)
|
|
472
|
+
onSuccess: result => {
|
|
473
|
+
console.log('Success!', result.orderId);
|
|
474
|
+
},
|
|
475
|
+
onError: error => {
|
|
476
|
+
console.error('Error!', error.message);
|
|
477
|
+
},
|
|
478
|
+
onStatusChange: (newState, oldState) => {
|
|
479
|
+
console.log(`${oldState} → ${newState}`);
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Custom Primer Options
|
|
485
|
+
|
|
486
|
+
```javascript
|
|
487
|
+
const checkout = await createCheckout({
|
|
488
|
+
priceId: 'price_123',
|
|
489
|
+
customer: {
|
|
490
|
+
externalId: 'user_456',
|
|
491
|
+
email: 'user@example.com',
|
|
492
|
+
},
|
|
493
|
+
container: '#checkout',
|
|
494
|
+
|
|
495
|
+
// Pass options to Primer SDK
|
|
496
|
+
universalCheckoutOptions: {
|
|
497
|
+
paypal: {
|
|
498
|
+
buttonColor: 'gold',
|
|
499
|
+
paymentFlow: 'PREFER_VAULT',
|
|
500
|
+
},
|
|
501
|
+
style: {
|
|
502
|
+
// Custom styling...
|
|
503
|
+
},
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Manual Session Creation
|
|
509
|
+
|
|
510
|
+
For advanced integrations where you want to control the Primer SDK directly:
|
|
511
|
+
|
|
512
|
+
```javascript
|
|
513
|
+
import { createClientSession, showUniversalCheckout } from '@funnelfox/billing';
|
|
514
|
+
|
|
515
|
+
// Step 1: Create session
|
|
516
|
+
const session = await createClientSession({
|
|
517
|
+
priceId: 'price_123',
|
|
518
|
+
externalId: 'user_456',
|
|
519
|
+
email: 'user@example.com',
|
|
520
|
+
orgId: 'your-org-id',
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
// Step 2: Use with Primer directly
|
|
524
|
+
const primerCheckout = await showUniversalCheckout(session.clientToken, {
|
|
525
|
+
container: '#checkout',
|
|
526
|
+
onTokenizeSuccess: async (data, handler) => {
|
|
527
|
+
// Your custom payment logic...
|
|
528
|
+
handler.handleSuccess();
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
## Browser Support
|
|
534
|
+
|
|
535
|
+
- Chrome 60+
|
|
536
|
+
- Firefox 55+
|
|
537
|
+
- Safari 12+
|
|
538
|
+
- Edge 79+
|
|
539
|
+
|
|
540
|
+
## Examples
|
|
541
|
+
|
|
542
|
+
See the [examples directory](./examples/) for more complete examples:
|
|
543
|
+
|
|
544
|
+
- [Basic Checkout](./examples/basic/) - Simple checkout integration
|
|
545
|
+
|
|
546
|
+
## License
|
|
547
|
+
|
|
548
|
+
MIT © Funnelfox
|