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.
- package/lib/module/index.js +11 -620
- package/lib/module/index.js.map +1 -1
- package/lib/module/navigation.js +4 -0
- package/lib/module/navigation.js.map +1 -1
- package/lib/module/screens/mainScreen.js +626 -0
- package/lib/module/screens/mainScreen.js.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation.d.ts +1 -0
- package/lib/typescript/src/navigation.d.ts.map +1 -1
- package/lib/typescript/src/screens/mainScreen.d.ts +3 -0
- package/lib/typescript/src/screens/mainScreen.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/index.tsx +10 -739
- package/src/navigation.tsx +3 -0
- package/src/screens/mainScreen.tsx +747 -0
|
@@ -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;
|