@tagadapay/plugin-sdk 3.1.2 → 3.1.8
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 +1129 -1129
- package/build-cdn.js +113 -113
- package/dist/external-tracker.js +1104 -491
- package/dist/external-tracker.min.js +2 -2
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/hooks/useApplePay.js +25 -36
- package/dist/react/hooks/usePaymentPolling.d.ts +9 -3
- package/dist/react/providers/TagadaProvider.js +5 -5
- package/dist/react/utils/money.d.ts +4 -3
- package/dist/react/utils/money.js +39 -6
- package/dist/react/utils/trackingUtils.js +1 -0
- package/dist/v2/core/client.js +34 -2
- package/dist/v2/core/config/environment.js +9 -2
- package/dist/v2/core/funnelClient.d.ts +92 -1
- package/dist/v2/core/funnelClient.js +247 -3
- package/dist/v2/core/resources/apiClient.js +1 -1
- package/dist/v2/core/resources/checkout.d.ts +68 -0
- package/dist/v2/core/resources/funnel.d.ts +15 -0
- package/dist/v2/core/resources/payments.d.ts +50 -3
- package/dist/v2/core/resources/payments.js +38 -7
- package/dist/v2/core/utils/pluginConfig.js +40 -5
- package/dist/v2/core/utils/previewMode.d.ts +3 -0
- package/dist/v2/core/utils/previewMode.js +44 -14
- package/dist/v2/core/utils/previewModeIndicator.d.ts +19 -0
- package/dist/v2/core/utils/previewModeIndicator.js +414 -0
- package/dist/v2/core/utils/tokenStorage.d.ts +4 -0
- package/dist/v2/core/utils/tokenStorage.js +15 -1
- package/dist/v2/index.d.ts +6 -1
- package/dist/v2/index.js +6 -1
- package/dist/v2/react/components/ApplePayButton.d.ts +21 -121
- package/dist/v2/react/components/ApplePayButton.js +221 -290
- package/dist/v2/react/components/FunnelScriptInjector.d.ts +3 -1
- package/dist/v2/react/components/FunnelScriptInjector.js +128 -24
- package/dist/v2/react/components/PreviewModeIndicator.d.ts +46 -0
- package/dist/v2/react/components/PreviewModeIndicator.js +113 -0
- package/dist/v2/react/hooks/useApplePayCheckout.d.ts +16 -0
- package/dist/v2/react/hooks/useApplePayCheckout.js +193 -0
- package/dist/v2/react/hooks/useFunnel.d.ts +42 -6
- package/dist/v2/react/hooks/useFunnel.js +25 -5
- package/dist/v2/react/hooks/usePaymentPolling.d.ts +9 -3
- package/dist/v2/react/hooks/usePaymentPolling.js +31 -9
- package/dist/v2/react/hooks/usePaymentQuery.d.ts +32 -2
- package/dist/v2/react/hooks/usePaymentQuery.js +304 -7
- package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +26 -0
- package/dist/v2/react/hooks/usePaymentRetrieve.js +175 -0
- package/dist/v2/react/hooks/useStepConfig.d.ts +62 -0
- package/dist/v2/react/hooks/useStepConfig.js +52 -0
- package/dist/v2/react/index.d.ts +9 -3
- package/dist/v2/react/index.js +5 -1
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +27 -19
- package/dist/v2/react/providers/TagadaProvider.js +7 -7
- package/dist/v2/standalone/external-tracker.d.ts +2 -0
- package/dist/v2/standalone/external-tracker.js +6 -3
- package/package.json +112 -112
- package/dist/v2/react/hooks/useApplePay.d.ts +0 -16
- package/dist/v2/react/hooks/useApplePay.js +0 -247
package/README.md
CHANGED
|
@@ -1,1129 +1,1129 @@
|
|
|
1
|
-
# TagadaPay Plugin SDK
|
|
2
|
-
|
|
3
|
-
A comprehensive React SDK for building plugins on the TagadaPay platform. Create custom checkout experiences, landing pages, and interactive components with automatic configuration injection and advanced routing capabilities.
|
|
4
|
-
|
|
5
|
-
> **🚀 V2 Now Available!** The new V2 architecture features TanStack Query integration, improved TypeScript support, and better performance. [See V2 Architecture](#-v2-architecture) for details.
|
|
6
|
-
>
|
|
7
|
-
> **Recommended**: Use `@tagadapay/plugin-sdk/v3` for new projects. V1 (`/react`) is still supported for existing projects.
|
|
8
|
-
|
|
9
|
-
## 📚 Documentation
|
|
10
|
-
|
|
11
|
-
### V2 Core APIs (Recommended)
|
|
12
|
-
|
|
13
|
-
- **[V2 Architecture](#-v2-architecture)** - New TanStack Query-based architecture
|
|
14
|
-
- **[V2 Examples](#-v2-examples)** - Complete examples using the new V2 hooks
|
|
15
|
-
- **[useCheckout (V2)](#usecheckout)** - TanStack Query-based checkout management
|
|
16
|
-
- **[useOffers (V2)](#useoffers)** - Dynamic pricing with automatic caching
|
|
17
|
-
- **[useProducts (V2)](#useproducts)** - Product data management
|
|
18
|
-
- **[useStoreConfig (V2)](#usestoreconfig)** - Store configuration with automatic caching
|
|
19
|
-
- **[usePayment (V2)](#usepayment)** - Payment processing with 3DS support
|
|
20
|
-
- **[V2 Utility Functions](#v2-utility-functions--advanced-features)** - Enhanced utilities and TanStack Query integration
|
|
21
|
-
|
|
22
|
-
### Legacy V1 APIs (Still Supported)
|
|
23
|
-
|
|
24
|
-
- **[useCheckout (V1)](./docs/README-useCheckout.md)** - Legacy checkout state management
|
|
25
|
-
- **[setCheckoutInfo](./docs/README-setCheckoutInfo.md)** - Customer information and validation
|
|
26
|
-
- **[useOffers (V1)](./docs/README-useOffers.md)** - Legacy dynamic pricing
|
|
27
|
-
- **[usePromotionCodes](./docs/README-usePromotionCodes.md)** - Legacy promotion code management
|
|
28
|
-
- **[Money utilities](./docs/README-money.md)** - Currency formatting and calculations
|
|
29
|
-
- **[URL utilities](./docs/README-urlUtils.md)** - Navigation and routing helpers
|
|
30
|
-
|
|
31
|
-
### Plugin Development
|
|
32
|
-
|
|
33
|
-
- **[Plugin Configuration](./docs/PLUGIN_CONFIG.md)** - How to access store context, config, and branding
|
|
34
|
-
- **[Initialization Modes](#-initialization-modes)** - Choose between blocking and non-blocking initialization
|
|
35
|
-
- **[Google Autocomplete](./docs/README-google-autocomplete.md)** - Address autocomplete with Google Places API
|
|
36
|
-
- **[ISO Data](./docs/README-iso-data.md)** - Country and region data with Google integration
|
|
37
|
-
|
|
38
|
-
### Examples
|
|
39
|
-
|
|
40
|
-
- **[Vite Checkout Demo](../checkout-vite)** - Complete checkout implementation
|
|
41
|
-
|
|
42
|
-
## 🏗️ Building a Plugin
|
|
43
|
-
|
|
44
|
-
### Plugin Structure
|
|
45
|
-
|
|
46
|
-
Every TagadaPay plugin follows this simple structure:
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
my-plugin/
|
|
50
|
-
├── plugin.manifest.json # Plugin metadata & routing
|
|
51
|
-
├── .local.json # Local dev config (auto-injected in production)
|
|
52
|
-
├── config/ # Optional deployment configs
|
|
53
|
-
│ ├── theme-green.json # Config variant A
|
|
54
|
-
│ └── theme-blue.json # Config variant B
|
|
55
|
-
├── src/
|
|
56
|
-
│ └── App.tsx # Your plugin code
|
|
57
|
-
└── dist/ # Built plugin files
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Configuration Flow
|
|
61
|
-
|
|
62
|
-
```mermaid
|
|
63
|
-
graph TD
|
|
64
|
-
A[🛠️ Local Development] --> B[.local.json]
|
|
65
|
-
A --> C[config/*.json]
|
|
66
|
-
B --> D[usePluginConfig()]
|
|
67
|
-
C --> D
|
|
68
|
-
|
|
69
|
-
E[🚀 Production] --> F[Platform Injection]
|
|
70
|
-
F --> G[HTTP Headers & Meta Tags]
|
|
71
|
-
G --> D
|
|
72
|
-
|
|
73
|
-
D --> H[Your Plugin Component]
|
|
74
|
-
|
|
75
|
-
style A fill:#e1f5fe
|
|
76
|
-
style E fill:#f3e5f5
|
|
77
|
-
style D fill:#fff3e0
|
|
78
|
-
style H fill:#e8f5e8
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Essential Files
|
|
82
|
-
|
|
83
|
-
#### 1. **`plugin.manifest.json`** - Plugin Metadata
|
|
84
|
-
|
|
85
|
-
```json
|
|
86
|
-
{
|
|
87
|
-
"pluginId": "my-awesome-plugin",
|
|
88
|
-
"name": "My Awesome Plugin",
|
|
89
|
-
"version": "1.0.0",
|
|
90
|
-
"mode": "direct-mode",
|
|
91
|
-
"router": {
|
|
92
|
-
"basePath": "/",
|
|
93
|
-
"matcher": ".*",
|
|
94
|
-
"excluder": "/checkout"
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
#### 2. **`.local.json`** - Local Development Context
|
|
100
|
-
|
|
101
|
-
```json
|
|
102
|
-
{
|
|
103
|
-
"storeId": "store_abc123",
|
|
104
|
-
"accountId": "acc_xyz789",
|
|
105
|
-
"basePath": "/"
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
> ⚠️ **Auto-managed**: This file is only for local dev. In production, the platform injects this data automatically.
|
|
110
|
-
|
|
111
|
-
#### 3. **`config/my-theme.json`** - Optional Deployment Config
|
|
112
|
-
|
|
113
|
-
```json
|
|
114
|
-
{
|
|
115
|
-
"configName": "green-theme",
|
|
116
|
-
"branding": {
|
|
117
|
-
"primaryColor": "#059669",
|
|
118
|
-
"companyName": "My Store",
|
|
119
|
-
"logoUrl": "https://example.com/logo.png"
|
|
120
|
-
},
|
|
121
|
-
"features": {
|
|
122
|
-
"enableChat": true,
|
|
123
|
-
"maxItems": 10
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
> 📝 **Note**: Config can contain any keys you need - the SDK doesn't enforce a specific structure.
|
|
129
|
-
|
|
130
|
-
## 🚀 Quick Start
|
|
131
|
-
|
|
132
|
-
### Installation
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
npm install @tagadapay/plugin-sdk
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Basic Plugin Setup
|
|
139
|
-
|
|
140
|
-
```tsx
|
|
141
|
-
import React from 'react';
|
|
142
|
-
import {
|
|
143
|
-
TagadaProvider,
|
|
144
|
-
usePluginConfig,
|
|
145
|
-
useGoogleAutocomplete,
|
|
146
|
-
useISOData,
|
|
147
|
-
useCheckout,
|
|
148
|
-
formatMoney,
|
|
149
|
-
} from '@tagadapay/plugin-sdk/v3';
|
|
150
|
-
|
|
151
|
-
function MyPlugin() {
|
|
152
|
-
const { config, storeId, accountId, basePath, loading } = usePluginConfig();
|
|
153
|
-
|
|
154
|
-
// Optional: Add address autocomplete
|
|
155
|
-
const { searchPlaces, predictions } = useGoogleAutocomplete({
|
|
156
|
-
apiKey: config?.googleMapsApiKey || 'YOUR_API_KEY',
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Optional: Add country/region data
|
|
160
|
-
const { countries } = useISOData('en');
|
|
161
|
-
|
|
162
|
-
// V2: Use TanStack Query-based checkout hook
|
|
163
|
-
const { checkout, init, updateLineItems, isLoading } = useCheckout({
|
|
164
|
-
checkoutToken: 'your-checkout-token',
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
if (loading) return <div>Loading...</div>;
|
|
168
|
-
|
|
169
|
-
return (
|
|
170
|
-
<div style={{ '--primary': config?.branding?.primaryColor }}>
|
|
171
|
-
<h1>Welcome to {config?.branding?.companyName}</h1>
|
|
172
|
-
<p>Store: {storeId}</p>
|
|
173
|
-
<p>Base Path: {basePath}</p>
|
|
174
|
-
<p>Available Countries: {Object.keys(countries).length}</p>
|
|
175
|
-
|
|
176
|
-
{checkout && (
|
|
177
|
-
<div>
|
|
178
|
-
<h2>Checkout Total: {formatMoney(checkout.summary.total, checkout.currency)}</h2>
|
|
179
|
-
<p>Items: {checkout.lineItems.length}</p>
|
|
180
|
-
</div>
|
|
181
|
-
)}
|
|
182
|
-
</div>
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Wrap your plugin with TagadaProvider
|
|
187
|
-
function App() {
|
|
188
|
-
return (
|
|
189
|
-
<TagadaProvider>
|
|
190
|
-
<MyPlugin />
|
|
191
|
-
</TagadaProvider>
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export default App;
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## 🏗️ V2 Architecture
|
|
199
|
-
|
|
200
|
-
### What's New in V2
|
|
201
|
-
|
|
202
|
-
The TagadaPay Plugin SDK v3 introduces a clean architecture with significant improvements:
|
|
203
|
-
|
|
204
|
-
#### **🔄 TanStack Query Integration**
|
|
205
|
-
|
|
206
|
-
- **Automatic Caching**: All API calls are cached and synchronized across components
|
|
207
|
-
- **Background Refetching**: Data stays fresh with automatic background updates
|
|
208
|
-
- **Optimistic Updates**: Instant UI feedback with automatic rollback on errors
|
|
209
|
-
- **Request Deduplication**: Multiple components can use the same data without duplicate requests
|
|
210
|
-
|
|
211
|
-
#### **🏗️ Clean Architecture**
|
|
212
|
-
|
|
213
|
-
- **Core Layer**: Pure functions and API clients without React dependencies
|
|
214
|
-
- **React Layer**: Hooks and components that use core functions
|
|
215
|
-
- **Better Testing**: Easier to test business logic separately from UI logic
|
|
216
|
-
|
|
217
|
-
#### **📦 Import Paths**
|
|
218
|
-
|
|
219
|
-
```tsx
|
|
220
|
-
// V2 (Recommended)
|
|
221
|
-
import { useCheckout, useOffers, TagadaProvider } from '@tagadapay/plugin-sdk/v3';
|
|
222
|
-
|
|
223
|
-
// Legacy (Still supported)
|
|
224
|
-
import { useCheckout, useOffers, TagadaProvider } from '@tagadapay/plugin-sdk/react';
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
#### **🔧 Enhanced Developer Experience**
|
|
228
|
-
|
|
229
|
-
- **TypeScript First**: Better type inference and autocomplete
|
|
230
|
-
- **Debug Tools**: Built-in debug drawer for development
|
|
231
|
-
- **Performance**: Reduced bundle size and better performance
|
|
232
|
-
|
|
233
|
-
### Migration from V1
|
|
234
|
-
|
|
235
|
-
Most hooks have the same names but improved APIs:
|
|
236
|
-
|
|
237
|
-
```tsx
|
|
238
|
-
// V1 Pattern
|
|
239
|
-
const { checkout, loading, error } = useCheckout();
|
|
240
|
-
|
|
241
|
-
// V2 Pattern (TanStack Query)
|
|
242
|
-
const { checkout, isLoading, error, refresh } = useCheckout({
|
|
243
|
-
checkoutToken: 'token',
|
|
244
|
-
enabled: true,
|
|
245
|
-
});
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### 📚 V2 Examples
|
|
249
|
-
|
|
250
|
-
#### Complete Checkout Flow
|
|
251
|
-
|
|
252
|
-
```tsx
|
|
253
|
-
import React, { useState } from 'react';
|
|
254
|
-
import {
|
|
255
|
-
TagadaProvider,
|
|
256
|
-
useCheckout,
|
|
257
|
-
useProducts,
|
|
258
|
-
usePluginConfig,
|
|
259
|
-
formatMoney,
|
|
260
|
-
} from '@tagadapay/plugin-sdk/v3';
|
|
261
|
-
|
|
262
|
-
function CheckoutPage() {
|
|
263
|
-
const [checkoutToken, setCheckoutToken] = useState<string>();
|
|
264
|
-
const { config } = usePluginConfig();
|
|
265
|
-
|
|
266
|
-
// Load products
|
|
267
|
-
const { products, isLoading: productsLoading } = useProducts({
|
|
268
|
-
storeId: config.storeId,
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
// Initialize checkout when needed
|
|
272
|
-
const {
|
|
273
|
-
checkout,
|
|
274
|
-
isLoading: checkoutLoading,
|
|
275
|
-
init,
|
|
276
|
-
updateLineItems,
|
|
277
|
-
updateCustomer,
|
|
278
|
-
applyPromotionCode,
|
|
279
|
-
} = useCheckout({
|
|
280
|
-
checkoutToken,
|
|
281
|
-
enabled: !!checkoutToken,
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
const handleInitCheckout = async () => {
|
|
285
|
-
if (!products?.length) return;
|
|
286
|
-
|
|
287
|
-
const result = await init({
|
|
288
|
-
lineItems: [
|
|
289
|
-
{
|
|
290
|
-
variantId: products[0].variants[0].id,
|
|
291
|
-
quantity: 1,
|
|
292
|
-
},
|
|
293
|
-
],
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
setCheckoutToken(result.checkoutToken);
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
const handleApplyPromo = async (code: string) => {
|
|
300
|
-
try {
|
|
301
|
-
await applyPromotionCode(code);
|
|
302
|
-
// TanStack Query automatically refetches and updates the UI
|
|
303
|
-
} catch (error) {
|
|
304
|
-
console.error('Failed to apply promo:', error);
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
if (productsLoading) return <div>Loading products...</div>;
|
|
309
|
-
|
|
310
|
-
return (
|
|
311
|
-
<div>
|
|
312
|
-
<h1>Checkout</h1>
|
|
313
|
-
|
|
314
|
-
{!checkoutToken ? (
|
|
315
|
-
<button onClick={handleInitCheckout}>Start Checkout</button>
|
|
316
|
-
) : (
|
|
317
|
-
<div>
|
|
318
|
-
{checkoutLoading ? (
|
|
319
|
-
<div>Loading checkout...</div>
|
|
320
|
-
) : checkout ? (
|
|
321
|
-
<div>
|
|
322
|
-
<h2>Order Summary</h2>
|
|
323
|
-
<p>Total: {formatMoney(checkout.summary.total, checkout.currency)}</p>
|
|
324
|
-
<p>Items: {checkout.lineItems.length}</p>
|
|
325
|
-
|
|
326
|
-
<div>
|
|
327
|
-
<input
|
|
328
|
-
type="text"
|
|
329
|
-
placeholder="Promo code"
|
|
330
|
-
onKeyDown={(e) => {
|
|
331
|
-
if (e.key === 'Enter') {
|
|
332
|
-
handleApplyPromo(e.currentTarget.value);
|
|
333
|
-
}
|
|
334
|
-
}}
|
|
335
|
-
/>
|
|
336
|
-
</div>
|
|
337
|
-
</div>
|
|
338
|
-
) : null}
|
|
339
|
-
</div>
|
|
340
|
-
)}
|
|
341
|
-
</div>
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function App() {
|
|
346
|
-
return (
|
|
347
|
-
<TagadaProvider>
|
|
348
|
-
<CheckoutPage />
|
|
349
|
-
</TagadaProvider>
|
|
350
|
-
);
|
|
351
|
-
}
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
#### Multi-Component Data Sharing
|
|
355
|
-
|
|
356
|
-
```tsx
|
|
357
|
-
import React from 'react';
|
|
358
|
-
import { TagadaProvider, useCheckout, useOffers, formatMoney } from '@tagadapay/plugin-sdk/v3';
|
|
359
|
-
|
|
360
|
-
// Component 1: Cart Summary
|
|
361
|
-
function CartSummary({ checkoutToken }: { checkoutToken: string }) {
|
|
362
|
-
const { checkout, isLoading } = useCheckout({ checkoutToken });
|
|
363
|
-
|
|
364
|
-
if (isLoading) return <div>Loading...</div>;
|
|
365
|
-
if (!checkout) return null;
|
|
366
|
-
|
|
367
|
-
return (
|
|
368
|
-
<div>
|
|
369
|
-
<h3>Cart ({checkout.lineItems.length} items)</h3>
|
|
370
|
-
<p>Total: {formatMoney(checkout.summary.total, checkout.currency)}</p>
|
|
371
|
-
</div>
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Component 2: Available Offers (shares same checkout data automatically)
|
|
376
|
-
function OffersPanel({ checkoutToken }: { checkoutToken: string }) {
|
|
377
|
-
const { checkout } = useCheckout({ checkoutToken }); // Same data, no extra request!
|
|
378
|
-
const { offers, isLoading } = useOffers({
|
|
379
|
-
storeId: checkout?.storeId,
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
if (isLoading) return <div>Loading offers...</div>;
|
|
383
|
-
|
|
384
|
-
return (
|
|
385
|
-
<div>
|
|
386
|
-
<h3>Special Offers</h3>
|
|
387
|
-
{offers?.map((offer) => (
|
|
388
|
-
<div key={offer.id}>
|
|
389
|
-
<p>{offer.title}</p>
|
|
390
|
-
<p>Save {formatMoney(offer.discount, checkout?.currency || 'USD')}</p>
|
|
391
|
-
</div>
|
|
392
|
-
))}
|
|
393
|
-
</div>
|
|
394
|
-
);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// Main component
|
|
398
|
-
function CheckoutWithOffers() {
|
|
399
|
-
const checkoutToken = 'your-checkout-token';
|
|
400
|
-
|
|
401
|
-
return (
|
|
402
|
-
<div style={{ display: 'flex', gap: '20px' }}>
|
|
403
|
-
<CartSummary checkoutToken={checkoutToken} />
|
|
404
|
-
<OffersPanel checkoutToken={checkoutToken} />
|
|
405
|
-
</div>
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
#### Optimistic Updates
|
|
411
|
-
|
|
412
|
-
```tsx
|
|
413
|
-
import React from 'react';
|
|
414
|
-
import { useCheckout } from '@tagadapay/plugin-sdk/v3';
|
|
415
|
-
|
|
416
|
-
function QuantitySelector({
|
|
417
|
-
checkoutToken,
|
|
418
|
-
variantId,
|
|
419
|
-
currentQuantity,
|
|
420
|
-
}: {
|
|
421
|
-
checkoutToken: string;
|
|
422
|
-
variantId: string;
|
|
423
|
-
currentQuantity: number;
|
|
424
|
-
}) {
|
|
425
|
-
const { checkout, setItemQuantity, updateLineItemsOptimistic } = useCheckout({ checkoutToken });
|
|
426
|
-
|
|
427
|
-
const handleQuantityChange = async (newQuantity: number) => {
|
|
428
|
-
// 1. Optimistic update (instant UI feedback)
|
|
429
|
-
const updatedItems =
|
|
430
|
-
checkout?.lineItems.map((item) =>
|
|
431
|
-
item.variantId === variantId ? { ...item, quantity: newQuantity } : item,
|
|
432
|
-
) || [];
|
|
433
|
-
|
|
434
|
-
updateLineItemsOptimistic(updatedItems);
|
|
435
|
-
|
|
436
|
-
// 2. Server update (with automatic rollback on error)
|
|
437
|
-
try {
|
|
438
|
-
await setItemQuantity(variantId, newQuantity);
|
|
439
|
-
// TanStack Query automatically syncs the real data
|
|
440
|
-
} catch (error) {
|
|
441
|
-
// Automatic rollback to previous state
|
|
442
|
-
console.error('Failed to update quantity:', error);
|
|
443
|
-
}
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
return (
|
|
447
|
-
<div>
|
|
448
|
-
<button onClick={() => handleQuantityChange(currentQuantity - 1)}>-</button>
|
|
449
|
-
<span>{currentQuantity}</span>
|
|
450
|
-
<button onClick={() => handleQuantityChange(currentQuantity + 1)}>+</button>
|
|
451
|
-
</div>
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
## 🚀 Initialization Modes
|
|
457
|
-
|
|
458
|
-
The TagadaProvider supports two initialization modes to give you control over when your components render:
|
|
459
|
-
|
|
460
|
-
### Non-Blocking Mode (Default - Recommended)
|
|
461
|
-
|
|
462
|
-
```tsx
|
|
463
|
-
<TagadaProvider>
|
|
464
|
-
{/* Children render immediately after config loads */}
|
|
465
|
-
<YourApp />
|
|
466
|
-
</TagadaProvider>
|
|
467
|
-
|
|
468
|
-
// OR explicitly
|
|
469
|
-
<TagadaProvider blockUntilSessionReady={false}>
|
|
470
|
-
<YourApp />
|
|
471
|
-
</TagadaProvider>
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
**Flow:**
|
|
475
|
-
|
|
476
|
-
1. **Phase 1 & 2** ✅ Plugin config loads → Children render immediately
|
|
477
|
-
2. **Phase 3** 🔄 Session initialization runs in background
|
|
478
|
-
3. **API calls** 🔄 Hooks automatically wait for session to be ready
|
|
479
|
-
|
|
480
|
-
**Benefits:**
|
|
481
|
-
|
|
482
|
-
- ⚡ **Faster rendering** - UI appears immediately
|
|
483
|
-
- 🎯 **Better UX** - Show loading states while session initializes
|
|
484
|
-
- 🔄 **Automatic waiting** - Hooks handle session timing for you
|
|
485
|
-
|
|
486
|
-
### Blocking Mode (Legacy Behavior)
|
|
487
|
-
|
|
488
|
-
```tsx
|
|
489
|
-
<TagadaProvider blockUntilSessionReady={true}>
|
|
490
|
-
{/* Children render only after ALL initialization completes */}
|
|
491
|
-
<YourApp />
|
|
492
|
-
</TagadaProvider>
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
**Flow:**
|
|
496
|
-
|
|
497
|
-
1. **All Phases** ⏳ Config + Session must complete before children render
|
|
498
|
-
2. **API calls** ✅ Work immediately (no waiting needed)
|
|
499
|
-
|
|
500
|
-
**Use when:**
|
|
501
|
-
|
|
502
|
-
- 🔄 **Migrating** from older SDK versions
|
|
503
|
-
- 🎯 **Simple apps** that don't need progressive loading
|
|
504
|
-
|
|
505
|
-
### Console Logs
|
|
506
|
-
|
|
507
|
-
The SDK logs help you understand which mode you're using:
|
|
508
|
-
|
|
509
|
-
**Non-blocking mode:**
|
|
510
|
-
|
|
511
|
-
```
|
|
512
|
-
✅ Phase 1 & 2 Complete - Plugin config loaded
|
|
513
|
-
🚀 Non-blocking mode: Children can now render - Phase 3 will continue in background
|
|
514
|
-
🔄 [useCheckout] Waiting for session initialization to complete...
|
|
515
|
-
✅ Phase 3 Complete - Session initialization completed successfully
|
|
516
|
-
✅ [useCheckout] Session initialized, proceeding with checkout init
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
**Blocking mode:**
|
|
520
|
-
|
|
521
|
-
```
|
|
522
|
-
✅ Phase 1 & 2 Complete - Plugin config loaded
|
|
523
|
-
⏳ Blocking mode: Children will render after Phase 3 completes
|
|
524
|
-
✅ Phase 3 Complete - Session initialization completed successfully
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
### TagadaProvider API
|
|
528
|
-
|
|
529
|
-
```tsx
|
|
530
|
-
interface TagadaProviderProps {
|
|
531
|
-
children: ReactNode;
|
|
532
|
-
environment?: 'local' | 'development' | 'staging' | 'production';
|
|
533
|
-
customApiConfig?: Partial<EnvironmentConfig>;
|
|
534
|
-
debugMode?: boolean;
|
|
535
|
-
localConfig?: string; // LOCAL DEV ONLY: Override config variant
|
|
536
|
-
blockUntilSessionReady?: boolean; // Default: false
|
|
537
|
-
|
|
538
|
-
// V2 Specific Options
|
|
539
|
-
queryClientConfig?: QueryClientConfig; // TanStack Query configuration
|
|
540
|
-
enableDevtools?: boolean; // Enable React Query Devtools
|
|
541
|
-
}
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
| Prop | Type | Default | Description |
|
|
545
|
-
| ------------------------ | ----------- | -------------------- | ------------------------------ |
|
|
546
|
-
| `children` | `ReactNode` | - | Your plugin components |
|
|
547
|
-
| `environment` | `string` | auto-detect | Override environment detection |
|
|
548
|
-
| `customApiConfig` | `object` | - | Custom API configuration |
|
|
549
|
-
| `debugMode` | `boolean` | auto (false in prod) | Enable debug features & drawer |
|
|
550
|
-
| `localConfig` | `string` | `'default'` | Config variant for local dev |
|
|
551
|
-
| `blockUntilSessionReady` | `boolean` | `false` | Use legacy blocking behavior |
|
|
552
|
-
| `queryClientConfig` | `object` | - | **V2**: TanStack Query config |
|
|
553
|
-
| `enableDevtools` | `boolean` | `false` | **V2**: React Query Devtools |
|
|
554
|
-
|
|
555
|
-
#### V2 Enhanced Provider Features
|
|
556
|
-
|
|
557
|
-
```tsx
|
|
558
|
-
import { TagadaProvider } from '@tagadapay/plugin-sdk/v3';
|
|
559
|
-
|
|
560
|
-
function App() {
|
|
561
|
-
return (
|
|
562
|
-
<TagadaProvider
|
|
563
|
-
debugMode={true}
|
|
564
|
-
enableDevtools={true} // Shows React Query Devtools
|
|
565
|
-
queryClientConfig={{
|
|
566
|
-
defaultOptions: {
|
|
567
|
-
queries: {
|
|
568
|
-
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
569
|
-
cacheTime: 10 * 60 * 1000, // 10 minutes
|
|
570
|
-
},
|
|
571
|
-
},
|
|
572
|
-
}}
|
|
573
|
-
>
|
|
574
|
-
<YourApp />
|
|
575
|
-
</TagadaProvider>
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
> **Version Compatibility:** V2 features require `@tagadapay/plugin-sdk/v3`. The `blockUntilSessionReady` option was added in v2.3.0. For older versions, the blocking behavior was the default and only option.
|
|
581
|
-
|
|
582
|
-
### Development vs Production
|
|
583
|
-
|
|
584
|
-
| Environment | Store/Account ID | Deployment Config | How it Works |
|
|
585
|
-
| -------------- | ---------------- | ----------------- | ------------------ |
|
|
586
|
-
| **Local Dev** | `.local.json` | `config/*.json` | Files on disk |
|
|
587
|
-
| **Production** | HTTP Headers | Meta Tags | Platform injection |
|
|
588
|
-
|
|
589
|
-
```tsx
|
|
590
|
-
// ✅ ALWAYS use hooks - works in both environments
|
|
591
|
-
const { storeId, accountId, basePath, config } = usePluginConfig();
|
|
592
|
-
|
|
593
|
-
// ❌ NEVER access directly
|
|
594
|
-
// const config = window.__PLUGIN_CONFIG__; // Doesn't exist!
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
## 🎯 Key Features
|
|
598
|
-
|
|
599
|
-
### 🔧 **Plugin Configuration System**
|
|
600
|
-
|
|
601
|
-
- **Automatic Context Injection** - Store ID, Account ID, and custom config
|
|
602
|
-
- **Development & Production** - Seamless environment switching
|
|
603
|
-
- **Generic Configuration** - Support for any JSON structure
|
|
604
|
-
- **React Hooks** - Clean, type-safe configuration access
|
|
605
|
-
|
|
606
|
-
### 💳 **Payment Processing**
|
|
607
|
-
|
|
608
|
-
- Secure tokenized payments
|
|
609
|
-
- Multiple payment method support
|
|
610
|
-
- Real-time validation
|
|
611
|
-
- PCI compliance
|
|
612
|
-
|
|
613
|
-
### 🌍 **Address & Location**
|
|
614
|
-
|
|
615
|
-
- **Google Places Autocomplete** - Automatic API loading and address parsing
|
|
616
|
-
- **ISO Country/Region Data** - Complete ISO 3166-1/3166-2 database
|
|
617
|
-
- **Address Validation** - Structured component extraction
|
|
618
|
-
- **Multi-language Support** - 11+ languages for international users
|
|
619
|
-
|
|
620
|
-
### 🛒 **E-commerce Features**
|
|
621
|
-
|
|
622
|
-
- Dynamic pricing and promotional offers
|
|
623
|
-
- Cart management and tax calculations
|
|
624
|
-
- Currency conversion and formatting
|
|
625
|
-
- Customer profile management
|
|
626
|
-
|
|
627
|
-
### 🎨 **UI & Development**
|
|
628
|
-
|
|
629
|
-
- Pre-built React components
|
|
630
|
-
- Customizable themes with configuration
|
|
631
|
-
- Mobile-optimized and accessible
|
|
632
|
-
- TypeScript support throughout
|
|
633
|
-
|
|
634
|
-
## 📖 API Reference
|
|
635
|
-
|
|
636
|
-
### V2 Core Hooks (TanStack Query-based)
|
|
637
|
-
|
|
638
|
-
#### useCheckout()
|
|
639
|
-
|
|
640
|
-
Primary hook for checkout state management using TanStack Query for automatic caching and synchronization.
|
|
641
|
-
|
|
642
|
-
```typescript
|
|
643
|
-
const {
|
|
644
|
-
// Query data
|
|
645
|
-
checkout, // CheckoutData | undefined
|
|
646
|
-
isLoading, // boolean
|
|
647
|
-
error, // Error | null
|
|
648
|
-
isSuccess, // boolean
|
|
649
|
-
|
|
650
|
-
// Actions
|
|
651
|
-
init, // (params: CheckoutInitParams) => Promise<{checkoutUrl, checkoutSession, checkoutToken}>
|
|
652
|
-
refresh, // () => Promise<void>
|
|
653
|
-
|
|
654
|
-
// Checkout operations
|
|
655
|
-
updateLineItems, // (lineItems: CheckoutLineItem[]) => Promise<any>
|
|
656
|
-
updateLineItemsOptimistic, // (lineItems: CheckoutLineItem[]) => void
|
|
657
|
-
setItemQuantity, // (variantId: string, quantity: number, priceId?: string) => Promise<any>
|
|
658
|
-
updateCustomer, // (data: {email: string, acceptsMarketing?: boolean}) => Promise<any>
|
|
659
|
-
applyPromotionCode, // (code: string) => Promise<any>
|
|
660
|
-
removePromotion, // (promotionId: string) => Promise<any>
|
|
661
|
-
} = useCheckout({ checkoutToken, enabled });
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
#### useOffers()
|
|
665
|
-
|
|
666
|
-
Hook for managing dynamic offers and pricing with automatic cache management.
|
|
667
|
-
|
|
668
|
-
```typescript
|
|
669
|
-
const {
|
|
670
|
-
// Query data
|
|
671
|
-
offers, // Offer[] | undefined
|
|
672
|
-
isLoading, // boolean
|
|
673
|
-
error, // Error | null
|
|
674
|
-
|
|
675
|
-
// Actions
|
|
676
|
-
refresh, // () => Promise<void>
|
|
677
|
-
} = useOffers({ storeId, enabled });
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
#### usePromotions()
|
|
681
|
-
|
|
682
|
-
Hook for managing promotion codes in checkout sessions with TanStack Query.
|
|
683
|
-
|
|
684
|
-
```typescript
|
|
685
|
-
const {
|
|
686
|
-
// Query data
|
|
687
|
-
promotions, // Promotion[] | undefined
|
|
688
|
-
isLoading, // boolean
|
|
689
|
-
error, // Error | null
|
|
690
|
-
|
|
691
|
-
// Actions
|
|
692
|
-
refresh, // () => Promise<void>
|
|
693
|
-
} = usePromotions({ checkoutToken, enabled });
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
#### useProducts()
|
|
697
|
-
|
|
698
|
-
Hook for product data management with caching.
|
|
699
|
-
|
|
700
|
-
```typescript
|
|
701
|
-
const {
|
|
702
|
-
// Query data
|
|
703
|
-
products, // Product[] | undefined
|
|
704
|
-
isLoading, // boolean
|
|
705
|
-
error, // Error | null
|
|
706
|
-
|
|
707
|
-
// Actions
|
|
708
|
-
refresh, // () => Promise<void>
|
|
709
|
-
} = useProducts({ storeId, enabled });
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
#### useStoreConfig()
|
|
713
|
-
|
|
714
|
-
Hook for fetching store configuration with automatic caching.
|
|
715
|
-
|
|
716
|
-
```typescript
|
|
717
|
-
const {
|
|
718
|
-
// Query data
|
|
719
|
-
storeConfig, // StoreConfig | undefined
|
|
720
|
-
isLoading, // boolean
|
|
721
|
-
error, // Error | null
|
|
722
|
-
isSuccess, // boolean
|
|
723
|
-
|
|
724
|
-
// Actions
|
|
725
|
-
refetch, // () => Promise<void>
|
|
726
|
-
} = useStoreConfig({ storeId, enabled });
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
**Example:**
|
|
730
|
-
|
|
731
|
-
```tsx
|
|
732
|
-
import { useStoreConfig } from '@tagadapay/plugin-sdk/v3';
|
|
733
|
-
|
|
734
|
-
function StoreInfo() {
|
|
735
|
-
const { storeConfig, isLoading } = useStoreConfig();
|
|
736
|
-
|
|
737
|
-
if (isLoading) return <div>Loading...</div>;
|
|
738
|
-
|
|
739
|
-
return (
|
|
740
|
-
<div>
|
|
741
|
-
<h1>{storeConfig?.storeName}</h1>
|
|
742
|
-
<p>Currency: {storeConfig?.currency}</p>
|
|
743
|
-
</div>
|
|
744
|
-
);
|
|
745
|
-
}
|
|
746
|
-
```
|
|
747
|
-
|
|
748
|
-
See [useStoreConfig documentation](./docs/README-useStoreConfig.md) for more details.
|
|
749
|
-
|
|
750
|
-
#### useOrder()
|
|
751
|
-
|
|
752
|
-
Hook for order management and tracking.
|
|
753
|
-
|
|
754
|
-
```typescript
|
|
755
|
-
const {
|
|
756
|
-
// Query data
|
|
757
|
-
order, // Order | undefined
|
|
758
|
-
isLoading, // boolean
|
|
759
|
-
error, // Error | null
|
|
760
|
-
|
|
761
|
-
// Actions
|
|
762
|
-
refresh, // () => Promise<void>
|
|
763
|
-
} = useOrder({ orderId, enabled });
|
|
764
|
-
```
|
|
765
|
-
|
|
766
|
-
#### usePostPurchases()
|
|
767
|
-
|
|
768
|
-
Hook for post-purchase offers management.
|
|
769
|
-
|
|
770
|
-
```typescript
|
|
771
|
-
const {
|
|
772
|
-
// Query data
|
|
773
|
-
postPurchases, // PostPurchaseOffer[] | undefined
|
|
774
|
-
isLoading, // boolean
|
|
775
|
-
error, // Error | null
|
|
776
|
-
|
|
777
|
-
// Actions
|
|
778
|
-
refresh, // () => Promise<void>
|
|
779
|
-
} = usePostPurchases({ orderId, enabled });
|
|
780
|
-
```
|
|
781
|
-
|
|
782
|
-
#### usePayment()
|
|
783
|
-
|
|
784
|
-
Hook for payment processing with 3DS support.
|
|
785
|
-
|
|
786
|
-
```typescript
|
|
787
|
-
const {
|
|
788
|
-
// Payment methods
|
|
789
|
-
processPayment, // (options: PaymentOptions) => Promise<PaymentResponse>
|
|
790
|
-
processApplePayPayment, // (token: ApplePayToken) => Promise<PaymentResponse>
|
|
791
|
-
|
|
792
|
-
// State
|
|
793
|
-
isProcessing, // boolean
|
|
794
|
-
error, // Error | null
|
|
795
|
-
} = usePayment();
|
|
796
|
-
```
|
|
797
|
-
|
|
798
|
-
### V2 Utility Functions & Advanced Features
|
|
799
|
-
|
|
800
|
-
#### TanStack Query Integration
|
|
801
|
-
|
|
802
|
-
V2 provides direct access to TanStack Query features for advanced use cases:
|
|
803
|
-
|
|
804
|
-
```typescript
|
|
805
|
-
import {
|
|
806
|
-
useApiQuery,
|
|
807
|
-
useApiMutation,
|
|
808
|
-
useInvalidateQuery,
|
|
809
|
-
usePreloadQuery,
|
|
810
|
-
queryKeys,
|
|
811
|
-
} from '@tagadapay/plugin-sdk/v3';
|
|
812
|
-
|
|
813
|
-
// Custom API queries
|
|
814
|
-
const { data, isLoading } = useApiQuery({
|
|
815
|
-
queryKey: ['custom', 'endpoint'],
|
|
816
|
-
queryFn: () => apiClient.get('/custom-endpoint'),
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
// Mutations with automatic cache updates
|
|
820
|
-
const mutation = useApiMutation({
|
|
821
|
-
mutationFn: (data) => apiClient.post('/update', data),
|
|
822
|
-
onSuccess: () => {
|
|
823
|
-
// Invalidate related queries
|
|
824
|
-
invalidateQuery(['checkout']);
|
|
825
|
-
},
|
|
826
|
-
});
|
|
827
|
-
|
|
828
|
-
// Preload data for better UX
|
|
829
|
-
const preloadCheckout = usePreloadQuery();
|
|
830
|
-
preloadCheckout({
|
|
831
|
-
queryKey: queryKeys.checkout(checkoutToken),
|
|
832
|
-
queryFn: () => checkoutResource.get(checkoutToken),
|
|
833
|
-
});
|
|
834
|
-
```
|
|
835
|
-
|
|
836
|
-
#### Money Utilities
|
|
837
|
-
|
|
838
|
-
Enhanced money formatting with better TypeScript support:
|
|
839
|
-
|
|
840
|
-
```typescript
|
|
841
|
-
import { formatMoney, convertCurrency, getCurrencyInfo } from '@tagadapay/plugin-sdk/v3';
|
|
842
|
-
|
|
843
|
-
// Format money with automatic currency detection
|
|
844
|
-
const formatted = formatMoney(2999, 'USD'); // "$29.99"
|
|
845
|
-
const simple = formatSimpleMoney(2999, 'EUR'); // "29.99"
|
|
846
|
-
const withoutSymbol = formatMoneyWithoutSymbol(2999, 'GBP'); // "29.99"
|
|
847
|
-
|
|
848
|
-
// Currency conversion (if rates available)
|
|
849
|
-
const converted = convertCurrency(2999, 'USD', 'EUR'); // 2699
|
|
850
|
-
|
|
851
|
-
// Get currency information
|
|
852
|
-
const currencyInfo = getCurrencyInfo('USD');
|
|
853
|
-
// { symbol: '$', code: 'USD', minorUnits: 2 }
|
|
854
|
-
|
|
855
|
-
// Convert between major and minor units
|
|
856
|
-
const major = minorUnitsToMajorUnits(2999); // 29.99
|
|
857
|
-
const minor = moneyStringOrNumberToMinorUnits('29.99'); // 2999
|
|
858
|
-
```
|
|
859
|
-
|
|
860
|
-
#### Core Functions (Pure Functions)
|
|
861
|
-
|
|
862
|
-
V2 separates core business logic from React hooks:
|
|
863
|
-
|
|
864
|
-
```typescript
|
|
865
|
-
import {
|
|
866
|
-
CheckoutResource,
|
|
867
|
-
ProductsResource,
|
|
868
|
-
PaymentsResource,
|
|
869
|
-
PluginConfigUtils,
|
|
870
|
-
} from '@tagadapay/plugin-sdk/v3';
|
|
871
|
-
|
|
872
|
-
// Use core functions directly (useful for server-side or non-React contexts)
|
|
873
|
-
const checkoutResource = new CheckoutResource(apiClient);
|
|
874
|
-
const checkout = await checkoutResource.get(checkoutToken);
|
|
875
|
-
|
|
876
|
-
// Plugin config utilities
|
|
877
|
-
const config = PluginConfigUtils.getPluginConfig(rawConfig, context);
|
|
878
|
-
const isValid = PluginConfigUtils.validateConfig(config);
|
|
879
|
-
```
|
|
880
|
-
|
|
881
|
-
#### Debug Tools
|
|
882
|
-
|
|
883
|
-
V2 includes built-in debugging tools:
|
|
884
|
-
|
|
885
|
-
```tsx
|
|
886
|
-
import { TagadaProvider } from '@tagadapay/plugin-sdk/v3';
|
|
887
|
-
|
|
888
|
-
function App() {
|
|
889
|
-
return (
|
|
890
|
-
<TagadaProvider
|
|
891
|
-
debugMode={true} // Shows debug drawer in development
|
|
892
|
-
environment="development"
|
|
893
|
-
>
|
|
894
|
-
<YourApp />
|
|
895
|
-
</TagadaProvider>
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
```
|
|
899
|
-
|
|
900
|
-
## 🛠️ Development
|
|
901
|
-
|
|
902
|
-
### Local Development
|
|
903
|
-
|
|
904
|
-
```bash
|
|
905
|
-
# Install dependencies
|
|
906
|
-
npm install
|
|
907
|
-
|
|
908
|
-
# Start development server
|
|
909
|
-
npm run dev
|
|
910
|
-
|
|
911
|
-
# Build for production
|
|
912
|
-
npm run build
|
|
913
|
-
```
|
|
914
|
-
|
|
915
|
-
### Testing
|
|
916
|
-
|
|
917
|
-
```bash
|
|
918
|
-
# Run tests
|
|
919
|
-
npm test
|
|
920
|
-
|
|
921
|
-
# Run tests with coverage
|
|
922
|
-
npm run test:coverage
|
|
923
|
-
```
|
|
924
|
-
|
|
925
|
-
### Linting
|
|
926
|
-
|
|
927
|
-
```bash
|
|
928
|
-
# Check code style
|
|
929
|
-
npm run lint
|
|
930
|
-
|
|
931
|
-
# Fix linting issues
|
|
932
|
-
npm run lint:fix
|
|
933
|
-
```
|
|
934
|
-
|
|
935
|
-
## 📦 Build & Deploy
|
|
936
|
-
|
|
937
|
-
### Building Your Plugin
|
|
938
|
-
|
|
939
|
-
```bash
|
|
940
|
-
# Build optimized bundle
|
|
941
|
-
npm run build
|
|
942
|
-
|
|
943
|
-
# Analyze bundle size
|
|
944
|
-
npm run analyze
|
|
945
|
-
```
|
|
946
|
-
|
|
947
|
-
### Deployment
|
|
948
|
-
|
|
949
|
-
```bash
|
|
950
|
-
# Deploy using TagadaPay CLI
|
|
951
|
-
npx @tagadapay/plugin-cli deploy
|
|
952
|
-
|
|
953
|
-
# Deploy specific environment
|
|
954
|
-
npx @tagadapay/plugin-cli deploy --env production
|
|
955
|
-
```
|
|
956
|
-
|
|
957
|
-
## 🔧 Configuration
|
|
958
|
-
|
|
959
|
-
### Plugin Configuration
|
|
960
|
-
|
|
961
|
-
```json
|
|
962
|
-
{
|
|
963
|
-
"name": "My Checkout Plugin",
|
|
964
|
-
"version": "1.0.0",
|
|
965
|
-
"description": "Custom checkout experience",
|
|
966
|
-
"main": "dist/index.js",
|
|
967
|
-
"tagadapay": {
|
|
968
|
-
"type": "checkout",
|
|
969
|
-
"mode": "direct",
|
|
970
|
-
"framework": "react"
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
```
|
|
974
|
-
|
|
975
|
-
## 🔐 Security
|
|
976
|
-
|
|
977
|
-
### Best Practices
|
|
978
|
-
|
|
979
|
-
- Never store sensitive payment data
|
|
980
|
-
- Always validate user inputs
|
|
981
|
-
- Use HTTPS in production
|
|
982
|
-
- Implement proper error handling
|
|
983
|
-
- Follow PCI DSS guidelines
|
|
984
|
-
|
|
985
|
-
### Token Management
|
|
986
|
-
|
|
987
|
-
```typescript
|
|
988
|
-
// ✅ Good: Use tokenized payments
|
|
989
|
-
const paymentToken = await tokenizePayment(cardData);
|
|
990
|
-
await processPayment({ token: paymentToken });
|
|
991
|
-
|
|
992
|
-
// ❌ Bad: Never store raw card data
|
|
993
|
-
// const cardNumber = '4111111111111111'; // Don't do this
|
|
994
|
-
```
|
|
995
|
-
|
|
996
|
-
## 📱 Mobile Optimization
|
|
997
|
-
|
|
998
|
-
### Responsive Design
|
|
999
|
-
|
|
1000
|
-
```css
|
|
1001
|
-
.checkout-container {
|
|
1002
|
-
max-width: 600px;
|
|
1003
|
-
margin: 0 auto;
|
|
1004
|
-
padding: 16px;
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
@media (max-width: 768px) {
|
|
1008
|
-
.checkout-container {
|
|
1009
|
-
padding: 8px;
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
.checkout-form {
|
|
1013
|
-
font-size: 16px; /* Prevent zoom on iOS */
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
```
|
|
1017
|
-
|
|
1018
|
-
### Touch Interactions
|
|
1019
|
-
|
|
1020
|
-
```typescript
|
|
1021
|
-
const handleTouchStart = (e) => {
|
|
1022
|
-
// Optimize for touch devices
|
|
1023
|
-
e.preventDefault();
|
|
1024
|
-
// Handle touch interaction
|
|
1025
|
-
};
|
|
1026
|
-
```
|
|
1027
|
-
|
|
1028
|
-
## 🌐 Internationalization
|
|
1029
|
-
|
|
1030
|
-
### Multi-language Support
|
|
1031
|
-
|
|
1032
|
-
```typescript
|
|
1033
|
-
import { useTranslation } from '@tagadapay/plugin-sdk';
|
|
1034
|
-
|
|
1035
|
-
function CheckoutForm() {
|
|
1036
|
-
const { t } = useTranslation();
|
|
1037
|
-
|
|
1038
|
-
return (
|
|
1039
|
-
<form>
|
|
1040
|
-
<label>{t('checkout.email')}</label>
|
|
1041
|
-
<input type="email" placeholder={t('checkout.email_placeholder')} />
|
|
1042
|
-
</form>
|
|
1043
|
-
);
|
|
1044
|
-
}
|
|
1045
|
-
```
|
|
1046
|
-
|
|
1047
|
-
### Currency Support
|
|
1048
|
-
|
|
1049
|
-
```typescript
|
|
1050
|
-
import { useCurrency } from '@tagadapay/plugin-sdk';
|
|
1051
|
-
|
|
1052
|
-
function PriceDisplay({ amount }) {
|
|
1053
|
-
const { formatPrice, currency } = useCurrency();
|
|
1054
|
-
|
|
1055
|
-
return <span>{formatPrice(amount, currency)}</span>;
|
|
1056
|
-
}
|
|
1057
|
-
```
|
|
1058
|
-
|
|
1059
|
-
## 📊 Analytics & Monitoring
|
|
1060
|
-
|
|
1061
|
-
### Event Tracking
|
|
1062
|
-
|
|
1063
|
-
```typescript
|
|
1064
|
-
import { trackEvent } from '@tagadapay/plugin-sdk';
|
|
1065
|
-
|
|
1066
|
-
// Track user interactions
|
|
1067
|
-
trackEvent('checkout_started', {
|
|
1068
|
-
product_id: 'prod_123',
|
|
1069
|
-
value: 2999,
|
|
1070
|
-
currency: 'USD',
|
|
1071
|
-
});
|
|
1072
|
-
|
|
1073
|
-
// Track conversions
|
|
1074
|
-
trackEvent('purchase_completed', {
|
|
1075
|
-
transaction_id: 'txn_456',
|
|
1076
|
-
value: 2999,
|
|
1077
|
-
currency: 'USD',
|
|
1078
|
-
});
|
|
1079
|
-
```
|
|
1080
|
-
|
|
1081
|
-
### Performance Monitoring
|
|
1082
|
-
|
|
1083
|
-
```typescript
|
|
1084
|
-
import { performance } from '@tagadapay/plugin-sdk';
|
|
1085
|
-
|
|
1086
|
-
// Measure load times
|
|
1087
|
-
performance.mark('checkout-start');
|
|
1088
|
-
// ... checkout logic
|
|
1089
|
-
performance.mark('checkout-end');
|
|
1090
|
-
performance.measure('checkout-duration', 'checkout-start', 'checkout-end');
|
|
1091
|
-
```
|
|
1092
|
-
|
|
1093
|
-
## 🤝 Contributing
|
|
1094
|
-
|
|
1095
|
-
### Development Setup
|
|
1096
|
-
|
|
1097
|
-
```bash
|
|
1098
|
-
# Clone the repository
|
|
1099
|
-
git clone https://github.com/tagadapay/plugin-sdk.git
|
|
1100
|
-
|
|
1101
|
-
# Install dependencies
|
|
1102
|
-
npm install
|
|
1103
|
-
|
|
1104
|
-
# Start development
|
|
1105
|
-
npm run dev
|
|
1106
|
-
```
|
|
1107
|
-
|
|
1108
|
-
### Submitting Changes
|
|
1109
|
-
|
|
1110
|
-
1. Fork the repository
|
|
1111
|
-
2. Create a feature branch
|
|
1112
|
-
3. Make your changes
|
|
1113
|
-
4. Add tests
|
|
1114
|
-
5. Submit a pull request
|
|
1115
|
-
|
|
1116
|
-
## 📄 License
|
|
1117
|
-
|
|
1118
|
-
MIT License - see [LICENSE](./LICENSE) for details.
|
|
1119
|
-
|
|
1120
|
-
## 🆘 Support
|
|
1121
|
-
|
|
1122
|
-
- **Documentation**: [docs.tagadapay.com](https://docs.tagadapay.com)
|
|
1123
|
-
- **Discord**: [discord.gg/tagadapay](https://discord.gg/tagadapay)
|
|
1124
|
-
- **Email**: support@tagadapay.com
|
|
1125
|
-
- **GitHub Issues**: [github.com/tagadapay/plugin-sdk/issues](https://github.com/tagadapay/plugin-sdk/issues)
|
|
1126
|
-
|
|
1127
|
-
---
|
|
1128
|
-
|
|
1129
|
-
Built with ❤️ by the TagadaPay team
|
|
1
|
+
# TagadaPay Plugin SDK
|
|
2
|
+
|
|
3
|
+
A comprehensive React SDK for building plugins on the TagadaPay platform. Create custom checkout experiences, landing pages, and interactive components with automatic configuration injection and advanced routing capabilities.
|
|
4
|
+
|
|
5
|
+
> **🚀 V2 Now Available!** The new V2 architecture features TanStack Query integration, improved TypeScript support, and better performance. [See V2 Architecture](#-v2-architecture) for details.
|
|
6
|
+
>
|
|
7
|
+
> **Recommended**: Use `@tagadapay/plugin-sdk/v3` for new projects. V1 (`/react`) is still supported for existing projects.
|
|
8
|
+
|
|
9
|
+
## 📚 Documentation
|
|
10
|
+
|
|
11
|
+
### V2 Core APIs (Recommended)
|
|
12
|
+
|
|
13
|
+
- **[V2 Architecture](#-v2-architecture)** - New TanStack Query-based architecture
|
|
14
|
+
- **[V2 Examples](#-v2-examples)** - Complete examples using the new V2 hooks
|
|
15
|
+
- **[useCheckout (V2)](#usecheckout)** - TanStack Query-based checkout management
|
|
16
|
+
- **[useOffers (V2)](#useoffers)** - Dynamic pricing with automatic caching
|
|
17
|
+
- **[useProducts (V2)](#useproducts)** - Product data management
|
|
18
|
+
- **[useStoreConfig (V2)](#usestoreconfig)** - Store configuration with automatic caching
|
|
19
|
+
- **[usePayment (V2)](#usepayment)** - Payment processing with 3DS support
|
|
20
|
+
- **[V2 Utility Functions](#v2-utility-functions--advanced-features)** - Enhanced utilities and TanStack Query integration
|
|
21
|
+
|
|
22
|
+
### Legacy V1 APIs (Still Supported)
|
|
23
|
+
|
|
24
|
+
- **[useCheckout (V1)](./docs/README-useCheckout.md)** - Legacy checkout state management
|
|
25
|
+
- **[setCheckoutInfo](./docs/README-setCheckoutInfo.md)** - Customer information and validation
|
|
26
|
+
- **[useOffers (V1)](./docs/README-useOffers.md)** - Legacy dynamic pricing
|
|
27
|
+
- **[usePromotionCodes](./docs/README-usePromotionCodes.md)** - Legacy promotion code management
|
|
28
|
+
- **[Money utilities](./docs/README-money.md)** - Currency formatting and calculations
|
|
29
|
+
- **[URL utilities](./docs/README-urlUtils.md)** - Navigation and routing helpers
|
|
30
|
+
|
|
31
|
+
### Plugin Development
|
|
32
|
+
|
|
33
|
+
- **[Plugin Configuration](./docs/PLUGIN_CONFIG.md)** - How to access store context, config, and branding
|
|
34
|
+
- **[Initialization Modes](#-initialization-modes)** - Choose between blocking and non-blocking initialization
|
|
35
|
+
- **[Google Autocomplete](./docs/README-google-autocomplete.md)** - Address autocomplete with Google Places API
|
|
36
|
+
- **[ISO Data](./docs/README-iso-data.md)** - Country and region data with Google integration
|
|
37
|
+
|
|
38
|
+
### Examples
|
|
39
|
+
|
|
40
|
+
- **[Vite Checkout Demo](../checkout-vite)** - Complete checkout implementation
|
|
41
|
+
|
|
42
|
+
## 🏗️ Building a Plugin
|
|
43
|
+
|
|
44
|
+
### Plugin Structure
|
|
45
|
+
|
|
46
|
+
Every TagadaPay plugin follows this simple structure:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
my-plugin/
|
|
50
|
+
├── plugin.manifest.json # Plugin metadata & routing
|
|
51
|
+
├── .local.json # Local dev config (auto-injected in production)
|
|
52
|
+
├── config/ # Optional deployment configs
|
|
53
|
+
│ ├── theme-green.json # Config variant A
|
|
54
|
+
│ └── theme-blue.json # Config variant B
|
|
55
|
+
├── src/
|
|
56
|
+
│ └── App.tsx # Your plugin code
|
|
57
|
+
└── dist/ # Built plugin files
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Configuration Flow
|
|
61
|
+
|
|
62
|
+
```mermaid
|
|
63
|
+
graph TD
|
|
64
|
+
A[🛠️ Local Development] --> B[.local.json]
|
|
65
|
+
A --> C[config/*.json]
|
|
66
|
+
B --> D[usePluginConfig()]
|
|
67
|
+
C --> D
|
|
68
|
+
|
|
69
|
+
E[🚀 Production] --> F[Platform Injection]
|
|
70
|
+
F --> G[HTTP Headers & Meta Tags]
|
|
71
|
+
G --> D
|
|
72
|
+
|
|
73
|
+
D --> H[Your Plugin Component]
|
|
74
|
+
|
|
75
|
+
style A fill:#e1f5fe
|
|
76
|
+
style E fill:#f3e5f5
|
|
77
|
+
style D fill:#fff3e0
|
|
78
|
+
style H fill:#e8f5e8
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Essential Files
|
|
82
|
+
|
|
83
|
+
#### 1. **`plugin.manifest.json`** - Plugin Metadata
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"pluginId": "my-awesome-plugin",
|
|
88
|
+
"name": "My Awesome Plugin",
|
|
89
|
+
"version": "1.0.0",
|
|
90
|
+
"mode": "direct-mode",
|
|
91
|
+
"router": {
|
|
92
|
+
"basePath": "/",
|
|
93
|
+
"matcher": ".*",
|
|
94
|
+
"excluder": "/checkout"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### 2. **`.local.json`** - Local Development Context
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"storeId": "store_abc123",
|
|
104
|
+
"accountId": "acc_xyz789",
|
|
105
|
+
"basePath": "/"
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
> ⚠️ **Auto-managed**: This file is only for local dev. In production, the platform injects this data automatically.
|
|
110
|
+
|
|
111
|
+
#### 3. **`config/my-theme.json`** - Optional Deployment Config
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"configName": "green-theme",
|
|
116
|
+
"branding": {
|
|
117
|
+
"primaryColor": "#059669",
|
|
118
|
+
"companyName": "My Store",
|
|
119
|
+
"logoUrl": "https://example.com/logo.png"
|
|
120
|
+
},
|
|
121
|
+
"features": {
|
|
122
|
+
"enableChat": true,
|
|
123
|
+
"maxItems": 10
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
> 📝 **Note**: Config can contain any keys you need - the SDK doesn't enforce a specific structure.
|
|
129
|
+
|
|
130
|
+
## 🚀 Quick Start
|
|
131
|
+
|
|
132
|
+
### Installation
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npm install @tagadapay/plugin-sdk
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Basic Plugin Setup
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
import React from 'react';
|
|
142
|
+
import {
|
|
143
|
+
TagadaProvider,
|
|
144
|
+
usePluginConfig,
|
|
145
|
+
useGoogleAutocomplete,
|
|
146
|
+
useISOData,
|
|
147
|
+
useCheckout,
|
|
148
|
+
formatMoney,
|
|
149
|
+
} from '@tagadapay/plugin-sdk/v3';
|
|
150
|
+
|
|
151
|
+
function MyPlugin() {
|
|
152
|
+
const { config, storeId, accountId, basePath, loading } = usePluginConfig();
|
|
153
|
+
|
|
154
|
+
// Optional: Add address autocomplete
|
|
155
|
+
const { searchPlaces, predictions } = useGoogleAutocomplete({
|
|
156
|
+
apiKey: config?.googleMapsApiKey || 'YOUR_API_KEY',
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Optional: Add country/region data
|
|
160
|
+
const { countries } = useISOData('en');
|
|
161
|
+
|
|
162
|
+
// V2: Use TanStack Query-based checkout hook
|
|
163
|
+
const { checkout, init, updateLineItems, isLoading } = useCheckout({
|
|
164
|
+
checkoutToken: 'your-checkout-token',
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
if (loading) return <div>Loading...</div>;
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<div style={{ '--primary': config?.branding?.primaryColor }}>
|
|
171
|
+
<h1>Welcome to {config?.branding?.companyName}</h1>
|
|
172
|
+
<p>Store: {storeId}</p>
|
|
173
|
+
<p>Base Path: {basePath}</p>
|
|
174
|
+
<p>Available Countries: {Object.keys(countries).length}</p>
|
|
175
|
+
|
|
176
|
+
{checkout && (
|
|
177
|
+
<div>
|
|
178
|
+
<h2>Checkout Total: {formatMoney(checkout.summary.total, checkout.currency)}</h2>
|
|
179
|
+
<p>Items: {checkout.lineItems.length}</p>
|
|
180
|
+
</div>
|
|
181
|
+
)}
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Wrap your plugin with TagadaProvider
|
|
187
|
+
function App() {
|
|
188
|
+
return (
|
|
189
|
+
<TagadaProvider>
|
|
190
|
+
<MyPlugin />
|
|
191
|
+
</TagadaProvider>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export default App;
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## 🏗️ V2 Architecture
|
|
199
|
+
|
|
200
|
+
### What's New in V2
|
|
201
|
+
|
|
202
|
+
The TagadaPay Plugin SDK v3 introduces a clean architecture with significant improvements:
|
|
203
|
+
|
|
204
|
+
#### **🔄 TanStack Query Integration**
|
|
205
|
+
|
|
206
|
+
- **Automatic Caching**: All API calls are cached and synchronized across components
|
|
207
|
+
- **Background Refetching**: Data stays fresh with automatic background updates
|
|
208
|
+
- **Optimistic Updates**: Instant UI feedback with automatic rollback on errors
|
|
209
|
+
- **Request Deduplication**: Multiple components can use the same data without duplicate requests
|
|
210
|
+
|
|
211
|
+
#### **🏗️ Clean Architecture**
|
|
212
|
+
|
|
213
|
+
- **Core Layer**: Pure functions and API clients without React dependencies
|
|
214
|
+
- **React Layer**: Hooks and components that use core functions
|
|
215
|
+
- **Better Testing**: Easier to test business logic separately from UI logic
|
|
216
|
+
|
|
217
|
+
#### **📦 Import Paths**
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
// V2 (Recommended)
|
|
221
|
+
import { useCheckout, useOffers, TagadaProvider } from '@tagadapay/plugin-sdk/v3';
|
|
222
|
+
|
|
223
|
+
// Legacy (Still supported)
|
|
224
|
+
import { useCheckout, useOffers, TagadaProvider } from '@tagadapay/plugin-sdk/react';
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### **🔧 Enhanced Developer Experience**
|
|
228
|
+
|
|
229
|
+
- **TypeScript First**: Better type inference and autocomplete
|
|
230
|
+
- **Debug Tools**: Built-in debug drawer for development
|
|
231
|
+
- **Performance**: Reduced bundle size and better performance
|
|
232
|
+
|
|
233
|
+
### Migration from V1
|
|
234
|
+
|
|
235
|
+
Most hooks have the same names but improved APIs:
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
// V1 Pattern
|
|
239
|
+
const { checkout, loading, error } = useCheckout();
|
|
240
|
+
|
|
241
|
+
// V2 Pattern (TanStack Query)
|
|
242
|
+
const { checkout, isLoading, error, refresh } = useCheckout({
|
|
243
|
+
checkoutToken: 'token',
|
|
244
|
+
enabled: true,
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 📚 V2 Examples
|
|
249
|
+
|
|
250
|
+
#### Complete Checkout Flow
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
import React, { useState } from 'react';
|
|
254
|
+
import {
|
|
255
|
+
TagadaProvider,
|
|
256
|
+
useCheckout,
|
|
257
|
+
useProducts,
|
|
258
|
+
usePluginConfig,
|
|
259
|
+
formatMoney,
|
|
260
|
+
} from '@tagadapay/plugin-sdk/v3';
|
|
261
|
+
|
|
262
|
+
function CheckoutPage() {
|
|
263
|
+
const [checkoutToken, setCheckoutToken] = useState<string>();
|
|
264
|
+
const { config } = usePluginConfig();
|
|
265
|
+
|
|
266
|
+
// Load products
|
|
267
|
+
const { products, isLoading: productsLoading } = useProducts({
|
|
268
|
+
storeId: config.storeId,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Initialize checkout when needed
|
|
272
|
+
const {
|
|
273
|
+
checkout,
|
|
274
|
+
isLoading: checkoutLoading,
|
|
275
|
+
init,
|
|
276
|
+
updateLineItems,
|
|
277
|
+
updateCustomer,
|
|
278
|
+
applyPromotionCode,
|
|
279
|
+
} = useCheckout({
|
|
280
|
+
checkoutToken,
|
|
281
|
+
enabled: !!checkoutToken,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const handleInitCheckout = async () => {
|
|
285
|
+
if (!products?.length) return;
|
|
286
|
+
|
|
287
|
+
const result = await init({
|
|
288
|
+
lineItems: [
|
|
289
|
+
{
|
|
290
|
+
variantId: products[0].variants[0].id,
|
|
291
|
+
quantity: 1,
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
setCheckoutToken(result.checkoutToken);
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const handleApplyPromo = async (code: string) => {
|
|
300
|
+
try {
|
|
301
|
+
await applyPromotionCode(code);
|
|
302
|
+
// TanStack Query automatically refetches and updates the UI
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error('Failed to apply promo:', error);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
if (productsLoading) return <div>Loading products...</div>;
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<div>
|
|
312
|
+
<h1>Checkout</h1>
|
|
313
|
+
|
|
314
|
+
{!checkoutToken ? (
|
|
315
|
+
<button onClick={handleInitCheckout}>Start Checkout</button>
|
|
316
|
+
) : (
|
|
317
|
+
<div>
|
|
318
|
+
{checkoutLoading ? (
|
|
319
|
+
<div>Loading checkout...</div>
|
|
320
|
+
) : checkout ? (
|
|
321
|
+
<div>
|
|
322
|
+
<h2>Order Summary</h2>
|
|
323
|
+
<p>Total: {formatMoney(checkout.summary.total, checkout.currency)}</p>
|
|
324
|
+
<p>Items: {checkout.lineItems.length}</p>
|
|
325
|
+
|
|
326
|
+
<div>
|
|
327
|
+
<input
|
|
328
|
+
type="text"
|
|
329
|
+
placeholder="Promo code"
|
|
330
|
+
onKeyDown={(e) => {
|
|
331
|
+
if (e.key === 'Enter') {
|
|
332
|
+
handleApplyPromo(e.currentTarget.value);
|
|
333
|
+
}
|
|
334
|
+
}}
|
|
335
|
+
/>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
) : null}
|
|
339
|
+
</div>
|
|
340
|
+
)}
|
|
341
|
+
</div>
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function App() {
|
|
346
|
+
return (
|
|
347
|
+
<TagadaProvider>
|
|
348
|
+
<CheckoutPage />
|
|
349
|
+
</TagadaProvider>
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
#### Multi-Component Data Sharing
|
|
355
|
+
|
|
356
|
+
```tsx
|
|
357
|
+
import React from 'react';
|
|
358
|
+
import { TagadaProvider, useCheckout, useOffers, formatMoney } from '@tagadapay/plugin-sdk/v3';
|
|
359
|
+
|
|
360
|
+
// Component 1: Cart Summary
|
|
361
|
+
function CartSummary({ checkoutToken }: { checkoutToken: string }) {
|
|
362
|
+
const { checkout, isLoading } = useCheckout({ checkoutToken });
|
|
363
|
+
|
|
364
|
+
if (isLoading) return <div>Loading...</div>;
|
|
365
|
+
if (!checkout) return null;
|
|
366
|
+
|
|
367
|
+
return (
|
|
368
|
+
<div>
|
|
369
|
+
<h3>Cart ({checkout.lineItems.length} items)</h3>
|
|
370
|
+
<p>Total: {formatMoney(checkout.summary.total, checkout.currency)}</p>
|
|
371
|
+
</div>
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Component 2: Available Offers (shares same checkout data automatically)
|
|
376
|
+
function OffersPanel({ checkoutToken }: { checkoutToken: string }) {
|
|
377
|
+
const { checkout } = useCheckout({ checkoutToken }); // Same data, no extra request!
|
|
378
|
+
const { offers, isLoading } = useOffers({
|
|
379
|
+
storeId: checkout?.storeId,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
if (isLoading) return <div>Loading offers...</div>;
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
<div>
|
|
386
|
+
<h3>Special Offers</h3>
|
|
387
|
+
{offers?.map((offer) => (
|
|
388
|
+
<div key={offer.id}>
|
|
389
|
+
<p>{offer.title}</p>
|
|
390
|
+
<p>Save {formatMoney(offer.discount, checkout?.currency || 'USD')}</p>
|
|
391
|
+
</div>
|
|
392
|
+
))}
|
|
393
|
+
</div>
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Main component
|
|
398
|
+
function CheckoutWithOffers() {
|
|
399
|
+
const checkoutToken = 'your-checkout-token';
|
|
400
|
+
|
|
401
|
+
return (
|
|
402
|
+
<div style={{ display: 'flex', gap: '20px' }}>
|
|
403
|
+
<CartSummary checkoutToken={checkoutToken} />
|
|
404
|
+
<OffersPanel checkoutToken={checkoutToken} />
|
|
405
|
+
</div>
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
#### Optimistic Updates
|
|
411
|
+
|
|
412
|
+
```tsx
|
|
413
|
+
import React from 'react';
|
|
414
|
+
import { useCheckout } from '@tagadapay/plugin-sdk/v3';
|
|
415
|
+
|
|
416
|
+
function QuantitySelector({
|
|
417
|
+
checkoutToken,
|
|
418
|
+
variantId,
|
|
419
|
+
currentQuantity,
|
|
420
|
+
}: {
|
|
421
|
+
checkoutToken: string;
|
|
422
|
+
variantId: string;
|
|
423
|
+
currentQuantity: number;
|
|
424
|
+
}) {
|
|
425
|
+
const { checkout, setItemQuantity, updateLineItemsOptimistic } = useCheckout({ checkoutToken });
|
|
426
|
+
|
|
427
|
+
const handleQuantityChange = async (newQuantity: number) => {
|
|
428
|
+
// 1. Optimistic update (instant UI feedback)
|
|
429
|
+
const updatedItems =
|
|
430
|
+
checkout?.lineItems.map((item) =>
|
|
431
|
+
item.variantId === variantId ? { ...item, quantity: newQuantity } : item,
|
|
432
|
+
) || [];
|
|
433
|
+
|
|
434
|
+
updateLineItemsOptimistic(updatedItems);
|
|
435
|
+
|
|
436
|
+
// 2. Server update (with automatic rollback on error)
|
|
437
|
+
try {
|
|
438
|
+
await setItemQuantity(variantId, newQuantity);
|
|
439
|
+
// TanStack Query automatically syncs the real data
|
|
440
|
+
} catch (error) {
|
|
441
|
+
// Automatic rollback to previous state
|
|
442
|
+
console.error('Failed to update quantity:', error);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
return (
|
|
447
|
+
<div>
|
|
448
|
+
<button onClick={() => handleQuantityChange(currentQuantity - 1)}>-</button>
|
|
449
|
+
<span>{currentQuantity}</span>
|
|
450
|
+
<button onClick={() => handleQuantityChange(currentQuantity + 1)}>+</button>
|
|
451
|
+
</div>
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
## 🚀 Initialization Modes
|
|
457
|
+
|
|
458
|
+
The TagadaProvider supports two initialization modes to give you control over when your components render:
|
|
459
|
+
|
|
460
|
+
### Non-Blocking Mode (Default - Recommended)
|
|
461
|
+
|
|
462
|
+
```tsx
|
|
463
|
+
<TagadaProvider>
|
|
464
|
+
{/* Children render immediately after config loads */}
|
|
465
|
+
<YourApp />
|
|
466
|
+
</TagadaProvider>
|
|
467
|
+
|
|
468
|
+
// OR explicitly
|
|
469
|
+
<TagadaProvider blockUntilSessionReady={false}>
|
|
470
|
+
<YourApp />
|
|
471
|
+
</TagadaProvider>
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
**Flow:**
|
|
475
|
+
|
|
476
|
+
1. **Phase 1 & 2** ✅ Plugin config loads → Children render immediately
|
|
477
|
+
2. **Phase 3** 🔄 Session initialization runs in background
|
|
478
|
+
3. **API calls** 🔄 Hooks automatically wait for session to be ready
|
|
479
|
+
|
|
480
|
+
**Benefits:**
|
|
481
|
+
|
|
482
|
+
- ⚡ **Faster rendering** - UI appears immediately
|
|
483
|
+
- 🎯 **Better UX** - Show loading states while session initializes
|
|
484
|
+
- 🔄 **Automatic waiting** - Hooks handle session timing for you
|
|
485
|
+
|
|
486
|
+
### Blocking Mode (Legacy Behavior)
|
|
487
|
+
|
|
488
|
+
```tsx
|
|
489
|
+
<TagadaProvider blockUntilSessionReady={true}>
|
|
490
|
+
{/* Children render only after ALL initialization completes */}
|
|
491
|
+
<YourApp />
|
|
492
|
+
</TagadaProvider>
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Flow:**
|
|
496
|
+
|
|
497
|
+
1. **All Phases** ⏳ Config + Session must complete before children render
|
|
498
|
+
2. **API calls** ✅ Work immediately (no waiting needed)
|
|
499
|
+
|
|
500
|
+
**Use when:**
|
|
501
|
+
|
|
502
|
+
- 🔄 **Migrating** from older SDK versions
|
|
503
|
+
- 🎯 **Simple apps** that don't need progressive loading
|
|
504
|
+
|
|
505
|
+
### Console Logs
|
|
506
|
+
|
|
507
|
+
The SDK logs help you understand which mode you're using:
|
|
508
|
+
|
|
509
|
+
**Non-blocking mode:**
|
|
510
|
+
|
|
511
|
+
```
|
|
512
|
+
✅ Phase 1 & 2 Complete - Plugin config loaded
|
|
513
|
+
🚀 Non-blocking mode: Children can now render - Phase 3 will continue in background
|
|
514
|
+
🔄 [useCheckout] Waiting for session initialization to complete...
|
|
515
|
+
✅ Phase 3 Complete - Session initialization completed successfully
|
|
516
|
+
✅ [useCheckout] Session initialized, proceeding with checkout init
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Blocking mode:**
|
|
520
|
+
|
|
521
|
+
```
|
|
522
|
+
✅ Phase 1 & 2 Complete - Plugin config loaded
|
|
523
|
+
⏳ Blocking mode: Children will render after Phase 3 completes
|
|
524
|
+
✅ Phase 3 Complete - Session initialization completed successfully
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### TagadaProvider API
|
|
528
|
+
|
|
529
|
+
```tsx
|
|
530
|
+
interface TagadaProviderProps {
|
|
531
|
+
children: ReactNode;
|
|
532
|
+
environment?: 'local' | 'development' | 'staging' | 'production';
|
|
533
|
+
customApiConfig?: Partial<EnvironmentConfig>;
|
|
534
|
+
debugMode?: boolean;
|
|
535
|
+
localConfig?: string; // LOCAL DEV ONLY: Override config variant
|
|
536
|
+
blockUntilSessionReady?: boolean; // Default: false
|
|
537
|
+
|
|
538
|
+
// V2 Specific Options
|
|
539
|
+
queryClientConfig?: QueryClientConfig; // TanStack Query configuration
|
|
540
|
+
enableDevtools?: boolean; // Enable React Query Devtools
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
| Prop | Type | Default | Description |
|
|
545
|
+
| ------------------------ | ----------- | -------------------- | ------------------------------ |
|
|
546
|
+
| `children` | `ReactNode` | - | Your plugin components |
|
|
547
|
+
| `environment` | `string` | auto-detect | Override environment detection |
|
|
548
|
+
| `customApiConfig` | `object` | - | Custom API configuration |
|
|
549
|
+
| `debugMode` | `boolean` | auto (false in prod) | Enable debug features & drawer |
|
|
550
|
+
| `localConfig` | `string` | `'default'` | Config variant for local dev |
|
|
551
|
+
| `blockUntilSessionReady` | `boolean` | `false` | Use legacy blocking behavior |
|
|
552
|
+
| `queryClientConfig` | `object` | - | **V2**: TanStack Query config |
|
|
553
|
+
| `enableDevtools` | `boolean` | `false` | **V2**: React Query Devtools |
|
|
554
|
+
|
|
555
|
+
#### V2 Enhanced Provider Features
|
|
556
|
+
|
|
557
|
+
```tsx
|
|
558
|
+
import { TagadaProvider } from '@tagadapay/plugin-sdk/v3';
|
|
559
|
+
|
|
560
|
+
function App() {
|
|
561
|
+
return (
|
|
562
|
+
<TagadaProvider
|
|
563
|
+
debugMode={true}
|
|
564
|
+
enableDevtools={true} // Shows React Query Devtools
|
|
565
|
+
queryClientConfig={{
|
|
566
|
+
defaultOptions: {
|
|
567
|
+
queries: {
|
|
568
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
569
|
+
cacheTime: 10 * 60 * 1000, // 10 minutes
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
}}
|
|
573
|
+
>
|
|
574
|
+
<YourApp />
|
|
575
|
+
</TagadaProvider>
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
> **Version Compatibility:** V2 features require `@tagadapay/plugin-sdk/v3`. The `blockUntilSessionReady` option was added in v2.3.0. For older versions, the blocking behavior was the default and only option.
|
|
581
|
+
|
|
582
|
+
### Development vs Production
|
|
583
|
+
|
|
584
|
+
| Environment | Store/Account ID | Deployment Config | How it Works |
|
|
585
|
+
| -------------- | ---------------- | ----------------- | ------------------ |
|
|
586
|
+
| **Local Dev** | `.local.json` | `config/*.json` | Files on disk |
|
|
587
|
+
| **Production** | HTTP Headers | Meta Tags | Platform injection |
|
|
588
|
+
|
|
589
|
+
```tsx
|
|
590
|
+
// ✅ ALWAYS use hooks - works in both environments
|
|
591
|
+
const { storeId, accountId, basePath, config } = usePluginConfig();
|
|
592
|
+
|
|
593
|
+
// ❌ NEVER access directly
|
|
594
|
+
// const config = window.__PLUGIN_CONFIG__; // Doesn't exist!
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
## 🎯 Key Features
|
|
598
|
+
|
|
599
|
+
### 🔧 **Plugin Configuration System**
|
|
600
|
+
|
|
601
|
+
- **Automatic Context Injection** - Store ID, Account ID, and custom config
|
|
602
|
+
- **Development & Production** - Seamless environment switching
|
|
603
|
+
- **Generic Configuration** - Support for any JSON structure
|
|
604
|
+
- **React Hooks** - Clean, type-safe configuration access
|
|
605
|
+
|
|
606
|
+
### 💳 **Payment Processing**
|
|
607
|
+
|
|
608
|
+
- Secure tokenized payments
|
|
609
|
+
- Multiple payment method support
|
|
610
|
+
- Real-time validation
|
|
611
|
+
- PCI compliance
|
|
612
|
+
|
|
613
|
+
### 🌍 **Address & Location**
|
|
614
|
+
|
|
615
|
+
- **Google Places Autocomplete** - Automatic API loading and address parsing
|
|
616
|
+
- **ISO Country/Region Data** - Complete ISO 3166-1/3166-2 database
|
|
617
|
+
- **Address Validation** - Structured component extraction
|
|
618
|
+
- **Multi-language Support** - 11+ languages for international users
|
|
619
|
+
|
|
620
|
+
### 🛒 **E-commerce Features**
|
|
621
|
+
|
|
622
|
+
- Dynamic pricing and promotional offers
|
|
623
|
+
- Cart management and tax calculations
|
|
624
|
+
- Currency conversion and formatting
|
|
625
|
+
- Customer profile management
|
|
626
|
+
|
|
627
|
+
### 🎨 **UI & Development**
|
|
628
|
+
|
|
629
|
+
- Pre-built React components
|
|
630
|
+
- Customizable themes with configuration
|
|
631
|
+
- Mobile-optimized and accessible
|
|
632
|
+
- TypeScript support throughout
|
|
633
|
+
|
|
634
|
+
## 📖 API Reference
|
|
635
|
+
|
|
636
|
+
### V2 Core Hooks (TanStack Query-based)
|
|
637
|
+
|
|
638
|
+
#### useCheckout()
|
|
639
|
+
|
|
640
|
+
Primary hook for checkout state management using TanStack Query for automatic caching and synchronization.
|
|
641
|
+
|
|
642
|
+
```typescript
|
|
643
|
+
const {
|
|
644
|
+
// Query data
|
|
645
|
+
checkout, // CheckoutData | undefined
|
|
646
|
+
isLoading, // boolean
|
|
647
|
+
error, // Error | null
|
|
648
|
+
isSuccess, // boolean
|
|
649
|
+
|
|
650
|
+
// Actions
|
|
651
|
+
init, // (params: CheckoutInitParams) => Promise<{checkoutUrl, checkoutSession, checkoutToken}>
|
|
652
|
+
refresh, // () => Promise<void>
|
|
653
|
+
|
|
654
|
+
// Checkout operations
|
|
655
|
+
updateLineItems, // (lineItems: CheckoutLineItem[]) => Promise<any>
|
|
656
|
+
updateLineItemsOptimistic, // (lineItems: CheckoutLineItem[]) => void
|
|
657
|
+
setItemQuantity, // (variantId: string, quantity: number, priceId?: string) => Promise<any>
|
|
658
|
+
updateCustomer, // (data: {email: string, acceptsMarketing?: boolean}) => Promise<any>
|
|
659
|
+
applyPromotionCode, // (code: string) => Promise<any>
|
|
660
|
+
removePromotion, // (promotionId: string) => Promise<any>
|
|
661
|
+
} = useCheckout({ checkoutToken, enabled });
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
#### useOffers()
|
|
665
|
+
|
|
666
|
+
Hook for managing dynamic offers and pricing with automatic cache management.
|
|
667
|
+
|
|
668
|
+
```typescript
|
|
669
|
+
const {
|
|
670
|
+
// Query data
|
|
671
|
+
offers, // Offer[] | undefined
|
|
672
|
+
isLoading, // boolean
|
|
673
|
+
error, // Error | null
|
|
674
|
+
|
|
675
|
+
// Actions
|
|
676
|
+
refresh, // () => Promise<void>
|
|
677
|
+
} = useOffers({ storeId, enabled });
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
#### usePromotions()
|
|
681
|
+
|
|
682
|
+
Hook for managing promotion codes in checkout sessions with TanStack Query.
|
|
683
|
+
|
|
684
|
+
```typescript
|
|
685
|
+
const {
|
|
686
|
+
// Query data
|
|
687
|
+
promotions, // Promotion[] | undefined
|
|
688
|
+
isLoading, // boolean
|
|
689
|
+
error, // Error | null
|
|
690
|
+
|
|
691
|
+
// Actions
|
|
692
|
+
refresh, // () => Promise<void>
|
|
693
|
+
} = usePromotions({ checkoutToken, enabled });
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
#### useProducts()
|
|
697
|
+
|
|
698
|
+
Hook for product data management with caching.
|
|
699
|
+
|
|
700
|
+
```typescript
|
|
701
|
+
const {
|
|
702
|
+
// Query data
|
|
703
|
+
products, // Product[] | undefined
|
|
704
|
+
isLoading, // boolean
|
|
705
|
+
error, // Error | null
|
|
706
|
+
|
|
707
|
+
// Actions
|
|
708
|
+
refresh, // () => Promise<void>
|
|
709
|
+
} = useProducts({ storeId, enabled });
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
#### useStoreConfig()
|
|
713
|
+
|
|
714
|
+
Hook for fetching store configuration with automatic caching.
|
|
715
|
+
|
|
716
|
+
```typescript
|
|
717
|
+
const {
|
|
718
|
+
// Query data
|
|
719
|
+
storeConfig, // StoreConfig | undefined
|
|
720
|
+
isLoading, // boolean
|
|
721
|
+
error, // Error | null
|
|
722
|
+
isSuccess, // boolean
|
|
723
|
+
|
|
724
|
+
// Actions
|
|
725
|
+
refetch, // () => Promise<void>
|
|
726
|
+
} = useStoreConfig({ storeId, enabled });
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
**Example:**
|
|
730
|
+
|
|
731
|
+
```tsx
|
|
732
|
+
import { useStoreConfig } from '@tagadapay/plugin-sdk/v3';
|
|
733
|
+
|
|
734
|
+
function StoreInfo() {
|
|
735
|
+
const { storeConfig, isLoading } = useStoreConfig();
|
|
736
|
+
|
|
737
|
+
if (isLoading) return <div>Loading...</div>;
|
|
738
|
+
|
|
739
|
+
return (
|
|
740
|
+
<div>
|
|
741
|
+
<h1>{storeConfig?.storeName}</h1>
|
|
742
|
+
<p>Currency: {storeConfig?.currency}</p>
|
|
743
|
+
</div>
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
See [useStoreConfig documentation](./docs/README-useStoreConfig.md) for more details.
|
|
749
|
+
|
|
750
|
+
#### useOrder()
|
|
751
|
+
|
|
752
|
+
Hook for order management and tracking.
|
|
753
|
+
|
|
754
|
+
```typescript
|
|
755
|
+
const {
|
|
756
|
+
// Query data
|
|
757
|
+
order, // Order | undefined
|
|
758
|
+
isLoading, // boolean
|
|
759
|
+
error, // Error | null
|
|
760
|
+
|
|
761
|
+
// Actions
|
|
762
|
+
refresh, // () => Promise<void>
|
|
763
|
+
} = useOrder({ orderId, enabled });
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
#### usePostPurchases()
|
|
767
|
+
|
|
768
|
+
Hook for post-purchase offers management.
|
|
769
|
+
|
|
770
|
+
```typescript
|
|
771
|
+
const {
|
|
772
|
+
// Query data
|
|
773
|
+
postPurchases, // PostPurchaseOffer[] | undefined
|
|
774
|
+
isLoading, // boolean
|
|
775
|
+
error, // Error | null
|
|
776
|
+
|
|
777
|
+
// Actions
|
|
778
|
+
refresh, // () => Promise<void>
|
|
779
|
+
} = usePostPurchases({ orderId, enabled });
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
#### usePayment()
|
|
783
|
+
|
|
784
|
+
Hook for payment processing with 3DS support.
|
|
785
|
+
|
|
786
|
+
```typescript
|
|
787
|
+
const {
|
|
788
|
+
// Payment methods
|
|
789
|
+
processPayment, // (options: PaymentOptions) => Promise<PaymentResponse>
|
|
790
|
+
processApplePayPayment, // (token: ApplePayToken) => Promise<PaymentResponse>
|
|
791
|
+
|
|
792
|
+
// State
|
|
793
|
+
isProcessing, // boolean
|
|
794
|
+
error, // Error | null
|
|
795
|
+
} = usePayment();
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
### V2 Utility Functions & Advanced Features
|
|
799
|
+
|
|
800
|
+
#### TanStack Query Integration
|
|
801
|
+
|
|
802
|
+
V2 provides direct access to TanStack Query features for advanced use cases:
|
|
803
|
+
|
|
804
|
+
```typescript
|
|
805
|
+
import {
|
|
806
|
+
useApiQuery,
|
|
807
|
+
useApiMutation,
|
|
808
|
+
useInvalidateQuery,
|
|
809
|
+
usePreloadQuery,
|
|
810
|
+
queryKeys,
|
|
811
|
+
} from '@tagadapay/plugin-sdk/v3';
|
|
812
|
+
|
|
813
|
+
// Custom API queries
|
|
814
|
+
const { data, isLoading } = useApiQuery({
|
|
815
|
+
queryKey: ['custom', 'endpoint'],
|
|
816
|
+
queryFn: () => apiClient.get('/custom-endpoint'),
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
// Mutations with automatic cache updates
|
|
820
|
+
const mutation = useApiMutation({
|
|
821
|
+
mutationFn: (data) => apiClient.post('/update', data),
|
|
822
|
+
onSuccess: () => {
|
|
823
|
+
// Invalidate related queries
|
|
824
|
+
invalidateQuery(['checkout']);
|
|
825
|
+
},
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
// Preload data for better UX
|
|
829
|
+
const preloadCheckout = usePreloadQuery();
|
|
830
|
+
preloadCheckout({
|
|
831
|
+
queryKey: queryKeys.checkout(checkoutToken),
|
|
832
|
+
queryFn: () => checkoutResource.get(checkoutToken),
|
|
833
|
+
});
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
#### Money Utilities
|
|
837
|
+
|
|
838
|
+
Enhanced money formatting with better TypeScript support:
|
|
839
|
+
|
|
840
|
+
```typescript
|
|
841
|
+
import { formatMoney, convertCurrency, getCurrencyInfo } from '@tagadapay/plugin-sdk/v3';
|
|
842
|
+
|
|
843
|
+
// Format money with automatic currency detection
|
|
844
|
+
const formatted = formatMoney(2999, 'USD'); // "$29.99"
|
|
845
|
+
const simple = formatSimpleMoney(2999, 'EUR'); // "29.99"
|
|
846
|
+
const withoutSymbol = formatMoneyWithoutSymbol(2999, 'GBP'); // "29.99"
|
|
847
|
+
|
|
848
|
+
// Currency conversion (if rates available)
|
|
849
|
+
const converted = convertCurrency(2999, 'USD', 'EUR'); // 2699
|
|
850
|
+
|
|
851
|
+
// Get currency information
|
|
852
|
+
const currencyInfo = getCurrencyInfo('USD');
|
|
853
|
+
// { symbol: '$', code: 'USD', minorUnits: 2 }
|
|
854
|
+
|
|
855
|
+
// Convert between major and minor units
|
|
856
|
+
const major = minorUnitsToMajorUnits(2999); // 29.99
|
|
857
|
+
const minor = moneyStringOrNumberToMinorUnits('29.99'); // 2999
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
#### Core Functions (Pure Functions)
|
|
861
|
+
|
|
862
|
+
V2 separates core business logic from React hooks:
|
|
863
|
+
|
|
864
|
+
```typescript
|
|
865
|
+
import {
|
|
866
|
+
CheckoutResource,
|
|
867
|
+
ProductsResource,
|
|
868
|
+
PaymentsResource,
|
|
869
|
+
PluginConfigUtils,
|
|
870
|
+
} from '@tagadapay/plugin-sdk/v3';
|
|
871
|
+
|
|
872
|
+
// Use core functions directly (useful for server-side or non-React contexts)
|
|
873
|
+
const checkoutResource = new CheckoutResource(apiClient);
|
|
874
|
+
const checkout = await checkoutResource.get(checkoutToken);
|
|
875
|
+
|
|
876
|
+
// Plugin config utilities
|
|
877
|
+
const config = PluginConfigUtils.getPluginConfig(rawConfig, context);
|
|
878
|
+
const isValid = PluginConfigUtils.validateConfig(config);
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
#### Debug Tools
|
|
882
|
+
|
|
883
|
+
V2 includes built-in debugging tools:
|
|
884
|
+
|
|
885
|
+
```tsx
|
|
886
|
+
import { TagadaProvider } from '@tagadapay/plugin-sdk/v3';
|
|
887
|
+
|
|
888
|
+
function App() {
|
|
889
|
+
return (
|
|
890
|
+
<TagadaProvider
|
|
891
|
+
debugMode={true} // Shows debug drawer in development
|
|
892
|
+
environment="development"
|
|
893
|
+
>
|
|
894
|
+
<YourApp />
|
|
895
|
+
</TagadaProvider>
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
## 🛠️ Development
|
|
901
|
+
|
|
902
|
+
### Local Development
|
|
903
|
+
|
|
904
|
+
```bash
|
|
905
|
+
# Install dependencies
|
|
906
|
+
npm install
|
|
907
|
+
|
|
908
|
+
# Start development server
|
|
909
|
+
npm run dev
|
|
910
|
+
|
|
911
|
+
# Build for production
|
|
912
|
+
npm run build
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
### Testing
|
|
916
|
+
|
|
917
|
+
```bash
|
|
918
|
+
# Run tests
|
|
919
|
+
npm test
|
|
920
|
+
|
|
921
|
+
# Run tests with coverage
|
|
922
|
+
npm run test:coverage
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
### Linting
|
|
926
|
+
|
|
927
|
+
```bash
|
|
928
|
+
# Check code style
|
|
929
|
+
npm run lint
|
|
930
|
+
|
|
931
|
+
# Fix linting issues
|
|
932
|
+
npm run lint:fix
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
## 📦 Build & Deploy
|
|
936
|
+
|
|
937
|
+
### Building Your Plugin
|
|
938
|
+
|
|
939
|
+
```bash
|
|
940
|
+
# Build optimized bundle
|
|
941
|
+
npm run build
|
|
942
|
+
|
|
943
|
+
# Analyze bundle size
|
|
944
|
+
npm run analyze
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
### Deployment
|
|
948
|
+
|
|
949
|
+
```bash
|
|
950
|
+
# Deploy using TagadaPay CLI
|
|
951
|
+
npx @tagadapay/plugin-cli deploy
|
|
952
|
+
|
|
953
|
+
# Deploy specific environment
|
|
954
|
+
npx @tagadapay/plugin-cli deploy --env production
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
## 🔧 Configuration
|
|
958
|
+
|
|
959
|
+
### Plugin Configuration
|
|
960
|
+
|
|
961
|
+
```json
|
|
962
|
+
{
|
|
963
|
+
"name": "My Checkout Plugin",
|
|
964
|
+
"version": "1.0.0",
|
|
965
|
+
"description": "Custom checkout experience",
|
|
966
|
+
"main": "dist/index.js",
|
|
967
|
+
"tagadapay": {
|
|
968
|
+
"type": "checkout",
|
|
969
|
+
"mode": "direct",
|
|
970
|
+
"framework": "react"
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
## 🔐 Security
|
|
976
|
+
|
|
977
|
+
### Best Practices
|
|
978
|
+
|
|
979
|
+
- Never store sensitive payment data
|
|
980
|
+
- Always validate user inputs
|
|
981
|
+
- Use HTTPS in production
|
|
982
|
+
- Implement proper error handling
|
|
983
|
+
- Follow PCI DSS guidelines
|
|
984
|
+
|
|
985
|
+
### Token Management
|
|
986
|
+
|
|
987
|
+
```typescript
|
|
988
|
+
// ✅ Good: Use tokenized payments
|
|
989
|
+
const paymentToken = await tokenizePayment(cardData);
|
|
990
|
+
await processPayment({ token: paymentToken });
|
|
991
|
+
|
|
992
|
+
// ❌ Bad: Never store raw card data
|
|
993
|
+
// const cardNumber = '4111111111111111'; // Don't do this
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
## 📱 Mobile Optimization
|
|
997
|
+
|
|
998
|
+
### Responsive Design
|
|
999
|
+
|
|
1000
|
+
```css
|
|
1001
|
+
.checkout-container {
|
|
1002
|
+
max-width: 600px;
|
|
1003
|
+
margin: 0 auto;
|
|
1004
|
+
padding: 16px;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
@media (max-width: 768px) {
|
|
1008
|
+
.checkout-container {
|
|
1009
|
+
padding: 8px;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
.checkout-form {
|
|
1013
|
+
font-size: 16px; /* Prevent zoom on iOS */
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
### Touch Interactions
|
|
1019
|
+
|
|
1020
|
+
```typescript
|
|
1021
|
+
const handleTouchStart = (e) => {
|
|
1022
|
+
// Optimize for touch devices
|
|
1023
|
+
e.preventDefault();
|
|
1024
|
+
// Handle touch interaction
|
|
1025
|
+
};
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
## 🌐 Internationalization
|
|
1029
|
+
|
|
1030
|
+
### Multi-language Support
|
|
1031
|
+
|
|
1032
|
+
```typescript
|
|
1033
|
+
import { useTranslation } from '@tagadapay/plugin-sdk';
|
|
1034
|
+
|
|
1035
|
+
function CheckoutForm() {
|
|
1036
|
+
const { t } = useTranslation();
|
|
1037
|
+
|
|
1038
|
+
return (
|
|
1039
|
+
<form>
|
|
1040
|
+
<label>{t('checkout.email')}</label>
|
|
1041
|
+
<input type="email" placeholder={t('checkout.email_placeholder')} />
|
|
1042
|
+
</form>
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
### Currency Support
|
|
1048
|
+
|
|
1049
|
+
```typescript
|
|
1050
|
+
import { useCurrency } from '@tagadapay/plugin-sdk';
|
|
1051
|
+
|
|
1052
|
+
function PriceDisplay({ amount }) {
|
|
1053
|
+
const { formatPrice, currency } = useCurrency();
|
|
1054
|
+
|
|
1055
|
+
return <span>{formatPrice(amount, currency)}</span>;
|
|
1056
|
+
}
|
|
1057
|
+
```
|
|
1058
|
+
|
|
1059
|
+
## 📊 Analytics & Monitoring
|
|
1060
|
+
|
|
1061
|
+
### Event Tracking
|
|
1062
|
+
|
|
1063
|
+
```typescript
|
|
1064
|
+
import { trackEvent } from '@tagadapay/plugin-sdk';
|
|
1065
|
+
|
|
1066
|
+
// Track user interactions
|
|
1067
|
+
trackEvent('checkout_started', {
|
|
1068
|
+
product_id: 'prod_123',
|
|
1069
|
+
value: 2999,
|
|
1070
|
+
currency: 'USD',
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
// Track conversions
|
|
1074
|
+
trackEvent('purchase_completed', {
|
|
1075
|
+
transaction_id: 'txn_456',
|
|
1076
|
+
value: 2999,
|
|
1077
|
+
currency: 'USD',
|
|
1078
|
+
});
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
### Performance Monitoring
|
|
1082
|
+
|
|
1083
|
+
```typescript
|
|
1084
|
+
import { performance } from '@tagadapay/plugin-sdk';
|
|
1085
|
+
|
|
1086
|
+
// Measure load times
|
|
1087
|
+
performance.mark('checkout-start');
|
|
1088
|
+
// ... checkout logic
|
|
1089
|
+
performance.mark('checkout-end');
|
|
1090
|
+
performance.measure('checkout-duration', 'checkout-start', 'checkout-end');
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
## 🤝 Contributing
|
|
1094
|
+
|
|
1095
|
+
### Development Setup
|
|
1096
|
+
|
|
1097
|
+
```bash
|
|
1098
|
+
# Clone the repository
|
|
1099
|
+
git clone https://github.com/tagadapay/plugin-sdk.git
|
|
1100
|
+
|
|
1101
|
+
# Install dependencies
|
|
1102
|
+
npm install
|
|
1103
|
+
|
|
1104
|
+
# Start development
|
|
1105
|
+
npm run dev
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
### Submitting Changes
|
|
1109
|
+
|
|
1110
|
+
1. Fork the repository
|
|
1111
|
+
2. Create a feature branch
|
|
1112
|
+
3. Make your changes
|
|
1113
|
+
4. Add tests
|
|
1114
|
+
5. Submit a pull request
|
|
1115
|
+
|
|
1116
|
+
## 📄 License
|
|
1117
|
+
|
|
1118
|
+
MIT License - see [LICENSE](./LICENSE) for details.
|
|
1119
|
+
|
|
1120
|
+
## 🆘 Support
|
|
1121
|
+
|
|
1122
|
+
- **Documentation**: [docs.tagadapay.com](https://docs.tagadapay.com)
|
|
1123
|
+
- **Discord**: [discord.gg/tagadapay](https://discord.gg/tagadapay)
|
|
1124
|
+
- **Email**: support@tagadapay.com
|
|
1125
|
+
- **GitHub Issues**: [github.com/tagadapay/plugin-sdk/issues](https://github.com/tagadapay/plugin-sdk/issues)
|
|
1126
|
+
|
|
1127
|
+
---
|
|
1128
|
+
|
|
1129
|
+
Built with ❤️ by the TagadaPay team
|