boxpay-checkout-reactnative-sdk 1.0.0-beta03 → 1.0.0-beta05

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.
@@ -0,0 +1,747 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { View, Text, BackHandler, AppState, Image, ScrollView, StatusBar, Alert } from 'react-native'; // Added ScrollView
3
+ import Header from '../components/header';
4
+ import upiPostRequest from '../postRequest/upiPostRequest';
5
+ import { decode as atob } from 'base-64';
6
+ import { Linking } from 'react-native';
7
+ import LottieView from 'lottie-react-native';
8
+ import PaymentSuccess from '../components/paymentSuccess';
9
+ import SessionExpire from '../components/sessionExpire';
10
+ import PaymentFailed from '../components/paymentFailed';
11
+ import fetchStatus from '../postRequest/fetchStatus';
12
+ import UpiScreen from '../screens/upiScreen';
13
+ import { useNavigation, type NavigationProp } from "@react-navigation/native";
14
+ import type { CheckoutStackParamList } from '../navigation';
15
+ import { paymentHandler } from "../sharedContext/paymentStatusHandler";
16
+ import { loadCustomFonts, loadInterCustomFonts } from '../components/fontFamily';
17
+ import { setUserDataHandler, userDataHandler } from '../sharedContext/userdataHandler';
18
+ import { type PaymentResultObject, type PaymentClass, type InstrumentDetails, type PaymentMethod, type OrderItem, APIStatus, AnalyticsEvents } from '../interface';
19
+ import { checkoutDetailsHandler, setCheckoutDetailsHandler } from '../sharedContext/checkoutDetailsHandler';
20
+ import WebViewScreen from '../screens/webViewScreen';
21
+ import styles from '../styles/indexStyles';
22
+ import getSymbolFromCurrency from 'currency-symbol-map';
23
+ import type { ItemsProp } from '../components/orderDetails';
24
+ import OrderDetails from '../components/orderDetails';
25
+ import { SafeAreaView } from 'react-native-safe-area-context';
26
+ import PaymentSelectorView from '../components/paymentSelector';
27
+ import SavedCardComponentView from '../components/savedCardComponent';
28
+ import ShimmerView from '../components/shimmerView';
29
+ import AddressComponent from '../components/addressCard';
30
+ import fetchSessionDetails from '../postRequest/fetchSessionDetails';
31
+ import MorePaymentMethods from '../components/morePaymentMethods';
32
+ import { fetchSavedInstrumentsHandler, handleFetchStatusResponseHandler, handlePaymentResponse } from '../sharedContext/handlePaymentResponseHandler';
33
+ import callUIAnalytics from '../postRequest/callUIAnalytics';
34
+
35
+ const MainScreen = () => {
36
+ const [status, setStatus] = useState('NOACTION');
37
+ const [transactionId, setTransactionId] = useState('');
38
+ const appStateListenerRef = useRef<any>(null);
39
+ const [loadingState, setLoadingState] = useState(false);
40
+ const [isFirstLoading, setIsFirstLoading] = useState(true);
41
+ const [amount, setAmount] = useState('');
42
+ const totalItemsRef = useRef(0);
43
+ const [address, setAddress] = useState('');
44
+ const [failedModalOpen, setFailedModalState] = useState(false);
45
+ const [successModalOpen, setSuccessModalState] = useState(false);
46
+ const lastOpenendUrl = useRef<string>('');
47
+ const paymentFailedMessage = useRef<string>(
48
+ 'You may have cancelled the payment or there was a delay in response. Please retry.'
49
+ );
50
+ const [sessionExpireModalOpen, setSessionExppireModalState] = useState(false);
51
+ const [successfulTimeStamp, setSuccessfulTimeStamp] = useState('');
52
+ const navigation = useNavigation<NavigationProp<CheckoutStackParamList>>(); // Use correct type
53
+ const timerRef = useRef<NodeJS.Timeout | null>(null);
54
+ const [showWebView, setShowWebView] = useState(false);
55
+ const [paymentUrl, setPaymentUrl] = useState<string | null>(null);
56
+ const [paymentHtml, setPaymentHtml] = useState<string | null>(null);
57
+ const isUpiOpeningRef = useRef(false);
58
+ const shippingAmountRef = useRef('');
59
+ const taxAmountRef = useRef('');
60
+ const subTotalAmountRef = useRef('');
61
+ const orderItemsArrayRef = useRef<ItemsProp[]>([]);
62
+ const [recommendedInstrumentsArray, setRecommendedInstruments] = useState<PaymentClass[]>([]);
63
+ const [savedCardArray, setSavedCardArray] = useState<PaymentClass[]>([]);
64
+ const [savedUpiArray, setSavedUpiArray] = useState<PaymentClass[]>([]);
65
+
66
+ let isFirstTimeLoadRef = true;
67
+
68
+ const handlePaymentIntent = async (selectedIntent: string) => {
69
+ setLoadingState(true);
70
+ const response = await upiPostRequest({
71
+ type: 'upi/intent',
72
+ ...(selectedIntent && { upiAppDetails: { upiApp: selectedIntent } }), // Conditionally add upiAppDetails only if upiIntent is present
73
+ });
74
+ handlePaymentResponse({
75
+ response: response,
76
+ checkoutDetailsErrorMessage: checkoutDetailsHandler.checkoutDetails.errorMessage,
77
+ onSetStatus: setStatus,
78
+ onSetTransactionId: setTransactionId,
79
+ onSetPaymentHtml: setPaymentHtml,
80
+ onSetPaymentUrl: setPaymentUrl,
81
+ onSetFailedMessage: (msg) => (paymentFailedMessage.current = msg),
82
+ onShowFailedModal: () => setFailedModalState(true),
83
+ onShowSuccessModal: (ts) => {
84
+ setSuccessfulTimeStamp(ts);
85
+ setSuccessModalState(true);
86
+ },
87
+ onShowSessionExpiredModal: () => setSessionExppireModalState(true),
88
+ onNavigateToTimer: (id:string)=> navigation.navigate("UpiTimerScreen", {upiId : id}),
89
+ onOpenUpiIntent : (url) => {
90
+ urlToBase64(url);
91
+ },
92
+ setLoading: setLoadingState
93
+ });
94
+ };
95
+
96
+ const handleUpiCollectPayment = async (
97
+ upiId: string,
98
+ instrumentRef: string,
99
+ type: string
100
+ ) => {
101
+ const requestPayload: InstrumentDetails =
102
+ type === 'Card'
103
+ ? {
104
+ type: 'card/token',
105
+ savedCard: { instrumentRef: instrumentRef },
106
+ }
107
+ : {
108
+ type: 'upi/collect',
109
+ upi: instrumentRef
110
+ ? { instrumentRef: instrumentRef }
111
+ : { shopperVpa: upiId },
112
+ };
113
+ setLoadingState(true);
114
+ const response = await upiPostRequest(requestPayload);
115
+ handlePaymentResponse({
116
+ response: response,
117
+ upiId: upiId,
118
+ checkoutDetailsErrorMessage: checkoutDetailsHandler.checkoutDetails.errorMessage,
119
+ onSetStatus: setStatus,
120
+ onSetTransactionId: setTransactionId,
121
+ onSetPaymentHtml: setPaymentHtml,
122
+ onSetPaymentUrl: setPaymentUrl,
123
+ onSetFailedMessage: (msg) => (paymentFailedMessage.current = msg),
124
+ onShowFailedModal: () => setFailedModalState(true),
125
+ onShowSuccessModal: (ts) => {
126
+ setSuccessfulTimeStamp(ts);
127
+ setSuccessModalState(true);
128
+ },
129
+ onShowSessionExpiredModal: () => setSessionExppireModalState(true),
130
+ onNavigateToTimer: (id:string)=> navigation.navigate("UpiTimerScreen", {upiId : id}),
131
+ onOpenUpiIntent : (url) => {
132
+ urlToBase64(url);
133
+ },
134
+ setLoading: setLoadingState
135
+ });
136
+ };
137
+
138
+ useEffect(() => {
139
+ if (isFirstTimeLoadRef) {
140
+ isFirstTimeLoadRef = false;
141
+ return;
142
+ }
143
+ const refreshData = () => {
144
+ const address1Ref = userDataHandler.userData.address1;
145
+ const address2Ref = userDataHandler.userData.address2;
146
+ const cityRef = userDataHandler.userData.city;
147
+ const stateRef = userDataHandler.userData.state;
148
+ const postalCodeRef = userDataHandler.userData.pincode;
149
+
150
+ if (address2Ref == null || address2Ref == '') {
151
+ setAddress(
152
+ `${address1Ref}, ${cityRef}, ${stateRef}, ${postalCodeRef}`
153
+ );
154
+ } else {
155
+ setAddress(
156
+ `${address1Ref}, ${address2Ref}, ${cityRef}, ${stateRef}, ${postalCodeRef}`
157
+ );
158
+ }
159
+ };
160
+
161
+ refreshData();
162
+ });
163
+
164
+ const urlToBase64 = (base64String: string) => {
165
+ try {
166
+ const decodedString = atob(base64String);
167
+ lastOpenendUrl.current = decodedString;
168
+ openUPIIntent(decodedString);
169
+ } catch (error) {
170
+ setFailedModalState(true);
171
+ callUIAnalytics(AnalyticsEvents.FAILED_TO_LAUNCH_UPI_INTENT,"Index Screen UrlToBase64 failed",`${error}`)
172
+ setLoadingState(false);
173
+ }
174
+ };
175
+
176
+ const getRecommendedInstruments = async () => {
177
+ fetchSavedInstrumentsHandler({
178
+ setRecommendedList: setRecommendedInstruments,
179
+ setUpiInstrumentList : setSavedUpiArray,
180
+ setCardInstrumentList : setSavedCardArray
181
+ });
182
+ setIsFirstLoading(false)
183
+ };
184
+
185
+ useEffect(() => {
186
+ if (paymentUrl || paymentHtml) {
187
+ setShowWebView(true);
188
+ }
189
+ }, [paymentUrl, paymentHtml]);
190
+
191
+ const openUPIIntent = async (url: string) => {
192
+ try {
193
+ await Linking.openURL(url); // Open the UPI app
194
+ appStateListenerRef.current = AppState.addEventListener(
195
+ 'change',
196
+ handleAppStateChange
197
+ );
198
+ isUpiOpeningRef.current = true;
199
+ } catch (error) {
200
+ isUpiOpeningRef.current = false;
201
+ callUIAnalytics(AnalyticsEvents.FAILED_TO_LAUNCH_UPI_INTENT,"Index Screen open UPI Intent failed",`${error}`)
202
+ setFailedModalState(true);
203
+ setLoadingState(false);
204
+ }
205
+ };
206
+
207
+ const handleAppStateChange = () => {
208
+ if (AppState.currentState === 'active' && isUpiOpeningRef.current) {
209
+ callFetchStatusApi();
210
+ }
211
+ };
212
+
213
+ const stopExpireTimerCountDown = () => {
214
+ if (timerRef.current) {
215
+ clearInterval(timerRef.current);
216
+ }
217
+ };
218
+
219
+ const callFetchStatusApi = async () => {
220
+ const response = await fetchStatus();
221
+ handleFetchStatusResponseHandler({
222
+ response: response,
223
+ checkoutDetailsErrorMessage: checkoutDetailsHandler.checkoutDetails.errorMessage,
224
+ onSetStatus: setStatus,
225
+ onSetTransactionId: setTransactionId,
226
+ onSetFailedMessage: (msg) => (paymentFailedMessage.current = msg),
227
+ onShowFailedModal: () => setFailedModalState(true),
228
+ onShowSuccessModal: (ts) => {
229
+ setSuccessfulTimeStamp(ts);
230
+ setSuccessModalState(true);
231
+ },
232
+ onShowSessionExpiredModal: () => setSessionExppireModalState(true),
233
+ setLoading: setLoadingState
234
+ });
235
+ appStateListenerRef.current?.remove();
236
+ isUpiOpeningRef.current = false;
237
+ };
238
+
239
+ const onExitCheckout = () => {
240
+ if (!loadingState) {
241
+ stopExpireTimerCountDown();
242
+ const mockPaymentResult: PaymentResultObject = {
243
+ status: status,
244
+ transactionId: transactionId,
245
+ };
246
+ paymentHandler.onPaymentResult(mockPaymentResult);
247
+ return true;
248
+ }
249
+ return false;
250
+ };
251
+
252
+ useEffect(() => {
253
+ const backHandler = BackHandler.addEventListener(
254
+ 'hardwareBackPress',
255
+ () => {
256
+ if (showWebView) {
257
+ setShowWebView(false);
258
+ paymentFailedMessage.current =
259
+ checkoutDetailsHandler.checkoutDetails.errorMessage;
260
+ setStatus('Failed');
261
+ setFailedModalState(true);
262
+ setLoadingState(false);
263
+ return true;
264
+ } else if (loadingState) {
265
+ return true;
266
+ }
267
+ return onExitCheckout(); // Allow back navigation if not loading
268
+ }
269
+ );
270
+
271
+ return () => backHandler.remove();
272
+ });
273
+
274
+ useEffect(() => {
275
+ async function loadFonts() {
276
+ await loadCustomFonts();
277
+ await loadInterCustomFonts();
278
+ }
279
+ loadFonts()
280
+
281
+ async function loadSession() {
282
+ if(checkoutDetailsHandler.checkoutDetails.token != "") {
283
+ const response = await fetchSessionDetails()
284
+ try {
285
+ switch(response.apiStatus) {
286
+ case APIStatus.Success : {
287
+ const paymentMethods = response.data.configs.paymentMethods;
288
+ const enabledFields = response.data.configs.enabledFields;
289
+ const paymentDetails = response.data.paymentDetails;
290
+ const methodFlags = {
291
+ isUPIIntentVisible: false,
292
+ isUPICollectVisible: false,
293
+ isCardsVisible: false,
294
+ isWalletVisible: false,
295
+ isNetbankingVisible: false,
296
+ isEMIVisible: false,
297
+ isBNPLVisible: false,
298
+ };
299
+
300
+ paymentMethods.forEach((method: PaymentMethod) => {
301
+ if (method.type === 'Upi') {
302
+ if (method.brand === 'UpiIntent') {
303
+ methodFlags.isUPIIntentVisible = true;
304
+ } else if (method.brand === 'UpiCollect') {
305
+ methodFlags.isUPICollectVisible = true;
306
+ }
307
+ } else if (method.type === 'Card') {
308
+ methodFlags.isCardsVisible = true;
309
+ } else if (method.type === 'Wallet') {
310
+ methodFlags.isWalletVisible = true;
311
+ } else if (method.type === 'NetBanking') {
312
+ methodFlags.isNetbankingVisible = true;
313
+ } else if (method.type === 'Emi') {
314
+ methodFlags.isEMIVisible = true;
315
+ } else if (method.type === 'BuyNowPayLater') {
316
+ methodFlags.isBNPLVisible = true;
317
+ }
318
+ });
319
+
320
+ setAmount(paymentDetails.money.amountLocaleFull);
321
+ const currencyCode: string | undefined =
322
+ paymentDetails?.money?.currencyCode;
323
+ const symbol = currencyCode
324
+ ? (getSymbolFromCurrency(currencyCode) ?? '₹')
325
+ : '₹';
326
+ if (
327
+ paymentDetails.order != null &&
328
+ paymentDetails.order.items != null
329
+ ) {
330
+ const total = paymentDetails.order.items.reduce(
331
+ (sum: number, item: OrderItem) => sum + (item.quantity || 1),
332
+ 0
333
+ );
334
+ totalItemsRef.current = total;
335
+ shippingAmountRef.current =
336
+ paymentDetails.order.shippingAmountLocaleFull != null
337
+ ? paymentDetails.order.shippingAmountLocaleFull
338
+ : '';
339
+ taxAmountRef.current =
340
+ paymentDetails.order.taxAmountLocaleFull != null
341
+ ? paymentDetails.order.taxAmountLocaleFull
342
+ : '';
343
+ subTotalAmountRef.current =
344
+ paymentDetails.order.originalAmountLocaleFull != null
345
+ ? paymentDetails.order.originalAmountLocaleFull
346
+ : '';
347
+ const formattedItemsArray: ItemsProp[] =
348
+ paymentDetails.order.items.map((item: OrderItem) => ({
349
+ imageUrl: item.imageUrl,
350
+ imageTitle: item.itemName,
351
+ imageOty: item.quantity,
352
+ imageAmount: item.amountWithoutTaxLocaleFull,
353
+ }));
354
+ orderItemsArrayRef.current = formattedItemsArray;
355
+ }
356
+ const emailRef = paymentDetails.shopper.email;
357
+ const firstNameRef = paymentDetails.shopper.firstName;
358
+ const lastNameRef = paymentDetails.shopper.lastName;
359
+ const phoneRef = paymentDetails.shopper.phoneNumber;
360
+ const uniqueIdRef = paymentDetails.shopper.uniqueReference;
361
+ const dobRef = paymentDetails.shopper.dateOfBirth;
362
+ const panRef = paymentDetails.shopper.panNumber;
363
+ startCountdown(response.data.sessionExpiryTimestamp);
364
+ let labelTypeRef = null;
365
+ let address1Ref = null;
366
+ let labelNameRef = null;
367
+ let address2Ref = null;
368
+ let cityRef = null;
369
+ let stateRef = null;
370
+ let postalCodeRef = null;
371
+ let countryCodeRef = null;
372
+ if (paymentDetails.shopper.deliveryAddress != null) {
373
+ const deliveryObject = paymentDetails.shopper.deliveryAddress;
374
+ labelTypeRef = deliveryObject.labelType;
375
+ labelNameRef = deliveryObject.labelName;
376
+ address1Ref = deliveryObject.address1;
377
+ address2Ref = deliveryObject.address2;
378
+ cityRef = deliveryObject.city;
379
+ stateRef = deliveryObject.state;
380
+ postalCodeRef = deliveryObject.postalCode;
381
+ countryCodeRef = deliveryObject.countryCode;
382
+ if (address2Ref == null || address2Ref == '') {
383
+ setAddress(
384
+ `${address1Ref}, ${cityRef}, ${stateRef}, ${postalCodeRef}`
385
+ );
386
+ } else {
387
+ setAddress(
388
+ `${address1Ref}, ${address2Ref}, ${cityRef}, ${stateRef}, ${postalCodeRef}`
389
+ );
390
+ }
391
+ }
392
+ if (['APPROVED', 'SUCCESS', 'PAID'].includes(response.data.status)) {
393
+ setSuccessfulTimeStamp(response.data.lastPaidAtTimestamp);
394
+ setTransactionId(response.data.lastTransactionId);
395
+ setStatus(response.data.status);
396
+ setSuccessModalState(true);
397
+ } else if (['EXPIRED'].includes(response.data.status)) {
398
+ setSessionExppireModalState(true);
399
+ }
400
+ setUserDataHandler({
401
+ userData: {
402
+ email: emailRef,
403
+ firstName: firstNameRef,
404
+ lastName: lastNameRef,
405
+ phone: phoneRef,
406
+ uniqueId: uniqueIdRef,
407
+ dob: dobRef,
408
+ pan: panRef,
409
+ address1: address1Ref,
410
+ address2: address2Ref,
411
+ city: cityRef,
412
+ state: stateRef,
413
+ pincode: postalCodeRef,
414
+ country: countryCodeRef,
415
+ labelType: labelTypeRef,
416
+ labelName: labelNameRef,
417
+ },
418
+ });
419
+ const isFieldEnabled = (fieldName: string) => {
420
+ return enabledFields.some(
421
+ (field: { field: string }) => field.field === fieldName
422
+ );
423
+ };
424
+
425
+ const isFieldEditable = (fieldName: string) => {
426
+ const field = enabledFields.find(
427
+ (field: { field: string; editable: boolean }) =>
428
+ field.field === fieldName
429
+ );
430
+ return field?.editable === true;
431
+ };
432
+
433
+ setCheckoutDetailsHandler({
434
+ checkoutDetails: {
435
+ currencySymbol: symbol,
436
+ amount: paymentDetails.money.amountLocaleFull,
437
+ token: checkoutDetailsHandler.checkoutDetails.token,
438
+ brandColor:
439
+ response.data.merchantDetails.checkoutTheme.primaryButtonColor,
440
+ env: checkoutDetailsHandler.checkoutDetails.env,
441
+ itemsLength: totalItemsRef.current,
442
+ errorMessage:
443
+ 'You may have cancelled the payment or there was a delay in response. Please retry.',
444
+ shopperToken: checkoutDetailsHandler.checkoutDetails.shopperToken,
445
+ isSuccessScreenVisible: checkoutDetailsHandler.checkoutDetails.isSuccessScreenVisible,
446
+ isShippingAddressEnabled: isFieldEnabled('SHIPPING_ADDRESS'),
447
+ isShippingAddressEditable: isFieldEditable('SHIPPING_ADDRESS'),
448
+ isFullNameEnabled: isFieldEnabled('SHOPPER_NAME'),
449
+ isFullNameEditable: isFieldEditable('SHOPPER_NAME'),
450
+ isEmailEnabled: isFieldEnabled('SHOPPER_EMAIL'),
451
+ isEmailEditable: isFieldEditable('SHOPPER_EMAIL'),
452
+ isPhoneEnabled: isFieldEnabled('SHOPPER_PHONE'),
453
+ isPhoneEditable: isFieldEditable('SHOPPER_PHONE'),
454
+ isPanEnabled: isFieldEnabled('SHOPPER_PAN'),
455
+ isPanEditable: isFieldEditable('SHOPPER_PAN'),
456
+ isDOBEnabled: isFieldEnabled('SHOPPER_DOB'),
457
+ isDOBEditable: isFieldEditable('SHOPPER_DOB'),
458
+ isUpiIntentMethodEnabled : methodFlags.isUPIIntentVisible,
459
+ isUpiCollectMethodEnabled : methodFlags.isUPICollectVisible,
460
+ isCardMethodEnabled : methodFlags.isCardsVisible,
461
+ isWalletMethodEnabled : methodFlags.isWalletVisible,
462
+ isNetBankingMethodEnabled : methodFlags.isNetbankingVisible,
463
+ isEmiMethodEnabled : methodFlags.isEMIVisible,
464
+ isBnplMethodEnabled : methodFlags.isBNPLVisible
465
+ },
466
+ });
467
+ callUIAnalytics(AnalyticsEvents.CHECKOUT_LOADED,"Index Screen Session Loaded","")
468
+ break;
469
+ }
470
+ case APIStatus.Failed : {
471
+ Alert.alert('Error', response.data.status.reason);
472
+ break
473
+ }
474
+ default : {
475
+ break
476
+ }
477
+ }
478
+ } catch(error) {
479
+ Alert.alert('Error', `${error}`);
480
+ }
481
+ } else {
482
+ Alert.alert('Error', `Token is empty`);
483
+ }
484
+ }
485
+ loadSession()
486
+
487
+ if(checkoutDetailsHandler.checkoutDetails.shopperToken != null && checkoutDetailsHandler.checkoutDetails.shopperToken != "") {
488
+ getRecommendedInstruments()
489
+ }
490
+
491
+ });
492
+
493
+ const handleRecommendedSectionClick = (instrumentValue: string) => {
494
+ const updatedList = recommendedInstrumentsArray.map((item) => ({
495
+ ...item,
496
+ isSelected: item.id === instrumentValue,
497
+ }));
498
+ setRecommendedInstruments(updatedList);
499
+ setDefaultSavedUpiList();
500
+ setDefaultSavedCardsList();
501
+ };
502
+
503
+ const handleSavedUpiSectionClick = (instrumentValue: string) => {
504
+ const updatedList = savedUpiArray.map((item) => ({
505
+ ...item,
506
+ isSelected: item.id === instrumentValue,
507
+ }));
508
+ setSavedUpiArray(updatedList);
509
+ setDefaultRecommendedList();
510
+ setDefaultSavedCardsList();
511
+ };
512
+
513
+ const handleSavedCardSectionClick = (instrumentValue: string) => {
514
+ const updatedList = savedCardArray.map((item) => ({
515
+ ...item,
516
+ isSelected: item.id === instrumentValue,
517
+ }));
518
+ setSavedCardArray(updatedList);
519
+ setDefaultRecommendedList();
520
+ setDefaultSavedUpiList();
521
+ };
522
+
523
+ const setDefaultRecommendedList = () => {
524
+ const updatedList = recommendedInstrumentsArray.map((item) => ({
525
+ ...item,
526
+ isSelected: false,
527
+ }));
528
+ setRecommendedInstruments(updatedList);
529
+ };
530
+
531
+ const setDefaultSavedCardsList = () => {
532
+ const updatedList = savedCardArray.map((item) => ({
533
+ ...item,
534
+ isSelected: false,
535
+ }));
536
+ setSavedCardArray(updatedList);
537
+ };
538
+
539
+ const setDefaultSavedUpiList = () => {
540
+ const updatedList = savedUpiArray.map((item) => ({
541
+ ...item,
542
+ isSelected: false,
543
+ }));
544
+ setSavedUpiArray(updatedList);
545
+ };
546
+
547
+ function startCountdown(sessionExpiryTimestamp: string) {
548
+ if (sessionExpiryTimestamp === '') {
549
+ return;
550
+ }
551
+ const expiryTime = new Date(sessionExpiryTimestamp);
552
+ const expiryTimeIST = new Date(expiryTime.getTime() + 5.5 * 60 * 60 * 1000);
553
+
554
+ timerRef.current = setInterval(() => {
555
+ const currentTimeIST = new Date(
556
+ new Date().getTime() + 5.5 * 60 * 60 * 1000
557
+ );
558
+ const timeDiff = expiryTimeIST.getTime() - currentTimeIST.getTime();
559
+ if (timeDiff <= 0) {
560
+ if (timerRef.current) {
561
+ clearInterval(timerRef.current);
562
+ }
563
+ setStatus('EXPIRED');
564
+ setSessionExppireModalState(true);
565
+ }
566
+ // const hours = Math.floor((timeDiff / (1000 * 60 * 60)) % 24);
567
+ // const minutes = Math.floor((timeDiff / (1000 * 60)) % 60);
568
+ // const seconds = Math.floor((timeDiff / 1000) % 60);
569
+
570
+ // console.log(`${hours}hr ${minutes}min ${seconds}sec`)
571
+ }, 1000);
572
+ }
573
+
574
+ return (
575
+ <SafeAreaView style={styles.screenView}>
576
+ <StatusBar barStyle="dark-content" />
577
+ {isFirstLoading ? (
578
+ <ShimmerView />
579
+ ) : loadingState ? (
580
+ <View
581
+ style={styles.loadingContainer}
582
+ >
583
+ <LottieView
584
+ source={require('../../assets/animations/boxpayLogo.json')}
585
+ autoPlay
586
+ loop
587
+ style={styles.lottieStyle}
588
+ />
589
+ <Text>Loading...</Text>
590
+ </View>
591
+ ) : (
592
+ <View style={styles.screenView}>
593
+ <ScrollView
594
+ contentContainerStyle={{ flexGrow: 1 }}
595
+ keyboardShouldPersistTaps="handled"
596
+ >
597
+ <View style={{ flex: 1 }}>
598
+ <Header
599
+ onBackPress={onExitCheckout}
600
+ showDesc={true}
601
+ showSecure={true}
602
+ text="Payment Details"
603
+ />
604
+ <AddressComponent address={address} navigateToAddressScreen= {() => navigation.navigate("AddressScreen", {})}/>
605
+
606
+ {recommendedInstrumentsArray.length > 0 && (
607
+ <>
608
+ <View
609
+ style={styles.container}
610
+ >
611
+ <Text
612
+ style={styles.headingText}
613
+ >
614
+ Recommended
615
+ </Text>
616
+ </View>
617
+ <View
618
+ style={styles.paymentContainer}
619
+ >
620
+ <PaymentSelectorView
621
+ providerList={recommendedInstrumentsArray}
622
+ onProceedForward={(displayValue, instrumentValue, type) =>
623
+ handleUpiCollectPayment(
624
+ displayValue,
625
+ instrumentValue,
626
+ type
627
+ )
628
+ }
629
+ errorImage={require('../../assets/images/ic_upi.png')}
630
+ isLastUsed={true}
631
+ onClickRadio={(selectedInstrumentRef) => {
632
+ handleRecommendedSectionClick(selectedInstrumentRef);
633
+ }}
634
+ />
635
+ </View>
636
+ </>
637
+ )}
638
+
639
+ <UpiScreen
640
+ handleUpiPayment={(selectedIntent) =>
641
+ handlePaymentIntent(selectedIntent)
642
+ }
643
+ handleCollectPayment={(displayValue, instrumentValue, type) =>
644
+ handleUpiCollectPayment(displayValue, instrumentValue, type)
645
+ }
646
+ savedUpiArray={savedUpiArray}
647
+ onClickRadio={handleSavedUpiSectionClick}
648
+ />
649
+
650
+ {savedCardArray.length != 0 && (
651
+ <View>
652
+ <Text
653
+ style={styles.headingText}
654
+ >
655
+ Credit & Debit Cards
656
+ </Text>
657
+ <View
658
+ style={styles.paymentContainer}
659
+ >
660
+ <SavedCardComponentView
661
+ savedCards={savedCardArray}
662
+ onProceedForward={(instrumentValue) => {
663
+ handleUpiCollectPayment('', instrumentValue, 'Card');
664
+ }}
665
+ errorImage={require('../../assets/images/ic_card.png')}
666
+ onClickAddCard={() => navigation.navigate("CardScreen", {})}
667
+ onClickRadio={(selectedInstrumentRef) =>
668
+ handleSavedCardSectionClick(selectedInstrumentRef)
669
+ }
670
+ />
671
+ </View>
672
+ </View>
673
+ )}
674
+ <MorePaymentMethods savedCards={savedCardArray}/>
675
+ <View>
676
+ <Text
677
+ style={styles.headingText}
678
+ >
679
+ Order Summary
680
+ </Text>
681
+
682
+ <OrderDetails
683
+ subTotalAmount={subTotalAmountRef.current}
684
+ shippingAmount={shippingAmountRef.current}
685
+ totalAmount={amount}
686
+ itemsArray={orderItemsArrayRef.current}
687
+ taxAmount={taxAmountRef.current}
688
+ />
689
+ </View>
690
+
691
+ {/* Secured by BoxPay - Fixed at Bottom */}
692
+ <View
693
+ style={styles.footerContainer}
694
+ >
695
+ <Text
696
+ style={styles.footerText}
697
+ >
698
+ Secured by
699
+ </Text>
700
+ <Image
701
+ source={require('../../assets/images/splash-icon.png')}
702
+ style={styles.footerImage}
703
+ />
704
+ </View>
705
+ </View>
706
+ </ScrollView>
707
+ </View>
708
+ )}
709
+
710
+ {/* Modals for Different Payment Statuses */}
711
+ {failedModalOpen && (
712
+ <PaymentFailed
713
+ onClick={() => setFailedModalState(false)}
714
+ errorMessage={paymentFailedMessage.current}
715
+ />
716
+ )}
717
+
718
+ {successModalOpen && (
719
+ <PaymentSuccess
720
+ onClick={onExitCheckout}
721
+ transactionId={transactionId}
722
+ method="UPI"
723
+ localDateTime={successfulTimeStamp}
724
+ />
725
+ )}
726
+
727
+ {sessionExpireModalOpen && <SessionExpire onClick={onExitCheckout} />}
728
+
729
+ {showWebView && (
730
+ <View
731
+ style={styles.webViewScreenStyle}
732
+ >
733
+ <WebViewScreen
734
+ url={paymentUrl}
735
+ html={paymentHtml}
736
+ onBackPress={() => {
737
+ callFetchStatusApi();
738
+ setShowWebView(false);
739
+ }}
740
+ />
741
+ </View>
742
+ )}
743
+ </SafeAreaView>
744
+ );
745
+ };
746
+
747
+ export default MainScreen;