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