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,441 @@
1
+ import axios from "axios";
2
+ import * as Clipboard from "expo-clipboard";
3
+ import { useEffect, useState } from "react";
4
+ import { Dimensions, Platform } from "react-native";
5
+
6
+ const { width, height } = Dimensions.get("window");
7
+
8
+ export function usePaymentCheckout(
9
+ amount: number,
10
+ requestId: string,
11
+ customerEmail: string,
12
+ customerName: string,
13
+ customerPhone: string,
14
+ onSuccess?: (reference: string) => void,
15
+ ) {
16
+ const [selectedMethod, setSelectedMethod] = useState("card");
17
+ const [cardNumber, setCardNumber] = useState("");
18
+ const [expiryDate, setExpiryDate] = useState("");
19
+ const [cvv, setCvv] = useState("");
20
+ const [selectedBank, setSelectedBank] = useState<{
21
+ name: string;
22
+ code: string;
23
+ } | null>(null);
24
+
25
+ const [showBankDropdown, setShowBankDropdown] = useState(false);
26
+ const [pin, setPin] = useState("");
27
+ const [otp, setOtp] = useState("");
28
+ const [message, setMessage] = useState("");
29
+ const [copied, setCopied] = useState(false);
30
+ const [showPin, setShowPin] = useState(false);
31
+ const [showOtp, setShowOtp] = useState(false);
32
+ const [cardcontinue, setcardcontinue] = useState(false);
33
+ const [loading, setLoading] = useState(false);
34
+ const [ussdBanks, setUssdBanks] = useState([]);
35
+ const [ussdBankResponse, setUssdBankResponse] = useState({
36
+ ussdString: "",
37
+ codeRef: "",
38
+ amount: "",
39
+ });
40
+ const [transferDetail, setTransferDetail] = useState({
41
+ accountName: "",
42
+ accountNumber: "",
43
+ bankName: "",
44
+ amount: 0,
45
+ validityPeriodMins: 0,
46
+ transactionReference: "",
47
+ });
48
+ const [cardDetails, setCardDetails] = useState({
49
+ transactionRef: "",
50
+ paymentId: "",
51
+ });
52
+ const [timeLeft, setTimeLeft] = useState(0);
53
+
54
+ // Timer logic
55
+ useEffect(() => {
56
+ if (transferDetail.validityPeriodMins > 0) {
57
+ setTimeLeft(transferDetail.validityPeriodMins * 60);
58
+ }
59
+ }, [transferDetail.validityPeriodMins]);
60
+
61
+ useEffect(() => {
62
+ if (timeLeft <= 0) return;
63
+
64
+ const timer = setInterval(() => {
65
+ setTimeLeft((prev) => {
66
+ if (prev <= 1) {
67
+ clearInterval(timer);
68
+ return 0;
69
+ }
70
+ return prev - 1;
71
+ });
72
+ }, 1000);
73
+
74
+ return () => clearInterval(timer);
75
+ }, [timeLeft]);
76
+
77
+ // Fetch transfer details or USSD banks based on selected method
78
+ useEffect(() => {
79
+ if (selectedMethod === "transfer" || selectedMethod === "card") {
80
+ fetchTransferDetails();
81
+ } else if (selectedMethod === "ussd") {
82
+ getBanks();
83
+ }
84
+ }, [selectedMethod]);
85
+
86
+ const fetchTransferDetails = async () => {
87
+ setLoading(true);
88
+ try {
89
+ const payload = {
90
+ amount,
91
+ accountName: "Fairsure",
92
+ isBill: true,
93
+ requestId,
94
+ };
95
+
96
+ const res = await axios.post(
97
+ "https://api.fairsurepayment.com.ng/api/pub/generate-account",
98
+ payload,
99
+ {
100
+ headers: {
101
+ Accept: "application/json",
102
+ "Content-Type": "application/json",
103
+ publickey: "LIVE_7AXSCCY8R3HABAN14QP7",
104
+ "User-Agent": "PostmanRuntime/7.51.0",
105
+ },
106
+ },
107
+ );
108
+ setTransferDetail(res?.data?.data);
109
+ } catch (error) {
110
+ console.log("FETCH ERROR:", error);
111
+ } finally {
112
+ setLoading(false);
113
+ }
114
+ };
115
+
116
+ const generateUssdCode = async () => {
117
+ setLoading(true);
118
+ const payload = {
119
+ amount,
120
+ requestId,
121
+ bankCode: selectedBank?.code,
122
+ };
123
+
124
+ try {
125
+ const res = await axios.post(
126
+ "https://api.fairsurepayment.com.ng/api/pub/generate-ussd",
127
+ payload,
128
+ {
129
+ headers: {
130
+ Accept: "application/json",
131
+ "Content-Type": "application/json",
132
+ publickey: "LIVE_7AXSCCY8R3HABAN14QP7",
133
+ "User-Agent": "PostmanRuntime/7.51.0",
134
+ },
135
+ },
136
+ );
137
+ setUssdBankResponse(res?.data?.data);
138
+ } catch (error) {
139
+ console.log("FETCH ERROR:", error);
140
+ } finally {
141
+ setLoading(false);
142
+ }
143
+ };
144
+
145
+ const verifyUssd = async () => {
146
+ try {
147
+ setLoading(true);
148
+ const payload = {
149
+ codeReference: ussdBankResponse?.codeRef,
150
+ amount: ussdBankResponse?.amount,
151
+ };
152
+ const res = await axios.post(
153
+ "https://api.fairsurepayment.com.ng/api/pub/ussd-status",
154
+ payload,
155
+ {
156
+ headers: {
157
+ Accept: "application/json",
158
+ "Content-Type": "application/json",
159
+ publickey: "LIVE_7AXSCCY8R3HABAN14QP7",
160
+ "User-Agent": "PostmanRuntime/7.51.0",
161
+ },
162
+ },
163
+ );
164
+ onSuccess?.(cardDetails.transactionRef);
165
+ setLoading(false);
166
+ } catch (error) {
167
+ console.log("FETCH ERROR:", error);
168
+ } finally {
169
+ setLoading(false);
170
+ }
171
+ };
172
+
173
+ useEffect(() => {
174
+ if (selectedBank) {
175
+ generateUssdCode();
176
+ }
177
+ }, [selectedBank]);
178
+
179
+ const getBanks = async () => {
180
+ setLoading(true);
181
+ try {
182
+ setLoading(true);
183
+ const res = await axios.get(
184
+ "https://api.fairsurepayment.com.ng/api/pub/list-ussd-banks",
185
+ {
186
+ timeout: 15000,
187
+ headers: {
188
+ Accept: "application/json",
189
+ "Content-Type": "application/json",
190
+ publickey: "LIVE_7AXSCCY8R3HABAN14QP7",
191
+ "User-Agent": "PostmanRuntime/7.36.0",
192
+ },
193
+ },
194
+ );
195
+
196
+ const banks = res.data.data.map((bank: any) => ({
197
+ name: bank.BankName,
198
+ code: bank.BankCode,
199
+ }));
200
+ setLoading(false);
201
+ setUssdBanks(banks);
202
+ } catch (error: any) {
203
+ console.log("Failed to fetch banks", error.response?.data);
204
+ } finally {
205
+ setLoading(false);
206
+ }
207
+ };
208
+
209
+ const handleCopy = async (text: string) => {
210
+ await Clipboard.setStringAsync(text);
211
+ setCopied(true);
212
+ setTimeout(() => setCopied(false), 2000);
213
+ };
214
+
215
+ const formatTime = (seconds: number) => {
216
+ const m = Math.floor(seconds / 60);
217
+ const s = seconds % 60;
218
+ return `${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
219
+ };
220
+
221
+ const formatAmount = (value: number) => {
222
+ return value.toLocaleString("en-NG", {
223
+ minimumFractionDigits: 2,
224
+ maximumFractionDigits: 2,
225
+ });
226
+ };
227
+
228
+ const formatCardNumber = (value: string) => {
229
+ const v = value.replace(/\s+/g, "").replace(/[^0-9]/gi, "");
230
+ const matches = v.match(/\d{4,16}/g);
231
+ const match = (matches && matches[0]) || "";
232
+ const parts = [];
233
+ for (let i = 0; i < match.length; i += 4) {
234
+ parts.push(match.substring(i, i + 4));
235
+ }
236
+ return parts.length ? parts.join(" ") : value;
237
+ };
238
+
239
+ const formatExpiry = (value: string) => {
240
+ const v = value.replace(/\s+/g, "").replace(/[^0-9]/gi, "");
241
+ if (v.length >= 2) {
242
+ return v.slice(0, 2) + "/" + v.slice(2, 4);
243
+ }
244
+ return v;
245
+ };
246
+ const completeCard = async (data: string) => {
247
+ setLoading(true);
248
+ try {
249
+ const payload = {
250
+ customerId: customerEmail,
251
+ amount: String(amount),
252
+ transactionRef: requestId,
253
+ currency: "NGN",
254
+ callbackUrl: "https://yourcallbackurl.com",
255
+ isBill: "true",
256
+ data: data,
257
+ deviceInformation: {
258
+ deviceChannel: "MOBILE",
259
+ userAgentBrowserValue: `ReactNative/${Platform.OS}`,
260
+ httpBrowserLanguage: "en-NG",
261
+ httpBrowserJavaEnabled: false,
262
+ httpBrowserJavaScriptEnabled: true,
263
+ httpBrowserColorDepth: 32,
264
+ httpBrowserScreenHeight: Math.round(height),
265
+ httpBrowserScreenWidth: Math.round(width),
266
+ httpBrowserTimeDifference: `${-new Date().getTimezoneOffset()}`,
267
+ },
268
+ };
269
+ const res = await axios.post(
270
+ "https://api.fairsurepayment.com.ng/api/pub/card-payment",
271
+ payload,
272
+ {
273
+ headers: {
274
+ Accept: "application/json",
275
+ "Content-Type": "application/json",
276
+ publickey: "LIVE_7AXSCCY8R3HABAN14QP7",
277
+ "User-Agent": "PostmanRuntime/7.51.0",
278
+ },
279
+ },
280
+ );
281
+ setLoading(false);
282
+ return res.data;
283
+ } catch (error: any) {
284
+ setLoading(false);
285
+ if (axios.isAxiosError(error)) {
286
+ console.log("STATUS:", error.response?.status);
287
+ console.log("DATA:", error.response?.data);
288
+ console.log("HEADERS:", error.response?.headers);
289
+ } else {
290
+ console.log("UNKNOWN ERROR:", error);
291
+ }
292
+ }
293
+ };
294
+ const handleCardPayment = async () => {
295
+ setLoading(true);
296
+ try {
297
+ const payload = {
298
+ pan: cardNumber.replace(/\s/g, ""),
299
+ expiryDate: expiryDate.replace("/", ""),
300
+ cvv,
301
+ pin,
302
+ };
303
+
304
+ const res = await axios.post(
305
+ "https://api.fairsurepayment.com.ng/api/pub/encrypt-test",
306
+ payload,
307
+ {
308
+ timeout: 15000,
309
+ headers: {
310
+ Accept: "application/json",
311
+ "Content-Type": "application/json",
312
+ publickey: "LIVE_7AXSCCY8R3HABAN14QP7",
313
+ "User-Agent": "PostmanRuntime/7.51.0",
314
+ },
315
+ },
316
+ );
317
+ const data = await completeCard(res?.data?.data);
318
+ setMessage(data?.data?.message);
319
+ setCardDetails(data?.data);
320
+ setOtp("");
321
+ setPin("");
322
+ setShowOtp(true);
323
+ return data;
324
+ } catch (error) {
325
+ console.log(error);
326
+ } finally {
327
+ setLoading(false);
328
+ }
329
+ };
330
+ const verifyCardPayment = async () => {
331
+ setLoading(true);
332
+ try {
333
+ const payload = {
334
+ transactionId: cardDetails?.transactionRef,
335
+ paymentId: cardDetails?.paymentId,
336
+ // eciFlag: "07",
337
+ otp: otp,
338
+ customerName,
339
+ customerPhone,
340
+ customerEmail,
341
+ };
342
+ const res = await axios.post(
343
+ "https://api.fairsurepayment.com.ng/api/pub/submit-otp",
344
+ payload,
345
+ {
346
+ headers: {
347
+ Accept: "application/json",
348
+ "Content-Type": "application/json",
349
+ publickey: "LIVE_7AXSCCY8R3HABAN14QP7",
350
+ "User-Agent": "PostmanRuntime/7.36.0",
351
+ },
352
+ },
353
+ );
354
+ onSuccess?.(transferDetail.transactionReference);
355
+ setLoading(false);
356
+ return res?.data;
357
+ } catch (error) {
358
+ setLoading(false);
359
+ console.log("ERROR", error);
360
+ }
361
+ };
362
+ const verifyTransfer = async () => {
363
+ setLoading(true);
364
+ try {
365
+ const res = await axios.get(
366
+ `https://api.fairsurepayment.com.ng/api/pub/transfer-status?transactionReference=${transferDetail?.transactionReference}`,
367
+ {
368
+ timeout: 15000,
369
+ headers: {
370
+ Accept: "application/json",
371
+ "Content-Type": "application/json",
372
+ publickey: "LIVE_7AXSCCY8R3HABAN14QP7",
373
+ "User-Agent": "PostmanRuntime/7.36.0",
374
+ },
375
+ },
376
+ );
377
+ onSuccess?.(transferDetail.transactionReference);
378
+ return res?.data;
379
+ } catch (error: any) {
380
+ console.log("Failed to Verify transfer", error.response?.data);
381
+ } finally {
382
+ setLoading(false);
383
+ }
384
+ };
385
+
386
+ const resetForm = () => {
387
+ setCardNumber("");
388
+ setExpiryDate("");
389
+ setCvv("");
390
+ setSelectedBank(null);
391
+ setcardcontinue(false);
392
+ setSelectedMethod("card");
393
+ };
394
+
395
+ return {
396
+ // State
397
+ selectedMethod,
398
+ cardNumber,
399
+ expiryDate,
400
+ cvv,
401
+ selectedBank,
402
+ showBankDropdown,
403
+ pin,
404
+ message,
405
+ otp,
406
+ copied,
407
+ showPin,
408
+ showOtp,
409
+ cardcontinue,
410
+ loading,
411
+ ussdBanks,
412
+ transferDetail,
413
+ cardDetails,
414
+ timeLeft,
415
+ ussdBankResponse,
416
+
417
+ // Setters
418
+ setSelectedMethod,
419
+ setCardNumber,
420
+ setExpiryDate,
421
+ setCvv,
422
+ setSelectedBank,
423
+ setShowBankDropdown,
424
+ setPin,
425
+ setOtp,
426
+ setcardcontinue,
427
+ setShowOtp,
428
+
429
+ // Utilities
430
+ formatTime,
431
+ formatAmount,
432
+ formatCardNumber,
433
+ formatExpiry,
434
+ handleCopy,
435
+ handleCardPayment,
436
+ verifyTransfer,
437
+ verifyCardPayment,
438
+ resetForm,
439
+ verifyUssd,
440
+ };
441
+ }
@@ -0,0 +1,37 @@
1
+ import React, { createContext, useContext, useState } from "react";
2
+
3
+ import { CheckoutConfig } from "../types";
4
+ import Checkout from "../components/PaystackCheckout";
5
+
6
+ const CheckoutContext = createContext<any>(null);
7
+
8
+ export function CheckoutProvider({ children }: { children: React.ReactNode }) {
9
+ const [config, setConfig] = useState<CheckoutConfig | null>(null);
10
+
11
+ const openCheckout = (options: CheckoutConfig) => {
12
+ setConfig(options);
13
+ };
14
+
15
+ const closeCheckout = () => setConfig(null);
16
+
17
+ return (
18
+ <CheckoutContext.Provider value={{ openCheckout }}>
19
+ {children}
20
+
21
+ {config && (
22
+ <Checkout
23
+ amount={config.amount}
24
+ merchantName={config.merchantName}
25
+ onSuccess={config.onSuccess}
26
+ onClose={closeCheckout}
27
+ requestId={config.requestId}
28
+ customerEmail={config.customerEmail}
29
+ customerName={config.customerName}
30
+ customerPhone={config.customerPhone}
31
+ />
32
+ )}
33
+ </CheckoutContext.Provider>
34
+ );
35
+ }
36
+
37
+ export const useCheckoutContext = () => useContext(CheckoutContext);
@@ -0,0 +1,11 @@
1
+ import { useCheckoutContext } from "../context/CheckoutProvider";
2
+
3
+ export function useCheckout() {
4
+ const ctx = useCheckoutContext();
5
+
6
+ if (!ctx) {
7
+ throw new Error("usePaystackCheckout must be used inside CheckoutProvider");
8
+ }
9
+
10
+ return ctx;
11
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { CheckoutProvider } from "./context/CheckoutProvider";
2
+ export { useCheckout } from "./hooks/useCheckout";
package/src/types.ts ADDED
@@ -0,0 +1,9 @@
1
+ export interface CheckoutConfig {
2
+ amount: number;
3
+ merchantName?: string;
4
+ onSuccess?: (reference: string) => void;
5
+ requestId: string;
6
+ customerEmail: string;
7
+ customerName: string;
8
+ customerPhone: string;
9
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "module": "ESNext",
5
+ "jsx": "react-native",
6
+
7
+ "moduleResolution": "node",
8
+ "allowSyntheticDefaultImports": true,
9
+ "esModuleInterop": true,
10
+
11
+ "strict": true,
12
+ "skipLibCheck": true,
13
+
14
+ "declaration": true,
15
+ "emitDeclarationOnly": false,
16
+ "outDir": "dist",
17
+
18
+ "types": ["react", "react-native"]
19
+ },
20
+ "include": ["src"],
21
+ "exclude": ["node_modules", "dist"]
22
+ }