react-native-payvessel 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 +21 -0
- package/README.md +243 -0
- package/lib/PayvesselCheckout.d.ts +30 -0
- package/lib/PayvesselCheckout.d.ts.map +1 -0
- package/lib/PayvesselCheckout.js +387 -0
- package/lib/PayvesselCheckout.js.map +1 -0
- package/lib/index.d.ts +14 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +29 -0
- package/lib/index.js.map +1 -0
- package/lib/types.d.ts +168 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +22 -0
- package/lib/types.js.map +1 -0
- package/lib/usePayvessel.d.ts +74 -0
- package/lib/usePayvessel.d.ts.map +1 -0
- package/lib/usePayvessel.js +97 -0
- package/lib/usePayvessel.js.map +1 -0
- package/package.json +55 -0
- package/src/PayvesselCheckout.tsx +507 -0
- package/src/index.ts +50 -0
- package/src/types.ts +200 -0
- package/src/usePayvessel.ts +156 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import React, { useRef, useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Modal,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
SafeAreaView,
|
|
7
|
+
TouchableOpacity,
|
|
8
|
+
Text,
|
|
9
|
+
ActivityIndicator,
|
|
10
|
+
ViewStyle,
|
|
11
|
+
TextStyle,
|
|
12
|
+
} from 'react-native';
|
|
13
|
+
import { WebView, WebViewMessageEvent } from 'react-native-webview';
|
|
14
|
+
import {
|
|
15
|
+
PaymentStatus,
|
|
16
|
+
PayvesselCheckoutProps,
|
|
17
|
+
TransactionData,
|
|
18
|
+
PayvesselSuccessResponse,
|
|
19
|
+
PayvesselErrorResponse,
|
|
20
|
+
} from './types';
|
|
21
|
+
|
|
22
|
+
const PAYVESSEL_BRAND_COLOR = '#ff6b00';
|
|
23
|
+
const CHECKOUT_URL = 'https://checkout.payvessel.com';
|
|
24
|
+
|
|
25
|
+
interface InitializePayload {
|
|
26
|
+
amount: string;
|
|
27
|
+
currency: string;
|
|
28
|
+
customer_email: string;
|
|
29
|
+
customer_name: string;
|
|
30
|
+
metadata: Record<string, unknown>;
|
|
31
|
+
customer_phone_number?: string;
|
|
32
|
+
channels?: string[];
|
|
33
|
+
reference?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface ApiResponse {
|
|
37
|
+
success?: boolean;
|
|
38
|
+
message?: string;
|
|
39
|
+
data?: TransactionData;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface WebViewEventData {
|
|
43
|
+
event: string;
|
|
44
|
+
payload?: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* PayvesselCheckout Component
|
|
49
|
+
*
|
|
50
|
+
* A React Native component that displays Payvessel checkout in a WebView modal.
|
|
51
|
+
* Directly calls the Payvessel API to initialize transactions.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* <PayvesselCheckout
|
|
56
|
+
* visible={showCheckout}
|
|
57
|
+
* apiKey="YOUR_API_KEY"
|
|
58
|
+
* customerEmail="customer@example.com"
|
|
59
|
+
* customerPhoneNumber="08012345678"
|
|
60
|
+
* amount="1000"
|
|
61
|
+
* currency="NGN"
|
|
62
|
+
* customerName="John Doe"
|
|
63
|
+
* channels={['BANK_TRANSFER', 'CARD']}
|
|
64
|
+
* metadata={{ order_id: '12345' }}
|
|
65
|
+
* onSuccess={(response) => console.log('Initialized:', response)}
|
|
66
|
+
* onSuccessfulOrder={(response) => console.log('Payment confirmed:', response)}
|
|
67
|
+
* onError={(error) => console.log('Error:', error)}
|
|
68
|
+
* onClose={() => setShowCheckout(false)}
|
|
69
|
+
* />
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
const PayvesselCheckout: React.FC<PayvesselCheckoutProps> = ({
|
|
73
|
+
// Visibility
|
|
74
|
+
visible = false,
|
|
75
|
+
|
|
76
|
+
// Config
|
|
77
|
+
apiKey,
|
|
78
|
+
|
|
79
|
+
// Customer details (required)
|
|
80
|
+
customerEmail,
|
|
81
|
+
customerPhoneNumber,
|
|
82
|
+
amount,
|
|
83
|
+
currency = 'NGN',
|
|
84
|
+
customerName,
|
|
85
|
+
|
|
86
|
+
// Optional
|
|
87
|
+
channels = ['BANK_TRANSFER'],
|
|
88
|
+
metadata,
|
|
89
|
+
reference,
|
|
90
|
+
|
|
91
|
+
// Callbacks
|
|
92
|
+
onSuccess,
|
|
93
|
+
onError,
|
|
94
|
+
onClose,
|
|
95
|
+
onSuccessfulOrder,
|
|
96
|
+
|
|
97
|
+
// UI customization
|
|
98
|
+
showCloseButton = true,
|
|
99
|
+
closeButtonText = '✕',
|
|
100
|
+
loadingText = 'Initializing checkout...',
|
|
101
|
+
headerTitle = 'Checkout',
|
|
102
|
+
showHeader = true,
|
|
103
|
+
}) => {
|
|
104
|
+
const webViewRef = useRef<WebView>(null);
|
|
105
|
+
const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
106
|
+
const [isInitializing, setIsInitializing] = useState<boolean>(false);
|
|
107
|
+
const [hasError, setHasError] = useState<boolean>(false);
|
|
108
|
+
const [errorMessage, setErrorMessage] = useState<string>('');
|
|
109
|
+
const [checkoutUrl, setCheckoutUrl] = useState<string | null>(null);
|
|
110
|
+
const [transactionData, setTransactionData] = useState<TransactionData | null>(null);
|
|
111
|
+
|
|
112
|
+
// Get the API base URL based on API key (test or live)
|
|
113
|
+
const getApiBaseUrl = useCallback((): string => {
|
|
114
|
+
if (apiKey?.startsWith('PVTESTKEY-')) {
|
|
115
|
+
return 'https://sandbox.payvessel.com';
|
|
116
|
+
}
|
|
117
|
+
return 'https://api.payvessel.com';
|
|
118
|
+
}, [apiKey]);
|
|
119
|
+
|
|
120
|
+
// Initialize the transaction by calling Payvessel API directly
|
|
121
|
+
const initializeTransaction = useCallback(async (): Promise<void> => {
|
|
122
|
+
if (!apiKey) {
|
|
123
|
+
setHasError(true);
|
|
124
|
+
setErrorMessage('API key is required');
|
|
125
|
+
onError?.({
|
|
126
|
+
status: PaymentStatus.FAILED,
|
|
127
|
+
message: 'API key is required',
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!customerEmail || !amount || !customerName) {
|
|
133
|
+
setHasError(true);
|
|
134
|
+
setErrorMessage('Customer email, amount, and name are required');
|
|
135
|
+
onError?.({
|
|
136
|
+
status: PaymentStatus.FAILED,
|
|
137
|
+
message: 'Customer email, amount, and name are required',
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
setIsInitializing(true);
|
|
143
|
+
setHasError(false);
|
|
144
|
+
setErrorMessage('');
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const baseUrl = getApiBaseUrl();
|
|
148
|
+
|
|
149
|
+
const payload: InitializePayload = {
|
|
150
|
+
amount: String(amount),
|
|
151
|
+
currency,
|
|
152
|
+
customer_email: customerEmail,
|
|
153
|
+
customer_name: customerName,
|
|
154
|
+
metadata: metadata || {},
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Add optional fields
|
|
158
|
+
if (customerPhoneNumber) {
|
|
159
|
+
payload.customer_phone_number = customerPhoneNumber;
|
|
160
|
+
}
|
|
161
|
+
if (channels && channels.length > 0) {
|
|
162
|
+
payload.channels = channels as string[];
|
|
163
|
+
}
|
|
164
|
+
if (reference) {
|
|
165
|
+
payload.reference = reference;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const response = await fetch(`${baseUrl}/pms/checkout/initialize/`, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers: {
|
|
171
|
+
'Content-Type': 'application/json',
|
|
172
|
+
'api-key': apiKey,
|
|
173
|
+
},
|
|
174
|
+
body: JSON.stringify(payload),
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const result: ApiResponse = await response.json();
|
|
178
|
+
|
|
179
|
+
if (result?.success === false || !result?.data?.access_code) {
|
|
180
|
+
throw new Error(result?.message || 'Failed to initialize checkout');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const data = result.data;
|
|
184
|
+
|
|
185
|
+
// Store transaction data
|
|
186
|
+
setTransactionData(data);
|
|
187
|
+
|
|
188
|
+
// Call onSuccess callback with transaction initialization response
|
|
189
|
+
onSuccess?.({
|
|
190
|
+
status: PaymentStatus.SUCCESS,
|
|
191
|
+
reference: data.reference,
|
|
192
|
+
transactionId: data.id,
|
|
193
|
+
accessCode: data.access_code,
|
|
194
|
+
data: data,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Set the checkout URL with access code
|
|
198
|
+
setCheckoutUrl(`${CHECKOUT_URL}/${data.access_code}`);
|
|
199
|
+
|
|
200
|
+
} catch (error) {
|
|
201
|
+
const errorMsg = error instanceof Error ? error.message : 'Failed to initialize checkout';
|
|
202
|
+
setHasError(true);
|
|
203
|
+
setErrorMessage(errorMsg);
|
|
204
|
+
onError?.({
|
|
205
|
+
status: PaymentStatus.FAILED,
|
|
206
|
+
message: errorMsg,
|
|
207
|
+
});
|
|
208
|
+
} finally {
|
|
209
|
+
setIsInitializing(false);
|
|
210
|
+
}
|
|
211
|
+
}, [
|
|
212
|
+
apiKey,
|
|
213
|
+
customerEmail,
|
|
214
|
+
customerPhoneNumber,
|
|
215
|
+
amount,
|
|
216
|
+
currency,
|
|
217
|
+
customerName,
|
|
218
|
+
channels,
|
|
219
|
+
metadata,
|
|
220
|
+
reference,
|
|
221
|
+
getApiBaseUrl,
|
|
222
|
+
onSuccess,
|
|
223
|
+
onError
|
|
224
|
+
]);
|
|
225
|
+
|
|
226
|
+
// Handle messages from WebView
|
|
227
|
+
const handleMessage = useCallback((event: WebViewMessageEvent): void => {
|
|
228
|
+
try {
|
|
229
|
+
const data: WebViewEventData = JSON.parse(event.nativeEvent.data);
|
|
230
|
+
const { event: eventType, payload: eventPayload } = data;
|
|
231
|
+
|
|
232
|
+
switch (eventType) {
|
|
233
|
+
case 'payment_success':
|
|
234
|
+
onSuccessfulOrder?.({
|
|
235
|
+
status: PaymentStatus.SUCCESS,
|
|
236
|
+
reference: transactionData?.reference,
|
|
237
|
+
transactionId: transactionData?.id,
|
|
238
|
+
data: eventPayload,
|
|
239
|
+
});
|
|
240
|
+
break;
|
|
241
|
+
|
|
242
|
+
case 'payment_failed':
|
|
243
|
+
onError?.({
|
|
244
|
+
status: PaymentStatus.FAILED,
|
|
245
|
+
message: 'Payment failed',
|
|
246
|
+
});
|
|
247
|
+
break;
|
|
248
|
+
|
|
249
|
+
case 'payment_closed':
|
|
250
|
+
case 'checkout_closed':
|
|
251
|
+
onClose?.();
|
|
252
|
+
break;
|
|
253
|
+
|
|
254
|
+
default:
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
} catch {
|
|
258
|
+
// Not a JSON message, ignore
|
|
259
|
+
}
|
|
260
|
+
}, [transactionData, onSuccessfulOrder, onError, onClose]);
|
|
261
|
+
|
|
262
|
+
// Inject script to capture postMessage events from checkout
|
|
263
|
+
const injectedJavaScript = `
|
|
264
|
+
(function() {
|
|
265
|
+
// Override window.parent.postMessage to capture events
|
|
266
|
+
const originalPostMessage = window.parent.postMessage;
|
|
267
|
+
window.parent.postMessage = function(message, targetOrigin) {
|
|
268
|
+
// Forward to React Native
|
|
269
|
+
if (window.ReactNativeWebView) {
|
|
270
|
+
try {
|
|
271
|
+
const data = typeof message === 'string' ? JSON.parse(message) : message;
|
|
272
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(data));
|
|
273
|
+
} catch (e) {
|
|
274
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({ event: 'message', payload: message }));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Call original
|
|
278
|
+
return originalPostMessage.call(window.parent, message, targetOrigin);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// Also listen for message events
|
|
282
|
+
window.addEventListener('message', function(event) {
|
|
283
|
+
if (window.ReactNativeWebView) {
|
|
284
|
+
try {
|
|
285
|
+
const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
|
|
286
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(data));
|
|
287
|
+
} catch (e) {}
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
true;
|
|
292
|
+
})();
|
|
293
|
+
`;
|
|
294
|
+
|
|
295
|
+
// Handle close button press
|
|
296
|
+
const handleClose = useCallback((): void => {
|
|
297
|
+
onClose?.();
|
|
298
|
+
}, [onClose]);
|
|
299
|
+
|
|
300
|
+
// Initialize transaction when modal becomes visible
|
|
301
|
+
useEffect(() => {
|
|
302
|
+
if (visible) {
|
|
303
|
+
setIsLoading(true);
|
|
304
|
+
setHasError(false);
|
|
305
|
+
setErrorMessage('');
|
|
306
|
+
setCheckoutUrl(null);
|
|
307
|
+
setTransactionData(null);
|
|
308
|
+
initializeTransaction();
|
|
309
|
+
}
|
|
310
|
+
}, [visible, initializeTransaction]);
|
|
311
|
+
|
|
312
|
+
// Update loading state when checkout URL is set
|
|
313
|
+
useEffect(() => {
|
|
314
|
+
if (checkoutUrl) {
|
|
315
|
+
setIsLoading(false);
|
|
316
|
+
}
|
|
317
|
+
}, [checkoutUrl]);
|
|
318
|
+
|
|
319
|
+
if (!visible) return null;
|
|
320
|
+
|
|
321
|
+
return (
|
|
322
|
+
<Modal
|
|
323
|
+
visible={visible}
|
|
324
|
+
animationType="slide"
|
|
325
|
+
transparent={false}
|
|
326
|
+
onRequestClose={handleClose}
|
|
327
|
+
>
|
|
328
|
+
<SafeAreaView style={styles.container}>
|
|
329
|
+
{/* Header */}
|
|
330
|
+
{showHeader && (
|
|
331
|
+
<View style={styles.header}>
|
|
332
|
+
<Text style={styles.headerTitle}>{headerTitle}</Text>
|
|
333
|
+
{showCloseButton && (
|
|
334
|
+
<TouchableOpacity onPress={handleClose} style={styles.closeButton}>
|
|
335
|
+
<Text style={styles.closeButtonText}>{closeButtonText}</Text>
|
|
336
|
+
</TouchableOpacity>
|
|
337
|
+
)}
|
|
338
|
+
</View>
|
|
339
|
+
)}
|
|
340
|
+
|
|
341
|
+
{/* Content */}
|
|
342
|
+
<View style={styles.webviewContainer}>
|
|
343
|
+
{/* Loading/Initializing State */}
|
|
344
|
+
{(isLoading || isInitializing) && !hasError && (
|
|
345
|
+
<View style={styles.loadingOverlay}>
|
|
346
|
+
<ActivityIndicator size="large" color={PAYVESSEL_BRAND_COLOR} />
|
|
347
|
+
<Text style={styles.loadingText}>{loadingText}</Text>
|
|
348
|
+
</View>
|
|
349
|
+
)}
|
|
350
|
+
|
|
351
|
+
{/* WebView - only show when we have checkout URL */}
|
|
352
|
+
{checkoutUrl && !hasError && (
|
|
353
|
+
<WebView
|
|
354
|
+
ref={webViewRef}
|
|
355
|
+
source={{ uri: checkoutUrl }}
|
|
356
|
+
style={styles.webview}
|
|
357
|
+
onMessage={handleMessage}
|
|
358
|
+
onLoadStart={() => setIsLoading(true)}
|
|
359
|
+
onLoadEnd={() => setIsLoading(false)}
|
|
360
|
+
onError={(e) => {
|
|
361
|
+
setHasError(true);
|
|
362
|
+
setErrorMessage(e.nativeEvent.description || 'WebView error');
|
|
363
|
+
setIsLoading(false);
|
|
364
|
+
onError?.({
|
|
365
|
+
status: PaymentStatus.FAILED,
|
|
366
|
+
message: e.nativeEvent.description || 'WebView error',
|
|
367
|
+
});
|
|
368
|
+
}}
|
|
369
|
+
injectedJavaScript={injectedJavaScript}
|
|
370
|
+
javaScriptEnabled={true}
|
|
371
|
+
domStorageEnabled={true}
|
|
372
|
+
startInLoadingState={false}
|
|
373
|
+
scalesPageToFit={true}
|
|
374
|
+
mixedContentMode="compatibility"
|
|
375
|
+
allowsInlineMediaPlayback={true}
|
|
376
|
+
mediaPlaybackRequiresUserAction={false}
|
|
377
|
+
originWhitelist={['*']}
|
|
378
|
+
/>
|
|
379
|
+
)}
|
|
380
|
+
|
|
381
|
+
{/* Error View */}
|
|
382
|
+
{hasError && (
|
|
383
|
+
<View style={styles.errorOverlay}>
|
|
384
|
+
<Text style={styles.errorIcon}>⚠️</Text>
|
|
385
|
+
<Text style={styles.errorTitle}>Failed to load checkout</Text>
|
|
386
|
+
<Text style={styles.errorMessage}>
|
|
387
|
+
{errorMessage || 'Please try again or contact support.'}
|
|
388
|
+
</Text>
|
|
389
|
+
<TouchableOpacity
|
|
390
|
+
style={styles.retryButton}
|
|
391
|
+
onPress={() => {
|
|
392
|
+
setHasError(false);
|
|
393
|
+
setIsLoading(true);
|
|
394
|
+
initializeTransaction();
|
|
395
|
+
}}
|
|
396
|
+
>
|
|
397
|
+
<Text style={styles.retryButtonText}>Retry</Text>
|
|
398
|
+
</TouchableOpacity>
|
|
399
|
+
</View>
|
|
400
|
+
)}
|
|
401
|
+
</View>
|
|
402
|
+
</SafeAreaView>
|
|
403
|
+
</Modal>
|
|
404
|
+
);
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
interface Styles {
|
|
408
|
+
container: ViewStyle;
|
|
409
|
+
header: ViewStyle;
|
|
410
|
+
headerTitle: TextStyle;
|
|
411
|
+
closeButton: ViewStyle;
|
|
412
|
+
closeButtonText: TextStyle;
|
|
413
|
+
webviewContainer: ViewStyle;
|
|
414
|
+
webview: ViewStyle;
|
|
415
|
+
loadingOverlay: ViewStyle;
|
|
416
|
+
loadingText: TextStyle;
|
|
417
|
+
errorOverlay: ViewStyle;
|
|
418
|
+
errorIcon: TextStyle;
|
|
419
|
+
errorTitle: TextStyle;
|
|
420
|
+
errorMessage: TextStyle;
|
|
421
|
+
retryButton: ViewStyle;
|
|
422
|
+
retryButtonText: TextStyle;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const styles = StyleSheet.create<Styles>({
|
|
426
|
+
container: {
|
|
427
|
+
flex: 1,
|
|
428
|
+
backgroundColor: '#fff',
|
|
429
|
+
},
|
|
430
|
+
header: {
|
|
431
|
+
flexDirection: 'row',
|
|
432
|
+
alignItems: 'center',
|
|
433
|
+
justifyContent: 'center',
|
|
434
|
+
paddingHorizontal: 16,
|
|
435
|
+
paddingVertical: 12,
|
|
436
|
+
borderBottomWidth: 1,
|
|
437
|
+
borderBottomColor: '#eee',
|
|
438
|
+
backgroundColor: '#fff',
|
|
439
|
+
},
|
|
440
|
+
headerTitle: {
|
|
441
|
+
fontSize: 17,
|
|
442
|
+
fontWeight: '600',
|
|
443
|
+
color: '#333',
|
|
444
|
+
},
|
|
445
|
+
closeButton: {
|
|
446
|
+
position: 'absolute',
|
|
447
|
+
right: 16,
|
|
448
|
+
padding: 8,
|
|
449
|
+
},
|
|
450
|
+
closeButtonText: {
|
|
451
|
+
fontSize: 20,
|
|
452
|
+
color: '#666',
|
|
453
|
+
},
|
|
454
|
+
webviewContainer: {
|
|
455
|
+
flex: 1,
|
|
456
|
+
},
|
|
457
|
+
webview: {
|
|
458
|
+
flex: 1,
|
|
459
|
+
},
|
|
460
|
+
loadingOverlay: {
|
|
461
|
+
...StyleSheet.absoluteFillObject,
|
|
462
|
+
backgroundColor: '#fff',
|
|
463
|
+
alignItems: 'center',
|
|
464
|
+
justifyContent: 'center',
|
|
465
|
+
},
|
|
466
|
+
loadingText: {
|
|
467
|
+
marginTop: 16,
|
|
468
|
+
fontSize: 14,
|
|
469
|
+
color: '#666',
|
|
470
|
+
},
|
|
471
|
+
errorOverlay: {
|
|
472
|
+
...StyleSheet.absoluteFillObject,
|
|
473
|
+
backgroundColor: '#fff',
|
|
474
|
+
alignItems: 'center',
|
|
475
|
+
justifyContent: 'center',
|
|
476
|
+
padding: 24,
|
|
477
|
+
},
|
|
478
|
+
errorIcon: {
|
|
479
|
+
fontSize: 48,
|
|
480
|
+
marginBottom: 16,
|
|
481
|
+
},
|
|
482
|
+
errorTitle: {
|
|
483
|
+
fontSize: 18,
|
|
484
|
+
fontWeight: '600',
|
|
485
|
+
color: '#333',
|
|
486
|
+
marginBottom: 8,
|
|
487
|
+
},
|
|
488
|
+
errorMessage: {
|
|
489
|
+
fontSize: 14,
|
|
490
|
+
color: '#666',
|
|
491
|
+
textAlign: 'center',
|
|
492
|
+
marginBottom: 24,
|
|
493
|
+
},
|
|
494
|
+
retryButton: {
|
|
495
|
+
backgroundColor: PAYVESSEL_BRAND_COLOR,
|
|
496
|
+
paddingHorizontal: 32,
|
|
497
|
+
paddingVertical: 12,
|
|
498
|
+
borderRadius: 8,
|
|
499
|
+
},
|
|
500
|
+
retryButtonText: {
|
|
501
|
+
color: '#fff',
|
|
502
|
+
fontSize: 16,
|
|
503
|
+
fontWeight: '600',
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
export default PayvesselCheckout;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native Payvessel SDK
|
|
3
|
+
*
|
|
4
|
+
* A React Native package for integrating Payvessel payment gateway.
|
|
5
|
+
* Supports Bank Transfer and Card payments via WebView checkout.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Main component
|
|
11
|
+
export { default as PayvesselCheckout } from './PayvesselCheckout';
|
|
12
|
+
export { default } from './PayvesselCheckout';
|
|
13
|
+
|
|
14
|
+
// Hook
|
|
15
|
+
export { usePayvessel, default as usePayvesselHook } from './usePayvessel';
|
|
16
|
+
export type { UsePayvesselOptions, UsePayvesselReturn } from './usePayvessel';
|
|
17
|
+
|
|
18
|
+
// Types
|
|
19
|
+
export {
|
|
20
|
+
// Enums
|
|
21
|
+
PaymentStatus,
|
|
22
|
+
PaymentChannel,
|
|
23
|
+
|
|
24
|
+
// Checkout params
|
|
25
|
+
type CheckoutParams,
|
|
26
|
+
type CheckoutConfig,
|
|
27
|
+
type PaymentInfo,
|
|
28
|
+
|
|
29
|
+
// Customer
|
|
30
|
+
type CustomerInfo,
|
|
31
|
+
|
|
32
|
+
// Transaction data
|
|
33
|
+
type TransactionData,
|
|
34
|
+
|
|
35
|
+
// Callbacks
|
|
36
|
+
type OnSuccessCallback,
|
|
37
|
+
type OnErrorCallback,
|
|
38
|
+
type OnCloseCallback,
|
|
39
|
+
type OnSuccessfulOrderCallback,
|
|
40
|
+
|
|
41
|
+
// Props
|
|
42
|
+
type PayvesselCheckoutProps,
|
|
43
|
+
|
|
44
|
+
// API responses
|
|
45
|
+
type PayvesselSuccessResponse,
|
|
46
|
+
type PayvesselErrorResponse,
|
|
47
|
+
type PayvesselCloseResponse,
|
|
48
|
+
type PayvesselResponse,
|
|
49
|
+
} from './types';
|
|
50
|
+
|