react-native-fpay 0.2.9 → 0.3.2
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/lib/module/FountainPayProvider.js +5 -1
- package/lib/module/FountainPayProvider.js.map +1 -1
- package/lib/module/core/api/index.js +22 -12
- package/lib/module/core/api/index.js.map +1 -1
- package/lib/module/engine/BLEReceiverService.js.map +1 -1
- package/lib/module/engine/FPEngine.js +24 -13
- package/lib/module/engine/FPEngine.js.map +1 -1
- package/lib/module/engine/useIsForeground.js +17 -0
- package/lib/module/engine/useIsForeground.js.map +1 -0
- package/lib/module/ui/components/AnimatedDots.js +68 -0
- package/lib/module/ui/components/AnimatedDots.js.map +1 -0
- package/lib/module/ui/components/ConfirmScreen.js +333 -0
- package/lib/module/ui/components/ConfirmScreen.js.map +1 -0
- package/lib/module/ui/modals/FPPaymentRequestModal.js +6 -8
- package/lib/module/ui/modals/FPPaymentRequestModal.js.map +1 -1
- package/lib/module/ui/modals/FPShell.js +7 -4
- package/lib/module/ui/modals/FPShell.js.map +1 -1
- package/lib/module/ui/screens/ReceiveScreen.js +379 -274
- package/lib/module/ui/screens/ReceiveScreen.js.map +1 -1
- package/lib/module/ui/screens/SendScreen.js +154 -45
- package/lib/module/ui/screens/SendScreen.js.map +1 -1
- package/lib/module/ui/screens/styles.js +89 -0
- package/lib/module/ui/screens/styles.js.map +1 -0
- package/lib/module/ui/screens/sub/receivePayment/Nfc/index.js +361 -0
- package/lib/module/ui/screens/sub/receivePayment/Nfc/index.js.map +1 -0
- package/lib/module/ui/screens/sub/receivePayment/Qr/index.js +338 -0
- package/lib/module/ui/screens/sub/receivePayment/Qr/index.js.map +1 -0
- package/lib/module/ui/screens/sub/receivePayment/Transfer/index.js +453 -0
- package/lib/module/ui/screens/sub/receivePayment/Transfer/index.js.map +1 -0
- package/lib/module/ui/screens/sub/{BluetoothSubScreen.js → sendPayment/BluetoothSubScreen.js} +25 -32
- package/lib/module/ui/screens/sub/sendPayment/BluetoothSubScreen.js.map +1 -0
- package/lib/module/ui/screens/sub/sendPayment/NFCSubScreen.js +354 -0
- package/lib/module/ui/screens/sub/sendPayment/NFCSubScreen.js.map +1 -0
- package/lib/module/ui/screens/sub/sendPayment/NQRSubScreen.js +440 -0
- package/lib/module/ui/screens/sub/sendPayment/NQRSubScreen.js.map +1 -0
- package/lib/module/ui/screens/sub/{ProximitySubScreen.js → sendPayment/ProximitySubScreen.js} +20 -111
- package/lib/module/ui/screens/sub/sendPayment/ProximitySubScreen.js.map +1 -0
- package/lib/module/ui/screens/sub/sendPayment/TransferSubScreen.js +327 -0
- package/lib/module/ui/screens/sub/sendPayment/TransferSubScreen.js.map +1 -0
- package/lib/typescript/src/FountainPayProvider.d.ts.map +1 -1
- package/lib/typescript/src/core/api/index.d.ts +20 -27
- package/lib/typescript/src/core/api/index.d.ts.map +1 -1
- package/lib/typescript/src/core/types/index.d.ts +56 -13
- package/lib/typescript/src/core/types/index.d.ts.map +1 -1
- package/lib/typescript/src/engine/BLEReceiverService.d.ts +2 -0
- package/lib/typescript/src/engine/BLEReceiverService.d.ts.map +1 -1
- package/lib/typescript/src/engine/FPEngine.d.ts +3 -1
- package/lib/typescript/src/engine/FPEngine.d.ts.map +1 -1
- package/lib/typescript/src/engine/useIsForeground.d.ts +2 -0
- package/lib/typescript/src/engine/useIsForeground.d.ts.map +1 -0
- package/lib/typescript/src/ui/components/AnimatedDots.d.ts +2 -0
- package/lib/typescript/src/ui/components/AnimatedDots.d.ts.map +1 -0
- package/lib/typescript/src/ui/components/ConfirmScreen.d.ts +10 -0
- package/lib/typescript/src/ui/components/ConfirmScreen.d.ts.map +1 -0
- package/lib/typescript/src/ui/components/OtpInput/Styles.d.ts +3 -3
- package/lib/typescript/src/ui/modals/FPPaymentRequestModal.d.ts.map +1 -1
- package/lib/typescript/src/ui/modals/FPShell.d.ts.map +1 -1
- package/lib/typescript/src/ui/screens/ReceiveScreen.d.ts +2 -9
- package/lib/typescript/src/ui/screens/ReceiveScreen.d.ts.map +1 -1
- package/lib/typescript/src/ui/screens/SendScreen.d.ts +4 -2
- package/lib/typescript/src/ui/screens/SendScreen.d.ts.map +1 -1
- package/lib/typescript/src/ui/screens/styles.d.ts +1390 -0
- package/lib/typescript/src/ui/screens/styles.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Nfc/index.d.ts +10 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Nfc/index.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Qr/index.d.ts +10 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Qr/index.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Transfer/index.d.ts +5 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Transfer/index.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/{BluetoothSubScreen.d.ts → sendPayment/BluetoothSubScreen.d.ts} +2 -11
- package/lib/typescript/src/ui/screens/sub/sendPayment/BluetoothSubScreen.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/NFCSubScreen.d.ts +3 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/NFCSubScreen.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/NQRSubScreen.d.ts +3 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/NQRSubScreen.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/{ProximitySubScreen.d.ts → sendPayment/ProximitySubScreen.d.ts} +2 -10
- package/lib/typescript/src/ui/screens/sub/sendPayment/ProximitySubScreen.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/TransferSubScreen.d.ts +3 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/TransferSubScreen.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/FountainPayProvider.tsx +7 -1
- package/src/core/api/index.ts +34 -19
- package/src/core/types/index.ts +67 -13
- package/src/engine/BLEReceiverService.ts +2 -0
- package/src/engine/FPEngine.ts +29 -14
- package/src/engine/useIsForeground.ts +18 -0
- package/src/ui/components/AnimatedDots.tsx +81 -0
- package/src/ui/components/ConfirmScreen.tsx +421 -0
- package/src/ui/modals/FPPaymentRequestModal.tsx +7 -6
- package/src/ui/modals/FPShell.tsx +9 -9
- package/src/ui/screens/ReceiveScreen.tsx +266 -115
- package/src/ui/screens/SendScreen.tsx +141 -19
- package/src/ui/screens/styles.ts +101 -0
- package/src/ui/screens/sub/receivePayment/Nfc/index.tsx +418 -0
- package/src/ui/screens/sub/receivePayment/Qr/index.tsx +391 -0
- package/src/ui/screens/sub/receivePayment/Transfer/index.tsx +512 -0
- package/src/ui/screens/sub/{BluetoothSubScreen.tsx → sendPayment/BluetoothSubScreen.tsx} +27 -46
- package/src/ui/screens/sub/sendPayment/NFCSubScreen.tsx +302 -0
- package/src/ui/screens/sub/sendPayment/NQRSubScreen.tsx +490 -0
- package/src/ui/screens/sub/{ProximitySubScreen.tsx → sendPayment/ProximitySubScreen.tsx} +24 -44
- package/src/ui/screens/sub/sendPayment/TransferSubScreen.tsx +345 -0
- package/lib/module/ui/screens/sub/BluetoothSubScreen.js.map +0 -1
- package/lib/module/ui/screens/sub/NFCSubScreen.js +0 -164
- package/lib/module/ui/screens/sub/NFCSubScreen.js.map +0 -1
- package/lib/module/ui/screens/sub/NQRSubScreen.js +0 -131
- package/lib/module/ui/screens/sub/NQRSubScreen.js.map +0 -1
- package/lib/module/ui/screens/sub/ProximitySubScreen.js.map +0 -1
- package/lib/module/ui/screens/sub/TransferSubScreen.js +0 -353
- package/lib/module/ui/screens/sub/TransferSubScreen.js.map +0 -1
- package/lib/typescript/src/ui/screens/sub/BluetoothSubScreen.d.ts.map +0 -1
- package/lib/typescript/src/ui/screens/sub/NFCSubScreen.d.ts +0 -18
- package/lib/typescript/src/ui/screens/sub/NFCSubScreen.d.ts.map +0 -1
- package/lib/typescript/src/ui/screens/sub/NQRSubScreen.d.ts +0 -12
- package/lib/typescript/src/ui/screens/sub/NQRSubScreen.d.ts.map +0 -1
- package/lib/typescript/src/ui/screens/sub/ProximitySubScreen.d.ts.map +0 -1
- package/lib/typescript/src/ui/screens/sub/TransferSubScreen.d.ts +0 -11
- package/lib/typescript/src/ui/screens/sub/TransferSubScreen.d.ts.map +0 -1
- package/src/ui/screens/sub/NFCSubScreen.tsx +0 -86
- package/src/ui/screens/sub/NQRSubScreen.tsx +0 -62
- package/src/ui/screens/sub/TransferSubScreen.tsx +0 -147
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Share, Clipboard, Alert } from 'react-native';
|
|
3
|
+
import styled from 'styled-components/native';
|
|
4
|
+
import { Buffer } from 'buffer';
|
|
5
|
+
import Ionicons from 'react-native-vector-icons/Ionicons';
|
|
6
|
+
import type { ReceiveTransferScreenProps } from '../../../../../core/types';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
// Styled Components
|
|
10
|
+
const Container = styled.View`
|
|
11
|
+
flex: 1;
|
|
12
|
+
background-color: #f5f5f5;
|
|
13
|
+
padding-bottom: 60%;
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const Header = styled.View`
|
|
17
|
+
flex-direction: row;
|
|
18
|
+
align-items: center;
|
|
19
|
+
padding: 16px;
|
|
20
|
+
padding-top: 40px;
|
|
21
|
+
background-color: #ffffff;
|
|
22
|
+
border-bottom-width: 1px;
|
|
23
|
+
border-bottom-color: #e0e0e0;
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
const BackButton = styled.TouchableOpacity`
|
|
27
|
+
padding: 8px;
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
const BackIcon = styled.Text`
|
|
31
|
+
font-size: 24px;
|
|
32
|
+
color: #000000;
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const HeaderTitle = styled.Text`
|
|
36
|
+
flex: 1;
|
|
37
|
+
font-size: 18px;
|
|
38
|
+
font-weight: 600;
|
|
39
|
+
color: #000000;
|
|
40
|
+
text-align: center;
|
|
41
|
+
margin-right: 40px;
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
const ScrollContainer = styled.ScrollView`
|
|
45
|
+
flex: 1;
|
|
46
|
+
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
const ContentContainer = styled.View`
|
|
50
|
+
padding: 24px;
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const InfoBanner = styled.View`
|
|
54
|
+
background-color: #E8F5F1;
|
|
55
|
+
border-radius: 12px;
|
|
56
|
+
padding: 16px;
|
|
57
|
+
margin-bottom: 24px;
|
|
58
|
+
flex-direction: row;
|
|
59
|
+
align-items: flex-start;
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const InfoIcon = styled.Text`
|
|
63
|
+
font-size: 20px;
|
|
64
|
+
margin-right: 12px;
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const InfoText = styled.Text`
|
|
68
|
+
flex: 1;
|
|
69
|
+
font-size: 13px;
|
|
70
|
+
color: #00A876;
|
|
71
|
+
line-height: 20px;
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
const AmountCard = styled.View`
|
|
75
|
+
background-color: #ffffff;
|
|
76
|
+
border-radius: 16px;
|
|
77
|
+
padding: 24px;
|
|
78
|
+
align-items: center;
|
|
79
|
+
margin-bottom: 24px;
|
|
80
|
+
shadow-color: #000;
|
|
81
|
+
shadow-offset: 0px 2px;
|
|
82
|
+
shadow-opacity: 0.05;
|
|
83
|
+
shadow-radius: 8px;
|
|
84
|
+
elevation: 3;
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
const AmountLabel = styled.Text`
|
|
88
|
+
font-size: 14px;
|
|
89
|
+
color: #666666;
|
|
90
|
+
margin-bottom: 8px;
|
|
91
|
+
`;
|
|
92
|
+
|
|
93
|
+
const AmountText = styled.Text`
|
|
94
|
+
font-size: 42px;
|
|
95
|
+
font-weight: 700;
|
|
96
|
+
color: #0a3d2e;
|
|
97
|
+
margin-bottom: 8px;
|
|
98
|
+
`;
|
|
99
|
+
|
|
100
|
+
const DescriptionText = styled.Text`
|
|
101
|
+
font-size: 14px;
|
|
102
|
+
color: #999999;
|
|
103
|
+
font-style: italic;
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
const SectionTitle = styled.Text`
|
|
107
|
+
font-size: 15px;
|
|
108
|
+
font-weight: 600;
|
|
109
|
+
color: #333333;
|
|
110
|
+
margin-bottom: 16px;
|
|
111
|
+
`;
|
|
112
|
+
|
|
113
|
+
const DetailsCard = styled.View`
|
|
114
|
+
background-color: #ffffff;
|
|
115
|
+
border-radius: 16px;
|
|
116
|
+
padding: 20px;
|
|
117
|
+
margin-bottom: 24px;
|
|
118
|
+
shadow-color: #000;
|
|
119
|
+
shadow-offset: 0px 2px;
|
|
120
|
+
shadow-opacity: 0.05;
|
|
121
|
+
shadow-radius: 8px;
|
|
122
|
+
elevation: 3;
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
const DetailItem = styled.View`
|
|
126
|
+
margin-bottom: 20px;
|
|
127
|
+
`;
|
|
128
|
+
|
|
129
|
+
const DetailLabel = styled.Text`
|
|
130
|
+
font-size: 13px;
|
|
131
|
+
color: #999999;
|
|
132
|
+
margin-bottom: 8px;
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
const DetailValueContainer = styled.View`
|
|
136
|
+
flex-direction: row;
|
|
137
|
+
align-items: center;
|
|
138
|
+
justify-content: space-between;
|
|
139
|
+
`;
|
|
140
|
+
|
|
141
|
+
const DetailValue = styled.Text`
|
|
142
|
+
font-size: 16px;
|
|
143
|
+
font-weight: 600;
|
|
144
|
+
color: #333333;
|
|
145
|
+
flex: 1;
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
const CopyButton = styled.TouchableOpacity`
|
|
149
|
+
background-color: #E8F5F1;
|
|
150
|
+
border-radius: 20px;
|
|
151
|
+
padding: 8px 16px;
|
|
152
|
+
margin-left: 12px;
|
|
153
|
+
`;
|
|
154
|
+
|
|
155
|
+
const CopyButtonText = styled.Text`
|
|
156
|
+
font-size: 13px;
|
|
157
|
+
font-weight: 600;
|
|
158
|
+
color: #0a3d2e;
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
const Divider = styled.View`
|
|
162
|
+
height: 1px;
|
|
163
|
+
background-color: #F0F0F0;
|
|
164
|
+
margin: 4px 0;
|
|
165
|
+
`;
|
|
166
|
+
|
|
167
|
+
const PaymentLinkCard = styled.View`
|
|
168
|
+
background-color: #FFF3E0;
|
|
169
|
+
border-radius: 12px;
|
|
170
|
+
padding: 16px;
|
|
171
|
+
margin-bottom: 24px;
|
|
172
|
+
`;
|
|
173
|
+
|
|
174
|
+
const PaymentLinkTitle = styled.Text`
|
|
175
|
+
font-size: 14px;
|
|
176
|
+
font-weight: 600;
|
|
177
|
+
color: #F57C00;
|
|
178
|
+
margin-bottom: 8px;
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
const PaymentLinkText = styled.Text`
|
|
182
|
+
font-size: 12px;
|
|
183
|
+
color: #F57C00;
|
|
184
|
+
margin-bottom: 12px;
|
|
185
|
+
`;
|
|
186
|
+
|
|
187
|
+
const PaymentLinkContainer = styled.View`
|
|
188
|
+
background-color: #ffffff;
|
|
189
|
+
border-radius: 8px;
|
|
190
|
+
padding: 12px;
|
|
191
|
+
margin-bottom: 12px;
|
|
192
|
+
`;
|
|
193
|
+
|
|
194
|
+
const PaymentLinkUrl = styled.Text`
|
|
195
|
+
font-size: 12px;
|
|
196
|
+
color: #666666;
|
|
197
|
+
font-family: monospace;
|
|
198
|
+
`;
|
|
199
|
+
|
|
200
|
+
const LinkButtonRow = styled.View`
|
|
201
|
+
flex-direction: row;
|
|
202
|
+
gap: 8px;
|
|
203
|
+
`;
|
|
204
|
+
|
|
205
|
+
const SmallButton = styled.TouchableOpacity`
|
|
206
|
+
flex: 1;
|
|
207
|
+
background-color: #ffffff;
|
|
208
|
+
border: 1px solid #F57C00;
|
|
209
|
+
border-radius: 20px;
|
|
210
|
+
padding: 10px;
|
|
211
|
+
align-items: center;
|
|
212
|
+
`;
|
|
213
|
+
|
|
214
|
+
const SmallButtonText = styled.Text`
|
|
215
|
+
font-size: 13px;
|
|
216
|
+
font-weight: 600;
|
|
217
|
+
color: #F57C00;
|
|
218
|
+
`;
|
|
219
|
+
|
|
220
|
+
const ButtonContainer = styled.View`
|
|
221
|
+
gap: 12px;
|
|
222
|
+
`;
|
|
223
|
+
|
|
224
|
+
const PrimaryButton = styled.TouchableOpacity`
|
|
225
|
+
background-color: #0a3d2e;
|
|
226
|
+
border-radius: 28px;
|
|
227
|
+
padding: 16px;
|
|
228
|
+
align-items: center;
|
|
229
|
+
`;
|
|
230
|
+
|
|
231
|
+
const PrimaryButtonText = styled.Text`
|
|
232
|
+
font-size: 15px;
|
|
233
|
+
font-weight: 600;
|
|
234
|
+
color: #ffffff;
|
|
235
|
+
`;
|
|
236
|
+
|
|
237
|
+
const OutlineButton = styled.TouchableOpacity`
|
|
238
|
+
background-color: #ffffff;
|
|
239
|
+
border: 1.5px solid #0a3d2e;
|
|
240
|
+
border-radius: 28px;
|
|
241
|
+
padding: 16px;
|
|
242
|
+
align-items: center;
|
|
243
|
+
`;
|
|
244
|
+
|
|
245
|
+
const OutlineButtonText = styled.Text`
|
|
246
|
+
font-size: 15px;
|
|
247
|
+
font-weight: 600;
|
|
248
|
+
color: #0a3d2e;
|
|
249
|
+
`;
|
|
250
|
+
|
|
251
|
+
const SecondaryButton = styled.TouchableOpacity`
|
|
252
|
+
background-color: #ffffff;
|
|
253
|
+
border: 1.5px solid #E0E0E0;
|
|
254
|
+
border-radius: 28px;
|
|
255
|
+
padding: 16px;
|
|
256
|
+
align-items: center;
|
|
257
|
+
`;
|
|
258
|
+
|
|
259
|
+
const SecondaryButtonText = styled.Text`
|
|
260
|
+
font-size: 15px;
|
|
261
|
+
font-weight: 600;
|
|
262
|
+
color: #666666;
|
|
263
|
+
`;
|
|
264
|
+
|
|
265
|
+
const Footer = styled.View`
|
|
266
|
+
padding: 16px 24px 24px;
|
|
267
|
+
`;
|
|
268
|
+
|
|
269
|
+
const FooterText = styled.Text`
|
|
270
|
+
font-size: 12px;
|
|
271
|
+
color: #999999;
|
|
272
|
+
text-align: center;
|
|
273
|
+
line-height: 18px;
|
|
274
|
+
`;
|
|
275
|
+
|
|
276
|
+
// Main Component
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
const Transfer: React.FC<ReceiveTransferScreenProps> = ({
|
|
280
|
+
amount = 0,
|
|
281
|
+
description = '',
|
|
282
|
+
user,
|
|
283
|
+
recipient,
|
|
284
|
+
onClose,
|
|
285
|
+
onPaymentReceived,
|
|
286
|
+
}) => {
|
|
287
|
+
const [copiedField, setCopiedField] = useState<string | null>(null);
|
|
288
|
+
|
|
289
|
+
const formatAmount = (value: number): string => {
|
|
290
|
+
return value.toLocaleString('en-NG', {
|
|
291
|
+
minimumFractionDigits: 2,
|
|
292
|
+
maximumFractionDigits: 2,
|
|
293
|
+
});
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const handleCopy = async (text: string, fieldName: string) => {
|
|
297
|
+
try {
|
|
298
|
+
await Clipboard.setString(text);
|
|
299
|
+
setCopiedField(fieldName);
|
|
300
|
+
|
|
301
|
+
// Reset copied state after 2 seconds
|
|
302
|
+
setTimeout(() => {
|
|
303
|
+
setCopiedField(null);
|
|
304
|
+
}, 2000);
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error('Failed to copy:', error);
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const generatePaymentLink = (): string => {
|
|
311
|
+
// Generate a payment link (in production, this would be a real URL)
|
|
312
|
+
const base64 = Buffer.from(JSON.stringify({
|
|
313
|
+
account: recipient.accountNumber,
|
|
314
|
+
recipient: recipient.accountName,
|
|
315
|
+
})).toString('base64');
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
return `https://tapit.app/pay/${base64}`;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const handleShareDetails = async () => {
|
|
322
|
+
//Please send ₦${formatAmount(amount)} to:
|
|
323
|
+
const message = `
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
Account Number: ${recipient.accountNumber}
|
|
327
|
+
Account Name: ${recipient.accountName}
|
|
328
|
+
Bank: ${recipient.bankName}
|
|
329
|
+
${description ? `Payment for: ${description}` : ''}
|
|
330
|
+
|
|
331
|
+
Thank you!
|
|
332
|
+
`.trim();
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
await Share.share({
|
|
336
|
+
message,
|
|
337
|
+
title: 'Payment Details',
|
|
338
|
+
});
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.error('Error sharing:', error);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
const handleShareLink = async () => {
|
|
345
|
+
const link = generatePaymentLink();
|
|
346
|
+
try {
|
|
347
|
+
await Share.share({
|
|
348
|
+
message: `Pay send money via this link: ${link}`,
|
|
349
|
+
title: 'Payment Link',
|
|
350
|
+
});
|
|
351
|
+
} catch (error) {
|
|
352
|
+
console.error('Error sharing link:', error);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const handleCopyLink = async () => {
|
|
357
|
+
const link = generatePaymentLink();
|
|
358
|
+
await handleCopy(link, 'link');
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
const handleCopyAll = async () => {
|
|
362
|
+
const allDetails = `Account: ${recipient?.accountNumber}\nName: ${recipient?.accountName}\nBank: ${recipient?.bankName}}`; //\nAmount: ₦${formatAmount(amount)
|
|
363
|
+
await handleCopy(allDetails, 'all');
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
return (
|
|
367
|
+
<Container>
|
|
368
|
+
{/* Header */}
|
|
369
|
+
<Header>
|
|
370
|
+
<BackButton onPress={onClose}>
|
|
371
|
+
<Ionicons name="close" size={24} />
|
|
372
|
+
</BackButton>
|
|
373
|
+
<HeaderTitle>Receive via Transfer</HeaderTitle>
|
|
374
|
+
</Header>
|
|
375
|
+
|
|
376
|
+
{/* Content */}
|
|
377
|
+
<ScrollContainer showsVerticalScrollIndicator={false}>
|
|
378
|
+
<ContentContainer>
|
|
379
|
+
{/* Info Banner */}
|
|
380
|
+
<InfoBanner>
|
|
381
|
+
<InfoIcon>ℹ️</InfoIcon>
|
|
382
|
+
<InfoText>
|
|
383
|
+
Share your account details below with the payer. You'll be
|
|
384
|
+
notified when the payment is received.
|
|
385
|
+
</InfoText>
|
|
386
|
+
</InfoBanner>
|
|
387
|
+
|
|
388
|
+
{/* Amount Display */}
|
|
389
|
+
{amount > 0 && (<AmountCard>
|
|
390
|
+
<AmountLabel>Expected Amount</AmountLabel>
|
|
391
|
+
<AmountText>₦{formatAmount(amount)}</AmountText>
|
|
392
|
+
{description && <DescriptionText>{description}</DescriptionText>}
|
|
393
|
+
</AmountCard>)}
|
|
394
|
+
|
|
395
|
+
{/* Account Details */}
|
|
396
|
+
<SectionTitle>Send money to this account</SectionTitle>
|
|
397
|
+
<DetailsCard>
|
|
398
|
+
<DetailItem>
|
|
399
|
+
<DetailLabel>Account Number</DetailLabel>
|
|
400
|
+
<DetailValueContainer>
|
|
401
|
+
<DetailValue>{recipient.accountNumber}</DetailValue>
|
|
402
|
+
<CopyButton
|
|
403
|
+
onPress={() => handleCopy(recipient.accountNumber, 'account')}
|
|
404
|
+
>
|
|
405
|
+
<CopyButtonText>
|
|
406
|
+
{copiedField === 'account' ? 'Copied!' : 'Copy'}
|
|
407
|
+
</CopyButtonText>
|
|
408
|
+
</CopyButton>
|
|
409
|
+
</DetailValueContainer>
|
|
410
|
+
</DetailItem>
|
|
411
|
+
|
|
412
|
+
<Divider />
|
|
413
|
+
|
|
414
|
+
<DetailItem>
|
|
415
|
+
<DetailLabel>Account Name</DetailLabel>
|
|
416
|
+
<DetailValueContainer>
|
|
417
|
+
<DetailValue>{recipient.accountName}</DetailValue>
|
|
418
|
+
<CopyButton onPress={() => handleCopy(recipient.accountName, 'name')}>
|
|
419
|
+
<CopyButtonText>
|
|
420
|
+
{copiedField === 'name' ? 'Copied!' : 'Copy'}
|
|
421
|
+
</CopyButtonText>
|
|
422
|
+
</CopyButton>
|
|
423
|
+
</DetailValueContainer>
|
|
424
|
+
</DetailItem>
|
|
425
|
+
|
|
426
|
+
<Divider />
|
|
427
|
+
|
|
428
|
+
<DetailItem>
|
|
429
|
+
<DetailLabel>Bank</DetailLabel>
|
|
430
|
+
<DetailValueContainer>
|
|
431
|
+
<DetailValue>{recipient.bankName}</DetailValue>
|
|
432
|
+
<CopyButton onPress={() => handleCopy(recipient.bankName, 'bank')}>
|
|
433
|
+
<CopyButtonText>
|
|
434
|
+
{copiedField === 'bank' ? 'Copied!' : 'Copy'}
|
|
435
|
+
</CopyButtonText>
|
|
436
|
+
</CopyButton>
|
|
437
|
+
</DetailValueContainer>
|
|
438
|
+
</DetailItem>
|
|
439
|
+
|
|
440
|
+
<Divider />
|
|
441
|
+
|
|
442
|
+
{amount > 0 && (
|
|
443
|
+
<DetailItem style={{ marginBottom: 0 }}>
|
|
444
|
+
<DetailLabel>Amount</DetailLabel>
|
|
445
|
+
<DetailValueContainer>
|
|
446
|
+
<DetailValue>₦{formatAmount(amount)}</DetailValue>
|
|
447
|
+
<CopyButton
|
|
448
|
+
onPress={() => handleCopy(amount.toString(), 'amount')}
|
|
449
|
+
>
|
|
450
|
+
<CopyButtonText>
|
|
451
|
+
{copiedField === 'amount' ? 'Copied!' : 'Copy'}
|
|
452
|
+
</CopyButtonText>
|
|
453
|
+
</CopyButton>
|
|
454
|
+
</DetailValueContainer>
|
|
455
|
+
</DetailItem>
|
|
456
|
+
)}
|
|
457
|
+
</DetailsCard>
|
|
458
|
+
|
|
459
|
+
{/* Payment Link */}
|
|
460
|
+
<PaymentLinkCard>
|
|
461
|
+
<PaymentLinkTitle>📎 Quick Payment Link</PaymentLinkTitle>
|
|
462
|
+
<PaymentLinkText>
|
|
463
|
+
Share this link with anyone to receive payment instantly
|
|
464
|
+
</PaymentLinkText>
|
|
465
|
+
<PaymentLinkContainer>
|
|
466
|
+
<PaymentLinkUrl numberOfLines={1} ellipsizeMode="middle">
|
|
467
|
+
{generatePaymentLink()}
|
|
468
|
+
</PaymentLinkUrl>
|
|
469
|
+
</PaymentLinkContainer>
|
|
470
|
+
<LinkButtonRow>
|
|
471
|
+
<SmallButton onPress={handleCopyLink}>
|
|
472
|
+
<SmallButtonText>
|
|
473
|
+
{copiedField === 'link' ? '✓ Copied' : 'Copy Link'}
|
|
474
|
+
</SmallButtonText>
|
|
475
|
+
</SmallButton>
|
|
476
|
+
<SmallButton onPress={handleShareLink}>
|
|
477
|
+
<SmallButtonText>Share Link</SmallButtonText>
|
|
478
|
+
</SmallButton>
|
|
479
|
+
</LinkButtonRow>
|
|
480
|
+
</PaymentLinkCard>
|
|
481
|
+
|
|
482
|
+
{/* Action Buttons */}
|
|
483
|
+
<ButtonContainer>
|
|
484
|
+
<PrimaryButton onPress={handleShareDetails}>
|
|
485
|
+
<PrimaryButtonText>Share All Details</PrimaryButtonText>
|
|
486
|
+
</PrimaryButton>
|
|
487
|
+
|
|
488
|
+
<OutlineButton onPress={handleCopyAll}>
|
|
489
|
+
<OutlineButtonText>
|
|
490
|
+
{copiedField === 'all' ? '✓ All Details Copied' : 'Copy All Details'}
|
|
491
|
+
</OutlineButtonText>
|
|
492
|
+
</OutlineButton>
|
|
493
|
+
|
|
494
|
+
<SecondaryButton onPress={onClose}>
|
|
495
|
+
<SecondaryButtonText>Done</SecondaryButtonText>
|
|
496
|
+
</SecondaryButton>
|
|
497
|
+
</ButtonContainer>
|
|
498
|
+
</ContentContainer>
|
|
499
|
+
|
|
500
|
+
{/* Footer */}
|
|
501
|
+
<Footer>
|
|
502
|
+
<FooterText>
|
|
503
|
+
You will receive a notification once the payment is confirmed in
|
|
504
|
+
your account. Please allow 1-5 minutes for bank transfers.
|
|
505
|
+
</FooterText>
|
|
506
|
+
</Footer>
|
|
507
|
+
</ScrollContainer>
|
|
508
|
+
</Container>
|
|
509
|
+
);
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
export default Transfer;
|
|
@@ -17,15 +17,15 @@ import styled from 'styled-components/native';
|
|
|
17
17
|
import Svg, { Path } from 'react-native-svg';
|
|
18
18
|
import Ionicons from 'react-native-vector-icons/Ionicons';
|
|
19
19
|
|
|
20
|
-
import { transferAPI } from '
|
|
21
|
-
import { FPButton } from '
|
|
22
|
-
import { C, R, S, F, shadow } from '
|
|
23
|
-
import type { FPCurrency, FPError, FPTransaction, FintechDevice } from '
|
|
20
|
+
import { transferAPI } from '../../../../core/api';
|
|
21
|
+
import { FPButton } from '../../../components/FPButton';
|
|
22
|
+
import { C, R, S, F, shadow } from '../../../theme';
|
|
23
|
+
import type { FPCurrency, FPError, FPSendPaymentRequest, FPTransaction, FintechDevice, Props } from '../../../../core/types';
|
|
24
24
|
|
|
25
|
-
import { FPEngine } from '
|
|
25
|
+
import { FPEngine } from '../../../../engine/FPEngine';
|
|
26
26
|
|
|
27
|
-
import { PulseAnimation } from '
|
|
28
|
-
import InLoading from '
|
|
27
|
+
import { PulseAnimation } from '../../../components/PulseAnimation';
|
|
28
|
+
import InLoading from '../../../components/LoadingAnimation/InLoading';
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
const Container = styled(View)`
|
|
@@ -195,29 +195,17 @@ const PHASE_LABEL: Record<Phase, string> = {
|
|
|
195
195
|
error: 'Something went wrong',
|
|
196
196
|
};
|
|
197
197
|
|
|
198
|
-
interface Props {
|
|
199
|
-
mode: 'send' | 'receive';
|
|
200
|
-
amount: number;
|
|
201
|
-
currency: FPCurrency;
|
|
202
|
-
onDone: () => void;
|
|
203
|
-
onSuccess?: (tx: FPTransaction) => void;
|
|
204
|
-
onError?: (err: FPError) => void;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
198
|
export function BluetoothSubScreen({
|
|
208
|
-
mode,
|
|
209
199
|
amount,
|
|
210
200
|
currency,
|
|
211
|
-
|
|
212
|
-
|
|
201
|
+
onClose,
|
|
202
|
+
onProcessTransaction,
|
|
213
203
|
onError,
|
|
214
204
|
}: Props) {
|
|
215
205
|
const [devices, setDevices] = useState<FintechDevice[]>([]);
|
|
216
206
|
const [scanning, setScanning] = useState(false);
|
|
217
207
|
const [status, setStatus] = useState('Ready to scan');
|
|
218
208
|
const [currentDevice, setCurrentDevice] = useState<FintechDevice | null>(null);
|
|
219
|
-
|
|
220
|
-
|
|
221
209
|
|
|
222
210
|
// ── 1. Scan ───────────────────────────────────────────────
|
|
223
211
|
|
|
@@ -263,14 +251,14 @@ export function BluetoothSubScreen({
|
|
|
263
251
|
},
|
|
264
252
|
{
|
|
265
253
|
text: `Continue`,
|
|
266
|
-
onPress: () => initiatePayment(device
|
|
254
|
+
onPress: () => initiatePayment(device),
|
|
267
255
|
},
|
|
268
256
|
]
|
|
269
257
|
);
|
|
270
258
|
};
|
|
271
259
|
|
|
272
260
|
|
|
273
|
-
const initiatePayment = async (device: FintechDevice
|
|
261
|
+
const initiatePayment = async (device: FintechDevice) => {
|
|
274
262
|
try {
|
|
275
263
|
setCurrentDevice(device);
|
|
276
264
|
// Connect to the device
|
|
@@ -281,8 +269,10 @@ export function BluetoothSubScreen({
|
|
|
281
269
|
const request: any = {
|
|
282
270
|
amount,
|
|
283
271
|
currency,
|
|
284
|
-
senderId: user?.userId ??
|
|
285
|
-
senderName: user?.
|
|
272
|
+
senderId: user?.userId ?? 'unknown',
|
|
273
|
+
senderName: user?.name ?? 'FountainPay User',
|
|
274
|
+
senderPhone: user?.phone ?? 'unknown',
|
|
275
|
+
senderEmail: user?.email ?? 'unknown',
|
|
286
276
|
timestamp: Date.now(),
|
|
287
277
|
};
|
|
288
278
|
|
|
@@ -311,29 +301,17 @@ export function BluetoothSubScreen({
|
|
|
311
301
|
} as FPError;
|
|
312
302
|
}
|
|
313
303
|
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
narration:
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const tx: FPTransaction = {
|
|
323
|
-
id: result.reference,
|
|
324
|
-
reference: result.reference,
|
|
325
|
-
type: 'debit',
|
|
326
|
-
channel: 'bluetooth',
|
|
327
|
-
amount: result.amount,
|
|
328
|
-
currency: result.currency,
|
|
329
|
-
status: result.status,
|
|
330
|
-
recipient: { accountName: response.accountDetails.accountName },
|
|
331
|
-
createdAt: result.createdAt,
|
|
332
|
-
};
|
|
304
|
+
const payload: FPSendPaymentRequest = {
|
|
305
|
+
amount: amount,
|
|
306
|
+
currency: currency,
|
|
307
|
+
channel: 'bluetooth',
|
|
308
|
+
narration: `Send ${currency}${amount} to ${response.accountDetails.accountName}`,
|
|
309
|
+
recipient: response.accountDetails
|
|
310
|
+
}
|
|
333
311
|
|
|
334
|
-
|
|
312
|
+
onProcessTransaction?.(payload);
|
|
335
313
|
setCurrentDevice(null);
|
|
336
|
-
setStatus('
|
|
314
|
+
setStatus('Payment sent!');
|
|
337
315
|
} catch (error: any) {
|
|
338
316
|
const fp = error as FPError;
|
|
339
317
|
setCurrentDevice(null);
|
|
@@ -356,6 +334,9 @@ export function BluetoothSubScreen({
|
|
|
356
334
|
{/* Status Bar */}
|
|
357
335
|
|
|
358
336
|
<Header>
|
|
337
|
+
<HeaderButton onPress={onClose}>
|
|
338
|
+
<Ionicons name="close" size={24} />
|
|
339
|
+
</HeaderButton>
|
|
359
340
|
<HeaderCenter>
|
|
360
341
|
<Text style={{ fontWeight: 'bold', fontSize: 16, color:'#FFF' }}>Bluetooth</Text>
|
|
361
342
|
<Text style={{ fontSize: 10, color: '#FFF' }}>Transfer</Text>
|