@anpayeras/agnostic-checkout 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/README.md +439 -0
- package/dist/index.cjs +1622 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +321 -0
- package/dist/index.d.ts +321 -0
- package/dist/index.js +1564 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
# ⚡ Agnostic Checkout SDK
|
|
2
|
+
|
|
3
|
+
A provider-agnostic, embeddable React checkout component with dynamic theming, plugin architecture, and full TypeScript support.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🛒 **Embeddable checkout component** — drop into any React app
|
|
10
|
+
- 🎨 **Theme system** — 5 built-in presets (`light`, `dark`, `minimal`, `corporate`, `neon`)
|
|
11
|
+
- 🎯 **Brand Auto Theme** — generate a full theme from a single brand color
|
|
12
|
+
- 🔌 **Plugin architecture** — extend checkout behavior without modifying core code
|
|
13
|
+
- 🏷️ **Discount Plugin** — coupon input, apply/remove with event-driven updates
|
|
14
|
+
- ✏️ **Cart Edit Plugin** — quantity stepper, item removal with configurable limits
|
|
15
|
+
- 🌍 **i18n** — built-in locales (`en`, `es`, `pt-BR`, `auto`)
|
|
16
|
+
- 💱 **Multi-currency** — 25+ currencies with automatic formatting
|
|
17
|
+
- 📱 **Responsive** — mobile-first layout with collapsible order summary
|
|
18
|
+
- 🔒 **Provider-agnostic** — works with any payment backend
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install agnostic-checkout
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { Checkout } from "agnostic-checkout";
|
|
34
|
+
|
|
35
|
+
function App() {
|
|
36
|
+
const items = [
|
|
37
|
+
{
|
|
38
|
+
id: "item_1",
|
|
39
|
+
name: "Pro Plan",
|
|
40
|
+
unitPrice: 49.00,
|
|
41
|
+
quantity: 1,
|
|
42
|
+
total: 49.00,
|
|
43
|
+
image: "https://example.com/pro-plan.png",
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const totals = {
|
|
48
|
+
subtotal: 49.00,
|
|
49
|
+
taxes: 4.90,
|
|
50
|
+
total: 53.90,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const paymentMethods = [
|
|
54
|
+
{
|
|
55
|
+
id: "pm_card",
|
|
56
|
+
label: "Credit Card",
|
|
57
|
+
type: "card",
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Checkout
|
|
63
|
+
items={items}
|
|
64
|
+
totals={totals}
|
|
65
|
+
paymentMethods={paymentMethods}
|
|
66
|
+
provider={yourPaymentProvider}
|
|
67
|
+
currency="USD"
|
|
68
|
+
locale="en"
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
| Prop | Description |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `items` | Array of cart items to display |
|
|
77
|
+
| `totals` | Object with subtotal, taxes, shipping, discount, and total |
|
|
78
|
+
| `paymentMethods` | Available payment methods |
|
|
79
|
+
| `provider` | Your `PaymentProvider` implementation |
|
|
80
|
+
| `currency` | Currency code for formatting (`"USD"`, `"EUR"`, etc.) |
|
|
81
|
+
| `locale` | Language for UI text (`"en"`, `"es"`, `"pt-BR"`, `"auto"`) |
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Theming
|
|
86
|
+
|
|
87
|
+
The checkout uses CSS variables internally and supports three theming strategies with the following priority chain:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
customTheme (overrides)
|
|
91
|
+
↓
|
|
92
|
+
brandColor (auto-generated theme)
|
|
93
|
+
↓
|
|
94
|
+
theme preset ("light" | "dark" | ...)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Preset Themes
|
|
98
|
+
|
|
99
|
+
Choose from 5 built-in themes:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
<Checkout theme="light" />
|
|
103
|
+
<Checkout theme="dark" />
|
|
104
|
+
<Checkout theme="minimal" />
|
|
105
|
+
<Checkout theme="corporate" />
|
|
106
|
+
<Checkout theme="neon" />
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Brand Auto Theme
|
|
110
|
+
|
|
111
|
+
Generate a complete theme from a single brand color. The SDK automatically derives background, surface, text, border, and accent colors:
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
<Checkout brandColor="#6366F1" />
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Custom Theme Overrides
|
|
118
|
+
|
|
119
|
+
Override specific tokens on top of any base theme:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
<Checkout
|
|
123
|
+
theme="light"
|
|
124
|
+
customTheme={{
|
|
125
|
+
colors: {
|
|
126
|
+
primary: "#ff0055",
|
|
127
|
+
success: "#00cc88",
|
|
128
|
+
},
|
|
129
|
+
radius: "1rem",
|
|
130
|
+
}}
|
|
131
|
+
/>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Combine with `brandColor`:
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
<Checkout
|
|
138
|
+
brandColor="#6366F1"
|
|
139
|
+
customTheme={{
|
|
140
|
+
radius: "20px",
|
|
141
|
+
fontFamily: "'Poppins', sans-serif",
|
|
142
|
+
}}
|
|
143
|
+
/>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Theme Tokens
|
|
147
|
+
|
|
148
|
+
| Token | CSS Variable | Description |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `colors.primary` | `--color-primary` | Buttons, links, active states |
|
|
151
|
+
| `colors.background` | `--color-background` | Page background |
|
|
152
|
+
| `colors.surface` | `--color-surface` | Card/panel backgrounds |
|
|
153
|
+
| `colors.text` | `--color-text` | Primary text color |
|
|
154
|
+
| `colors.border` | `--color-border` | Borders and dividers |
|
|
155
|
+
| `colors.success` | `--color-success` | Success states, discounts |
|
|
156
|
+
| `colors.error` | `--color-error` | Error states, remove actions |
|
|
157
|
+
| `radius` | `--theme-radius` | Border radius for cards/buttons |
|
|
158
|
+
| `fontFamily` | `--theme-font` | Font family stack |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Plugin System
|
|
163
|
+
|
|
164
|
+
Plugins extend checkout functionality through a **slot-based architecture**. Plugins inject UI into predefined areas and communicate via events — they never modify state directly.
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import { createDiscountPlugin, createCartEditPlugin } from "agnostic-checkout/plugins";
|
|
168
|
+
|
|
169
|
+
<Checkout
|
|
170
|
+
plugins={[
|
|
171
|
+
createDiscountPlugin(handleEvent),
|
|
172
|
+
createCartEditPlugin(handleEvent, { allowRemove: true }),
|
|
173
|
+
]}
|
|
174
|
+
onEvent={handleEvent}
|
|
175
|
+
/>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Plugins emit events. Your `onEvent` handler processes them and updates `items`/`totals` externally:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
const handleEvent = (event) => {
|
|
182
|
+
switch (event.type) {
|
|
183
|
+
case "COUPON_APPLY_REQUESTED":
|
|
184
|
+
// Validate event.code, update items/totals
|
|
185
|
+
break;
|
|
186
|
+
case "ITEM_REMOVE_REQUESTED":
|
|
187
|
+
// Remove item by event.itemId
|
|
188
|
+
break;
|
|
189
|
+
case "ITEM_QUANTITY_UPDATE_REQUESTED":
|
|
190
|
+
// Update quantity for event.itemId to event.quantity
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Discount Plugin
|
|
199
|
+
|
|
200
|
+
Adds a coupon input field to the order summary. Users can type a discount code, apply it, and remove it via a trash icon.
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
import { createDiscountPlugin } from "agnostic-checkout/plugins";
|
|
204
|
+
|
|
205
|
+
const plugins = [
|
|
206
|
+
createDiscountPlugin(handleEvent),
|
|
207
|
+
];
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Events emitted:**
|
|
211
|
+
|
|
212
|
+
| Event | Payload | Description |
|
|
213
|
+
|---|---|---|
|
|
214
|
+
| `COUPON_APPLY_REQUESTED` | `{ code: string }` | User submitted a coupon code |
|
|
215
|
+
| `COUPON_REMOVE_REQUESTED` | — | User clicked the remove button |
|
|
216
|
+
|
|
217
|
+
The plugin does **not** calculate discounts. Your `onEvent` handler validates the code and updates `items`/`totals` with the new discount values.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Cart Edit Plugin
|
|
222
|
+
|
|
223
|
+
Allows users to modify item quantities and/or remove items from the cart.
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
import { createCartEditPlugin } from "agnostic-checkout/plugins";
|
|
227
|
+
|
|
228
|
+
const plugins = [
|
|
229
|
+
createCartEditPlugin(handleEvent, {
|
|
230
|
+
allowRemove: true,
|
|
231
|
+
allowQuantityEdit: true,
|
|
232
|
+
minQuantity: 1,
|
|
233
|
+
maxQuantity: 10,
|
|
234
|
+
}),
|
|
235
|
+
];
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Options
|
|
239
|
+
|
|
240
|
+
| Option | Type | Default | Description |
|
|
241
|
+
|---|---|---|---|
|
|
242
|
+
| `allowRemove` | `boolean` | `false` | Show remove button per item |
|
|
243
|
+
| `allowQuantityEdit` | `boolean` | `false` | Show quantity stepper per item |
|
|
244
|
+
| `minQuantity` | `number` | `1` | Minimum allowed quantity |
|
|
245
|
+
| `maxQuantity` | `number` | `99` | Maximum allowed quantity |
|
|
246
|
+
|
|
247
|
+
**Events emitted:**
|
|
248
|
+
|
|
249
|
+
| Event | Payload | Description |
|
|
250
|
+
|---|---|---|
|
|
251
|
+
| `ITEM_QUANTITY_UPDATE_REQUESTED` | `{ itemId, quantity }` | User changed item quantity |
|
|
252
|
+
| `ITEM_REMOVE_REQUESTED` | `{ itemId }` | User clicked remove |
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Payment Provider
|
|
257
|
+
|
|
258
|
+
The checkout is payment-agnostic. Implement the `PaymentProvider` interface to connect any backend:
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
interface PaymentProvider {
|
|
262
|
+
initialize?(config: unknown): Promise<void>;
|
|
263
|
+
createPayment(data: unknown): Promise<PaymentResult>;
|
|
264
|
+
confirmPayment?(data: unknown): Promise<PaymentResult>;
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
class StripeProvider implements PaymentProvider {
|
|
270
|
+
async createPayment(data) {
|
|
271
|
+
const response = await fetch("/api/payments", {
|
|
272
|
+
method: "POST",
|
|
273
|
+
body: JSON.stringify(data),
|
|
274
|
+
});
|
|
275
|
+
return { status: "success" };
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
<Checkout provider={new StripeProvider()} />
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## API Reference
|
|
285
|
+
|
|
286
|
+
### `<Checkout />` Props
|
|
287
|
+
|
|
288
|
+
| Prop | Type | Required | Default | Description |
|
|
289
|
+
|---|---|---|---|---|
|
|
290
|
+
| `items` | `CheckoutItem[]` | ✅ | — | Cart items |
|
|
291
|
+
| `totals` | `CheckoutTotals` | ✅ | — | Order totals |
|
|
292
|
+
| `paymentMethods` | `PaymentMethod[]` | ✅ | — | Available payment methods |
|
|
293
|
+
| `provider` | `PaymentProvider` | ✅ | — | Payment provider implementation |
|
|
294
|
+
| `currency` | `SupportedCurrency` | ✅ | — | Currency code |
|
|
295
|
+
| `locale` | `CheckoutLocale` | — | `"es"` | UI language |
|
|
296
|
+
| `theme` | `CheckoutThemeName \| Partial<CheckoutTheme>` | — | `"light"` | Theme preset or partial theme object |
|
|
297
|
+
| `brandColor` | `string` | — | — | Auto-generate theme from hex color |
|
|
298
|
+
| `customTheme` | `Partial<CheckoutTheme>` | — | — | Override specific theme tokens |
|
|
299
|
+
| `onEvent` | `EventHandler` | — | — | Callback for checkout events |
|
|
300
|
+
| `plugins` | `CheckoutPlugin[]` | — | `[]` | Plugins to extend functionality |
|
|
301
|
+
| `messages` | `Partial<CheckoutMessages>` | — | — | Override default UI text |
|
|
302
|
+
| `formatters` | `CheckoutFormatters` | — | — | Custom currency formatter |
|
|
303
|
+
| `devTools` | `boolean` | — | `false` | Show dev tools panel |
|
|
304
|
+
| `initialState` | `PaymentResult` | — | — | Start in a specific state |
|
|
305
|
+
|
|
306
|
+
### `CheckoutItem`
|
|
307
|
+
|
|
308
|
+
```tsx
|
|
309
|
+
interface CheckoutItem {
|
|
310
|
+
id: string;
|
|
311
|
+
name: string;
|
|
312
|
+
quantity: number;
|
|
313
|
+
unitPrice: number;
|
|
314
|
+
total: number;
|
|
315
|
+
image?: string;
|
|
316
|
+
discount?: {
|
|
317
|
+
type: "percentage" | "fixed";
|
|
318
|
+
value: number;
|
|
319
|
+
label?: string;
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### `CheckoutTotals`
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
interface CheckoutTotals {
|
|
328
|
+
subtotal: number;
|
|
329
|
+
discount?: number;
|
|
330
|
+
taxes?: number;
|
|
331
|
+
shipping?: number;
|
|
332
|
+
total: number;
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Events
|
|
337
|
+
|
|
338
|
+
| Event | Description |
|
|
339
|
+
|---|---|
|
|
340
|
+
| `CHECKOUT_VIEWED` | Checkout component mounted |
|
|
341
|
+
| `STEP_CHANGED` | User navigated to a new step |
|
|
342
|
+
| `PAYMENT_METHOD_SELECTED` | User selected a payment method |
|
|
343
|
+
| `PAYMENT_SUBMITTED` | Payment form submitted |
|
|
344
|
+
| `PAYMENT_SUCCESS` | Payment completed successfully |
|
|
345
|
+
| `PAYMENT_ERROR` | Payment failed |
|
|
346
|
+
| `COUPON_APPLY_REQUESTED` | Coupon code submitted (Discount Plugin) |
|
|
347
|
+
| `COUPON_REMOVE_REQUESTED` | Coupon removed (Discount Plugin) |
|
|
348
|
+
| `ITEM_QUANTITY_UPDATE_REQUESTED` | Quantity changed (Cart Edit Plugin) |
|
|
349
|
+
| `ITEM_REMOVE_REQUESTED` | Item removed (Cart Edit Plugin) |
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Internationalization
|
|
354
|
+
|
|
355
|
+
Built-in support for three locales plus automatic detection:
|
|
356
|
+
|
|
357
|
+
```tsx
|
|
358
|
+
<Checkout locale="en" /> // English
|
|
359
|
+
<Checkout locale="es" /> // Spanish
|
|
360
|
+
<Checkout locale="pt-BR" /> // Portuguese (Brazil)
|
|
361
|
+
<Checkout locale="auto" /> // Auto-detect from browser
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Override specific messages:
|
|
365
|
+
|
|
366
|
+
```tsx
|
|
367
|
+
<Checkout
|
|
368
|
+
messages={{
|
|
369
|
+
payNow: "Complete Purchase",
|
|
370
|
+
continueToPay: "Proceed to Payment",
|
|
371
|
+
}}
|
|
372
|
+
/>
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Custom currency formatter:
|
|
376
|
+
|
|
377
|
+
```tsx
|
|
378
|
+
<Checkout
|
|
379
|
+
formatters={{
|
|
380
|
+
currency: (amount) => `US$ ${amount.toFixed(2)}`,
|
|
381
|
+
}}
|
|
382
|
+
/>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Demo
|
|
388
|
+
|
|
389
|
+
A built-in demo page is available with interactive controls:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
npm run dev
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
The demo includes a **Settings toolbar** to test:
|
|
396
|
+
|
|
397
|
+
- Theme switching (5 presets + Brand Auto Theme)
|
|
398
|
+
- Locale and currency changes
|
|
399
|
+
- Item count adjustments
|
|
400
|
+
- Plugin enable/disable toggles
|
|
401
|
+
- Brand color picker with 12 presets
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Development
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
# Install dependencies
|
|
409
|
+
npm install
|
|
410
|
+
|
|
411
|
+
# Start development server
|
|
412
|
+
npm run dev
|
|
413
|
+
|
|
414
|
+
# Type checking
|
|
415
|
+
npx tsc --noEmit
|
|
416
|
+
|
|
417
|
+
# Build
|
|
418
|
+
npm run build
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Project Structure
|
|
422
|
+
|
|
423
|
+
```
|
|
424
|
+
src/
|
|
425
|
+
├── core/ # State machine, context, events, types
|
|
426
|
+
├── theme/ # Theme system, tokens, color utilities
|
|
427
|
+
├── locales/ # i18n messages (en, es, pt-BR)
|
|
428
|
+
├── plugins/ # Plugin system, discount & cart-edit plugins
|
|
429
|
+
├── ui/ # React components (Checkout, Layout, Summary, etc.)
|
|
430
|
+
├── hooks/ # useCheckout hook
|
|
431
|
+
├── lib/ # Utilities (cn, formatNumber)
|
|
432
|
+
└── mock/ # Demo app, mock provider, toolbar
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## License
|
|
438
|
+
|
|
439
|
+
MIT
|