fairsure-checkout-sdk 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.
Files changed (40) hide show
  1. package/dist/components/CardPayment.d.ts +18 -0
  2. package/dist/components/CardPayment.js +302 -0
  3. package/dist/components/Checkout.d.ts +9 -0
  4. package/dist/components/Checkout.js +520 -0
  5. package/dist/components/OTPInput.d.ts +17 -0
  6. package/dist/components/OTPInput.js +132 -0
  7. package/dist/components/PaymentMethodSelector.d.ts +12 -0
  8. package/dist/components/PaymentMethodSelector.js +67 -0
  9. package/dist/components/PaystackCheckout.d.ts +13 -0
  10. package/dist/components/PaystackCheckout.js +385 -0
  11. package/dist/components/PurchaseModal.d.ts +17 -0
  12. package/dist/components/PurchaseModal.js +415 -0
  13. package/dist/components/TransferPayment.d.ts +16 -0
  14. package/dist/components/TransferPayment.js +311 -0
  15. package/dist/components/USSDPayment.d.ts +21 -0
  16. package/dist/components/USSDPayment.js +316 -0
  17. package/dist/components/usePaymentCheckout.d.ts +61 -0
  18. package/dist/components/usePaymentCheckout.js +398 -0
  19. package/dist/context/CheckoutProvider.d.ts +5 -0
  20. package/dist/context/CheckoutProvider.js +16 -0
  21. package/dist/hooks/useCheckout.d.ts +1 -0
  22. package/dist/hooks/useCheckout.js +8 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.js +2 -0
  25. package/dist/types.d.ts +9 -0
  26. package/dist/types.js +1 -0
  27. package/fairsure-checkout-sdk-1.0.0.tgz +0 -0
  28. package/package.json +24 -0
  29. package/src/components/CardPayment.tsx +386 -0
  30. package/src/components/OTPInput.tsx +204 -0
  31. package/src/components/PaymentMethodSelector.tsx +99 -0
  32. package/src/components/PaystackCheckout.tsx +506 -0
  33. package/src/components/TransferPayment.tsx +372 -0
  34. package/src/components/USSDPayment.tsx +422 -0
  35. package/src/components/usePaymentCheckout.ts +441 -0
  36. package/src/context/CheckoutProvider.tsx +37 -0
  37. package/src/hooks/useCheckout.ts +11 -0
  38. package/src/index.ts +2 -0
  39. package/src/types.ts +9 -0
  40. package/tsconfig.json +22 -0
@@ -0,0 +1,506 @@
1
+ import { Ionicons } from "@expo/vector-icons";
2
+ import {
3
+ ActivityIndicator,
4
+ Modal,
5
+ ScrollView,
6
+ StyleSheet,
7
+ Text,
8
+ TouchableOpacity,
9
+ View,
10
+ } from "react-native";
11
+ import CardPayment from "./CardPayment";
12
+ import PaymentMethodSelector from "./PaymentMethodSelector";
13
+ import TransferPayment from "./TransferPayment";
14
+ import USSDPayment from "./USSDPayment";
15
+ import { usePaymentCheckout } from "./usePaymentCheckout";
16
+
17
+ type CheckoutProps = {
18
+ theme?: "light" | "dark";
19
+ requestId: string;
20
+ customerEmail: string;
21
+ customerName: string;
22
+ customerPhone: string;
23
+ amount: number;
24
+ merchantName?: string;
25
+ onClose: () => void;
26
+ onSuccess?: (reference: string) => void;
27
+ };
28
+
29
+ const paymentMethods = [
30
+ { id: "card", name: "Card", icon: "card-outline" },
31
+ { id: "transfer", name: "Transfer", icon: "business-outline" },
32
+ { id: "ussd", name: "USSD", icon: "phone-portrait-outline" },
33
+ ];
34
+
35
+ export default function Checkout({
36
+ requestId,
37
+ customerEmail,
38
+ customerName,
39
+ customerPhone,
40
+ amount,
41
+ merchantName = "Merchant",
42
+ onClose,
43
+ onSuccess,
44
+ }: CheckoutProps) {
45
+ const checkout = usePaymentCheckout(
46
+ amount,
47
+ requestId,
48
+ customerEmail,
49
+ customerName,
50
+ customerPhone,
51
+ (reference) => {
52
+ onSuccess?.(reference);
53
+ handleClose();
54
+ },
55
+ );
56
+
57
+ const handleClose = () => {
58
+ checkout.resetForm();
59
+ checkout.setPin("");
60
+ checkout.setOtp("");
61
+ checkout.setcardcontinue(false);
62
+ checkout.setSelectedBank(null);
63
+ checkout.setShowOtp(false);
64
+ onClose();
65
+ };
66
+
67
+ const getButtonText = () => {
68
+ if (checkout.selectedMethod === "transfer") {
69
+ return "I have sent the money";
70
+ } else if (checkout.selectedMethod === "ussd") {
71
+ return "Confirm payment";
72
+ }
73
+ return `Pay ₦${checkout.formatAmount(amount)}`;
74
+ };
75
+
76
+ const isButtonDisabled = () => {
77
+ if (checkout.selectedMethod === "card") {
78
+ return !checkout.cardNumber || !checkout.expiryDate || !checkout.cvv;
79
+ }
80
+ if (checkout.selectedMethod === "ussd") {
81
+ return !checkout.selectedBank;
82
+ }
83
+ if (checkout.selectedMethod === "transfer") {
84
+ return checkout.timeLeft <= 0;
85
+ }
86
+ return false;
87
+ };
88
+
89
+ const handlePayment = () => {
90
+ if (checkout.selectedMethod === "transfer") {
91
+ checkout.verifyTransfer();
92
+ } else if (
93
+ checkout.selectedMethod === "card" &&
94
+ checkout.cardcontinue &&
95
+ checkout.showOtp
96
+ ) {
97
+ checkout.verifyCardPayment();
98
+ } else if (checkout.selectedMethod === "card" && checkout.cardcontinue) {
99
+ checkout.handleCardPayment();
100
+ } else if (checkout.selectedMethod === "card") {
101
+ checkout.setcardcontinue(true);
102
+ } else if (checkout.selectedMethod === "ussd") {
103
+ checkout.verifyUssd();
104
+ }
105
+ };
106
+
107
+ return (
108
+ <Modal
109
+ visible
110
+ transparent
111
+ animationType="slide"
112
+ onRequestClose={handleClose}
113
+ >
114
+ <View style={styles.overlay}>
115
+ <View style={styles.modalContainer}>
116
+ {/* Header */}
117
+ <CheckoutHeader
118
+ merchantName={merchantName}
119
+ amount={amount}
120
+ formatAmount={checkout.formatAmount}
121
+ onClose={handleClose}
122
+ />
123
+
124
+ <ScrollView style={styles.scrollContainer}>
125
+ <View style={styles.body}>
126
+ {/* Payment Method Selector */}
127
+ <PaymentMethodSelector
128
+ methods={paymentMethods}
129
+ selectedMethod={checkout.selectedMethod}
130
+ onSelectMethod={checkout.setSelectedMethod}
131
+ />
132
+
133
+ {/* Payment Forms */}
134
+ {checkout.selectedMethod === "card" && (
135
+ <CardPayment
136
+ cardNumber={checkout.cardNumber}
137
+ setCardNumber={checkout.setCardNumber}
138
+ expiryDate={checkout.expiryDate}
139
+ setExpiryDate={checkout.setExpiryDate}
140
+ cvv={checkout.cvv}
141
+ message={checkout.message}
142
+ setCvv={checkout.setCvv}
143
+ cardcontinue={checkout.cardcontinue}
144
+ showOtp={checkout.showOtp}
145
+ handleOTPComplete={(otp) => console.log(otp)}
146
+ handleOTPChange={checkout.setOtp}
147
+ handlePinChange={checkout.setPin}
148
+ formatCardNumber={checkout.formatCardNumber}
149
+ formatExpiry={checkout.formatExpiry}
150
+ />
151
+ )}
152
+
153
+ {checkout.selectedMethod === "transfer" && (
154
+ <TransferPayment
155
+ loading={checkout.loading}
156
+ amount={amount}
157
+ transferDetail={checkout.transferDetail}
158
+ timeLeft={checkout.timeLeft}
159
+ copied={checkout.copied}
160
+ handleCopy={checkout.handleCopy}
161
+ formatAmount={checkout.formatAmount}
162
+ formatTime={checkout.formatTime}
163
+ />
164
+ )}
165
+
166
+ {checkout.selectedMethod === "ussd" && (
167
+ <USSDPayment
168
+ selectedBank={checkout.selectedBank}
169
+ setSelectedBank={checkout.setSelectedBank}
170
+ showBankDropdown={checkout.showBankDropdown}
171
+ setShowBankDropdown={checkout.setShowBankDropdown}
172
+ ussdBanks={checkout.ussdBanks}
173
+ loading={checkout.loading}
174
+ ussString={checkout.ussdBankResponse}
175
+ copied={checkout.copied}
176
+ handleCopy={checkout.handleCopy}
177
+ />
178
+ )}
179
+
180
+ {/* Payment Button */}
181
+ <TouchableOpacity
182
+ onPress={handlePayment}
183
+ disabled={isButtonDisabled()}
184
+ style={[
185
+ styles.payButton,
186
+ isButtonDisabled()
187
+ ? styles.payButtonDisabled
188
+ : styles.payButtonActive,
189
+ ]}
190
+ >
191
+ <Text style={styles.payButtonText}>{getButtonText()}</Text>
192
+ {checkout.loading && (
193
+ <ActivityIndicator size="small" color="white" />
194
+ )}
195
+ </TouchableOpacity>
196
+ {!checkout.loading && checkout.timeLeft <= 0 && (
197
+ <TouchableOpacity>
198
+ <Text>Resend</Text>
199
+ </TouchableOpacity>
200
+ )}
201
+
202
+ {/* Footer */}
203
+ <View style={styles.footer}>
204
+ <Ionicons name="lock-closed" size={16} color="#9CA3AF" />
205
+ <Text style={styles.footerText}>Secured</Text>
206
+ </View>
207
+ </View>
208
+ </ScrollView>
209
+ </View>
210
+ </View>
211
+ </Modal>
212
+ );
213
+ }
214
+
215
+ function CheckoutHeader({
216
+ merchantName,
217
+ amount,
218
+ formatAmount,
219
+ onClose,
220
+ }: {
221
+ merchantName: string;
222
+ amount: number;
223
+ formatAmount: (value: number) => string;
224
+ onClose: () => void;
225
+ }) {
226
+ return (
227
+ <View style={styles.header}>
228
+ <View style={styles.headerTop}>
229
+ <View>
230
+ <Text style={styles.headerTitle}>Pay {merchantName}</Text>
231
+ <Text style={styles.headerSubtitle}>Complete your payment</Text>
232
+ </View>
233
+ <TouchableOpacity onPress={onClose} style={styles.closeBtn}>
234
+ <Ionicons name="close" size={24} color="white" />
235
+ </TouchableOpacity>
236
+ </View>
237
+
238
+ <View style={styles.amountBox}>
239
+ <Text style={styles.amountLabel}>Amount to pay</Text>
240
+ <Text style={styles.amountText}>₦{formatAmount(amount)}</Text>
241
+ </View>
242
+ </View>
243
+ );
244
+ }
245
+
246
+ const styles = StyleSheet.create({
247
+ overlay: {
248
+ flex: 1,
249
+ backgroundColor: "rgba(0,0,0,0.5)",
250
+ justifyContent: "center",
251
+ },
252
+ modalContainer: {
253
+ position: "relative",
254
+ width: "90%",
255
+ alignSelf: "center",
256
+ borderRadius: 20,
257
+ height: "80%",
258
+ backgroundColor: "white",
259
+ },
260
+
261
+ otpContainer: {
262
+ width: "100%",
263
+ marginHorizontal: "auto",
264
+ marginTop: 20,
265
+ marginBottom: 40,
266
+ },
267
+ header: {
268
+ backgroundColor: "#01448bff",
269
+ padding: 16,
270
+ borderTopLeftRadius: 20,
271
+ borderTopRightRadius: 20,
272
+ },
273
+ pinWrapper: {
274
+ flexDirection: "row",
275
+ alignItems: "center",
276
+ },
277
+ eyeButton: {
278
+ paddingHorizontal: 10,
279
+ },
280
+ eyeText: {
281
+ color: "#007AFF",
282
+ },
283
+
284
+ headerTop: {
285
+ flexDirection: "row",
286
+ justifyContent: "space-between",
287
+ alignItems: "flex-start",
288
+ marginBottom: 16,
289
+ },
290
+ headerTitle: { color: "white", fontSize: 24, fontWeight: "bold" },
291
+ headerSubtitle: { color: "#E0E7FF", fontSize: 14, marginTop: 4 },
292
+ closeBtn: {
293
+ backgroundColor: "rgba(255,255,255,0.2)",
294
+ borderRadius: 999,
295
+ padding: 8,
296
+ },
297
+ amountBox: {
298
+ backgroundColor: "rgba(114, 38, 38, 0.2)",
299
+ borderRadius: 12,
300
+ padding: 8,
301
+ },
302
+ amountLabel: { color: "#E0E7FF", fontSize: 12, marginBottom: 4 },
303
+ amountText: { color: "white", fontSize: 20, fontWeight: "bold" },
304
+ scrollContainer: { flex: 1, marginBottom: 16 },
305
+ body: {
306
+ padding: 18,
307
+ backgroundColor: "white",
308
+ borderBottomLeftRadius: 20,
309
+ borderBottomRightRadius: 20,
310
+ },
311
+ paymentMethodsContainer: {
312
+ flexDirection: "row",
313
+ marginBottom: 24,
314
+ backgroundColor: "#F3F4F6",
315
+ padding: 4,
316
+ borderRadius: 12,
317
+ },
318
+ paymentMethod: {
319
+ flex: 1,
320
+ flexDirection: "row",
321
+ alignItems: "center",
322
+ justifyContent: "center",
323
+ paddingVertical: 12,
324
+ borderRadius: 12,
325
+ marginHorizontal: 4,
326
+ },
327
+ paymentMethodSelected: { backgroundColor: "white" },
328
+ paymentMethodText: { fontSize: 14, color: "#4B5563", marginLeft: 8 },
329
+ paymentMethodTextSelected: { color: "#302aa3ff", fontWeight: "600" },
330
+ inputGroup: { marginBottom: 16 },
331
+ inputLabel: {
332
+ fontSize: 14,
333
+ fontWeight: "500",
334
+ color: "#374151",
335
+ marginBottom: 4,
336
+ },
337
+ inputWrapper: { position: "relative" },
338
+ input: {
339
+ width: "100%",
340
+ paddingHorizontal: 16,
341
+ paddingVertical: 12,
342
+ borderWidth: 1,
343
+ borderColor: "#D1D5DB",
344
+ borderRadius: 12,
345
+ backgroundColor: "white",
346
+ },
347
+ inputIcon: { position: "absolute", right: 16, top: 14 },
348
+ row: { flexDirection: "row", gap: 16, marginBottom: 12 },
349
+ flex1: { flex: 1 },
350
+ secureBox: {
351
+ flexDirection: "row",
352
+ alignItems: "center",
353
+ backgroundColor: "#F9FAFB",
354
+ padding: 12,
355
+ borderRadius: 12,
356
+ gap: 8,
357
+ marginBottom: 16,
358
+ },
359
+ secureText: { fontSize: 12, color: "#4B5563", flex: 1 },
360
+ transferBox: {
361
+ backgroundColor: "#EFF6FF",
362
+ borderWidth: 1,
363
+ borderColor: "#BFDBFE",
364
+ borderRadius: 12,
365
+ padding: 8,
366
+ marginBottom: 8,
367
+ },
368
+ transferText: {
369
+ fontSize: 14,
370
+ color: "#1E3A8A",
371
+ fontWeight: "500",
372
+ marginBottom: 12,
373
+ },
374
+ transferDetail: {
375
+ backgroundColor: "white",
376
+ borderRadius: 12,
377
+ padding: 8,
378
+ marginBottom: 12,
379
+ },
380
+ transferLabel: { fontSize: 10, color: "#4B5563", marginBottom: 4 },
381
+ transferValue: { fontWeight: "600", color: "#111827" },
382
+ accountNumber: {
383
+ fontWeight: "600",
384
+ color: "#111827",
385
+ fontSize: 16,
386
+ letterSpacing: 2,
387
+ flex: 1,
388
+ },
389
+ transferNote: {
390
+ backgroundColor: "#FEF3C7",
391
+ borderWidth: 1,
392
+ borderColor: "#FDE68A",
393
+ borderRadius: 12,
394
+ padding: 8,
395
+ },
396
+ transferNoteText: { fontSize: 14, color: "#78350F" },
397
+ dropdown: {
398
+ width: "100%",
399
+ paddingHorizontal: 16,
400
+ paddingVertical: 12,
401
+ borderWidth: 1,
402
+ borderColor: "#D1D5DB",
403
+ borderRadius: 12,
404
+ flexDirection: "row",
405
+ justifyContent: "space-between",
406
+ alignItems: "center",
407
+ },
408
+ dropdownText: { color: "#111827" },
409
+ dropdownPlaceholder: { color: "#9CA3AF" },
410
+ dropdownList: {
411
+ marginTop: 8,
412
+ backgroundColor: "white",
413
+ borderWidth: 1,
414
+ borderColor: "#E5E7EB",
415
+ borderRadius: 12,
416
+ overflow: "hidden",
417
+ maxHeight: 240,
418
+ },
419
+ dropdownItem: {
420
+ paddingVertical: 12,
421
+ paddingHorizontal: 16,
422
+ borderBottomWidth: 1,
423
+ borderBottomColor: "#F3F4F6",
424
+ },
425
+ dropdownItemText: { color: "#111827" },
426
+
427
+ ussdContainer: {
428
+ backgroundColor: "#E0E7FF", // bg-indigo-50
429
+ borderWidth: 1,
430
+ borderColor: "#C7D2FE", // border-indigo-200
431
+ borderRadius: 12, // rounded-xl
432
+ padding: 16, // p-4
433
+ marginBottom: 16,
434
+ },
435
+ ussdTitle: {
436
+ fontSize: 14, // text-sm
437
+ color: "#1E3A8A", // text-indigo-900
438
+ fontWeight: "500", // font-medium
439
+ marginBottom: 12, // mb-3
440
+ },
441
+ ussdCodeContainer: {
442
+ backgroundColor: "white", // bg-white
443
+ borderRadius: 12, // rounded-xl
444
+ padding: 16, // p-4
445
+ marginBottom: 12, // mb-3
446
+ },
447
+ ussdCodeRow: {
448
+ flexDirection: "row",
449
+ justifyContent: "space-between",
450
+ alignItems: "center",
451
+ },
452
+ ussdCodeText: {
453
+ fontFamily: "monospace", // font-mono
454
+ fontSize: 16, // text-base
455
+ fontWeight: "700", // font-bold
456
+ color: "#111827", // text-gray-900
457
+ flex: 1,
458
+ },
459
+ ussdCopyBtn: {
460
+ padding: 8, // p-2
461
+ },
462
+ ussdStepsLabel: {
463
+ fontSize: 14, // text-sm
464
+ color: "#1E3A8A", // text-indigo-900
465
+ fontWeight: "600", // font-semibold
466
+ marginBottom: 8, // mb-2
467
+ },
468
+ ussdStepsText: {
469
+ fontSize: 14, // text-sm
470
+ color: "#1E40AF", // text-indigo-800
471
+ },
472
+ payButton: {
473
+ display: "flex",
474
+ flexDirection: "row",
475
+ justifyContent: "center",
476
+ gap: 8,
477
+ width: "100%",
478
+ paddingVertical: 16, // py-4
479
+ borderRadius: 12, // rounded-xl
480
+ marginTop: 8, // mt-2
481
+ alignItems: "center",
482
+ },
483
+ payButtonActive: {
484
+ backgroundColor: "#4F46E5", // bg-indigo-600
485
+ },
486
+ payButtonDisabled: {
487
+ backgroundColor: "#D1D5DB", // bg-gray-300
488
+ },
489
+ payButtonText: {
490
+ color: "white",
491
+ fontWeight: "600",
492
+ fontSize: 18, // text-lg
493
+ textAlign: "center",
494
+ },
495
+ footer: {
496
+ flexDirection: "row",
497
+ justifyContent: "center",
498
+ alignItems: "center",
499
+ marginTop: 8, // mt-2
500
+ gap: 8, // supported in RN 0.71+, acts like Tailwind gap-2
501
+ },
502
+ footerText: {
503
+ fontSize: 14, // text-sm
504
+ color: "#6B7280", // text-gray-500
505
+ },
506
+ });