@airxpay/sdk-ui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tafseel Khan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,400 @@
1
+ # 📘 AirXPay Initialization UI Components
2
+
3
+ <div align="center">
4
+ <img src="./assets/images/flixora.png" alt="AirXPay Flixora SDK" width="100"/>
5
+ </div>
6
+
7
+ ## 🚀 Overview
8
+
9
+ AirXPay Initialization UI is a production-ready React & React Native component library designed to streamline seller onboarding in multi-tenant SaaS applications. Built with TypeScript and enterprise-grade architecture, it provides a seamless, animated, and validated multi-step flow for collecting seller information.
10
+
11
+ > **Part of the Flixora Ecosystem** — Integrated with AirXPay for payments, TizzyGo for logistics, TizzyOS for operations, and soon TizzyChat for real-time notifications.
12
+
13
+ ---
14
+
15
+ ## ✨ Key Features
16
+
17
+ | Feature | Description |
18
+ |---------|-------------|
19
+ | ✅ **Multi-step Flow** | Basic Details → KYC → Bank Details → Completion |
20
+ | 🎨 **Animated Transitions** | Smooth step transitions with progress tracking |
21
+ | 🔒 **Built-in Validation** | Form validation for each step |
22
+ | 📸 **Document Upload** | Integrated with Expo ImagePicker for KYC documents |
23
+ | 🏦 **Bank Verification** | Country-specific bank details collection |
24
+ | 📊 **Progress Tracking** | Visual progress indicator with step status |
25
+ | 🔧 **Configurable** | Customizable via AirXPayProvider |
26
+ | 📘 **TypeScript** | Fully typed components and hooks |
27
+ | 📱 **Cross-platform** | Works with React Native, Expo, and Web |
28
+
29
+ ---
30
+
31
+ ## 📦 Installation
32
+
33
+ ### Prerequisites
34
+
35
+ Ensure you're in your React Native/Expo project:
36
+
37
+ ```bash
38
+ # Required peer dependencies
39
+ npm install react-native-paper react-native-country-picker-modal @react-native-community/datetimepicker expo-image-picker expo-linear-gradient
40
+
41
+ # Or with Yarn
42
+ yarn add react-native-paper react-native-country-picker-modal @react-native-community/datetimepicker expo-image-picker expo-linear-gradient
43
+ ```
44
+
45
+ ### Install the Package
46
+
47
+ ```bash
48
+ # For local development
49
+ npm install --save path/to/airxpay-initialization-ui
50
+
51
+ # Or from npm (when published)
52
+ npm install airxpay-initialization-ui
53
+ ```
54
+
55
+ ### Requirements
56
+
57
+ - React 18+
58
+ - React Native >= 0.72
59
+ - Expo SDK (optional, for LinearGradient & ImagePicker)
60
+
61
+ ---
62
+
63
+ ## 🏗️ Architecture
64
+
65
+ ```
66
+ airxpay-initialization-ui/
67
+ ├── components/
68
+ │ ├── steps/
69
+ │ │ ├── BasicDetailsForm.tsx
70
+ │ │ ├── KYCVerification.tsx
71
+ │ │ ├── BankDetails.tsx
72
+ │ │ └── OnboardingComplete.tsx
73
+ │ └── SellerOnboardingSheet.tsx
74
+ ├── contexts/
75
+ │ └── AirXPayProvider.tsx
76
+ ├── api/
77
+ │ └── seller.ts
78
+ ├── hooks/
79
+ │ ├── useAirXPay.ts
80
+ │ ├── useAirXPaySafe.ts
81
+ │ └── useIsAirXPayReady.ts
82
+ └── index.ts
83
+ ```
84
+
85
+ ---
86
+
87
+ <div align="center">
88
+ <img src="./assets/images/airxpay.png" alt="AirXPay Flixora SDK" width="100"/>
89
+ </div>
90
+
91
+ ## 🚀 Quick Start
92
+
93
+ ### 1️⃣ Wrap with Provider
94
+
95
+ ```tsx
96
+ // Root.tsx
97
+ import React from 'react';
98
+ import { AirXPayProvider } from 'airxpay-initialization-ui';
99
+ import App from './App';
100
+
101
+ export default function Root() {
102
+ return (
103
+ <AirXPayProvider
104
+ config={{
105
+ baseUrl: 'https://api.airxpay.com',
106
+ publicKey: 'YOUR_PUBLIC_KEY_HERE',
107
+ }}
108
+ >
109
+ <App />
110
+ </AirXPayProvider>
111
+ );
112
+ }
113
+ ```
114
+
115
+ ### 2️⃣ Implement Onboarding
116
+
117
+ ```tsx
118
+ // SellerOnboardingScreen.tsx
119
+ import React from 'react';
120
+ import { SellerOnboardingSheet } from 'airxpay-initialization-ui';
121
+
122
+ const MySellerOnboarding = () => {
123
+ return (
124
+ <SellerOnboardingSheet
125
+ sellerId="seller_12345"
126
+ mode="live"
127
+ isKycCompleted={false}
128
+ isBankDetailsCompleted={false}
129
+ kycStatus="pending"
130
+ status="pending"
131
+ onNext={(stepData, currentStep) => {
132
+ console.log(`Step ${currentStep} completed:`, stepData);
133
+ }}
134
+ onBack={(currentStep) => {
135
+ console.log(`Navigated back from step ${currentStep}`);
136
+ }}
137
+ onComplete={(sellerData) => {
138
+ console.log('🎉 Onboarding complete!', sellerData);
139
+ }}
140
+ />
141
+ );
142
+ };
143
+
144
+ export default MySellerOnboarding;
145
+ ```
146
+
147
+ ---
148
+
149
+ ## 📋 Component API
150
+
151
+ ### SellerOnboardingSheet Props
152
+
153
+ | Prop | Type | Required | Default | Description |
154
+ |------|------|----------|---------|-------------|
155
+ | sellerId | string | ✅ | - | Unique seller identifier |
156
+ | mode | 'live' \| 'test' | ✅ | - | Environment mode |
157
+ | isKycCompleted | boolean | ✅ | - | KYC completion status |
158
+ | isBankDetailsCompleted | boolean | ✅ | - | Bank details status |
159
+ | kycStatus | string | ✅ | - | 'pending' \| 'verified' \| 'rejected' |
160
+ | status | string | ✅ | - | 'pending' \| 'active' \| 'suspended' |
161
+ | initialStep | number | ❌ | 1 | Starting step (1-4) |
162
+ | initialData | Partial\<Seller\> | ❌ | {} | Pre-filled seller data |
163
+ | loading | boolean | ❌ | false | External loading state |
164
+ | onNext | (data: Partial\<Seller\>, step: number) => void | ✅ | - | Step completion callback |
165
+ | onBack | (step: number) => void | ✅ | - | Back navigation callback |
166
+ | onComplete | (data: Seller) => void | ✅ | - | Final completion callback |
167
+
168
+ ### AirXPayProvider Props
169
+
170
+ | Prop | Type | Required | Description |
171
+ |------|------|----------|-------------|
172
+ | config.baseUrl | string | ✅ | API base URL |
173
+ | config.publicKey | string | ✅ | API public key |
174
+ | children | ReactNode | ✅ | Child components |
175
+
176
+ ---
177
+
178
+ ## 🎣 Hooks
179
+
180
+ ```tsx
181
+ import {
182
+ useAirXPay,
183
+ useAirXPaySafe,
184
+ useIsAirXPayReady,
185
+ useAirXPayConfig
186
+ } from 'airxpay-initialization-ui';
187
+
188
+ // Access config (throws if no provider)
189
+ const { baseUrl, publicKey } = useAirXPay();
190
+
191
+ // Safe access (returns null if no provider)
192
+ const config = useAirXPaySafe();
193
+
194
+ // Check if provider is ready
195
+ const isReady = useIsAirXPayReady();
196
+
197
+ // Access specific config value
198
+ const baseUrl = useAirXPayConfig('baseUrl');
199
+ ```
200
+
201
+ ---
202
+
203
+ ## 🎨 Customization
204
+
205
+ ### Styling
206
+
207
+ ```tsx
208
+ // Override default styles
209
+ <SellerOnboardingSheet
210
+ // ... props
211
+ styles={{
212
+ container: { backgroundColor: '#f5f5f5' },
213
+ stepIndicator: { backgroundColor: '#6200ee' }
214
+ }}
215
+ />
216
+ ```
217
+
218
+ ### Icons & Branding
219
+
220
+ ```tsx
221
+ // Custom logo
222
+ <SellerOnboardingSheet
223
+ // ... props
224
+ logo={require('./assets/custom-logo.png')}
225
+ />
226
+
227
+ // Custom step icons via STEPS array modification
228
+ ```
229
+
230
+ ### Theme Support
231
+
232
+ ```tsx
233
+ // Using with React Native Paper
234
+ import { Provider as PaperProvider } from 'react-native-paper';
235
+
236
+ <PaperProvider theme={yourTheme}>
237
+ <AirXPayProvider config={config}>
238
+ <SellerOnboardingSheet {...props} />
239
+ </AirXPayProvider>
240
+ </PaperProvider>
241
+ ```
242
+
243
+ ---
244
+
245
+ ## 🔄 Step Flow
246
+
247
+ ```
248
+ Step 1: Basic Details
249
+ ├── Seller Name
250
+ ├── Email Address
251
+ ├── Phone Number
252
+ └── Business Type
253
+
254
+ Step 2: KYC Verification
255
+ ├── Document Upload (PAN/Aadhar)
256
+ ├── Selfie Verification
257
+ └── Address Proof
258
+
259
+ Step 3: Bank Details
260
+ ├── Account Number
261
+ ├── IFSC Code
262
+ ├── Account Holder Name
263
+ └── Bank Branch
264
+
265
+ Step 4: Completion
266
+ ├── Success Animation
267
+ ├── Summary View
268
+ └── Next Steps
269
+ ```
270
+
271
+ ---
272
+
273
+ ## ⚡ Advanced Usage
274
+
275
+ ### Custom Step Implementation
276
+
277
+ ```tsx
278
+ import { useStepContext } from 'airxpay-initialization-ui';
279
+
280
+ const CustomStep = () => {
281
+ const { stepData, updateStepData, goToNextStep } = useStepContext();
282
+
283
+ const handleSubmit = () => {
284
+ updateStepData({ customField: 'value' });
285
+ goToNextStep();
286
+ };
287
+
288
+ return (
289
+ // Your custom UI
290
+ );
291
+ };
292
+ ```
293
+
294
+ ### Error Handling
295
+
296
+ ```tsx
297
+ <SellerOnboardingSheet
298
+ onError={(error, step) => {
299
+ switch(error.code) {
300
+ case 'KYC_FAILED':
301
+ showNotification('KYC verification failed');
302
+ break;
303
+ case 'BANK_INVALID':
304
+ showNotification('Invalid bank details');
305
+ break;
306
+ }
307
+ }}
308
+ />
309
+ ```
310
+
311
+ ---
312
+
313
+ ## 🧪 Testing
314
+
315
+ ```bash
316
+ # Run tests
317
+ npm test
318
+
319
+ # Run with coverage
320
+ npm test -- --coverage
321
+
322
+ # Lint code
323
+ npm run lint
324
+
325
+ # Type check
326
+ npm run type-check
327
+ ```
328
+
329
+ ---
330
+
331
+ ## 📈 Performance
332
+
333
+ - **Memoized Components:** All step components are memoized
334
+ - **Optimized Re-renders:** Context splitting prevents unnecessary renders
335
+ - **Lazy Loading:** Steps load on-demand
336
+ - **Animation Optimizations:** Native driver for smooth 60fps transitions
337
+
338
+ ---
339
+
340
+ ## 🔒 Security
341
+
342
+ - All API calls require valid publicKey
343
+ - File uploads are validated client-side
344
+ - Sensitive data never stored in logs
345
+ - HTTPS enforced for all requests
346
+ - XSS protection via input sanitization
347
+
348
+ ---
349
+
350
+ ## 🐛 Troubleshooting
351
+
352
+ | Issue | Solution |
353
+ |-------|----------|
354
+ | Provider not found | Wrap components in `<AirXPayProvider>` |
355
+ | Images not uploading | Check Expo ImagePicker permissions |
356
+ | Animation lag | Enable useNativeDriver in config |
357
+ | TypeScript errors | Update to latest version |
358
+ | Bank validation fails | Check country code format |
359
+
360
+ ---
361
+
362
+ ## 📝 Changelog
363
+
364
+ ### v1.0.5 (Latest)
365
+ - Added React 18 support
366
+ - Fixed animation performance issues
367
+ - Improved TypeScript types
368
+ - Added Expo SDK 50+ compatibility
369
+ - Enhanced error handling
370
+
371
+ ### v1.0.3
372
+ - Initial release
373
+ - Basic step flow
374
+ - KYC document upload
375
+ - Bank details validation
376
+
377
+ ---
378
+
379
+ ## 🤝 Contributing
380
+
381
+ 1. Fork the repository
382
+ 2. Create feature branch (`git checkout -b feature/amazing`)
383
+ 3. Commit changes (`git commit -m 'Add amazing feature'`)
384
+ 4. Push to branch (`git push origin feature/amazing`)
385
+ 5. Open a Pull Request
386
+
387
+ ---
388
+
389
+ ## 📄 License
390
+
391
+ MIT License © 2026 Flixora Technologies
392
+
393
+ ---
394
+
395
+ <div align="center">
396
+ <h3>Built with ❤️ by the Flixora Ecosystem</h3>
397
+ <p><strong>Your Smile, Our Simplicity 😊</strong></p>
398
+ <p><sub>Version 1.0.5 | Part of AirXPay, TizzyGo, TizzyOS, and TizzyChat</sub></p>
399
+ <p><i>We upgraded from v1.0.3 to v1.0.5 to maintain version consistency across our ecosystem. Thanks for your understanding!</i></p>
400
+ </div>
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verifyPublicKey = void 0;
4
+ const verifyPublicKey = async (baseUrl, publicKey) => {
5
+ const response = await fetch(`${baseUrl}/api/verify-public-key`, {
6
+ method: "POST",
7
+ headers: {
8
+ "Content-Type": "application/json",
9
+ },
10
+ body: JSON.stringify({ publicKey }),
11
+ });
12
+ if (!response.ok) {
13
+ throw new Error("Invalid public key or server error");
14
+ }
15
+ return await response.json();
16
+ };
17
+ exports.verifyPublicKey = verifyPublicKey;
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ // components/common/CountryDropdown.tsx
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const react_1 = __importStar(require("react"));
41
+ const react_native_1 = require("react-native");
42
+ const react_native_paper_1 = require("react-native-paper");
43
+ const react_native_country_picker_modal_1 = __importDefault(require("react-native-country-picker-modal"));
44
+ const CountryDropdown = ({ value, onChange, withFlag = true, withCallingCode = false, withCountryNameButton = true, withAlphaFilter = true, withFilter = true, placeholder = 'Select country', label = 'Country', error, disabled = false, }) => {
45
+ const [modalVisible, setModalVisible] = (0, react_1.useState)(false);
46
+ const [selectedCountry, setSelectedCountry] = (0, react_1.useState)();
47
+ const onSelect = (country) => {
48
+ setSelectedCountry(country);
49
+ onChange(country.name, country.cca2);
50
+ setModalVisible(false);
51
+ };
52
+ const getDisplayValue = () => {
53
+ if (!value) {
54
+ return <react_native_1.Text style={styles.placeholder}>{placeholder}</react_native_1.Text>;
55
+ }
56
+ return (<react_native_1.View style={styles.selectedCountry}>
57
+ {selectedCountry?.cca2 && withFlag && (<react_native_country_picker_modal_1.default countryCode={selectedCountry.cca2} withFlag={true} withCountryNameButton={false} withCallingCode={false} withFilter={false} withAlphaFilter={false} visible={false}/>)}
58
+ <react_native_1.Text style={styles.selectedCountryName} numberOfLines={1}>
59
+ {value}
60
+ </react_native_1.Text>
61
+ </react_native_1.View>);
62
+ };
63
+ return (<react_native_1.View style={styles.container}>
64
+ {label ? <react_native_1.Text style={styles.label}>{label}</react_native_1.Text> : null}
65
+
66
+ <react_native_1.TouchableOpacity style={[
67
+ styles.dropdown,
68
+ error && styles.dropdownError,
69
+ disabled && styles.dropdownDisabled,
70
+ ]} onPress={() => !disabled && setModalVisible(true)} activeOpacity={disabled ? 1 : 0.7} disabled={disabled}>
71
+ {getDisplayValue()}
72
+
73
+ <react_native_paper_1.IconButton icon="chevron-down" size={20} iconColor={disabled ? "#9CA3AF" : "#6B7280"}/>
74
+ </react_native_1.TouchableOpacity>
75
+
76
+ {error ? <react_native_1.Text style={styles.errorText}>{error}</react_native_1.Text> : null}
77
+
78
+ <react_native_country_picker_modal_1.default countryCode={(selectedCountry?.cca2 || 'US')} withFlag={withFlag} withCallingCode={withCallingCode} withCountryNameButton={withCountryNameButton} withAlphaFilter={withAlphaFilter} withFilter={withFilter} withEmoji={true} withModal={true} visible={modalVisible} onSelect={onSelect} onClose={() => setModalVisible(false)} containerButtonStyle={styles.hiddenPicker} preferredCountries={['IN', 'US', 'GB', 'CA', 'AU', 'SG', 'AE']} modalProps={{
79
+ animationType: 'slide',
80
+ presentationStyle: react_native_1.Platform.OS === 'ios' ? 'pageSheet' : 'overFullScreen',
81
+ }}/>
82
+ </react_native_1.View>);
83
+ };
84
+ const styles = react_native_1.StyleSheet.create({
85
+ container: {
86
+ width: '100%',
87
+ },
88
+ label: {
89
+ fontSize: 13,
90
+ fontWeight: '500',
91
+ color: '#374151',
92
+ marginBottom: 6,
93
+ },
94
+ dropdown: {
95
+ flexDirection: 'row',
96
+ alignItems: 'center',
97
+ justifyContent: 'space-between',
98
+ borderWidth: 1,
99
+ borderColor: '#E5E7EB',
100
+ borderRadius: 8,
101
+ paddingHorizontal: 12,
102
+ backgroundColor: '#FFFFFF',
103
+ minHeight: 48,
104
+ },
105
+ dropdownError: {
106
+ borderColor: '#EF4444',
107
+ borderWidth: 1.5,
108
+ },
109
+ dropdownDisabled: {
110
+ backgroundColor: '#F3F4F6',
111
+ borderColor: '#E5E7EB',
112
+ },
113
+ selectedCountry: {
114
+ flex: 1,
115
+ flexDirection: 'row',
116
+ alignItems: 'center',
117
+ },
118
+ selectedCountryName: {
119
+ fontSize: 14,
120
+ color: '#111827',
121
+ marginLeft: 8,
122
+ flex: 1,
123
+ },
124
+ placeholder: {
125
+ fontSize: 14,
126
+ color: '#9CA3AF',
127
+ flex: 1,
128
+ },
129
+ hiddenPicker: {
130
+ display: 'none',
131
+ },
132
+ errorText: {
133
+ fontSize: 11,
134
+ color: '#EF4444',
135
+ marginTop: 4,
136
+ },
137
+ });
138
+ exports.default = CountryDropdown;