boxpay-checkout-reactnative-sdk 1.0.0-beta31 → 1.0.0-beta33

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 (70) hide show
  1. package/assets/images/ic_qr.png +0 -0
  2. package/lib/assets/assets/images/ic_qr.png +0 -0
  3. package/lib/assets/images/ic_qr.png +0 -0
  4. package/lib/module/components/sessionExpire.js +0 -3
  5. package/lib/module/components/sessionExpire.js.map +1 -1
  6. package/lib/module/interface.js.map +1 -1
  7. package/lib/module/postRequest/callUIAnalytics.js +3 -14
  8. package/lib/module/postRequest/callUIAnalytics.js.map +1 -1
  9. package/lib/module/postRequest/cardPostRequest.js +5 -42
  10. package/lib/module/postRequest/cardPostRequest.js.map +1 -1
  11. package/lib/module/postRequest/emiPostRequest.js +5 -42
  12. package/lib/module/postRequest/emiPostRequest.js.map +1 -1
  13. package/lib/module/postRequest/methodsPostRequest.js +5 -42
  14. package/lib/module/postRequest/methodsPostRequest.js.map +1 -1
  15. package/lib/module/postRequest/upiPostRequest.js +5 -42
  16. package/lib/module/postRequest/upiPostRequest.js.map +1 -1
  17. package/lib/module/screens/mainScreen.js +70 -1
  18. package/lib/module/screens/mainScreen.js.map +1 -1
  19. package/lib/module/screens/upiScreen.js +154 -43
  20. package/lib/module/screens/upiScreen.js.map +1 -1
  21. package/lib/module/scripts/checkPeerDeps.js +27 -0
  22. package/lib/module/scripts/checkPeerDeps.js.map +1 -0
  23. package/lib/module/sharedContext/checkoutDetailsHandler.js +1 -0
  24. package/lib/module/sharedContext/checkoutDetailsHandler.js.map +1 -1
  25. package/lib/module/sharedContext/handlePaymentResponseHandler.js +6 -1
  26. package/lib/module/sharedContext/handlePaymentResponseHandler.js.map +1 -1
  27. package/lib/module/styles/screens/savedAddressScreenStyles.js +2 -2
  28. package/lib/module/styles/screens/upiScreenStyles.js +64 -0
  29. package/lib/module/styles/screens/upiScreenStyles.js.map +1 -1
  30. package/lib/module/utils/listAndObjectUtils.js +56 -6
  31. package/lib/module/utils/listAndObjectUtils.js.map +1 -1
  32. package/lib/module/utils/stringUtils.js +5 -0
  33. package/lib/module/utils/stringUtils.js.map +1 -1
  34. package/lib/typescript/src/components/sessionExpire.d.ts.map +1 -1
  35. package/lib/typescript/src/interface.d.ts +7 -1
  36. package/lib/typescript/src/interface.d.ts.map +1 -1
  37. package/lib/typescript/src/postRequest/callUIAnalytics.d.ts.map +1 -1
  38. package/lib/typescript/src/postRequest/cardPostRequest.d.ts.map +1 -1
  39. package/lib/typescript/src/postRequest/emiPostRequest.d.ts.map +1 -1
  40. package/lib/typescript/src/postRequest/methodsPostRequest.d.ts.map +1 -1
  41. package/lib/typescript/src/postRequest/upiPostRequest.d.ts.map +1 -1
  42. package/lib/typescript/src/screens/mainScreen.d.ts.map +1 -1
  43. package/lib/typescript/src/screens/upiScreen.d.ts +5 -0
  44. package/lib/typescript/src/screens/upiScreen.d.ts.map +1 -1
  45. package/lib/typescript/src/sharedContext/checkoutDetailsHandler.d.ts.map +1 -1
  46. package/lib/typescript/src/sharedContext/handlePaymentResponseHandler.d.ts +1 -2
  47. package/lib/typescript/src/sharedContext/handlePaymentResponseHandler.d.ts.map +1 -1
  48. package/lib/typescript/src/styles/screens/upiScreenStyles.d.ts +64 -0
  49. package/lib/typescript/src/styles/screens/upiScreenStyles.d.ts.map +1 -1
  50. package/lib/typescript/src/utils/listAndObjectUtils.d.ts +34 -2
  51. package/lib/typescript/src/utils/listAndObjectUtils.d.ts.map +1 -1
  52. package/lib/typescript/src/utils/stringUtils.d.ts +1 -0
  53. package/lib/typescript/src/utils/stringUtils.d.ts.map +1 -1
  54. package/package.json +4 -3
  55. package/src/components/sessionExpire.tsx +0 -3
  56. package/src/interface.ts +10 -2
  57. package/src/postRequest/callUIAnalytics.ts +3 -20
  58. package/src/postRequest/cardPostRequest.ts +6 -51
  59. package/src/postRequest/emiPostRequest.ts +6 -52
  60. package/src/postRequest/methodsPostRequest.tsx +6 -52
  61. package/src/postRequest/upiPostRequest.tsx +6 -52
  62. package/src/screens/mainScreen.tsx +72 -0
  63. package/src/screens/upiScreen.tsx +168 -40
  64. package/src/scripts/checkPeerDeps.js +23 -0
  65. package/src/sharedContext/checkoutDetailsHandler.ts +1 -0
  66. package/src/sharedContext/handlePaymentResponseHandler.ts +8 -1
  67. package/src/styles/screens/savedAddressScreenStyles.ts +2 -2
  68. package/src/styles/screens/upiScreenStyles.ts +64 -0
  69. package/src/utils/listAndObjectUtils.ts +58 -7
  70. package/src/utils/stringUtils.ts +7 -1
@@ -5,6 +5,7 @@ import {
5
5
  Image,
6
6
  Animated,
7
7
  ImageBackground,
8
+ TouchableOpacity,
8
9
  } from 'react-native';
9
10
  import React, { useEffect, useState } from 'react';
10
11
  import { TextInput } from 'react-native-paper';
@@ -13,12 +14,19 @@ import type { PaymentClass } from '../interface';
13
14
  import PaymentSelectorView from '../components/paymentSelector';
14
15
  import { getInstalledUpiApps } from '../components/getInstalledUPI';
15
16
  import styles from '../styles/screens/upiScreenStyles';
17
+ import DeviceInfo from 'react-native-device-info';
18
+ import { formattedTime } from '../utils/stringUtils';
16
19
 
17
20
  interface UpiScreenProps {
18
21
  handleUpiPayment: (selectedIntent: string) => void;
19
22
  handleCollectPayment: (item: string, id: string, type: string) => void;
20
23
  savedUpiArray: PaymentClass[];
21
24
  onClickRadio: (instrumentValue: string) => void;
25
+ qrImage : string | null,
26
+ qrIsExpired : boolean,
27
+ timeRemaining : number,
28
+ handleQRPayment : () => void,
29
+ stopTimer : () => void
22
30
  }
23
31
 
24
32
  const UpiScreen: React.FC<UpiScreenProps> = ({
@@ -26,6 +34,11 @@ const UpiScreen: React.FC<UpiScreenProps> = ({
26
34
  handleCollectPayment,
27
35
  savedUpiArray,
28
36
  onClickRadio,
37
+ qrImage,
38
+ qrIsExpired,
39
+ timeRemaining,
40
+ handleQRPayment,
41
+ stopTimer
29
42
  }) => {
30
43
  const [upiCollectError, setUpiCollectError] = useState(false);
31
44
  const [upiCollectValid, setUpiCollectValid] = useState(false);
@@ -36,9 +49,14 @@ const UpiScreen: React.FC<UpiScreenProps> = ({
36
49
  const [isPaytmInstalled, setIsPaytmInstalled] = useState(false);
37
50
  const { checkoutDetails } = checkoutDetailsHandler;
38
51
  const [upiCollectVisible, setUpiCollectVisible] = useState(false);
52
+ const [upiQRVisible, setUpiQRVisible] = useState(false)
39
53
  const [selectedIntent, setSelectedIntent] = useState<string | null>(null);
40
- const isUpiCollectVisible = checkoutDetails.isUpiCollectMethodEnabled
41
- const isUpiIntentVisible = checkoutDetails.isUpiIntentMethodEnabled
54
+ const {
55
+ isUpiCollectMethodEnabled: isUpiCollectVisible,
56
+ isUpiIntentMethodEnabled: isUpiIntentVisible,
57
+ isUpiQRMethodEnabled: isUpiQRVisible,
58
+ } = checkoutDetails;
59
+ const isTablet = DeviceInfo.isTablet()
42
60
 
43
61
  useEffect(() => {
44
62
  const checkUpiApps = async () => {
@@ -55,12 +73,22 @@ const UpiScreen: React.FC<UpiScreenProps> = ({
55
73
  checkUpiApps();
56
74
  }, []);
57
75
 
58
- const handleUpiChevronClick = () => {
76
+ const handleUpiCollectChevronClick = () => {
59
77
  setSelectedIntent(null);
60
78
  setUpiCollectVisible(!upiCollectVisible);
79
+ setUpiQRVisible(false)
61
80
  setDefaultStateOfSavedUpiArray();
81
+ stopTimer()
62
82
  };
63
83
 
84
+ const handleUpiQRChevronClick = () => {
85
+ setSelectedIntent(null);
86
+ setUpiCollectVisible(false);
87
+ handleQRPayment()
88
+ setUpiQRVisible(!upiQRVisible)
89
+ setDefaultStateOfSavedUpiArray();
90
+ }
91
+
64
92
  const handleTextChange = (text: string) => {
65
93
  setUpiCollectTextInput(text);
66
94
  setUpiCollectError(false);
@@ -85,6 +113,7 @@ const UpiScreen: React.FC<UpiScreenProps> = ({
85
113
  ) {
86
114
  onClickRadio('');
87
115
  }
116
+ stopTimer()
88
117
  };
89
118
 
90
119
  return (
@@ -273,25 +302,20 @@ const UpiScreen: React.FC<UpiScreenProps> = ({
273
302
  >
274
303
  <Pressable
275
304
  style={styles.pressableCollectContainer}
276
- onPress={() => handleUpiChevronClick()}
305
+ onPress={() => handleUpiCollectChevronClick()}
277
306
  >
278
307
  {/* Icon and Text Wrapper */}
279
308
  <View style={{ flexDirection: 'row', alignItems: 'center' }}>
280
309
  <Image
281
310
  source={require('../../assets/images/add_icon.png')}
282
- style={{
283
- height: 14,
284
- width: 14,
311
+ style={[styles.imageStyle,{
285
312
  tintColor: checkoutDetails.brandColor,
286
- }}
313
+ }]}
287
314
  />
288
315
  <Text
289
- style={{
290
- fontSize: 14,
316
+ style={[styles.subHeaderText,{
291
317
  color: checkoutDetails.brandColor,
292
- paddingStart: 10,
293
- fontFamily: 'Poppins-SemiBold',
294
- }}
318
+ }]}
295
319
  >
296
320
  Add new UPI Id
297
321
  </Text>
@@ -299,16 +323,13 @@ const UpiScreen: React.FC<UpiScreenProps> = ({
299
323
 
300
324
  <Animated.Image
301
325
  source={require('../../assets/images/chervon-down.png')}
302
- style={{
303
- alignSelf: 'center',
304
- height: 6,
305
- width: 14,
326
+ style={[styles.animatedIcon,{
306
327
  transform: [
307
328
  {
308
329
  rotate: upiCollectVisible ? '180deg' : '0deg',
309
330
  },
310
331
  ],
311
- }}
332
+ }]}
312
333
  />
313
334
  </Pressable>
314
335
  </ImageBackground>
@@ -316,35 +337,25 @@ const UpiScreen: React.FC<UpiScreenProps> = ({
316
337
  <View style={{ paddingBottom: isUpiCollectVisible ? 16 : 0 }}>
317
338
  {isUpiIntentVisible && (
318
339
  <View
319
- style={{
320
- flexDirection: 'row',
321
- height: 1,
322
- backgroundColor: '#F1F1F1',
323
- marginTop: 20,
324
- }}
340
+ style={styles.subContainerDivider}
325
341
  />
326
342
  )}
327
343
  <Pressable
328
344
  style={styles.pressableCollectContainer}
329
- onPress={() => handleUpiChevronClick()}
345
+ onPress={() => handleUpiCollectChevronClick()}
330
346
  >
331
347
  {/* Icon and Text Wrapper */}
332
348
  <View style={{ flexDirection: 'row', alignItems: 'center' }}>
333
349
  <Image
334
350
  source={require('../../assets/images/add_icon.png')}
335
- style={{
336
- height: 14,
337
- width: 14,
351
+ style={[styles.imageStyle,{
338
352
  tintColor: checkoutDetails.brandColor,
339
- }}
353
+ }]}
340
354
  />
341
355
  <Text
342
- style={{
343
- fontSize: 14,
344
- color: checkoutDetails.brandColor,
345
- paddingStart: 10,
346
- fontFamily: 'Poppins-SemiBold',
347
- }}
356
+ style={[styles.subHeaderText,{
357
+ color: checkoutDetails.brandColor
358
+ }]}
348
359
  >
349
360
  Add new UPI Id
350
361
  </Text>
@@ -352,16 +363,13 @@ const UpiScreen: React.FC<UpiScreenProps> = ({
352
363
 
353
364
  <Animated.Image
354
365
  source={require('../../assets/images/chervon-down.png')}
355
- style={{
356
- alignSelf: 'center',
357
- height: 6,
358
- width: 14,
366
+ style={[styles.animatedIcon,{
359
367
  transform: [
360
368
  {
361
369
  rotate: upiCollectVisible ? '180deg' : '0deg',
362
370
  },
363
371
  ],
364
- }}
372
+ }]}
365
373
  />
366
374
  </Pressable>
367
375
  </View>
@@ -457,6 +465,126 @@ const UpiScreen: React.FC<UpiScreenProps> = ({
457
465
  )}
458
466
  </View>
459
467
  )}
468
+
469
+ {(isUpiQRVisible && isTablet) && (
470
+ <View>
471
+ {upiQRVisible ? (
472
+ <ImageBackground
473
+ source={require('../../assets/images/add_upi_id_background.png')} // Replace with your background image
474
+ resizeMode="cover"
475
+ style={{
476
+ paddingBottom: 34,
477
+ marginTop: isUpiIntentVisible || isUpiCollectVisible ? 24 : 0,
478
+ }}
479
+ >
480
+ <Pressable
481
+ style={styles.pressableCollectContainer}
482
+ onPress={() => handleUpiQRChevronClick()}
483
+ >
484
+ {/* Icon and Text Wrapper */}
485
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
486
+ <Image
487
+ source={require('../../assets/images/ic_qr.png')}
488
+ style={[styles.imageStyle,{
489
+ tintColor: checkoutDetails.brandColor,
490
+ }]}
491
+ />
492
+ <Text
493
+ style={[styles.subHeaderText,{
494
+ color: checkoutDetails.brandColor
495
+ }]}
496
+ >
497
+ Pay Using QR
498
+ </Text>
499
+ </View>
500
+
501
+ <Animated.Image
502
+ source={require('../../assets/images/chervon-down.png')}
503
+ style={[styles.animatedIcon,{
504
+ transform: [
505
+ {
506
+ rotate: upiQRVisible ? '180deg' : '0deg',
507
+ },
508
+ ],
509
+ }]}
510
+ />
511
+ </Pressable>
512
+ </ImageBackground>
513
+ ) : (
514
+ <View style={{ paddingBottom: isUpiCollectVisible || isUpiIntentVisible ? 16 : 0 }}>
515
+ {(isUpiIntentVisible || isUpiCollectVisible) && (
516
+ <View
517
+ style={styles.subContainerDivider}
518
+ />
519
+ )}
520
+ <Pressable
521
+ style={styles.pressableCollectContainer}
522
+ onPress={() => handleUpiQRChevronClick()}
523
+ >
524
+ {/* Icon and Text Wrapper */}
525
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
526
+ <Image
527
+ source={require('../../assets/images/ic_qr.png')}
528
+ style={[styles.imageStyle,{
529
+ tintColor: checkoutDetails.brandColor,
530
+ }]}
531
+ />
532
+ <Text
533
+ style={[styles.subHeaderText,{
534
+ color: checkoutDetails.brandColor
535
+ }]}
536
+ >
537
+ Pay Using QR
538
+ </Text>
539
+ </View>
540
+
541
+ <Animated.Image
542
+ source={require('../../assets/images/chervon-down.png')}
543
+ style={[styles.animatedIcon,{
544
+ transform: [
545
+ {
546
+ rotate: upiQRVisible ? '180deg' : '0deg',
547
+ },
548
+ ],
549
+ }]}
550
+ />
551
+ </Pressable>
552
+ </View>
553
+ )}
554
+ </View>
555
+ )}
556
+
557
+ {upiQRVisible && (
558
+ <View style={{ paddingBottom: isUpiCollectVisible || isUpiIntentVisible ? 16 : 0 }}>
559
+ {qrImage && (
560
+ <View style={styles.qrContainer}>
561
+ <Image
562
+ source={{ uri: `data:image/png;base64,${qrImage}` }}
563
+ style={[
564
+ styles.qrImage,
565
+ { opacity: qrIsExpired ? 0.2 : 1 },
566
+ ]}
567
+ />
568
+
569
+ {qrIsExpired && (
570
+ <TouchableOpacity style={styles.retryButton} onPress={handleQRPayment}>
571
+ <Text style={[styles.retryText, { color: checkoutDetails.brandColor }]}>
572
+ ↻ Retry
573
+ </Text>
574
+ </TouchableOpacity>
575
+ )}
576
+ </View>
577
+ )}
578
+
579
+ <View style={styles.textContainer}>
580
+ <Text style={styles.label}>Scan & Pay with UPI Application</Text>
581
+ <Text style={styles.label}>QR code will expire in</Text>
582
+ <Text style={[styles.timer, { color: checkoutDetails.brandColor }]}>
583
+ {formattedTime(timeRemaining)}
584
+ </Text>
585
+ </View>
586
+ </View>
587
+ )}
460
588
  </View>
461
589
  </View>
462
590
  );
@@ -0,0 +1,23 @@
1
+ // src/scripts/checkPeerDeps.js
2
+ const isInNodeModules = __dirname.includes("node_modules");
3
+
4
+ // Skip when running inside SDK repo itself
5
+ if (!isInNodeModules) {
6
+ console.log("🛑 Skipping peerDeps check in SDK development mode");
7
+ process.exit(0);
8
+ }
9
+
10
+ const { execSync } = require("child_process");
11
+ const pkg = require("../../package.json");
12
+
13
+ const peers = pkg.peerDependencies || {};
14
+
15
+ Object.entries(peers).forEach(([dep, version]) => {
16
+ try {
17
+ require.resolve(dep);
18
+ console.log(`✅ ${dep} already installed`);
19
+ } catch {
20
+ console.log(`📦 Installing missing peer dependency: ${dep}@${version}`);
21
+ execSync(`npm install ${dep}@${version}`, { stdio: "inherit" });
22
+ }
23
+ });
@@ -28,6 +28,7 @@ export let checkoutDetailsHandler: CheckoutDetailsHandler = {
28
28
  isDOBEnabled: false,
29
29
  isDOBEditable: false,
30
30
  isUpiIntentMethodEnabled : false,
31
+ isUpiQRMethodEnabled:false,
31
32
  isUpiCollectMethodEnabled : false,
32
33
  isCardMethodEnabled : false,
33
34
  isWalletMethodEnabled : false,
@@ -17,7 +17,8 @@ export function handlePaymentResponse({
17
17
  onShowSuccessModal,
18
18
  onShowSessionExpiredModal,
19
19
  onNavigateToTimer,
20
- onOpenUpiIntent, // 👈 new
20
+ onOpenQr,
21
+ onOpenUpiIntent,
21
22
  setLoading
22
23
  }: HandlePaymentOptions) {
23
24
  switch(response.apiStatus) {
@@ -53,6 +54,12 @@ export function handlePaymentResponse({
53
54
  onOpenUpiIntent(action.url); // 👈 launch UPI intent
54
55
  }
55
56
  break;
57
+
58
+ case 'qrCode' :
59
+ if(action.content && onOpenQr) {
60
+ onOpenQr(action.content)
61
+ }
62
+ break
56
63
 
57
64
  default:
58
65
  break;
@@ -37,8 +37,8 @@ const styles = StyleSheet.create({
37
37
  alignItems:'center'
38
38
  },
39
39
  imageStyle : {
40
- height: 10,
41
- width: 10,
40
+ height: 20,
41
+ width: 20,
42
42
  marginStart: 12
43
43
  },
44
44
  insideContainerClickableText : {
@@ -104,6 +104,70 @@ const styles = StyleSheet.create({
104
104
  labelText : {
105
105
  fontSize: 14,
106
106
  fontFamily: 'Poppins-Regular',
107
+ },
108
+ container: {
109
+ flexDirection: "row",
110
+ alignItems: "center",
111
+ paddingHorizontal: 12,
112
+ paddingTop: 12,
113
+ },
114
+ qrContainer: {
115
+ position: "relative",
116
+ width: 300,
117
+ height: 300,
118
+ marginRight: 12,
119
+ },
120
+ qrImage: {
121
+ width: 300,
122
+ height: 300,
123
+ resizeMode: "cover",
124
+ borderRadius: 8,
125
+ },
126
+ retryButton: {
127
+ position: "absolute",
128
+ top: "40%",
129
+ left: "30%",
130
+ backgroundColor: "#fff",
131
+ paddingHorizontal: 24,
132
+ paddingVertical: 12,
133
+ borderRadius: 12,
134
+ },
135
+ retryText: {
136
+ fontSize: 20,
137
+ fontWeight: "600",
138
+ },
139
+ textContainer: {
140
+ flexDirection: "column",
141
+ justifyContent: "flex-start",
142
+ },
143
+ label: {
144
+ fontSize: 12,
145
+ fontWeight: "500",
146
+ color: "#2D2B32",
147
+ },
148
+ timer: {
149
+ fontSize: 20,
150
+ fontWeight: "600",
151
+ },
152
+ imageStyle : {
153
+ height: 14,
154
+ width: 14,
155
+ },
156
+ subHeaderText : {
157
+ fontSize: 14,
158
+ paddingStart: 10,
159
+ fontFamily: 'Poppins-SemiBold',
160
+ },
161
+ animatedIcon : {
162
+ alignSelf: 'center',
163
+ height: 6,
164
+ width: 14,
165
+ },
166
+ subContainerDivider : {
167
+ flexDirection: 'row',
168
+ height: 1,
169
+ backgroundColor: '#F1F1F1',
170
+ marginTop: 20,
107
171
  }
108
172
  })
109
173
 
@@ -1,5 +1,7 @@
1
- import type { PaymentMethod, PaymentClass } from '../interface';
2
- import { Platform } from 'react-native';
1
+ import DeviceInfo from 'react-native-device-info';
2
+ import type { PaymentMethod, PaymentClass, DeliveryAddress } from '../interface';
3
+ import { Dimensions } from 'react-native';
4
+ import { userDataHandler } from '../sharedContext/userdataHandler';
3
5
 
4
6
  export function transformAndFilterList(
5
7
  data: PaymentMethod[],
@@ -24,10 +26,59 @@ export function transformAndFilterList(
24
26
 
25
27
  export function getDeviceDetails() {
26
28
  return {
27
- browser: Platform.OS,
28
- platformVersion: Platform.Version.toString(),
29
- deviceType: Platform.OS === 'ios' || Platform.OS === 'android' ? 'Phone' : 'Web',
30
- deviceName: Platform.OS === 'ios' ? 'iOS Device' : 'Android Device',
31
- deviceBrandName: Platform.OS === 'ios' ? 'Apple' : 'Android'
29
+ browser: DeviceInfo.getBaseOs(),
30
+ platformVersion: DeviceInfo.getVersion(),
31
+ deviceType: DeviceInfo.getDeviceType(),
32
+ deviceName: DeviceInfo.getDeviceName(),
33
+ deviceBrandName: DeviceInfo.getBrand()
32
34
  };
35
+ }
36
+
37
+ export function getBrowserData() {
38
+ const { height, width } = Dimensions.get("window");
39
+ const bundleId = DeviceInfo.getBundleId()
40
+ return {
41
+ screenHeight: height,
42
+ screenWidth: width,
43
+ acceptHeader: 'application/json',
44
+ userAgentHeader: 'Expo App',
45
+ browserLanguage: 'en_US',
46
+ ipAddress: 'null',
47
+ colorDepth: 24,
48
+ javaEnabled: true,
49
+ timeZoneOffSet: new Date().getTimezoneOffset(),
50
+ packageId: bundleId || 'com.boxpay.checkout.sdk',
51
+ }
52
+ }
53
+
54
+ export function getShopperDetails() {
55
+ const { userData } = userDataHandler;
56
+ const isDeliveryAddressEmpty = (address: DeliveryAddress): boolean => {
57
+ return Object.values(address).every(
58
+ (value) => value === null || value === undefined || value === ''
59
+ );
60
+ };
61
+ const deliveryAddress = {
62
+ address1: userData.address1,
63
+ address2: userData.address2,
64
+ city: userData.city,
65
+ state: userData.state,
66
+ countryCode: userData.country,
67
+ postalCode: userData.pincode,
68
+ labelType: userData.labelType,
69
+ labelName: userData.labelName,
70
+ };
71
+ return {
72
+ email: userData.email,
73
+ firstName: userData.firstName,
74
+ gender: null,
75
+ lastName: userData.lastName,
76
+ phoneNumber: userData.phone,
77
+ uniqueReference: userData.uniqueId,
78
+ dateOfBirth: userData.dob,
79
+ panNumber: userData.pan,
80
+ deliveryAddress: isDeliveryAddressEmpty(deliveryAddress)
81
+ ? null
82
+ : deliveryAddress,
83
+ }
33
84
  }
@@ -55,4 +55,10 @@ export function extractNames(fullName: string): {
55
55
  const lastName = components.slice(1).join(' ') || '';
56
56
 
57
57
  return { firstName, lastName };
58
- }
58
+ }
59
+
60
+ export const formattedTime = (timeRemaining: number): string => {
61
+ const minutes = Math.floor(timeRemaining / 60);
62
+ const seconds = timeRemaining % 60;
63
+ return `${minutes}:${seconds.toString().padStart(2, "0")}`;
64
+ };