fairsure-checkout-sdk 1.0.7 → 1.0.9
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/dist/components/PaystackCheckout.js +15 -8
- package/dist/components/TransferPayment.js +103 -22
- package/dist/components/USSDPayment.js +37 -3
- package/dist/components/usePaymentCheckout.js +9 -9
- package/package.json +9 -2
- package/src/components/PaystackCheckout.tsx +53 -47
- package/src/components/TransferPayment.tsx +118 -33
- package/src/components/USSDPayment.tsx +41 -3
- package/src/components/usePaymentCheckout.ts +9 -10
|
@@ -74,13 +74,14 @@ export default function Checkout({ requestId, customerEmail, customerName, custo
|
|
|
74
74
|
<View style={styles.body}>
|
|
75
75
|
{/* Payment Method Selector */}
|
|
76
76
|
<PaymentMethodSelector methods={paymentMethods} selectedMethod={checkout.selectedMethod} onSelectMethod={checkout.setSelectedMethod}/>
|
|
77
|
+
<View style={styles.minHeight}>
|
|
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}/>)}
|
|
77
80
|
|
|
78
|
-
|
|
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}/>)}
|
|
81
|
+
{checkout.selectedMethod === "transfer" && (<TransferPayment loading={checkout.loading || !checkout.transferDetail} amount={amount} transferDetail={checkout.transferDetail} timeLeft={checkout.timeLeft} copied={checkout.copied} handleCopy={checkout.handleCopy} formatAmount={checkout.formatAmount} formatTime={checkout.formatTime}/>)}
|
|
80
82
|
|
|
81
|
-
|
|
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}/>)}
|
|
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
|
+
</View>
|
|
84
85
|
|
|
85
86
|
{/* Payment Button */}
|
|
86
87
|
<TouchableOpacity onPress={handlePayment} disabled={isButtonDisabled()} style={[
|
|
@@ -92,9 +93,11 @@ export default function Checkout({ requestId, customerEmail, customerName, custo
|
|
|
92
93
|
<Text style={styles.payButtonText}>{getButtonText()}</Text>
|
|
93
94
|
{checkout.loading && (<ActivityIndicator size="small" color="white"/>)}
|
|
94
95
|
</TouchableOpacity>
|
|
95
|
-
{!checkout.loading && checkout.timeLeft <= 0 && (
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
{/* {!checkout.loading && checkout.timeLeft <= 0 && (
|
|
97
|
+
<TouchableOpacity>
|
|
98
|
+
<Text>Resend</Text>
|
|
99
|
+
</TouchableOpacity>
|
|
100
|
+
)} */}
|
|
98
101
|
|
|
99
102
|
{/* Footer */}
|
|
100
103
|
<View style={styles.footer}>
|
|
@@ -161,6 +164,9 @@ const styles = StyleSheet.create({
|
|
|
161
164
|
eyeText: {
|
|
162
165
|
color: "#007AFF",
|
|
163
166
|
},
|
|
167
|
+
minHeight: {
|
|
168
|
+
minHeight: "50%",
|
|
169
|
+
},
|
|
164
170
|
headerTop: {
|
|
165
171
|
flexDirection: "row",
|
|
166
172
|
justifyContent: "space-between",
|
|
@@ -187,6 +193,7 @@ const styles = StyleSheet.create({
|
|
|
187
193
|
backgroundColor: "white",
|
|
188
194
|
borderBottomLeftRadius: 20,
|
|
189
195
|
borderBottomRightRadius: 20,
|
|
196
|
+
minHeight: "100%",
|
|
190
197
|
},
|
|
191
198
|
paymentMethodsContainer: {
|
|
192
199
|
flexDirection: "row",
|
|
@@ -1,33 +1,63 @@
|
|
|
1
1
|
import { Ionicons } from "@expo/vector-icons";
|
|
2
2
|
import { ActivityIndicator, StyleSheet, Text, TouchableOpacity, View, } from "react-native";
|
|
3
3
|
export default function TransferPayment({ loading, amount, transferDetail, timeLeft, copied, handleCopy, formatAmount, formatTime, }) {
|
|
4
|
-
if (loading) {
|
|
5
|
-
|
|
6
|
-
}
|
|
4
|
+
// if (loading) {
|
|
5
|
+
// return <ActivityIndicator size="large" color="#4F46E5" />;
|
|
6
|
+
// }
|
|
7
7
|
return (<View>
|
|
8
|
-
<View style={styles.
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
<View style={styles.tipCard}>
|
|
9
|
+
<View style={styles.tipHeader}>
|
|
10
|
+
<Text style={styles.tipIcon}>💡</Text>
|
|
11
|
+
<Text style={styles.tipTitle}>Quick tip before you transfer</Text>
|
|
12
|
+
</View>
|
|
12
13
|
|
|
13
|
-
<View>
|
|
14
|
-
<
|
|
14
|
+
<View style={styles.tipStep}>
|
|
15
|
+
<Text style={styles.stepNumber}>1</Text>
|
|
16
|
+
<Text style={styles.stepText}>Copy the account details below</Text>
|
|
17
|
+
</View>
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
</TouchableOpacity>
|
|
25
|
-
</View>
|
|
26
|
-
</View>
|
|
19
|
+
<View style={styles.tipStep}>
|
|
20
|
+
<Text style={styles.stepNumber}>2</Text>
|
|
21
|
+
<Text style={styles.stepText}>
|
|
22
|
+
Open your banking app on{" "}
|
|
23
|
+
<Text style={{ fontWeight: "bold" }}>this or another device</Text>{" "}
|
|
24
|
+
and make the transfer
|
|
25
|
+
</Text>
|
|
26
|
+
</View>
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
<View style={styles.tipStep}>
|
|
29
|
+
<Text style={styles.stepNumber}>3</Text>
|
|
30
|
+
<Text style={styles.stepText}>
|
|
31
|
+
Come back here and tap{" "}
|
|
32
|
+
<Text style={{ fontWeight: "bold" }}>"I have sent the money"</Text>
|
|
33
|
+
</Text>
|
|
29
34
|
</View>
|
|
30
35
|
</View>
|
|
36
|
+
<View style={styles.transferBox}>
|
|
37
|
+
{loading || !transferDetail ? (<ActivityIndicator size="large" color="#4F46E5"/>) : (<View style={{ width: "100%" }}>
|
|
38
|
+
<Text style={styles.transferText}>
|
|
39
|
+
Transfer ₦{formatAmount(amount)} to the account below
|
|
40
|
+
</Text>
|
|
41
|
+
|
|
42
|
+
<View>
|
|
43
|
+
<TransferDetailItem label="Bank Name" value={transferDetail.bankName}/>
|
|
44
|
+
|
|
45
|
+
<View style={styles.transferDetail}>
|
|
46
|
+
<Text style={styles.transferLabel}>Account Number</Text>
|
|
47
|
+
<View style={styles.row}>
|
|
48
|
+
<Text style={styles.accountNumber}>
|
|
49
|
+
{transferDetail.accountNumber}
|
|
50
|
+
</Text>
|
|
51
|
+
<TouchableOpacity onPress={() => handleCopy(transferDetail.accountNumber)}>
|
|
52
|
+
<Ionicons name={copied ? "checkmark" : "copy-outline"} size={20} color="#4F46E5"/>
|
|
53
|
+
</TouchableOpacity>
|
|
54
|
+
</View>
|
|
55
|
+
</View>
|
|
56
|
+
|
|
57
|
+
<TransferDetailItem label="Account Name" value={transferDetail.accountName}/>
|
|
58
|
+
</View>
|
|
59
|
+
</View>)}
|
|
60
|
+
</View>
|
|
31
61
|
|
|
32
62
|
<View style={styles.transferNote}>
|
|
33
63
|
<Text style={styles.transferNoteText}>
|
|
@@ -169,7 +199,12 @@ const styles = StyleSheet.create({
|
|
|
169
199
|
borderColor: "#BFDBFE",
|
|
170
200
|
borderRadius: 12,
|
|
171
201
|
padding: 8,
|
|
172
|
-
|
|
202
|
+
minHeight: 150,
|
|
203
|
+
display: "flex",
|
|
204
|
+
flexDirection: "column",
|
|
205
|
+
justifyContent: "center",
|
|
206
|
+
alignItems: "center",
|
|
207
|
+
marginBottom: 4,
|
|
173
208
|
},
|
|
174
209
|
transferText: {
|
|
175
210
|
fontSize: 14,
|
|
@@ -198,6 +233,7 @@ const styles = StyleSheet.create({
|
|
|
198
233
|
borderColor: "#FDE68A",
|
|
199
234
|
borderRadius: 12,
|
|
200
235
|
padding: 8,
|
|
236
|
+
marginBottom: 4,
|
|
201
237
|
},
|
|
202
238
|
transferNoteText: { fontSize: 14, color: "#78350F" },
|
|
203
239
|
dropdown: {
|
|
@@ -308,4 +344,49 @@ const styles = StyleSheet.create({
|
|
|
308
344
|
fontSize: 14, // text-sm
|
|
309
345
|
color: "#6B7280", // text-gray-500
|
|
310
346
|
},
|
|
347
|
+
tipCard: {
|
|
348
|
+
backgroundColor: "#EEF2FF", // light indigo
|
|
349
|
+
borderRadius: 12,
|
|
350
|
+
padding: 16,
|
|
351
|
+
marginBottom: 16,
|
|
352
|
+
borderLeftWidth: 4,
|
|
353
|
+
borderLeftColor: "#4F46E5",
|
|
354
|
+
},
|
|
355
|
+
tipHeader: {
|
|
356
|
+
flexDirection: "row",
|
|
357
|
+
alignItems: "center",
|
|
358
|
+
marginBottom: 12,
|
|
359
|
+
gap: 8,
|
|
360
|
+
},
|
|
361
|
+
tipIcon: {
|
|
362
|
+
fontSize: 18,
|
|
363
|
+
},
|
|
364
|
+
tipTitle: {
|
|
365
|
+
fontWeight: "700",
|
|
366
|
+
fontSize: 15,
|
|
367
|
+
color: "#3730A3",
|
|
368
|
+
},
|
|
369
|
+
tipStep: {
|
|
370
|
+
flexDirection: "row",
|
|
371
|
+
alignItems: "flex-start",
|
|
372
|
+
gap: 10,
|
|
373
|
+
marginBottom: 8,
|
|
374
|
+
},
|
|
375
|
+
stepNumber: {
|
|
376
|
+
backgroundColor: "#4F46E5",
|
|
377
|
+
color: "#fff",
|
|
378
|
+
borderRadius: 10,
|
|
379
|
+
width: 20,
|
|
380
|
+
height: 20,
|
|
381
|
+
textAlign: "center",
|
|
382
|
+
fontSize: 12,
|
|
383
|
+
fontWeight: "700",
|
|
384
|
+
lineHeight: 20,
|
|
385
|
+
},
|
|
386
|
+
stepText: {
|
|
387
|
+
flex: 1,
|
|
388
|
+
fontSize: 14,
|
|
389
|
+
color: "#374151",
|
|
390
|
+
lineHeight: 20,
|
|
391
|
+
},
|
|
311
392
|
});
|
|
@@ -12,6 +12,18 @@ export default function USSDPayment({ selectedBank, setSelectedBank, showBankDro
|
|
|
12
12
|
<Ionicons name="chevron-down" size={20} color="#9CA3AF"/>
|
|
13
13
|
</TouchableOpacity>
|
|
14
14
|
|
|
15
|
+
{!showBankDropdown && !selectedBank && (<View style={styles.instructionContainer}>
|
|
16
|
+
<Text style={styles.instructionTitle}>How to pay with USSD</Text>
|
|
17
|
+
|
|
18
|
+
<Text style={styles.instructionText}>
|
|
19
|
+
1. Select your bank{"\n"}
|
|
20
|
+
2. Dial the generated USSD code{"\n"}
|
|
21
|
+
3. Follow the prompts on your phone{"\n"}
|
|
22
|
+
4. Authorize payment with your PIN{"\n"}
|
|
23
|
+
5. Click the "Confirm Payment" button.
|
|
24
|
+
</Text>
|
|
25
|
+
</View>)}
|
|
26
|
+
|
|
15
27
|
{showBankDropdown && (<BankDropdown ussdBanks={ussdBanks} loading={loading} onSelectBank={(bank) => {
|
|
16
28
|
setSelectedBank(bank);
|
|
17
29
|
setShowBankDropdown(false);
|
|
@@ -51,12 +63,32 @@ function USSDInstructions({ ussdCode, copied, handleCopy, loading, }) {
|
|
|
51
63
|
<Text style={styles.ussdStepsText}>
|
|
52
64
|
1. Dial the USSD code on your phone{"\n"}
|
|
53
65
|
2. Follow the prompts on your screen{"\n"}
|
|
54
|
-
3. Authorize payment with your bank PIN
|
|
66
|
+
3. Authorize payment with your bank PIN{"\n"}
|
|
67
|
+
4. Click the "Confirm Payment" button.
|
|
55
68
|
</Text>
|
|
56
69
|
</View>
|
|
57
70
|
</View>);
|
|
58
71
|
}
|
|
59
72
|
const styles = StyleSheet.create({
|
|
73
|
+
instructionContainer: {
|
|
74
|
+
marginTop: 12,
|
|
75
|
+
padding: 16,
|
|
76
|
+
backgroundColor: "#F9FAFB",
|
|
77
|
+
borderRadius: 12,
|
|
78
|
+
borderWidth: 1,
|
|
79
|
+
borderColor: "#E5E7EB",
|
|
80
|
+
},
|
|
81
|
+
instructionTitle: {
|
|
82
|
+
fontSize: 14,
|
|
83
|
+
fontWeight: "600",
|
|
84
|
+
color: "#111827",
|
|
85
|
+
marginBottom: 8,
|
|
86
|
+
},
|
|
87
|
+
instructionText: {
|
|
88
|
+
fontSize: 13,
|
|
89
|
+
lineHeight: 22,
|
|
90
|
+
color: "#6B7280",
|
|
91
|
+
},
|
|
60
92
|
overlay: {
|
|
61
93
|
flex: 1,
|
|
62
94
|
backgroundColor: "rgba(0,0,0,0.5)",
|
|
@@ -250,8 +282,9 @@ const styles = StyleSheet.create({
|
|
|
250
282
|
},
|
|
251
283
|
ussdCodeContainer: {
|
|
252
284
|
backgroundColor: "white", // bg-white
|
|
253
|
-
borderRadius:
|
|
254
|
-
|
|
285
|
+
borderRadius: 8, // rounded-xl
|
|
286
|
+
paddingHorizontal: 12, // p-4
|
|
287
|
+
paddingTop: 12,
|
|
255
288
|
marginBottom: 12, // mb-3
|
|
256
289
|
},
|
|
257
290
|
ussdCodeRow: {
|
|
@@ -277,6 +310,7 @@ const styles = StyleSheet.create({
|
|
|
277
310
|
},
|
|
278
311
|
ussdStepsText: {
|
|
279
312
|
fontSize: 14, // text-sm
|
|
313
|
+
padding: 4, // px-2
|
|
280
314
|
color: "#1E40AF", // text-indigo-800
|
|
281
315
|
},
|
|
282
316
|
payButton: {
|
|
@@ -42,7 +42,7 @@ export function usePaymentCheckout(amount, requestId, customerEmail, customerNam
|
|
|
42
42
|
if (transferDetail.validityPeriodMins > 0) {
|
|
43
43
|
setTimeLeft(transferDetail.validityPeriodMins * 60);
|
|
44
44
|
}
|
|
45
|
-
}, [transferDetail.validityPeriodMins]);
|
|
45
|
+
}, [transferDetail, transferDetail.validityPeriodMins]);
|
|
46
46
|
useEffect(() => {
|
|
47
47
|
if (timeLeft <= 0)
|
|
48
48
|
return;
|
|
@@ -76,7 +76,7 @@ export function usePaymentCheckout(amount, requestId, customerEmail, customerNam
|
|
|
76
76
|
isBill: true,
|
|
77
77
|
requestId,
|
|
78
78
|
};
|
|
79
|
-
const res = await axios.post("https://api.
|
|
79
|
+
const res = await axios.post("https://api.fairsurepay.com/api/pub/generate-account", payload, {
|
|
80
80
|
headers: {
|
|
81
81
|
Accept: "application/json",
|
|
82
82
|
"Content-Type": "application/json",
|
|
@@ -102,7 +102,7 @@ export function usePaymentCheckout(amount, requestId, customerEmail, customerNam
|
|
|
102
102
|
bankCode: selectedBank === null || selectedBank === void 0 ? void 0 : selectedBank.code,
|
|
103
103
|
};
|
|
104
104
|
try {
|
|
105
|
-
const res = await axios.post("https://api.
|
|
105
|
+
const res = await axios.post("https://api.fairsurepay.com/api/pub/generate-ussd", payload, {
|
|
106
106
|
headers: {
|
|
107
107
|
Accept: "application/json",
|
|
108
108
|
"Content-Type": "application/json",
|
|
@@ -126,7 +126,7 @@ export function usePaymentCheckout(amount, requestId, customerEmail, customerNam
|
|
|
126
126
|
codeReference: ussdBankResponse === null || ussdBankResponse === void 0 ? void 0 : ussdBankResponse.codeRef,
|
|
127
127
|
amount: ussdBankResponse === null || ussdBankResponse === void 0 ? void 0 : ussdBankResponse.amount,
|
|
128
128
|
};
|
|
129
|
-
const res = await axios.post("https://api.
|
|
129
|
+
const res = await axios.post("https://api.fairsurepay.com/api/pub/ussd-status", payload, {
|
|
130
130
|
headers: {
|
|
131
131
|
Accept: "application/json",
|
|
132
132
|
"Content-Type": "application/json",
|
|
@@ -154,7 +154,7 @@ export function usePaymentCheckout(amount, requestId, customerEmail, customerNam
|
|
|
154
154
|
setLoading(true);
|
|
155
155
|
try {
|
|
156
156
|
setLoading(true);
|
|
157
|
-
const res = await axios.get("https://api.
|
|
157
|
+
const res = await axios.get("https://api.fairsurepay.com/api/pub/list-ussd-banks", {
|
|
158
158
|
timeout: 15000,
|
|
159
159
|
headers: {
|
|
160
160
|
Accept: "application/json",
|
|
@@ -242,7 +242,7 @@ export function usePaymentCheckout(amount, requestId, customerEmail, customerNam
|
|
|
242
242
|
httpBrowserTimeDifference: `${-new Date().getTimezoneOffset()}`,
|
|
243
243
|
},
|
|
244
244
|
};
|
|
245
|
-
const res = await axios.post("https://api.
|
|
245
|
+
const res = await axios.post("https://api.fairsurepay.com/api/pub/card-payment", payload, {
|
|
246
246
|
headers: {
|
|
247
247
|
Accept: "application/json",
|
|
248
248
|
"Content-Type": "application/json",
|
|
@@ -276,7 +276,7 @@ export function usePaymentCheckout(amount, requestId, customerEmail, customerNam
|
|
|
276
276
|
cvv,
|
|
277
277
|
pin,
|
|
278
278
|
};
|
|
279
|
-
const res = await axios.post("https://api.
|
|
279
|
+
const res = await axios.post("https://api.fairsurepay.com/api/pub/encrypt-test", payload, {
|
|
280
280
|
timeout: 15000,
|
|
281
281
|
headers: {
|
|
282
282
|
Accept: "application/json",
|
|
@@ -312,7 +312,7 @@ export function usePaymentCheckout(amount, requestId, customerEmail, customerNam
|
|
|
312
312
|
customerPhone,
|
|
313
313
|
customerEmail,
|
|
314
314
|
};
|
|
315
|
-
const res = await axios.post("https://api.
|
|
315
|
+
const res = await axios.post("https://api.fairsurepay.com/api/pub/submit-otp", payload, {
|
|
316
316
|
headers: {
|
|
317
317
|
Accept: "application/json",
|
|
318
318
|
"Content-Type": "application/json",
|
|
@@ -333,7 +333,7 @@ export function usePaymentCheckout(amount, requestId, customerEmail, customerNam
|
|
|
333
333
|
var _a;
|
|
334
334
|
setLoading(true);
|
|
335
335
|
try {
|
|
336
|
-
const res = await axios.get(`https://api.
|
|
336
|
+
const res = await axios.get(`https://api.fairsurepay.com/api/pub/transfer-status?transactionReference=${transferDetail === null || transferDetail === void 0 ? void 0 : transferDetail.transactionReference}`, {
|
|
337
337
|
timeout: 15000,
|
|
338
338
|
headers: {
|
|
339
339
|
Accept: "application/json",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fairsure-checkout-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -14,7 +14,13 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsc",
|
|
17
|
-
"pack": "npm pack"
|
|
17
|
+
"pack": "npm pack",
|
|
18
|
+
"dev": "tsc --watch",
|
|
19
|
+
"publish:local": "npm run build && yalc push",
|
|
20
|
+
"dev:push": "nodemon --watch src --ext ts,tsx --exec \"npm run build && yalc push\"",
|
|
21
|
+
"release:patch": "npm version patch && npm run build && npm publish",
|
|
22
|
+
"release:minor": "npm version minor && npm run build && npm publish",
|
|
23
|
+
"release:major": "npm version major && npm run build && npm publish"
|
|
18
24
|
},
|
|
19
25
|
"dependencies": {
|
|
20
26
|
"@expo/vector-icons": "^15.0.3",
|
|
@@ -23,6 +29,7 @@
|
|
|
23
29
|
},
|
|
24
30
|
"devDependencies": {
|
|
25
31
|
"@types/react": "^19.2.10",
|
|
32
|
+
"nodemon": "^3.1.14",
|
|
26
33
|
"typescript": "^5.9.3"
|
|
27
34
|
}
|
|
28
35
|
}
|
|
@@ -129,53 +129,54 @@ export default function Checkout({
|
|
|
129
129
|
selectedMethod={checkout.selectedMethod}
|
|
130
130
|
onSelectMethod={checkout.setSelectedMethod}
|
|
131
131
|
/>
|
|
132
|
+
<View style={styles.minHeight}>
|
|
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
|
+
)}
|
|
132
152
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
)}
|
|
153
|
+
{checkout.selectedMethod === "transfer" && (
|
|
154
|
+
<TransferPayment
|
|
155
|
+
loading={checkout.loading || !checkout.transferDetail}
|
|
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
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
+
</View>
|
|
179
180
|
|
|
180
181
|
{/* Payment Button */}
|
|
181
182
|
<TouchableOpacity
|
|
@@ -193,11 +194,11 @@ export default function Checkout({
|
|
|
193
194
|
<ActivityIndicator size="small" color="white" />
|
|
194
195
|
)}
|
|
195
196
|
</TouchableOpacity>
|
|
196
|
-
{!checkout.loading && checkout.timeLeft <= 0 && (
|
|
197
|
+
{/* {!checkout.loading && checkout.timeLeft <= 0 && (
|
|
197
198
|
<TouchableOpacity>
|
|
198
199
|
<Text>Resend</Text>
|
|
199
200
|
</TouchableOpacity>
|
|
200
|
-
)}
|
|
201
|
+
)} */}
|
|
201
202
|
|
|
202
203
|
{/* Footer */}
|
|
203
204
|
<View style={styles.footer}>
|
|
@@ -281,6 +282,10 @@ const styles = StyleSheet.create({
|
|
|
281
282
|
color: "#007AFF",
|
|
282
283
|
},
|
|
283
284
|
|
|
285
|
+
minHeight: {
|
|
286
|
+
minHeight: "50%",
|
|
287
|
+
},
|
|
288
|
+
|
|
284
289
|
headerTop: {
|
|
285
290
|
flexDirection: "row",
|
|
286
291
|
justifyContent: "space-between",
|
|
@@ -307,6 +312,7 @@ const styles = StyleSheet.create({
|
|
|
307
312
|
backgroundColor: "white",
|
|
308
313
|
borderBottomLeftRadius: 20,
|
|
309
314
|
borderBottomRightRadius: 20,
|
|
315
|
+
minHeight: "100%",
|
|
310
316
|
},
|
|
311
317
|
paymentMethodsContainer: {
|
|
312
318
|
flexDirection: "row",
|
|
@@ -32,46 +32,80 @@ export default function TransferPayment({
|
|
|
32
32
|
formatAmount,
|
|
33
33
|
formatTime,
|
|
34
34
|
}: TransferPaymentProps) {
|
|
35
|
-
if (loading) {
|
|
36
|
-
|
|
37
|
-
}
|
|
35
|
+
// if (loading) {
|
|
36
|
+
// return <ActivityIndicator size="large" color="#4F46E5" />;
|
|
37
|
+
// }
|
|
38
38
|
|
|
39
39
|
return (
|
|
40
40
|
<View>
|
|
41
|
+
<View style={styles.tipCard}>
|
|
42
|
+
<View style={styles.tipHeader}>
|
|
43
|
+
<Text style={styles.tipIcon}>💡</Text>
|
|
44
|
+
<Text style={styles.tipTitle}>Quick tip before you transfer</Text>
|
|
45
|
+
</View>
|
|
46
|
+
|
|
47
|
+
<View style={styles.tipStep}>
|
|
48
|
+
<Text style={styles.stepNumber}>1</Text>
|
|
49
|
+
<Text style={styles.stepText}>Copy the account details below</Text>
|
|
50
|
+
</View>
|
|
51
|
+
|
|
52
|
+
<View style={styles.tipStep}>
|
|
53
|
+
<Text style={styles.stepNumber}>2</Text>
|
|
54
|
+
<Text style={styles.stepText}>
|
|
55
|
+
Open your banking app on{" "}
|
|
56
|
+
<Text style={{ fontWeight: "bold" }}>this or another device</Text>{" "}
|
|
57
|
+
and make the transfer
|
|
58
|
+
</Text>
|
|
59
|
+
</View>
|
|
60
|
+
|
|
61
|
+
<View style={styles.tipStep}>
|
|
62
|
+
<Text style={styles.stepNumber}>3</Text>
|
|
63
|
+
<Text style={styles.stepText}>
|
|
64
|
+
Come back here and tap{" "}
|
|
65
|
+
<Text style={{ fontWeight: "bold" }}>"I have sent the money"</Text>
|
|
66
|
+
</Text>
|
|
67
|
+
</View>
|
|
68
|
+
</View>
|
|
41
69
|
<View style={styles.transferBox}>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
70
|
+
{loading || !transferDetail ? (
|
|
71
|
+
<ActivityIndicator size="large" color="#4F46E5" />
|
|
72
|
+
) : (
|
|
73
|
+
<View style={{ width: "100%" }}>
|
|
74
|
+
<Text style={styles.transferText}>
|
|
75
|
+
Transfer ₦{formatAmount(amount)} to the account below
|
|
76
|
+
</Text>
|
|
77
|
+
|
|
78
|
+
<View>
|
|
79
|
+
<TransferDetailItem
|
|
80
|
+
label="Bank Name"
|
|
81
|
+
value={transferDetail.bankName}
|
|
82
|
+
/>
|
|
45
83
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
84
|
+
<View style={styles.transferDetail}>
|
|
85
|
+
<Text style={styles.transferLabel}>Account Number</Text>
|
|
86
|
+
<View style={styles.row}>
|
|
87
|
+
<Text style={styles.accountNumber}>
|
|
88
|
+
{transferDetail.accountNumber}
|
|
89
|
+
</Text>
|
|
90
|
+
<TouchableOpacity
|
|
91
|
+
onPress={() => handleCopy(transferDetail.accountNumber)}
|
|
92
|
+
>
|
|
93
|
+
<Ionicons
|
|
94
|
+
name={copied ? "checkmark" : "copy-outline"}
|
|
95
|
+
size={20}
|
|
96
|
+
color="#4F46E5"
|
|
97
|
+
/>
|
|
98
|
+
</TouchableOpacity>
|
|
99
|
+
</View>
|
|
100
|
+
</View>
|
|
51
101
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
{transferDetail.accountNumber}
|
|
57
|
-
</Text>
|
|
58
|
-
<TouchableOpacity
|
|
59
|
-
onPress={() => handleCopy(transferDetail.accountNumber)}
|
|
60
|
-
>
|
|
61
|
-
<Ionicons
|
|
62
|
-
name={copied ? "checkmark" : "copy-outline"}
|
|
63
|
-
size={20}
|
|
64
|
-
color="#4F46E5"
|
|
65
|
-
/>
|
|
66
|
-
</TouchableOpacity>
|
|
102
|
+
<TransferDetailItem
|
|
103
|
+
label="Account Name"
|
|
104
|
+
value={transferDetail.accountName}
|
|
105
|
+
/>
|
|
67
106
|
</View>
|
|
68
107
|
</View>
|
|
69
|
-
|
|
70
|
-
<TransferDetailItem
|
|
71
|
-
label="Account Name"
|
|
72
|
-
value={transferDetail.accountName}
|
|
73
|
-
/>
|
|
74
|
-
</View>
|
|
108
|
+
)}
|
|
75
109
|
</View>
|
|
76
110
|
|
|
77
111
|
<View style={styles.transferNote}>
|
|
@@ -229,7 +263,12 @@ const styles = StyleSheet.create({
|
|
|
229
263
|
borderColor: "#BFDBFE",
|
|
230
264
|
borderRadius: 12,
|
|
231
265
|
padding: 8,
|
|
232
|
-
|
|
266
|
+
minHeight: 150,
|
|
267
|
+
display: "flex",
|
|
268
|
+
flexDirection: "column",
|
|
269
|
+
justifyContent: "center",
|
|
270
|
+
alignItems: "center",
|
|
271
|
+
marginBottom: 4,
|
|
233
272
|
},
|
|
234
273
|
transferText: {
|
|
235
274
|
fontSize: 14,
|
|
@@ -258,6 +297,7 @@ const styles = StyleSheet.create({
|
|
|
258
297
|
borderColor: "#FDE68A",
|
|
259
298
|
borderRadius: 12,
|
|
260
299
|
padding: 8,
|
|
300
|
+
marginBottom: 4,
|
|
261
301
|
},
|
|
262
302
|
transferNoteText: { fontSize: 14, color: "#78350F" },
|
|
263
303
|
dropdown: {
|
|
@@ -369,4 +409,49 @@ const styles = StyleSheet.create({
|
|
|
369
409
|
fontSize: 14, // text-sm
|
|
370
410
|
color: "#6B7280", // text-gray-500
|
|
371
411
|
},
|
|
412
|
+
tipCard: {
|
|
413
|
+
backgroundColor: "#EEF2FF", // light indigo
|
|
414
|
+
borderRadius: 12,
|
|
415
|
+
padding: 16,
|
|
416
|
+
marginBottom: 16,
|
|
417
|
+
borderLeftWidth: 4,
|
|
418
|
+
borderLeftColor: "#4F46E5",
|
|
419
|
+
},
|
|
420
|
+
tipHeader: {
|
|
421
|
+
flexDirection: "row",
|
|
422
|
+
alignItems: "center",
|
|
423
|
+
marginBottom: 12,
|
|
424
|
+
gap: 8,
|
|
425
|
+
},
|
|
426
|
+
tipIcon: {
|
|
427
|
+
fontSize: 18,
|
|
428
|
+
},
|
|
429
|
+
tipTitle: {
|
|
430
|
+
fontWeight: "700",
|
|
431
|
+
fontSize: 15,
|
|
432
|
+
color: "#3730A3",
|
|
433
|
+
},
|
|
434
|
+
tipStep: {
|
|
435
|
+
flexDirection: "row",
|
|
436
|
+
alignItems: "flex-start",
|
|
437
|
+
gap: 10,
|
|
438
|
+
marginBottom: 8,
|
|
439
|
+
},
|
|
440
|
+
stepNumber: {
|
|
441
|
+
backgroundColor: "#4F46E5",
|
|
442
|
+
color: "#fff",
|
|
443
|
+
borderRadius: 10,
|
|
444
|
+
width: 20,
|
|
445
|
+
height: 20,
|
|
446
|
+
textAlign: "center",
|
|
447
|
+
fontSize: 12,
|
|
448
|
+
fontWeight: "700",
|
|
449
|
+
lineHeight: 20,
|
|
450
|
+
},
|
|
451
|
+
stepText: {
|
|
452
|
+
flex: 1,
|
|
453
|
+
fontSize: 14,
|
|
454
|
+
color: "#374151",
|
|
455
|
+
lineHeight: 20,
|
|
456
|
+
},
|
|
372
457
|
});
|
|
@@ -56,6 +56,20 @@ export default function USSDPayment({
|
|
|
56
56
|
<Ionicons name="chevron-down" size={20} color="#9CA3AF" />
|
|
57
57
|
</TouchableOpacity>
|
|
58
58
|
|
|
59
|
+
{!showBankDropdown && !selectedBank && (
|
|
60
|
+
<View style={styles.instructionContainer}>
|
|
61
|
+
<Text style={styles.instructionTitle}>How to pay with USSD</Text>
|
|
62
|
+
|
|
63
|
+
<Text style={styles.instructionText}>
|
|
64
|
+
1. Select your bank{"\n"}
|
|
65
|
+
2. Dial the generated USSD code{"\n"}
|
|
66
|
+
3. Follow the prompts on your phone{"\n"}
|
|
67
|
+
4. Authorize payment with your PIN{"\n"}
|
|
68
|
+
5. Click the "Confirm Payment" button.
|
|
69
|
+
</Text>
|
|
70
|
+
</View>
|
|
71
|
+
)}
|
|
72
|
+
|
|
59
73
|
{showBankDropdown && (
|
|
60
74
|
<BankDropdown
|
|
61
75
|
ussdBanks={ussdBanks}
|
|
@@ -153,13 +167,35 @@ function USSDInstructions({
|
|
|
153
167
|
<Text style={styles.ussdStepsText}>
|
|
154
168
|
1. Dial the USSD code on your phone{"\n"}
|
|
155
169
|
2. Follow the prompts on your screen{"\n"}
|
|
156
|
-
3. Authorize payment with your bank PIN
|
|
170
|
+
3. Authorize payment with your bank PIN{"\n"}
|
|
171
|
+
4. Click the "Confirm Payment" button.
|
|
157
172
|
</Text>
|
|
158
173
|
</View>
|
|
159
174
|
</View>
|
|
160
175
|
);
|
|
161
176
|
}
|
|
162
177
|
const styles = StyleSheet.create({
|
|
178
|
+
instructionContainer: {
|
|
179
|
+
marginTop: 12,
|
|
180
|
+
padding: 16,
|
|
181
|
+
backgroundColor: "#F9FAFB",
|
|
182
|
+
borderRadius: 12,
|
|
183
|
+
borderWidth: 1,
|
|
184
|
+
borderColor: "#E5E7EB",
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
instructionTitle: {
|
|
188
|
+
fontSize: 14,
|
|
189
|
+
fontWeight: "600",
|
|
190
|
+
color: "#111827",
|
|
191
|
+
marginBottom: 8,
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
instructionText: {
|
|
195
|
+
fontSize: 13,
|
|
196
|
+
lineHeight: 22,
|
|
197
|
+
color: "#6B7280",
|
|
198
|
+
},
|
|
163
199
|
overlay: {
|
|
164
200
|
flex: 1,
|
|
165
201
|
backgroundColor: "rgba(0,0,0,0.5)",
|
|
@@ -356,8 +392,9 @@ const styles = StyleSheet.create({
|
|
|
356
392
|
},
|
|
357
393
|
ussdCodeContainer: {
|
|
358
394
|
backgroundColor: "white", // bg-white
|
|
359
|
-
borderRadius:
|
|
360
|
-
|
|
395
|
+
borderRadius: 8, // rounded-xl
|
|
396
|
+
paddingHorizontal: 12, // p-4
|
|
397
|
+
paddingTop: 12,
|
|
361
398
|
marginBottom: 12, // mb-3
|
|
362
399
|
},
|
|
363
400
|
ussdCodeRow: {
|
|
@@ -383,6 +420,7 @@ const styles = StyleSheet.create({
|
|
|
383
420
|
},
|
|
384
421
|
ussdStepsText: {
|
|
385
422
|
fontSize: 14, // text-sm
|
|
423
|
+
padding: 4, // px-2
|
|
386
424
|
color: "#1E40AF", // text-indigo-800
|
|
387
425
|
},
|
|
388
426
|
payButton: {
|
|
@@ -56,7 +56,7 @@ export function usePaymentCheckout(
|
|
|
56
56
|
if (transferDetail.validityPeriodMins > 0) {
|
|
57
57
|
setTimeLeft(transferDetail.validityPeriodMins * 60);
|
|
58
58
|
}
|
|
59
|
-
}, [transferDetail.validityPeriodMins]);
|
|
59
|
+
}, [transferDetail, transferDetail.validityPeriodMins]);
|
|
60
60
|
|
|
61
61
|
useEffect(() => {
|
|
62
62
|
if (timeLeft <= 0) return;
|
|
@@ -92,9 +92,8 @@ export function usePaymentCheckout(
|
|
|
92
92
|
isBill: true,
|
|
93
93
|
requestId,
|
|
94
94
|
};
|
|
95
|
-
|
|
96
95
|
const res = await axios.post(
|
|
97
|
-
"https://api.
|
|
96
|
+
"https://api.fairsurepay.com/api/pub/generate-account",
|
|
98
97
|
payload,
|
|
99
98
|
{
|
|
100
99
|
headers: {
|
|
@@ -123,7 +122,7 @@ export function usePaymentCheckout(
|
|
|
123
122
|
|
|
124
123
|
try {
|
|
125
124
|
const res = await axios.post(
|
|
126
|
-
"https://api.
|
|
125
|
+
"https://api.fairsurepay.com/api/pub/generate-ussd",
|
|
127
126
|
payload,
|
|
128
127
|
{
|
|
129
128
|
headers: {
|
|
@@ -150,7 +149,7 @@ export function usePaymentCheckout(
|
|
|
150
149
|
amount: ussdBankResponse?.amount,
|
|
151
150
|
};
|
|
152
151
|
const res = await axios.post(
|
|
153
|
-
"https://api.
|
|
152
|
+
"https://api.fairsurepay.com/api/pub/ussd-status",
|
|
154
153
|
payload,
|
|
155
154
|
{
|
|
156
155
|
headers: {
|
|
@@ -181,7 +180,7 @@ export function usePaymentCheckout(
|
|
|
181
180
|
try {
|
|
182
181
|
setLoading(true);
|
|
183
182
|
const res = await axios.get(
|
|
184
|
-
"https://api.
|
|
183
|
+
"https://api.fairsurepay.com/api/pub/list-ussd-banks",
|
|
185
184
|
{
|
|
186
185
|
timeout: 15000,
|
|
187
186
|
headers: {
|
|
@@ -279,7 +278,7 @@ export function usePaymentCheckout(
|
|
|
279
278
|
},
|
|
280
279
|
};
|
|
281
280
|
const res = await axios.post(
|
|
282
|
-
"https://api.
|
|
281
|
+
"https://api.fairsurepay.com/api/pub/card-payment",
|
|
283
282
|
payload,
|
|
284
283
|
{
|
|
285
284
|
headers: {
|
|
@@ -316,7 +315,7 @@ export function usePaymentCheckout(
|
|
|
316
315
|
};
|
|
317
316
|
|
|
318
317
|
const res = await axios.post(
|
|
319
|
-
"https://api.
|
|
318
|
+
"https://api.fairsurepay.com/api/pub/encrypt-test",
|
|
320
319
|
payload,
|
|
321
320
|
{
|
|
322
321
|
timeout: 15000,
|
|
@@ -354,7 +353,7 @@ export function usePaymentCheckout(
|
|
|
354
353
|
customerEmail,
|
|
355
354
|
};
|
|
356
355
|
const res = await axios.post(
|
|
357
|
-
"https://api.
|
|
356
|
+
"https://api.fairsurepay.com/api/pub/submit-otp",
|
|
358
357
|
payload,
|
|
359
358
|
{
|
|
360
359
|
headers: {
|
|
@@ -377,7 +376,7 @@ export function usePaymentCheckout(
|
|
|
377
376
|
setLoading(true);
|
|
378
377
|
try {
|
|
379
378
|
const res = await axios.get(
|
|
380
|
-
`https://api.
|
|
379
|
+
`https://api.fairsurepay.com/api/pub/transfer-status?transactionReference=${transferDetail?.transactionReference}`,
|
|
381
380
|
{
|
|
382
381
|
timeout: 15000,
|
|
383
382
|
headers: {
|