react-native-fpay 0.4.14 → 0.4.16

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 (49) hide show
  1. package/lib/module/FountainPayProvider.js +103 -12
  2. package/lib/module/FountainPayProvider.js.map +1 -1
  3. package/lib/module/core/api/client.js +1 -1
  4. package/lib/module/core/api/client.js.map +1 -1
  5. package/lib/module/core/api/index.js +2 -1
  6. package/lib/module/core/api/index.js.map +1 -1
  7. package/lib/module/core/types/index.js.map +1 -1
  8. package/lib/module/engine/BLESenderService.js +32 -7
  9. package/lib/module/engine/BLESenderService.js.map +1 -1
  10. package/lib/module/engine/FPEngine.js +17 -0
  11. package/lib/module/engine/FPEngine.js.map +1 -1
  12. package/lib/module/ui/components/Gradients/Skeleton.js +54 -0
  13. package/lib/module/ui/components/Gradients/Skeleton.js.map +1 -0
  14. package/lib/module/ui/components/Gradients/index.js +85 -0
  15. package/lib/module/ui/components/Gradients/index.js.map +1 -0
  16. package/lib/module/ui/screens/ResultScreen.js +18 -5
  17. package/lib/module/ui/screens/ResultScreen.js.map +1 -1
  18. package/lib/module/ui/screens/sub/sendPayment/BluetoothSubScreen.js +10 -9
  19. package/lib/module/ui/screens/sub/sendPayment/BluetoothSubScreen.js.map +1 -1
  20. package/lib/module/ui/screens/sub/sendPayment/ProximitySubScreen.js +3 -7
  21. package/lib/module/ui/screens/sub/sendPayment/ProximitySubScreen.js.map +1 -1
  22. package/lib/typescript/src/FountainPayProvider.d.ts +3 -2
  23. package/lib/typescript/src/FountainPayProvider.d.ts.map +1 -1
  24. package/lib/typescript/src/core/api/index.d.ts +16 -0
  25. package/lib/typescript/src/core/api/index.d.ts.map +1 -1
  26. package/lib/typescript/src/core/types/index.d.ts +2 -0
  27. package/lib/typescript/src/core/types/index.d.ts.map +1 -1
  28. package/lib/typescript/src/engine/BLESenderService.d.ts.map +1 -1
  29. package/lib/typescript/src/engine/FPEngine.d.ts +1 -0
  30. package/lib/typescript/src/engine/FPEngine.d.ts.map +1 -1
  31. package/lib/typescript/src/ui/components/Gradients/Skeleton.d.ts +3 -0
  32. package/lib/typescript/src/ui/components/Gradients/Skeleton.d.ts.map +1 -0
  33. package/lib/typescript/src/ui/components/Gradients/index.d.ts +3 -0
  34. package/lib/typescript/src/ui/components/Gradients/index.d.ts.map +1 -0
  35. package/lib/typescript/src/ui/screens/ResultScreen.d.ts.map +1 -1
  36. package/lib/typescript/src/ui/screens/sub/sendPayment/BluetoothSubScreen.d.ts.map +1 -1
  37. package/lib/typescript/src/ui/screens/sub/sendPayment/ProximitySubScreen.d.ts.map +1 -1
  38. package/package.json +1 -1
  39. package/src/FountainPayProvider.tsx +124 -17
  40. package/src/core/api/client.ts +1 -1
  41. package/src/core/api/index.ts +18 -0
  42. package/src/core/types/index.ts +2 -0
  43. package/src/engine/BLESenderService.ts +44 -17
  44. package/src/engine/FPEngine.ts +16 -0
  45. package/src/ui/components/Gradients/Skeleton.tsx +48 -0
  46. package/src/ui/components/Gradients/index.tsx +94 -0
  47. package/src/ui/screens/ResultScreen.tsx +23 -6
  48. package/src/ui/screens/sub/sendPayment/BluetoothSubScreen.tsx +10 -9
  49. package/src/ui/screens/sub/sendPayment/ProximitySubScreen.tsx +3 -2
@@ -63,7 +63,8 @@ export function useFountainPay(): FPInstance {
63
63
  // ── Provider ──────────────────────────────────────────────────────────────────
64
64
 
65
65
  interface ProviderProps {
66
- apiKey: string;
66
+ apiKey?: string;
67
+ accessToken?: string;
67
68
  options?: FPSDKOptions;
68
69
  children: ReactNode;
69
70
  onAuthReady?: () => void;
@@ -72,6 +73,7 @@ interface ProviderProps {
72
73
 
73
74
  export function FountainPayProvider({
74
75
  apiKey,
76
+ accessToken,
75
77
  options,
76
78
  children,
77
79
  onAuthReady,
@@ -87,6 +89,9 @@ export function FountainPayProvider({
87
89
  const account = useFPStore((s) => s.account);
88
90
  const user = useFPStore((s) => s.user);
89
91
  const balance = useFPStore((s) => s.balance);
92
+ // const resetToken = useFPStore((s) => s.clearAccessToken);
93
+ // const clearUser = useFPStore((s) => s.clearUser);
94
+
90
95
 
91
96
 
92
97
  useEffect(() => {
@@ -95,18 +100,96 @@ export function FountainPayProvider({
95
100
  // Make sure client is initialized before any API call
96
101
  console.log('[FountainPay] Provider mounted. HTTP client ready.', options);
97
102
  if (!clientBooted.current) {
98
- initClient(apiKey, {
99
- baseUrl: options?.baseUrl,
103
+ // For AGENT mode — store the token before booting the client
104
+ // so the request interceptor picks it up immediately
105
+ if (options?.userType === 'AGENT' && accessToken) {
106
+ getFPStore().setAccessToken(accessToken);
107
+ }
108
+
109
+ initClient(apiKey ?? '', {
110
+ baseUrl: options?.baseUrl,
100
111
  environment: options?.environment,
101
112
  });
113
+
102
114
  clientBooted.current = true;
103
- console.log(
104
- '[FountainPay] Provider mounted. HTTP client ready.: ',
105
- options
106
- );
115
+ console.log('[FountainPay] HTTP client ready. Mode:', options?.userType ?? 'USER');
107
116
  }
108
117
 
109
118
  const authenticate = async () => {
119
+ // await resetToken();
120
+ // await clearUser();
121
+ const userType = options?.userType ?? 'USER';
122
+
123
+ // ── AGENT mode ────────────────────────────────────────
124
+ if (userType === 'AGENT') {
125
+ if (!accessToken) {
126
+ onAuthError?.('accessToken is required for AGENT mode');
127
+ return;
128
+ }
129
+
130
+ try {
131
+ console.log('[FountainPay] Agent mode — validating token...');
132
+
133
+ // Store the token first so the HTTP interceptor picks it up
134
+ getFPStore().setAccessToken(accessToken);
135
+
136
+ // Fetch agent profile from backend
137
+ const response = await authenticateAPI.validateToken();
138
+ if (cancelled) return;
139
+
140
+ if (response.status) {
141
+ const agent = response.payload;
142
+
143
+ // Build FPUserInfo from agent model
144
+ const user: FPUserInfo = {
145
+ firstName: agent.firstName,
146
+ lastName: agent.lastName,
147
+ email: agent.email,
148
+ phone: agent.phone ?? '',
149
+ bvn: agent.bvn,
150
+ userId: agent.id,
151
+ };
152
+
153
+ // Build FPAccount from agent model
154
+ const account: FPAccount = {
155
+ id: agent.id,
156
+ firstName: agent.firstName,
157
+ lastName: agent.lastName,
158
+ phone: agent.phone,
159
+ accountName: `${agent.firstName} ${agent.lastName}`.toUpperCase(),
160
+ accountNumber: agent.accountNumber,
161
+ bankName: agent.bankName,
162
+ bankCode: agent.bankCode, // populate if backend returns it
163
+ };
164
+
165
+ // Store everything — no need to call _resolveAccount
166
+ getFPStore().setUser(user);
167
+ getFPStore().setPsspId(agent.id);
168
+ getFPStore().setAccount(account);
169
+
170
+ const banks: any = await transferAPI.getBanks();
171
+ if (banks.status) {
172
+ getFPStore().setBanks(banks.payload);
173
+ }
174
+
175
+ setAuthenticated(true);
176
+ console.log('[FountainPay] Agent validated:', agent.firstName, agent.lastName);
177
+ onAuthReady?.();
178
+ } else {
179
+ getFPStore().clearAccessToken();
180
+ setAuthenticated(false, response.message);
181
+ onAuthError?.(response.message);
182
+ }
183
+ } catch (err: any) {
184
+ if (cancelled) return;
185
+ getFPStore().clearAccessToken();
186
+ setAuthenticated(false, err.message);
187
+ onAuthError?.(err.message);
188
+ }
189
+
190
+ return; // ← don't fall through to PSSP auth
191
+ }
192
+
110
193
  if (isTokenValid()) {
111
194
  console.log('[FountainPay] Valid token found — skipping auth.');
112
195
  setAuthenticated(true);
@@ -116,7 +199,7 @@ export function FountainPayProvider({
116
199
 
117
200
  try {
118
201
  console.log('[FountainPay] Verifying API key...');
119
- const response = await authenticateAPI.login(apiKey);
202
+ const response = await authenticateAPI.login(apiKey || "");
120
203
  if (cancelled) return;
121
204
 
122
205
  if (response.status) {
@@ -152,14 +235,35 @@ export function FountainPayProvider({
152
235
  return () => {
153
236
  cancelled = true;
154
237
  };
155
- }, [apiKey]);
238
+ }, [apiKey, accessToken]);
239
+
240
+ useEffect(() => {
241
+ if (options?.userType !== 'AGENT') return;
242
+ if (!isAuthenticated) return;
243
+
244
+ const autoInitialize = async () => {
245
+ const store = getFPStore();
246
+ if (!store.user || !store.account) return;
247
+
248
+ console.log('[FountainPay] Agent mode — auto-initializing from stored data...');
249
+ await FPEngine.initialize(
250
+ accessToken ?? '',
251
+ store.user,
252
+ options ?? {},
253
+ {} // empty callbacks — host app sets these via initializeSDK if they want
254
+ );
255
+ console.log('[FountainPay] Agent auto-initialized.');
256
+ };
257
+
258
+ autoInitialize();
259
+ }, [isAuthenticated, options?.userType]);
156
260
 
157
261
  // Build the instance once per provider lifetime.
158
262
  // All components that call useFountainPay() share this exact object.
159
263
  const instance = useMemo<FPInstance>(() => {
160
264
  console.log(
161
265
  '[FountainPay] Creating shared FPInstance for apiKey:',
162
- apiKey.slice(0, 8) + '...'
266
+ apiKey?.toString().slice(0, 8) + '...'
163
267
  );
164
268
 
165
269
  return {
@@ -175,14 +279,17 @@ export function FountainPayProvider({
175
279
  '[FountainPay] initializeSDK() called for user:',
176
280
  user.firstName
177
281
  );
282
+
283
+ if (options?.userType === 'AGENT') {
284
+ if (callbacks) {
285
+ FPEngine.updateCallbacks(callbacks); // just register callbacks
286
+ }
287
+ console.log('[FountainPay] Agent already initialized — callbacks updated.');
288
+ return;
289
+ }
178
290
  try {
179
- // await FPEngine.initialize(
180
- // apiKey,
181
- // user,
182
- // options ?? {},
183
- // callbacks ?? {}
184
- // );
185
- await FPEngine.initialize(apiKey, user, options ?? {}, {
291
+
292
+ await FPEngine.initialize(apiKey || '', user, options ?? {}, {
186
293
  ...callbacks,
187
294
  onPaymentSent: async (tx) => {
188
295
  callbacks?.onPaymentSent?.(tx);
@@ -7,7 +7,7 @@ import type { FPError } from '../types';
7
7
  import { getFPStore } from '../../store/FPStore';
8
8
 
9
9
  const DEFAULT_BASE_URL =
10
- 'https://kenisha-happiest-nan.ngrok-free.dev/sdk/v1/fpip/';
10
+ 'https://kenisha-happiest-nan.ngrok-free.dev/modal/sdk/v1/fpip/';
11
11
 
12
12
  let _client: AxiosInstance | null = null;
13
13
  let _isRefreshing = false;
@@ -41,6 +41,24 @@ export const authenticateAPI = {
41
41
  http()
42
42
  .post<{ Response: any }>('/verify-otp', { otp, email })
43
43
  .then((r) => r.data),
44
+
45
+ validateToken: () =>
46
+ http().get<{
47
+ status: boolean;
48
+ message: string;
49
+ payload: {
50
+ id: string;
51
+ firstName: string;
52
+ lastName: string;
53
+ email: string;
54
+ phone: string;
55
+ bvn?: string;
56
+ accountNumber: string;
57
+ bankName: string;
58
+ bankCode?: string;
59
+ tradeName?: string;
60
+ };
61
+ }>('/auth/agent/validate-token').then(r => r.data),
44
62
  };
45
63
 
46
64
  export const accountAPI = {
@@ -28,6 +28,7 @@ export interface FPSDKOptions {
28
28
  environment?: 'sandbox' | 'production';
29
29
  proximityRadius?: number;
30
30
  bluetoothDisplayName?: string;
31
+ userType?: 'USER' | 'AGENT';
31
32
  }
32
33
 
33
34
  export interface FPTransferRecipient {
@@ -43,6 +44,7 @@ export interface FPWalletTransferRecipient {
43
44
  accountName: string;
44
45
  agentId?: string;
45
46
  userId?: string;
47
+ terminalId?: string;
46
48
  }
47
49
 
48
50
  export interface FPNfcRecipient {
@@ -195,9 +195,10 @@ class BLESenderService {
195
195
  rawDevice: device,
196
196
  };
197
197
  } catch (err) {
198
- try {
199
- await this.bleManager.cancelDeviceConnection(device.id);
200
- } catch {}
198
+ console.log("Read Device Info Error: ", err)
199
+ // try {
200
+ // await this.bleManager.cancelDeviceConnection(device.id);
201
+ // } catch {}
201
202
  return null;
202
203
  }
203
204
  }
@@ -233,10 +234,14 @@ class BLESenderService {
233
234
  ): Promise<BLESendPaymentResponse> {
234
235
  let responseReceived = false;
235
236
 
237
+
236
238
  try {
239
+ this.bleManager.stopDeviceScan();
240
+
237
241
  let device = this.connectedDevices.get(deviceId);
238
242
  if (!device) device = await this.connectToDevice(deviceId);
239
243
 
244
+
240
245
  // Start polling BEFORE writing the request, so we don't miss a fast response
241
246
  const responsePromise = new Promise<BLESendPaymentResponse>(
242
247
  async (resolve, reject) => {
@@ -257,6 +262,9 @@ class BLESenderService {
257
262
  await new Promise((r) => setTimeout(r, 1000));
258
263
  if (responseReceived) break;
259
264
 
265
+ // iOS caches BLE reads — force re-discovery to get fresh values
266
+
267
+
260
268
  console.log(
261
269
  '[FPay BLE Sender] Polling response characteristic...'
262
270
  );
@@ -292,6 +300,7 @@ class BLESenderService {
292
300
  }
293
301
  } catch (pollErr: any) {
294
302
  // errorCode 205 = device disconnected — abort immediately
303
+ console.log('[FPay BLE Sender] Poll error:', pollErr);
295
304
  if (pollErr.errorCode === 205) {
296
305
  reject(new Error('[FPay BLE Sender] Device disconnected'));
297
306
  break;
@@ -314,22 +323,40 @@ class BLESenderService {
314
323
  console.log('[FPay BLE Sender] Writing payment request...');
315
324
 
316
325
  try {
317
- await device.writeCharacteristicWithoutResponseForService(
318
- FP_SERVICE_UUID,
319
- FP_REQUEST_CHAR_UUID,
320
- base64
321
- );
322
- console.log('[FPay BLE Sender] Written without response (preferred)');
323
- } catch {
324
- console.log('[FPay BLE Sender] Retrying with response...');
325
- await device.writeCharacteristicWithResponseForService(
326
- FP_SERVICE_UUID,
327
- FP_REQUEST_CHAR_UUID,
328
- base64
329
- );
330
- console.log('[FPay BLE Sender] Written with response');
326
+ await device.writeCharacteristicWithResponseForService(
327
+ FP_SERVICE_UUID,
328
+ FP_REQUEST_CHAR_UUID,
329
+ base64
330
+ );
331
+ console.log('[FPay BLE Sender] Written with response');
332
+ } catch (e: any) {
333
+ console.log('[FPay BLE Sender] Write with response failed:', e.message);
334
+ console.log('[FPay BLE Sender] Retrying without response...');
335
+ await device.writeCharacteristicWithoutResponseForService(
336
+ FP_SERVICE_UUID,
337
+ FP_REQUEST_CHAR_UUID,
338
+ base64
339
+ );
340
+ console.log('[FPay BLE Sender] Written without response');
331
341
  }
332
342
 
343
+ // try {
344
+ // await device.writeCharacteristicWithoutResponseForService(
345
+ // FP_SERVICE_UUID,
346
+ // FP_REQUEST_CHAR_UUID,
347
+ // base64
348
+ // );
349
+ // console.log('[FPay BLE Sender] Written without response (preferred)');
350
+ // } catch {
351
+ // console.log('[FPay BLE Sender] Retrying with response...');
352
+ // await device.writeCharacteristicWithResponseForService(
353
+ // FP_SERVICE_UUID,
354
+ // FP_REQUEST_CHAR_UUID,
355
+ // base64
356
+ // );
357
+ // console.log('[FPay BLE Sender] Written with response');
358
+ // }
359
+
333
360
  console.log(
334
361
  '[FPay BLE Sender] Request sent. Waiting for accept/decline...'
335
362
  );
@@ -3,6 +3,7 @@
3
3
  // This replaces AlwaysOnPaymentListener entirely — all its logic lives here.
4
4
 
5
5
  import {
6
+ Alert,
6
7
  AppState,
7
8
  type AppStateStatus,
8
9
  Vibration,
@@ -434,6 +435,16 @@ export const FPEngine = {
434
435
  _isReady = true;
435
436
  console.log('[FPay Engine] Initialized. Advertising as:', displayName);
436
437
 
438
+ // AGENT — account already stored by provider, skip resolve
439
+ if (options.userType === 'AGENT') {
440
+ const account = getFPStore().account;
441
+ if (account) {
442
+ callbacks.onAccountReady?.(account);
443
+ _fetchBalance();
444
+ }
445
+ return;
446
+ }
447
+
437
448
  // Resolve account first — sets psspId in store
438
449
  // Then broadcast proximity — needs psspId to be set
439
450
  await _resolveAccount(user, callbacks);
@@ -563,6 +574,11 @@ export const FPEngine = {
563
574
  return BLESenderService.sendPaymentRequest(deviceId, request);
564
575
  },
565
576
 
577
+ async updateCallbacks(callbacks: FPCallbacks): Promise<void> {
578
+ _callbacks = { ..._callbacks, ...callbacks };
579
+ console.log('[FPay Engine] Callbacks updated.');
580
+ },
581
+
566
582
  // ── Other SDK actions ──────────────────────────────────────
567
583
 
568
584
  async generateAccount(req: FPUserInfo): Promise<FPAccount> {
@@ -0,0 +1,48 @@
1
+ import React, { useRef, useEffect } from 'react';
2
+ import { Animated, View, StyleSheet } from 'react-native';
3
+ import LinearGradient from 'react-native-linear-gradient';
4
+
5
+ const Skeleton = ({ width, height, borderRadius = 8, style }: any) => {
6
+ const translateX = useRef(new Animated.Value(-100)).current;
7
+
8
+ useEffect(() => {
9
+ Animated.loop(
10
+ Animated.timing(translateX, {
11
+ toValue: 300,
12
+ duration: 1200,
13
+ useNativeDriver: true,
14
+ })
15
+ ).start();
16
+ }, []);
17
+
18
+ return (
19
+ <View
20
+ style={[
21
+ {
22
+ width,
23
+ height,
24
+ borderRadius,
25
+ backgroundColor: '#e0e0e0',
26
+ overflow: 'hidden',
27
+ },
28
+ style,
29
+ ]}
30
+ >
31
+ <Animated.View
32
+ style={{
33
+ ...StyleSheet.absoluteFillObject,
34
+ transform: [{ translateX }],
35
+ }}
36
+ >
37
+ <LinearGradient
38
+ colors={['#e0e0e0', '#f5f5f5', '#e0e0e0']}
39
+ start={{ x: 0, y: 0 }}
40
+ end={{ x: 1, y: 0 }}
41
+ style={{ flex: 1 }}
42
+ />
43
+ </Animated.View>
44
+ </View>
45
+ );
46
+ };
47
+
48
+ export default Skeleton;
@@ -0,0 +1,94 @@
1
+ import { Animated, View } from "react-native";
2
+ import { C, R, S } from "../../theme";
3
+ import Skeleton from "./Skeleton";
4
+ import styled from "styled-components/native";
5
+
6
+
7
+
8
+ const Container = styled(Animated.View)`
9
+ flex: 1;
10
+ background-color: ${C.white};
11
+ padding: ${S.xxl}px ${S.lg}px ${S.xl}px;
12
+ align-items: center;
13
+ `;
14
+
15
+
16
+ const Card = styled.View`
17
+ width: 100%;
18
+ background-color: ${C.surface};
19
+ border-radius: ${R.xl}px;
20
+ padding: ${S.md}px ${S.lg}px;
21
+ margin-bottom: ${S.xl}px;
22
+ `;
23
+
24
+ const Footer = styled.View`
25
+ flex-direction: row;
26
+ align-items: center;
27
+ margin-top: auto;
28
+ `;
29
+
30
+
31
+ const Gradients = () => {
32
+ return (
33
+ <Container>
34
+ {/* Icon */}
35
+ <Skeleton
36
+ width={80}
37
+ height={80}
38
+ borderRadius={40}
39
+ style={{ alignSelf: 'center', marginBottom: 20 }}
40
+ />
41
+
42
+ {/* Status Text */}
43
+ <Skeleton
44
+ width={200}
45
+ height={20}
46
+ style={{ alignSelf: 'center', marginBottom: 16 }}
47
+ />
48
+
49
+ {/* Amount */}
50
+ <Skeleton
51
+ width={180}
52
+ height={30}
53
+ style={{ alignSelf: 'center', marginBottom: 24 }}
54
+ />
55
+
56
+ {/* Card */}
57
+ <Card>
58
+ {[...Array(5)].map((_, i) => (
59
+ <View
60
+ key={i}
61
+ style={{
62
+ flexDirection: 'row',
63
+ justifyContent: 'space-between',
64
+ marginBottom: 14,
65
+ }}
66
+ >
67
+ <Skeleton width={100} height={14} />
68
+ <Skeleton width={140} height={14} />
69
+ </View>
70
+ ))}
71
+ </Card>
72
+
73
+ {/* Footer */}
74
+ <Footer style={{ marginTop: 30 }}>
75
+ {/* Countdown ring placeholder */}
76
+ <Skeleton
77
+ width={60}
78
+ height={60}
79
+ borderRadius={30}
80
+ style={{ marginBottom: 20 }}
81
+ />
82
+
83
+ {/* Button */}
84
+ <Skeleton
85
+ width={'100%'}
86
+ height={50}
87
+ borderRadius={25}
88
+ />
89
+ </Footer>
90
+ </Container>
91
+ );
92
+ };
93
+
94
+ export default Gradients;
@@ -5,6 +5,7 @@ import styled from 'styled-components/native';
5
5
  import { C, F, R, S } from '../theme';
6
6
  import type { FPTransaction } from '../../core/types';
7
7
  import { transferAPI } from '../../core/api';
8
+ import Gradients from '../components/Gradients';
8
9
 
9
10
  // ── Icons ─────────────────────────────────────────────────────
10
11
 
@@ -203,6 +204,7 @@ interface Props {
203
204
  }
204
205
 
205
206
  export function ResultScreen({ transaction, onClose }: Props) {
207
+ const [loading, setLoading] = useState<boolean>(false);
206
208
  const [transactionDetail, setTransactionDetail] = useState<any>(null)
207
209
  const slideAnim = useRef(new Animated.Value(60)).current;
208
210
  const opacAnim = useRef(new Animated.Value(0)).current;
@@ -232,11 +234,20 @@ export function ResultScreen({ transaction, onClose }: Props) {
232
234
  : '—';
233
235
 
234
236
  const loadTransaction = async()=>{
235
- const response = await transferAPI.status(transaction.reference) as any;
236
- console.log("Transaction payload: ", response);
237
- if(response.status){
238
- setTransactionDetail(response.payload);
237
+ setLoading(true);
238
+ setTransactionDetail(null);
239
+ try{
240
+ const response = await transferAPI.status(transaction.reference) as any;
241
+ console.log("Transaction payload: ", response);
242
+ if(response.status){
243
+ setTransactionDetail(response.payload);
244
+ }
245
+ }catch(err: any){
246
+ console.error("Error loading transactions status")
247
+ }finally{
248
+ setLoading(false);
239
249
  }
250
+
240
251
  }
241
252
 
242
253
  useEffect(() => {
@@ -256,10 +267,16 @@ export function ResultScreen({ transaction, onClose }: Props) {
256
267
  if(transaction){
257
268
  loadTransaction();
258
269
  }
259
- }, [transaction])
270
+ }, [transaction]);
271
+
272
+
273
+ if (loading && !transactionDetail) {
274
+ return <Gradients />;
275
+ }
260
276
 
261
277
  return (
262
278
  <Container style={{ opacity: opacAnim, transform: [{ translateY: slideAnim }] }}>
279
+
263
280
  <IconWrap style={{ transform: [{ scale: scaleAnim }] }}>
264
281
  <IconBg bg={bgColor}>
265
282
  {isSuccess ? <SuccessIcon /> : <FailedIcon />}
@@ -282,7 +299,7 @@ export function ResultScreen({ transaction, onClose }: Props) {
282
299
 
283
300
  <Footer>
284
301
  <CountdownRing duration={COUNTDOWN_SECONDS * 1000} />
285
- <CloseBtn onPress={loadTransaction} activeOpacity={0.8}>
302
+ <CloseBtn onPress={onClose} activeOpacity={0.8}>
286
303
  <CloseBtnText>Close</CloseBtnText>
287
304
  </CloseBtn>
288
305
  </Footer>
@@ -17,15 +17,9 @@ 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 '../../../../core/api';
21
- import { FPButton } from '../../../components/FPButton';
22
- import { C, R, S, F, shadow } from '../../../theme';
23
20
  import type {
24
- FPCurrency,
25
21
  FPError,
26
- FPSendPaymentRequest,
27
22
  FPSendWalletPaymentRequest,
28
- FPTransaction,
29
23
  FPUserInfo,
30
24
  FintechDevice,
31
25
  Props,
@@ -61,7 +55,7 @@ const Header = styled(View)`
61
55
 
62
56
  const HeaderButton = styled(TouchableOpacity)`
63
57
  padding: 8px;
64
- background-color: #fff;
58
+ background-color: transparent;
65
59
  width: 40px;
66
60
  height: 40px;
67
61
  border-radius: 50px;
@@ -79,6 +73,7 @@ const HeaderRight = styled(View)`
79
73
  flex-direction: row;
80
74
  align-items: center;
81
75
  gap: 8px;
76
+ width: 25%;
82
77
  `;
83
78
 
84
79
  const QuickLinksContainer = styled(View)`
@@ -343,6 +338,8 @@ export function BluetoothSubScreen({
343
338
  request
344
339
  );
345
340
 
341
+ await FPEngine.stopListening();
342
+
346
343
  console.log('Component response: ', response);
347
344
 
348
345
  if (!response.accepted) {
@@ -367,8 +364,10 @@ export function BluetoothSubScreen({
367
364
  recipient: {
368
365
  accountName: response.accountDetails.accountName,
369
366
  accountNumber: response.accountDetails.accountNumber,
370
- userId: response.accountDetails.userId,
367
+ userId: response.accountDetails.userId || "",
368
+ agentId: response.accountDetails.agentId || "",
371
369
  type: response.accountDetails.receiverType ?? 'USER',
370
+ terminalId: response.accountDetails.terminalId || ''
372
371
  },
373
372
  amount: request.amount.toString(),
374
373
  reference: response.transactionId ?? `BT_${Date.now()}`,
@@ -402,6 +401,8 @@ export function BluetoothSubScreen({
402
401
  Alert.alert('Error', 'Failed to send payment request');
403
402
  }
404
403
  onError?.(fp);
404
+ }finally{
405
+ await FPEngine.startListening();
405
406
  }
406
407
  };
407
408
 
@@ -422,7 +423,7 @@ export function BluetoothSubScreen({
422
423
 
423
424
  <HeaderRight>
424
425
  <HeaderButton>
425
- <Ionicons name="ellipsis-vertical" size={22} />
426
+ {/* <Ionicons name="ellipsis-vertical" size={22} /> */}
426
427
  </HeaderButton>
427
428
  </HeaderRight>
428
429
  </Header>