react-native-fpay 0.4.31 → 0.4.34
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/core/api/index.js +86 -21
- package/lib/module/core/api/index.js.map +1 -1
- package/lib/module/ui/components/ConfirmScreen.js +32 -4
- package/lib/module/ui/components/ConfirmScreen.js.map +1 -1
- package/lib/module/ui/screens/BillsScreen.js +30 -13
- package/lib/module/ui/screens/BillsScreen.js.map +1 -1
- package/lib/module/ui/screens/styles.js +2 -1
- package/lib/module/ui/screens/styles.js.map +1 -1
- package/lib/module/ui/screens/sub/sendPayment/TransferSubScreen.js +10 -8
- package/lib/module/ui/screens/sub/sendPayment/TransferSubScreen.js.map +1 -1
- package/lib/typescript/src/core/api/index.d.ts +154 -46
- package/lib/typescript/src/core/api/index.d.ts.map +1 -1
- package/lib/typescript/src/ui/components/ConfirmScreen.d.ts.map +1 -1
- package/lib/typescript/src/ui/screens/BillsScreen.d.ts +1 -1
- package/lib/typescript/src/ui/screens/BillsScreen.d.ts.map +1 -1
- package/lib/typescript/src/ui/screens/styles.d.ts.map +1 -1
- package/lib/typescript/src/ui/screens/sub/sendPayment/TransferSubScreen.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/api/index.ts +197 -86
- package/src/ui/components/ConfirmScreen.tsx +37 -36
- package/src/ui/screens/BillsScreen.tsx +85 -25
- package/src/ui/screens/styles.ts +2 -1
- package/src/ui/screens/sub/sendPayment/TransferSubScreen.tsx +9 -8
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
// ─────────────────────────────────────────────
|
|
2
|
-
// Bills Screen
|
|
3
|
-
// Renders the category-specific sub-screen
|
|
4
|
-
// (Airtime / Data / Electricity / Cable), then
|
|
5
|
-
// runs the same Confirm (PIN) → Loading →
|
|
6
|
-
// Result flow SendScreen uses, generalized.
|
|
7
|
-
// ─────────────────────────────────────────────
|
|
8
1
|
import { useState } from 'react';
|
|
9
2
|
import { View, TouchableOpacity, Text } from 'react-native';
|
|
10
3
|
import styled from 'styled-components/native';
|
|
@@ -18,6 +11,7 @@ import { ResultScreen } from './ResultScreen';
|
|
|
18
11
|
import LoadingAnimation from '../components/LoadingAnimation';
|
|
19
12
|
import { billsAPI } from '../../core/api';
|
|
20
13
|
import { getFPStore } from '../../store/FPStore';
|
|
14
|
+
import { useLocation } from '../../hooks/useLocation';
|
|
21
15
|
import { C, F, S } from '../theme';
|
|
22
16
|
import type {
|
|
23
17
|
FPBillCategory,
|
|
@@ -28,6 +22,7 @@ import type {
|
|
|
28
22
|
FPUserInfo,
|
|
29
23
|
FPSummaryRow,
|
|
30
24
|
} from '../../core/types';
|
|
25
|
+
import type { FPBillPurchaseContext } from '../../core/api';
|
|
31
26
|
|
|
32
27
|
const Container = styled(View)`
|
|
33
28
|
flex: 1;
|
|
@@ -72,14 +67,29 @@ interface Props extends Pick<FPCallbacks, 'onError'> {
|
|
|
72
67
|
|
|
73
68
|
type FlowStage = 'FORM' | 'CONFIRM' | 'RESULT';
|
|
74
69
|
|
|
75
|
-
export default function BillsScreen({
|
|
70
|
+
export default function BillsScreen({
|
|
71
|
+
category,
|
|
72
|
+
amount,
|
|
73
|
+
user,
|
|
74
|
+
onClose,
|
|
75
|
+
onError,
|
|
76
|
+
}: Props) {
|
|
76
77
|
const [stage, setStage] = useState<FlowStage>('FORM');
|
|
77
78
|
const [loading, setLoading] = useState(false);
|
|
78
|
-
const [pendingPayload, setPendingPayload] =
|
|
79
|
-
|
|
80
|
-
const [
|
|
79
|
+
const [pendingPayload, setPendingPayload] =
|
|
80
|
+
useState<FPBillPurchaseRequest | null>(null);
|
|
81
|
+
const [pendingSummaryRows, setPendingSummaryRows] = useState<FPSummaryRow[]>(
|
|
82
|
+
[]
|
|
83
|
+
);
|
|
84
|
+
const [completedTx, setCompletedTx] = useState<FPBillTransaction | null>(
|
|
85
|
+
null
|
|
86
|
+
);
|
|
87
|
+
const { requestLocation } = useLocation();
|
|
81
88
|
|
|
82
|
-
const handleFormContinue = (
|
|
89
|
+
const handleFormContinue = (
|
|
90
|
+
payload: FPBillPurchaseRequest,
|
|
91
|
+
summaryRows: FPSummaryRow[]
|
|
92
|
+
) => {
|
|
83
93
|
setPendingPayload(payload);
|
|
84
94
|
setPendingSummaryRows(summaryRows);
|
|
85
95
|
setStage('CONFIRM');
|
|
@@ -95,26 +105,59 @@ export default function BillsScreen({ category, amount, user, onClose, onError }
|
|
|
95
105
|
if (!pendingPayload) return;
|
|
96
106
|
setLoading(true);
|
|
97
107
|
try {
|
|
108
|
+
const isTerminalTransaction = !!user?.terminalId;
|
|
109
|
+
let geoLocation: { latitude: number; longitude: number } | undefined;
|
|
110
|
+
if (isTerminalTransaction) {
|
|
111
|
+
const loc = await requestLocation();
|
|
112
|
+
if (loc) {
|
|
113
|
+
geoLocation = { latitude: loc.latitude, longitude: loc.longitude };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const ctx: FPBillPurchaseContext = {
|
|
118
|
+
agentId: getFPStore().psspId || '',
|
|
119
|
+
terminalId: user?.terminalId,
|
|
120
|
+
geoLocation,
|
|
121
|
+
currency: 'NGN',
|
|
122
|
+
tnxRef: `bill_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
123
|
+
forSelf:
|
|
124
|
+
pendingPayload.category === 'ELECTRICITY'
|
|
125
|
+
? pendingPayload.forSelf
|
|
126
|
+
: true,
|
|
127
|
+
agentName: user ? `${user.firstName} ${user.lastName}` : undefined,
|
|
128
|
+
agentEmail: user?.email,
|
|
129
|
+
agentPhone: user?.phone,
|
|
130
|
+
};
|
|
131
|
+
|
|
98
132
|
let response;
|
|
99
133
|
switch (pendingPayload.category) {
|
|
100
134
|
case 'AIRTIME':
|
|
101
|
-
response = await billsAPI.purchaseAirtime(
|
|
135
|
+
response = await billsAPI.purchaseAirtime(
|
|
136
|
+
pendingPayload,
|
|
137
|
+
ctx,
|
|
138
|
+
tempId
|
|
139
|
+
);
|
|
102
140
|
break;
|
|
103
141
|
case 'DATA':
|
|
104
|
-
response = await billsAPI.purchaseData(pendingPayload, tempId);
|
|
142
|
+
response = await billsAPI.purchaseData(pendingPayload, ctx, tempId);
|
|
105
143
|
break;
|
|
106
144
|
case 'ELECTRICITY':
|
|
107
|
-
response = await billsAPI.purchaseElectricity(
|
|
145
|
+
response = await billsAPI.purchaseElectricity(
|
|
146
|
+
pendingPayload,
|
|
147
|
+
ctx,
|
|
148
|
+
tempId
|
|
149
|
+
);
|
|
108
150
|
break;
|
|
109
151
|
case 'CABLE':
|
|
110
|
-
response = await billsAPI.purchaseCable(pendingPayload, tempId);
|
|
152
|
+
response = await billsAPI.purchaseCable(pendingPayload, ctx, tempId);
|
|
111
153
|
break;
|
|
112
154
|
}
|
|
113
155
|
|
|
114
156
|
if (!response?.status) {
|
|
115
|
-
onError?.({
|
|
116
|
-
|
|
117
|
-
|
|
157
|
+
onError?.({
|
|
158
|
+
code: 'PURCHASE_FAILED',
|
|
159
|
+
message: response?.message ?? 'Purchase failed',
|
|
160
|
+
} as FPError);
|
|
118
161
|
}
|
|
119
162
|
setCompletedTx(response?.payload ?? null);
|
|
120
163
|
setStage('RESULT');
|
|
@@ -128,9 +171,20 @@ export default function BillsScreen({ category, amount, user, onClose, onError }
|
|
|
128
171
|
const renderForm = () => {
|
|
129
172
|
switch (category) {
|
|
130
173
|
case 'AIRTIME':
|
|
131
|
-
return
|
|
174
|
+
return (
|
|
175
|
+
<AirtimeSubScreen
|
|
176
|
+
amount={amount}
|
|
177
|
+
onProcessTransaction={handleFormContinue}
|
|
178
|
+
onError={onError}
|
|
179
|
+
/>
|
|
180
|
+
);
|
|
132
181
|
case 'DATA':
|
|
133
|
-
return
|
|
182
|
+
return (
|
|
183
|
+
<DataSubScreen
|
|
184
|
+
onProcessTransaction={handleFormContinue}
|
|
185
|
+
onError={onError}
|
|
186
|
+
/>
|
|
187
|
+
);
|
|
134
188
|
case 'ELECTRICITY':
|
|
135
189
|
return (
|
|
136
190
|
<ElectricitySubScreen
|
|
@@ -141,7 +195,12 @@ export default function BillsScreen({ category, amount, user, onClose, onError }
|
|
|
141
195
|
/>
|
|
142
196
|
);
|
|
143
197
|
case 'CABLE':
|
|
144
|
-
return
|
|
198
|
+
return (
|
|
199
|
+
<CableSubScreen
|
|
200
|
+
onProcessTransaction={handleFormContinue}
|
|
201
|
+
onError={onError}
|
|
202
|
+
/>
|
|
203
|
+
);
|
|
145
204
|
}
|
|
146
205
|
};
|
|
147
206
|
|
|
@@ -154,7 +213,6 @@ export default function BillsScreen({ category, amount, user, onClose, onError }
|
|
|
154
213
|
statusFetcher={billsAPI.status}
|
|
155
214
|
onClose={handleResultClose}
|
|
156
215
|
/>
|
|
157
|
-
|
|
158
216
|
);
|
|
159
217
|
}
|
|
160
218
|
|
|
@@ -187,11 +245,13 @@ export default function BillsScreen({ category, amount, user, onClose, onError }
|
|
|
187
245
|
currency="₦"
|
|
188
246
|
subtitle="Double check the details before you proceed. Please note that successful bill payments cannot be reversed."
|
|
189
247
|
summaryRows={pendingSummaryRows}
|
|
190
|
-
validate={(pin: string) =>
|
|
248
|
+
validate={(pin: string) =>
|
|
249
|
+
billsAPI.validateBillPin(pin, getFPStore().psspId || '')
|
|
250
|
+
}
|
|
191
251
|
onContinue={handlePinAuthorized}
|
|
192
252
|
/>
|
|
193
253
|
</>
|
|
194
254
|
)}
|
|
195
255
|
</Container>
|
|
196
256
|
);
|
|
197
|
-
}
|
|
257
|
+
}
|
package/src/ui/screens/styles.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Text, TouchableOpacity } from 'react-native';
|
|
2
2
|
import styled from 'styled-components/native';
|
|
3
|
+
import { C } from '../theme';
|
|
3
4
|
|
|
4
5
|
export const ButtonContainer = styled.View`
|
|
5
6
|
width: 100%;
|
|
@@ -84,7 +85,7 @@ export const AccountText = styled.Text`
|
|
|
84
85
|
`;
|
|
85
86
|
|
|
86
87
|
export const Label = styled.Text`
|
|
87
|
-
color:
|
|
88
|
+
color: ${C.muted};
|
|
88
89
|
font-size: 14px;
|
|
89
90
|
font-weight: 600;
|
|
90
91
|
margin-bottom: 12px;
|
|
@@ -33,12 +33,13 @@ import {
|
|
|
33
33
|
StyledTextInput,
|
|
34
34
|
} from '../../styles';
|
|
35
35
|
import { getFPStore } from '../../../../store/FPStore';
|
|
36
|
+
import { C } from '../../../theme';
|
|
36
37
|
|
|
37
38
|
const { height } = Dimensions.get('window');
|
|
38
39
|
|
|
39
40
|
const Container = styled(View)`
|
|
40
41
|
flex: 1;
|
|
41
|
-
background-color:
|
|
42
|
+
background-color: ${C.white};
|
|
42
43
|
`;
|
|
43
44
|
|
|
44
45
|
const FormContainer = styled(View)`
|
|
@@ -66,7 +67,7 @@ const Header = styled(View)`
|
|
|
66
67
|
|
|
67
68
|
const HeaderButton = styled(TouchableOpacity)`
|
|
68
69
|
padding: 8px;
|
|
69
|
-
background-color:
|
|
70
|
+
background-color: ${C.surface};
|
|
70
71
|
width: 40px;
|
|
71
72
|
height: 40px;
|
|
72
73
|
border-radius: 50%;
|
|
@@ -165,7 +166,7 @@ const SwitchGroup = styled(View)`
|
|
|
165
166
|
|
|
166
167
|
const SwitchLabel = styled(Text)`
|
|
167
168
|
font-size: 14px;
|
|
168
|
-
color:
|
|
169
|
+
color: ${C.muted};
|
|
169
170
|
margin-left: 12px;
|
|
170
171
|
`;
|
|
171
172
|
|
|
@@ -308,14 +309,14 @@ export function TransferSubScreen({
|
|
|
308
309
|
{/* Header */}
|
|
309
310
|
<Header>
|
|
310
311
|
<HeaderButton onPress={onClose}>
|
|
311
|
-
<Ionicons name="close" size={24} />
|
|
312
|
+
<Ionicons name="close" size={24} color={C.ink} />
|
|
312
313
|
</HeaderButton>
|
|
313
314
|
|
|
314
315
|
<HeaderCenter>
|
|
315
|
-
<Text style={{ fontWeight: 'bold', fontSize: 16, color:
|
|
316
|
+
<Text style={{ fontWeight: 'bold', fontSize: 16, color: C.ink }}>
|
|
316
317
|
Bank
|
|
317
318
|
</Text>
|
|
318
|
-
<Text style={{ fontSize: 10, color:
|
|
319
|
+
<Text style={{ fontSize: 10, color: C.ink }}>Transfer</Text>
|
|
319
320
|
</HeaderCenter>
|
|
320
321
|
|
|
321
322
|
<HeaderRight />
|
|
@@ -323,11 +324,11 @@ export function TransferSubScreen({
|
|
|
323
324
|
<FormContainer>
|
|
324
325
|
<InputContainer>
|
|
325
326
|
<SwitchGroup>
|
|
326
|
-
<Label>Account
|
|
327
|
+
<Label>{isBank ? 'Bank Account Number' : 'Wallet Number'}</Label>
|
|
327
328
|
<ListGroup>
|
|
328
329
|
<SwitchLabel>To Banks</SwitchLabel>
|
|
329
330
|
<Switch
|
|
330
|
-
trackColor={{ false: '#767577', true:
|
|
331
|
+
trackColor={{ false: '#767577', true: C.ink }}
|
|
331
332
|
thumbColor={isBank ? '#003333' : '#f4f3f4'}
|
|
332
333
|
ios_backgroundColor="#3e3e3e"
|
|
333
334
|
value={isBank}
|