@tagadapay/plugin-sdk
v4.4.1
Published
Modern React SDK for building Tagada Pay plugins
Readme
TagadaPay Plugin SDK
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.
This SDK runs inside TagadaPay's plugin / funnel runtime (window.__TGD_STEP_CONFIG__). It is not the integration for WordPress, Shopify, or a site you host yourself — those are @tagadapay/headless-sdk (TagadaPay manages the cart) or @tagadapay/core-js + Node payments.process() (you already have the order). See Choose how you accept payments.
🚀 V2 is the supported surface. TanStack Query integration, improved TypeScript support, and better performance. See V2 Architecture for details.
Recommended: Use
@tagadapay/plugin-sdk/v2for new projects. V1 (/react) is still supported for existing projects but no longer receives new features.
📚 Documentation
V2 Core APIs (Recommended)
- V2 Architecture - New TanStack Query-based architecture
- V2 Examples - Complete examples using the new V2 hooks
- useCheckout (V2) - TanStack Query-based checkout management
- useOffers (V2) - Dynamic pricing with automatic caching
- usePreviewOffer (V2) - Post-checkout upsell page (auto-applies MIT/CIT)
- useProducts (V2) - Product data management
- useStoreConfig (V2) - Store configuration with automatic caching
- usePayment (V2) - Payment processing with 3DS support
- V2 Utility Functions - Enhanced utilities and TanStack Query integration
Legacy V1 APIs (Still Supported)
- useCheckout (V1) - Legacy checkout state management
- setCheckoutInfo - Customer information and validation
- useOffers (V1) - Legacy dynamic pricing
- Money utilities - Currency formatting and calculations
- URL utilities - Navigation and routing helpers
Plugin Development
- Plugin Configuration - How to access store context, config, and branding
- Initialization Modes - Choose between blocking and non-blocking initialization
- Google Autocomplete - Address autocomplete with Google Places API
- ISO Data - Country and region data with Google integration
Examples
- Vite Checkout Demo - Complete checkout implementation
🏗️ Building a Plugin
Plugin Structure
Every TagadaPay plugin follows this simple structure:
my-plugin/
├── plugin.manifest.json # Plugin metadata & routing
├── .local.json # Local dev config (auto-injected in production)
├── config/ # Optional deployment configs
│ ├── theme-green.json # Config variant A
│ └── theme-blue.json # Config variant B
├── src/
│ └── App.tsx # Your plugin code
└── dist/ # Built plugin filesConfiguration Flow
graph TD
A[🛠️ Local Development] --> B[.local.json]
A --> C[config/*.json]
B --> D[usePluginConfig()]
C --> D
E[🚀 Production] --> F[Platform Injection]
F --> G[HTTP Headers & Meta Tags]
G --> D
D --> H[Your Plugin Component]
style A fill:#e1f5fe
style E fill:#f3e5f5
style D fill:#fff3e0
style H fill:#e8f5e8Essential Files
1. plugin.manifest.json - Plugin Metadata
{
"pluginId": "my-awesome-plugin",
"name": "My Awesome Plugin",
"version": "1.0.0",
"mode": "direct-mode",
"router": {
"basePath": "/",
"matcher": ".*",
"excluder": "/checkout"
}
}2. .local.json - Local Development Context
{
"storeId": "store_abc123",
"accountId": "acc_xyz789",
"basePath": "/"
}⚠️ Auto-managed: This file is only for local dev. In production, the platform injects this data automatically.
3. config/my-theme.json - Optional Deployment Config
{
"configName": "green-theme",
"branding": {
"primaryColor": "#059669",
"companyName": "My Store",
"logoUrl": "https://example.com/logo.png"
},
"features": {
"enableChat": true,
"maxItems": 10
}
}📝 Note: Config can contain any keys you need - the SDK doesn't enforce a specific structure.
🚀 Quick Start
Installation
npm install @tagadapay/plugin-sdkBasic Plugin Setup
import React from 'react';
import {
TagadaProvider,
usePluginConfig,
useGoogleAutocomplete,
useISOData,
useCheckout,
formatMoney,
} from '@tagadapay/plugin-sdk/v2';
function MyPlugin() {
const { config, storeId, accountId, basePath, loading } = usePluginConfig();
// Optional: Add address autocomplete
const { searchPlaces, predictions } = useGoogleAutocomplete({
apiKey: config?.googleMapsApiKey || 'YOUR_API_KEY',
});
// Optional: Add country/region data
const { countries } = useISOData('en');
// V2: Use TanStack Query-based checkout hook
const { checkout, init, updateLineItems, isLoading } = useCheckout({
checkoutToken: 'your-checkout-token',
});
if (loading) return <div>Loading...</div>;
return (
<div style={{ '--primary': config?.branding?.primaryColor }}>
<h1>Welcome to {config?.branding?.companyName}</h1>
<p>Store: {storeId}</p>
<p>Base Path: {basePath}</p>
<p>Available Countries: {Object.keys(countries).length}</p>
{checkout && (
<div>
<h2>Checkout Total: {formatMoney(checkout.summary.total, checkout.currency)}</h2>
<p>Items: {checkout.lineItems.length}</p>
</div>
)}
</div>
);
}
// Wrap your plugin with TagadaProvider
function App() {
return (
<TagadaProvider>
<MyPlugin />
</TagadaProvider>
);
}
export default App;🏗️ V2 Architecture
What's New in V2
The TagadaPay Plugin SDK V2 introduces a clean architecture with significant improvements:
🔄 TanStack Query Integration
- Automatic Caching: All API calls are cached and synchronized across components
- Background Refetching: Data stays fresh with automatic background updates
- Optimistic Updates: Instant UI feedback with automatic rollback on errors
- Request Deduplication: Multiple components can use the same data without duplicate requests
🏗️ Clean Architecture
- Core Layer: Pure functions and API clients without React dependencies
- React Layer: Hooks and components that use core functions
- Better Testing: Easier to test business logic separately from UI logic
📦 Import Paths
// V2 (Recommended)
import { useCheckout, useOffers, TagadaProvider } from '@tagadapay/plugin-sdk/v2';
// Legacy (Still supported)
import { useCheckout, useOffers, TagadaProvider } from '@tagadapay/plugin-sdk/react';🔧 Enhanced Developer Experience
- TypeScript First: Better type inference and autocomplete
- Debug Tools: Built-in debug drawer for development
- Performance: Reduced bundle size and better performance
Migration from V1
Most hooks have the same names but improved APIs:
// V1 Pattern
const { checkout, loading, error } = useCheckout();
// V2 Pattern (TanStack Query)
const { checkout, isLoading, error, refresh } = useCheckout({
checkoutToken: 'token',
enabled: true,
});📚 V2 Examples
Complete Checkout Flow
import React, { useState } from 'react';
import {
TagadaProvider,
useCheckout,
useProducts,
usePluginConfig,
formatMoney,
} from '@tagadapay/plugin-sdk/v2';
function CheckoutPage() {
const [checkoutToken, setCheckoutToken] = useState<string>();
const { config } = usePluginConfig();
// Load products
const { products, isLoading: productsLoading } = useProducts({
storeId: config.storeId,
});
// Initialize checkout when needed
const {
checkout,
isLoading: checkoutLoading,
init,
updateLineItems,
updateCustomer,
applyPromotionCode,
} = useCheckout({
checkoutToken,
enabled: !!checkoutToken,
});
const handleInitCheckout = async () => {
if (!products?.length) return;
const result = await init({
lineItems: [
{
variantId: products[0].variants[0].id,
quantity: 1,
},
],
});
setCheckoutToken(result.checkoutToken);
};
const handleApplyPromo = async (code: string) => {
try {
await applyPromotionCode(code);
// TanStack Query automatically refetches and updates the UI
} catch (error) {
console.error('Failed to apply promo:', error);
}
};
if (productsLoading) return <div>Loading products...</div>;
return (
<div>
<h1>Checkout</h1>
{!checkoutToken ? (
<button onClick={handleInitCheckout}>Start Checkout</button>
) : (
<div>
{checkoutLoading ? (
<div>Loading checkout...</div>
) : checkout ? (
<div>
<h2>Order Summary</h2>
<p>Total: {formatMoney(checkout.summary.total, checkout.currency)}</p>
<p>Items: {checkout.lineItems.length}</p>
<div>
<input
type="text"
placeholder="Promo code"
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleApplyPromo(e.currentTarget.value);
}
}}
/>
</div>
</div>
) : null}
</div>
)}
</div>
);
}
function App() {
return (
<TagadaProvider>
<CheckoutPage />
</TagadaProvider>
);
}Multi-Component Data Sharing
import React from 'react';
import { TagadaProvider, useCheckout, useOffers, formatMoney } from '@tagadapay/plugin-sdk/v2';
// Component 1: Cart Summary
function CartSummary({ checkoutToken }: { checkoutToken: string }) {
const { checkout, isLoading } = useCheckout({ checkoutToken });
if (isLoading) return <div>Loading...</div>;
if (!checkout) return null;
return (
<div>
<h3>Cart ({checkout.lineItems.length} items)</h3>
<p>Total: {formatMoney(checkout.summary.total, checkout.currency)}</p>
</div>
);
}
// Component 2: Available Offers (shares same checkout data automatically)
function OffersPanel({ checkoutToken }: { checkoutToken: string }) {
const { checkout } = useCheckout({ checkoutToken }); // Same data, no extra request!
const { offers, isLoading } = useOffers({
storeId: checkout?.storeId,
});
if (isLoading) return <div>Loading offers...</div>;
return (
<div>
<h3>Special Offers</h3>
{offers?.map((offer) => (
<div key={offer.id}>
<p>{offer.title}</p>
<p>Save {formatMoney(offer.discount, checkout?.currency || 'USD')}</p>
</div>
))}
</div>
);
}
// Main component
function CheckoutWithOffers() {
const checkoutToken = 'your-checkout-token';
return (
<div style={{ display: 'flex', gap: '20px' }}>
<CartSummary checkoutToken={checkoutToken} />
<OffersPanel checkoutToken={checkoutToken} />
</div>
);
}Optimistic Updates
import React from 'react';
import { useCheckout } from '@tagadapay/plugin-sdk/v2';
function QuantitySelector({
checkoutToken,
variantId,
currentQuantity,
}: {
checkoutToken: string;
variantId: string;
currentQuantity: number;
}) {
const { checkout, setItemQuantity, updateLineItemsOptimistic } = useCheckout({ checkoutToken });
const handleQuantityChange = async (newQuantity: number) => {
// 1. Optimistic update (instant UI feedback)
const updatedItems =
checkout?.lineItems.map((item) =>
item.variantId === variantId ? { ...item, quantity: newQuantity } : item,
) || [];
updateLineItemsOptimistic(updatedItems);
// 2. Server update (with automatic rollback on error)
try {
await setItemQuantity(variantId, newQuantity);
// TanStack Query automatically syncs the real data
} catch (error) {
// Automatic rollback to previous state
console.error('Failed to update quantity:', error);
}
};
return (
<div>
<button onClick={() => handleQuantityChange(currentQuantity - 1)}>-</button>
<span>{currentQuantity}</span>
<button onClick={() => handleQuantityChange(currentQuantity + 1)}>+</button>
</div>
);
}🚀 Initialization Modes
The TagadaProvider supports two initialization modes to give you control over when your components render:
Non-Blocking Mode (Default - Recommended)
<TagadaProvider>
{/* Children render immediately after config loads */}
<YourApp />
</TagadaProvider>
// OR explicitly
<TagadaProvider blockUntilSessionReady={false}>
<YourApp />
</TagadaProvider>Flow:
- Phase 1 & 2 ✅ Plugin config loads → Children render immediately
- Phase 3 🔄 Session initialization runs in background
- API calls 🔄 Hooks automatically wait for session to be ready
Benefits:
- ⚡ Faster rendering - UI appears immediately
- 🎯 Better UX - Show loading states while session initializes
- 🔄 Automatic waiting - Hooks handle session timing for you
Blocking Mode (Legacy Behavior)
<TagadaProvider blockUntilSessionReady={true}>
{/* Children render only after ALL initialization completes */}
<YourApp />
</TagadaProvider>Flow:
- All Phases ⏳ Config + Session must complete before children render
- API calls ✅ Work immediately (no waiting needed)
Use when:
- 🔄 Migrating from older SDK versions
- 🎯 Simple apps that don't need progressive loading
Console Logs
The SDK logs help you understand which mode you're using:
Non-blocking mode:
✅ Phase 1 & 2 Complete - Plugin config loaded
🚀 Non-blocking mode: Children can now render - Phase 3 will continue in background
🔄 [useCheckout] Waiting for session initialization to complete...
✅ Phase 3 Complete - Session initialization completed successfully
✅ [useCheckout] Session initialized, proceeding with checkout initBlocking mode:
✅ Phase 1 & 2 Complete - Plugin config loaded
⏳ Blocking mode: Children will render after Phase 3 completes
✅ Phase 3 Complete - Session initialization completed successfullyTagadaProvider API
interface TagadaProviderProps {
children: ReactNode;
environment?: 'local' | 'development' | 'staging' | 'production';
customApiConfig?: Partial<EnvironmentConfig>;
debugMode?: boolean;
localConfig?: string; // LOCAL DEV ONLY: Override config variant
blockUntilSessionReady?: boolean; // Default: false
// V2 Specific Options
queryClientConfig?: QueryClientConfig; // TanStack Query configuration
enableDevtools?: boolean; // Enable React Query Devtools
}| Prop | Type | Default | Description |
| ------------------------ | ----------- | -------------------- | ------------------------------ |
| children | ReactNode | - | Your plugin components |
| environment | string | auto-detect | Override environment detection |
| customApiConfig | object | - | Custom API configuration |
| debugMode | boolean | auto (false in prod) | Enable debug features & drawer |
| localConfig | string | 'default' | Config variant for local dev |
| blockUntilSessionReady | boolean | false | Use legacy blocking behavior |
| queryClientConfig | object | - | V2: TanStack Query config |
| enableDevtools | boolean | false | V2: React Query Devtools |
V2 Enhanced Provider Features
import { TagadaProvider } from '@tagadapay/plugin-sdk/v2';
function App() {
return (
<TagadaProvider
debugMode={true}
enableDevtools={true} // Shows React Query Devtools
queryClientConfig={{
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 10 * 60 * 1000, // 10 minutes
},
},
}}
>
<YourApp />
</TagadaProvider>
);
}Version Compatibility: V2 features require
@tagadapay/plugin-sdk/v2. TheblockUntilSessionReadyoption was added in v2.3.0. For older versions, the blocking behavior was the default and only option.
Development vs Production
| Environment | Store/Account ID | Deployment Config | How it Works |
| -------------- | ---------------- | ----------------- | ------------------ |
| Local Dev | .local.json | config/*.json | Files on disk |
| Production | HTTP Headers | Meta Tags | Platform injection |
// ✅ ALWAYS use hooks - works in both environments
const { storeId, accountId, basePath, config } = usePluginConfig();
// ❌ NEVER access directly
// const config = window.__PLUGIN_CONFIG__; // Doesn't exist!🎯 Key Features
🔧 Plugin Configuration System
- Automatic Context Injection - Store ID, Account ID, and custom config
- Development & Production - Seamless environment switching
- Generic Configuration - Support for any JSON structure
- React Hooks - Clean, type-safe configuration access
💳 Payment Processing
- Secure tokenized payments
- Multiple payment method support
- Real-time validation
- PCI compliance
🌍 Address & Location
- Google Places Autocomplete - Automatic API loading and address parsing
- ISO Country/Region Data - Complete ISO 3166-1/3166-2 database
- Address Validation - Structured component extraction
- Multi-language Support - 11+ languages for international users
🛒 E-commerce Features
- Dynamic pricing and promotional offers
- Cart management and tax calculations
- Currency conversion and formatting
- Customer profile management
🎨 UI & Development
- Pre-built React components
- Customizable themes with configuration
- Mobile-optimized and accessible
- TypeScript support throughout
📖 API Reference
V2 Core Hooks (TanStack Query-based)
useCheckout()
Primary hook for checkout state management using TanStack Query for automatic caching and synchronization.
const {
// Query data
checkout, // CheckoutData | undefined
isLoading, // boolean
error, // Error | null
isSuccess, // boolean
// Actions
init, // (params: CheckoutInitParams) => Promise<{checkoutUrl, checkoutSession, checkoutToken}>
refresh, // () => Promise<void>
// Checkout operations
updateLineItems, // (lineItems: CheckoutLineItem[]) => Promise<any>
updateLineItemsOptimistic, // (lineItems: CheckoutLineItem[]) => void
setItemQuantity, // (variantId: string, quantity: number, priceId?: string) => Promise<any>
updateCustomer, // (data: {email: string, acceptsMarketing?: boolean}) => Promise<any>
applyPromotionCode, // (code: string) => Promise<any>
removePromotion, // (promotionId: string) => Promise<any>
} = useCheckout({ checkoutToken, enabled });useOffers()
Hook for managing dynamic offers and pricing with automatic cache management.
const {
// Query data
offers, // Offer[] | undefined
isLoading, // boolean
error, // Error | null
// Actions
refresh, // () => Promise<void>
} = useOffers({ storeId, enabled });⚠️
useOffer(singular) is deprecated. UseusePreviewOfferfor upsell pages — it skips the upfront checkout-session round-trip, recomputes pricing client-side, and auto-applies the CRM-configured MIT/CIT payment initiator.useOfferwill be removed in a future major version.
usePreviewOffer()
Lightweight hook for post-checkout upsell pages. Fetches an offer with a precomputed pricing summary, lets the customer pick variants and quantities, and recomputes totals client-side without round-tripping the backend on every change. Pay once the customer accepts.
const {
// Query state
offer, // Offer | null
isLoading, // boolean
isFetching, // boolean
isPaying, // boolean
error, // Error | null
// Pricing
summary, // { items, totalAmount, totalAdjustedAmount, currency, options }
selections, // Record<lineItemId, { variantId, quantity, priceId? }>
// Selection methods (by lineItemId)
selectVariant, // (lineItemId, variantId) => void
updateQuantity, // (lineItemId, quantity) => void
// Selection methods (by productId — convenience for single-line steps)
selectVariantByProduct,
updateQuantityByProduct,
getAvailableVariants, // (productId) => Array<{ variantId, variantName, unitAmount, currency, imageUrl }>
// Actions
pay, // (mainOrderId?, options?: { initiatedBy }) => Promise<{ checkoutUrl }>
toCheckout, // (mainOrderId?) => Promise<{ checkoutToken, customerId, status }>
} = usePreviewOffer({
offerId, // required
currency, // optional override; defaults to ?currency= URL param or funnel context
initialSelections, // optional preselected variants/quantities
});Example — accept-or-skip upsell page:
import { usePreviewOffer } from '@tagadapay/plugin-sdk/v2';
function UpsellOffer({ offerId, mainOrderId }: { offerId: string; mainOrderId: string }) {
const { offer, summary, pay, isPaying } = usePreviewOffer({ offerId });
const handleAccept = async () => {
const { checkoutUrl } = await pay(mainOrderId);
if (checkoutUrl) window.location.href = checkoutUrl;
};
return (
<div>
<h2>{offer?.name}</h2>
<p>Total: {summary?.totalAdjustedAmount} {summary?.currency}</p>
<button onClick={handleAccept} disabled={isPaying}>
{isPaying ? 'Processing…' : 'Add to my order'}
</button>
</div>
);
}MIT vs CIT auto-pickup
pay() reads stepConfig.paymentInitiator from the runtime
window.__TGD_STEP_CONFIG__ injection and applies it automatically:
paymentInitiator: 'merchant'(default) → MIT, sent to Stripe asoff_session: true. No 3DS challenge, higher conversion.paymentInitiator: 'customer'→ CIT, sent assetup_future_usage: 'off_session'. May trigger 3DS — useful when MIT charges are being declined by the issuer.
Merchants flip the toggle from the CRM step config; no plugin code change is
required. To override per call, pass { initiatedBy }:
await pay(mainOrderId, { initiatedBy: 'customer' });OffersResource.payOffer() and OffersResource.payWithCheckoutSession() share
the same auto-pickup under the hood.
usePromotions()
Hook for managing promotion codes in checkout sessions with TanStack Query.
const {
// Query data
promotions, // Promotion[] | undefined
isLoading, // boolean
error, // Error | null
// Actions
refresh, // () => Promise<void>
} = usePromotions({ checkoutToken, enabled });useProducts()
Hook for product data management with caching.
const {
// Query data
products, // Product[] | undefined
isLoading, // boolean
error, // Error | null
// Actions
refresh, // () => Promise<void>
} = useProducts({ storeId, enabled });useStoreConfig()
Hook for fetching store configuration with automatic caching.
const {
// Query data
storeConfig, // StoreConfig | undefined
isLoading, // boolean
error, // Error | null
isSuccess, // boolean
// Actions
refetch, // () => Promise<void>
} = useStoreConfig({ storeId, enabled });Example:
import { useStoreConfig } from '@tagadapay/plugin-sdk/v2';
function StoreInfo() {
const { storeConfig, isLoading } = useStoreConfig();
if (isLoading) return <div>Loading...</div>;
return (
<div>
<h1>{storeConfig?.storeName}</h1>
<p>Currency: {storeConfig?.currency}</p>
</div>
);
}See useStoreConfig documentation for more details.
useOrder()
Hook for order management and tracking.
const {
// Query data
order, // Order | undefined
isLoading, // boolean
error, // Error | null
// Actions
refresh, // () => Promise<void>
} = useOrder({ orderId, enabled });usePostPurchases()
Hook for post-purchase offers management.
const {
// Query data
postPurchases, // PostPurchaseOffer[] | undefined
isLoading, // boolean
error, // Error | null
// Actions
refresh, // () => Promise<void>
} = usePostPurchases({ orderId, enabled });usePayment()
Hook for payment processing with 3DS support.
const {
// Payment methods
processPayment, // (options: PaymentOptions) => Promise<PaymentResponse>
processApplePayPayment, // (token: ApplePayToken) => Promise<PaymentResponse>
// State
isProcessing, // boolean
error, // Error | null
} = usePayment();V2 Utility Functions & Advanced Features
TanStack Query Integration
V2 provides direct access to TanStack Query features for advanced use cases:
import {
useApiQuery,
useApiMutation,
useInvalidateQuery,
usePreloadQuery,
queryKeys,
} from '@tagadapay/plugin-sdk/v2';
// Custom API queries
const { data, isLoading } = useApiQuery({
queryKey: ['custom', 'endpoint'],
queryFn: () => apiClient.get('/custom-endpoint'),
});
// Mutations with automatic cache updates
const mutation = useApiMutation({
mutationFn: (data) => apiClient.post('/update', data),
onSuccess: () => {
// Invalidate related queries
invalidateQuery(['checkout']);
},
});
// Preload data for better UX
const preloadCheckout = usePreloadQuery();
preloadCheckout({
queryKey: queryKeys.checkout(checkoutToken),
queryFn: () => checkoutResource.get(checkoutToken),
});Money Utilities
Enhanced money formatting with better TypeScript support:
import { formatMoney, convertCurrency, getCurrencyInfo } from '@tagadapay/plugin-sdk/v2';
// Format money with automatic currency detection
const formatted = formatMoney(2999, 'USD'); // "$29.99"
const simple = formatSimpleMoney(2999, 'EUR'); // "29.99"
const withoutSymbol = formatMoneyWithoutSymbol(2999, 'GBP'); // "29.99"
// Currency conversion (if rates available)
const converted = convertCurrency(2999, 'USD', 'EUR'); // 2699
// Get currency information
const currencyInfo = getCurrencyInfo('USD');
// { symbol: '$', code: 'USD', minorUnits: 2 }
// Convert between major and minor units
const major = minorUnitsToMajorUnits(2999); // 29.99
const minor = moneyStringOrNumberToMinorUnits('29.99'); // 2999Core Functions (Pure Functions)
V2 separates core business logic from React hooks:
import {
CheckoutResource,
ProductsResource,
PaymentsResource,
PluginConfigUtils,
} from '@tagadapay/plugin-sdk/v2';
// Use core functions directly (useful for server-side or non-React contexts)
const checkoutResource = new CheckoutResource(apiClient);
const checkout = await checkoutResource.get(checkoutToken);
// Plugin config utilities
const config = PluginConfigUtils.getPluginConfig(rawConfig, context);
const isValid = PluginConfigUtils.validateConfig(config);Debug Tools
V2 includes built-in debugging tools:
import { TagadaProvider } from '@tagadapay/plugin-sdk/v2';
function App() {
return (
<TagadaProvider
debugMode={true} // Shows debug drawer in development
environment="development"
>
<YourApp />
</TagadaProvider>
);
}🛠️ Development
Local Development
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run buildTesting
# Run tests
npm test
# Run tests with coverage
npm run test:coverageLinting
# Check code style
npm run lint
# Fix linting issues
npm run lint:fix📦 Build & Deploy
Building Your Plugin
# Build optimized bundle
npm run build
# Analyze bundle size
npm run analyzeDeployment
# Deploy using TagadaPay CLI
npx @tagadapay/plugin-cli deploy
# Deploy specific environment
npx @tagadapay/plugin-cli deploy --env production🔧 Configuration
Plugin Configuration
{
"name": "My Checkout Plugin",
"version": "1.0.0",
"description": "Custom checkout experience",
"main": "dist/index.js",
"tagadapay": {
"type": "checkout",
"mode": "direct",
"framework": "react"
}
}🔐 Security
Best Practices
- Never store sensitive payment data
- Always validate user inputs
- Use HTTPS in production
- Implement proper error handling
- Follow PCI DSS guidelines
Token Management
// ✅ Good: Use tokenized payments
const paymentToken = await tokenizePayment(cardData);
await processPayment({ token: paymentToken });
// ❌ Bad: Never store raw card data
// const cardNumber = '4111111111111111'; // Don't do this📱 Mobile Optimization
Responsive Design
.checkout-container {
max-width: 600px;
margin: 0 auto;
padding: 16px;
}
@media (max-width: 768px) {
.checkout-container {
padding: 8px;
}
.checkout-form {
font-size: 16px; /* Prevent zoom on iOS */
}
}Touch Interactions
const handleTouchStart = (e) => {
// Optimize for touch devices
e.preventDefault();
// Handle touch interaction
};🌐 Internationalization
Multi-language Support
import { useTranslation } from '@tagadapay/plugin-sdk/v2';
function CheckoutForm() {
const { t } = useTranslation();
return (
<form>
<label>{t('checkout.email')}</label>
<input type="email" placeholder={t('checkout.email_placeholder')} />
</form>
);
}Currency Support
import { useCurrency } from '@tagadapay/plugin-sdk/v2';
function PriceDisplay({ amount }) {
const { formatPrice, currency } = useCurrency();
return <span>{formatPrice(amount, currency)}</span>;
}📊 Analytics & Tracking
Pixel tracking (Facebook, TikTok, Snapchat, GTM) is configured per-step in the CRM step editor and auto-bootstrapped at runtime — no plugin code is required. For external (non-Tagadapay-hosted) pages, the lightweight external tracker is available:
import { TagadaExternalTracker } from '@tagadapay/plugin-sdk/external-tracker';See External Page Tracker docs for the full integration guide.
🤝 Contributing
Development Setup
# Clone the repository
git clone https://github.com/tagadapay/plugin-sdk.git
# Install dependencies
npm install
# Start development
npm run devSubmitting Changes
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
📄 License
MIT License - see LICENSE for details.
🆘 Support
- Documentation: docs.tagadapay.com
- Discord: discord.gg/tagadapay
- Email: [email protected]
- GitHub Issues: github.com/tagadapay/plugin-sdk/issues
Built with ❤️ by the TagadaPay team
