@kiosinc/commons-rn 0.1.94 → 0.1.96

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 (37) hide show
  1. package/lib/commonjs/assets/icons/apple_pay.svg +8 -0
  2. package/lib/commonjs/helpers/notifications/NotificationHandler.js +2 -1
  3. package/lib/commonjs/helpers/notifications/NotificationHandler.js.map +1 -1
  4. package/lib/commonjs/localization/translations/en.json +6 -1
  5. package/lib/commonjs/screens/SavedCards/CardRow.js +35 -21
  6. package/lib/commonjs/screens/SavedCards/CardRow.js.map +1 -1
  7. package/lib/commonjs/screens/SavedCards/SavedCards.js +61 -44
  8. package/lib/commonjs/screens/SavedCards/SavedCards.js.map +1 -1
  9. package/lib/commonjs/screens/SavedCards/useApplePay.js +85 -0
  10. package/lib/commonjs/screens/SavedCards/useApplePay.js.map +1 -0
  11. package/lib/module/assets/icons/apple_pay.svg +8 -0
  12. package/lib/module/helpers/notifications/NotificationHandler.js +2 -1
  13. package/lib/module/helpers/notifications/NotificationHandler.js.map +1 -1
  14. package/lib/module/localization/translations/en.json +6 -1
  15. package/lib/module/screens/SavedCards/CardRow.js +35 -21
  16. package/lib/module/screens/SavedCards/CardRow.js.map +1 -1
  17. package/lib/module/screens/SavedCards/SavedCards.js +62 -45
  18. package/lib/module/screens/SavedCards/SavedCards.js.map +1 -1
  19. package/lib/module/screens/SavedCards/useApplePay.js +77 -0
  20. package/lib/module/screens/SavedCards/useApplePay.js.map +1 -0
  21. package/lib/typescript/src/helpers/notifications/NotificationHandler.d.ts +1 -1
  22. package/lib/typescript/src/helpers/notifications/NotificationHandler.d.ts.map +1 -1
  23. package/lib/typescript/src/localization/index.d.ts +5 -0
  24. package/lib/typescript/src/localization/index.d.ts.map +1 -1
  25. package/lib/typescript/src/screens/SavedCards/CardRow.d.ts +5 -4
  26. package/lib/typescript/src/screens/SavedCards/CardRow.d.ts.map +1 -1
  27. package/lib/typescript/src/screens/SavedCards/SavedCards.d.ts +3 -2
  28. package/lib/typescript/src/screens/SavedCards/SavedCards.d.ts.map +1 -1
  29. package/lib/typescript/src/screens/SavedCards/useApplePay.d.ts +6 -0
  30. package/lib/typescript/src/screens/SavedCards/useApplePay.d.ts.map +1 -0
  31. package/package.json +1 -1
  32. package/src/assets/icons/apple_pay.svg +8 -0
  33. package/src/helpers/notifications/NotificationHandler.ts +4 -1
  34. package/src/localization/translations/en.json +6 -1
  35. package/src/screens/SavedCards/CardRow.tsx +52 -31
  36. package/src/screens/SavedCards/SavedCards.tsx +71 -61
  37. package/src/screens/SavedCards/useApplePay.tsx +88 -0
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Alert, Card, IconButton, Text, View } from '../../components';
3
+ import ApplePayIcon from '../../assets/icons/apple_pay.svg';
3
4
  import { RadioButton, useTheme } from 'react-native-paper';
4
5
  import { useTranslation } from 'react-i18next';
5
6
  import { getCardName } from '../../utils/checkout';
@@ -12,48 +13,55 @@ export const CardRow = ({
12
13
  isSelectable,
13
14
  isSelected,
14
15
  isDisabled,
16
+ isApplePay = false,
15
17
  }: {
16
- card: CustomerCardPayment;
17
- onDelete: (id: string) => void;
18
- onSelect: (linkedObjectId: string) => void;
18
+ card?: CustomerCardPayment;
19
+ onDelete?: (id: string) => void;
20
+ onSelect?: (linkedObjectId: string) => void;
19
21
  isSelectable: boolean;
20
22
  isSelected: boolean;
21
23
  isDisabled: boolean;
24
+ isApplePay?: boolean;
22
25
  }) => {
23
- const name = getCardName(card);
24
26
  const { t } = useTranslation();
25
27
  const theme = useTheme();
26
28
 
27
29
  const onDeleteCard = () => {
28
- Alert.show({
29
- title: t('commons.confirmCardDelete'),
30
- description: t('commons.cardDeleteDesc', {
31
- card: card.cardDetails.last4,
32
- }),
33
- buttons: [
34
- {
35
- label: t('commons.delete'),
36
- textColor: theme.colors.error,
37
- onPress: () => onDelete(card.id),
38
- },
39
- {
40
- label: t('commons.cancel'),
41
- textColor: theme.colors.onSurface,
42
- },
43
- ],
44
- });
30
+ if (card) {
31
+ Alert.show({
32
+ title: t('commons.confirmCardDelete'),
33
+ description: t('commons.cardDeleteDesc', {
34
+ card: card.cardDetails.last4,
35
+ }),
36
+ buttons: [
37
+ {
38
+ label: t('commons.delete'),
39
+ textColor: theme.colors.error,
40
+ onPress: () => onDelete?.(card.id),
41
+ },
42
+ {
43
+ label: t('commons.cancel'),
44
+ textColor: theme.colors.onSurface,
45
+ },
46
+ ],
47
+ });
48
+ }
45
49
  };
46
50
 
51
+ const cardName = isApplePay ? 'Apple Pay' : getCardName(card!);
52
+
47
53
  return (
48
54
  <Card
49
55
  disabled={isDisabled}
50
56
  mode="outlined"
51
57
  borderRadius="4"
58
+ height={50}
59
+ justifyContent="center"
52
60
  mb="8"
53
61
  {...(isSelectable
54
62
  ? {
55
63
  onPress: () => {
56
- onSelect(card.linkedObjectId);
64
+ onSelect?.(card?.linkedObjectId ?? '');
57
65
  },
58
66
  }
59
67
  : {})}
@@ -66,10 +74,10 @@ export const CardRow = ({
66
74
  >
67
75
  {isSelectable && (
68
76
  <RadioButton.Android
69
- value={card.linkedObjectId}
77
+ value={card?.linkedObjectId || 'apple-pay'}
70
78
  status={isSelected ? 'checked' : 'unchecked'}
71
79
  onPress={() => {
72
- onSelect(card.linkedObjectId);
80
+ onSelect?.(card?.linkedObjectId ?? '');
73
81
  }}
74
82
  />
75
83
  )}
@@ -80,13 +88,26 @@ export const CardRow = ({
80
88
  alignItems="center"
81
89
  justifyContent="space-between"
82
90
  >
83
- <Text variant="bodyMedium">{name}</Text>
84
- <IconButton
85
- disabled={isDisabled}
86
- icon="trash-can-outline"
87
- bg="background"
88
- onPress={onDeleteCard}
89
- />
91
+ <View flexDirection="row" alignItems="center">
92
+ {isApplePay && (
93
+ <ApplePayIcon
94
+ height={24}
95
+ width={24}
96
+ color={theme.colors.onBackground}
97
+ />
98
+ )}
99
+ <Text ps={isApplePay ? '8' : '0'} variant="bodyMedium">
100
+ {cardName}
101
+ </Text>
102
+ </View>
103
+ {!isApplePay && (
104
+ <IconButton
105
+ disabled={isDisabled}
106
+ icon="trash-can-outline"
107
+ bg="background"
108
+ onPress={onDeleteCard}
109
+ />
110
+ )}
90
111
  </View>
91
112
  </View>
92
113
  </Card>
@@ -10,19 +10,22 @@ import {
10
10
  import { ActivityIndicator, useTheme } from 'react-native-paper';
11
11
  import uuid from 'react-native-uuid';
12
12
  import { useTranslation } from 'react-i18next';
13
- import { FlatList } from 'react-native';
13
+ import { FlatList, Platform } from 'react-native';
14
14
  import { onStartCardEntry } from '../../utils/square';
15
15
  import { useCustomerQueries } from '../../hooks/useCustomer';
16
16
  import { useCheckCustomer } from '../../hooks/useCheckCustomer';
17
17
  import { CardRow } from './CardRow';
18
18
  import { CustomerCardPayment } from '@kiosinc/commons-types';
19
+ import { useApplePay } from './useApplePay';
19
20
 
20
21
  export const SavedCards = ({
21
22
  onCheckout,
23
+ showApplePay,
22
24
  total,
23
25
  }: {
24
- onCheckout?: (selectedCard: CustomerCardPayment) => void;
26
+ onCheckout?: (selectedCard: CustomerCardPayment | 'ApplePay') => void;
25
27
  total?: string;
28
+ showApplePay?: boolean;
26
29
  }) => {
27
30
  const { t } = useTranslation();
28
31
  const theme = useTheme();
@@ -35,14 +38,15 @@ export const SavedCards = ({
35
38
  } = useCustomerQueries();
36
39
  const { saveCard, saveCardLoading } = useSaveCustomerCard();
37
40
  const { disableCard, disableCardLoading } = useDisableCard();
38
- const [selectedPaymentId, onSelectPaymentId] = useState('');
41
+ const [selectedPaymentId, setSelectedPaymentId] = useState('');
39
42
  const { updateCustomer, updateCustomerLoading } = useUpdateCustomer();
43
+ const { hasApplePay } = useApplePay();
40
44
 
41
45
  useEffect(() => {
42
46
  if (customer?.paymentId) {
43
- onSelectPaymentId(customer.paymentId);
47
+ setSelectedPaymentId(customer.paymentId);
44
48
  }
45
- }, [customer?.paymentId, onSelectPaymentId]);
49
+ }, [customer?.paymentId]);
46
50
 
47
51
  const {
48
52
  data: cards = [],
@@ -51,7 +55,7 @@ export const SavedCards = ({
51
55
  refetch,
52
56
  } = useFetchSavedCards();
53
57
 
54
- const onAddPress = () => {
58
+ const handleAddCard = () => {
55
59
  onStartCardEntry(
56
60
  (cardDetails) => {
57
61
  saveCard({
@@ -59,44 +63,50 @@ export const SavedCards = ({
59
63
  idempotentKey: uuid.v4() as string,
60
64
  });
61
65
  },
62
- () => {}
66
+ () => {
67
+ Alert.show({
68
+ title: t('commons.error'),
69
+ description: t('commons.cardSaveError'),
70
+ });
71
+ }
63
72
  );
64
73
  };
65
74
 
66
- const label =
67
- cards.length > 0 ? t('commons.payAndPlaceOrder') : t('commons.payByCard');
75
+ const handleUseCard = () => {
76
+ if (!cards.length && !(hasApplePay && Platform.OS === 'ios')) {
77
+ Alert.show({ title: t('commons.noPaymentMethods') });
78
+ return;
79
+ }
68
80
 
69
- if (isLoading) {
70
- return <ActivityIndicator />;
71
- }
81
+ if (!selectedPaymentId) {
82
+ Alert.show({ title: t('commons.selectPaymentMethod') });
83
+ return;
84
+ }
72
85
 
73
- const onUseCard = () => {
74
- if (cards.length === 0) {
75
- // If no cards then initiate add card flow.
76
- onStartCardEntry(
77
- (cardDetails) => {
78
- saveCard({
79
- sourceId: cardDetails.nonce,
80
- idempotentKey: uuid.v4() as string,
81
- });
82
- },
83
- () => {}
84
- );
85
- } else if (!customer.paymentId) {
86
- Alert.show({ title: 'Select a card' });
86
+ if (selectedPaymentId === 'ApplePay') {
87
+ onCheckout?.('ApplePay');
87
88
  } else {
88
- // Make payment
89
89
  const selectedCard = cards.find(
90
- (card: any) => card.linkedObjectId === customer.paymentId
90
+ (card: any) => card.linkedObjectId === selectedPaymentId
91
91
  );
92
+
92
93
  if (selectedCard) {
93
94
  onCheckout?.(selectedCard);
94
95
  } else {
95
- Alert.show({ title: 'Select a card' });
96
+ Alert.show({ title: t('commons.selectValidPaymentMethod') });
96
97
  }
97
98
  }
98
99
  };
99
100
 
101
+ const paymentMethods =
102
+ hasApplePay && showApplePay && Platform.OS === 'ios'
103
+ ? [{ linkedObjectId: 'ApplePay', isApplePay: true }, ...cards]
104
+ : cards;
105
+
106
+ if (isLoading) {
107
+ return <ActivityIndicator />;
108
+ }
109
+
100
110
  return (
101
111
  <View flex={1}>
102
112
  <View px="16" bg="background" flex={1}>
@@ -106,49 +116,51 @@ export const SavedCards = ({
106
116
  <FlatList
107
117
  extraData={{ selectedPaymentId, disableCardLoading }}
108
118
  refreshControl={
109
- <RefreshControl refreshing={false} onRefresh={refetch} />
119
+ <RefreshControl refreshing={isFetching} onRefresh={refetch} />
110
120
  }
111
- data={cards}
121
+ data={paymentMethods}
112
122
  showsVerticalScrollIndicator={false}
113
- renderItem={({ item: card, index }) => (
123
+ renderItem={({ item: card }) => (
114
124
  <CardRow
115
125
  isDisabled={disableCardLoading}
116
126
  isSelected={selectedPaymentId === card.linkedObjectId}
117
127
  isSelectable={!!onCheckout}
118
- key={index}
128
+ isApplePay={card.isApplePay}
119
129
  card={card}
120
130
  onDelete={(id: string) => {
121
- disableCard(id);
131
+ if (!card.isApplePay) {
132
+ disableCard(id);
133
+ }
122
134
  }}
123
135
  onSelect={(linkedObjectId: string) => {
124
- onSelectPaymentId(linkedObjectId);
125
- updateCustomer({
126
- paymentId: linkedObjectId,
127
- addressId: customer.addressId,
128
- squareCustomerId: customer.squareCustomerId,
129
- });
136
+ setSelectedPaymentId(linkedObjectId);
137
+ if (!card.isApplePay) {
138
+ updateCustomer({
139
+ paymentId: linkedObjectId,
140
+ addressId: customer.addressId,
141
+ squareCustomerId: customer.squareCustomerId,
142
+ });
143
+ }
130
144
  }}
131
145
  />
132
146
  )}
133
147
  ListHeaderComponent={
134
- <>
135
- {cards.length > 0 ? (
136
- <Text mb="8" variant="titleMedium">
137
- {t('commons.paymentMethods')}
138
- </Text>
139
- ) : (
140
- <Text variant="bodyLarge" color="outline">
141
- {t('commons.noSavedCards')}
142
- </Text>
143
- )}
144
- </>
148
+ paymentMethods.length > 0 ? (
149
+ <Text mb="8" variant="titleMedium">
150
+ {t('commons.paymentMethods')}
151
+ </Text>
152
+ ) : (
153
+ <Text variant="bodyLarge" color="outline">
154
+ {t('commons.noSavedCards')}
155
+ </Text>
156
+ )
145
157
  }
146
158
  ListFooterComponent={
147
159
  <View alignItems="flex-start">
148
160
  <Button
149
- pt={'16'}
161
+ pt="16"
150
162
  loading={saveCardLoading}
151
- onPress={onAddPress}
163
+ onPress={handleAddCard}
152
164
  icon="plus"
153
165
  mode="text"
154
166
  >
@@ -178,17 +190,15 @@ export const SavedCards = ({
178
190
  <Button
179
191
  mode="contained"
180
192
  height={40}
181
- width={'auto'}
193
+ width="auto"
182
194
  alignSelf="center"
183
195
  bg="primary"
184
- labelStyle={[
185
- {
186
- color: theme.colors.background,
187
- },
188
- ]}
189
- onPress={onUseCard}
196
+ labelStyle={{ color: theme.colors.background }}
197
+ onPress={handleUseCard}
190
198
  >
191
- {label}
199
+ {cards.length > 0 || hasApplePay
200
+ ? t('commons.payAndPlaceOrder')
201
+ : t('commons.payByCard')}
192
202
  </Button>
193
203
  </View>
194
204
  )}
@@ -0,0 +1,88 @@
1
+ import { useState, useCallback, useEffect } from 'react';
2
+ import { Platform } from 'react-native';
3
+ // @ts-ignore
4
+ import { SQIPApplePay } from 'react-native-square-in-app-payments';
5
+ import { Alert } from '@kiosinc/commons-rn';
6
+
7
+ export const useApplePay = () => {
8
+ const [isProcessing, setIsProcessing] = useState(false);
9
+ const [hasApplePay, setHasApplePay] = useState(false);
10
+
11
+ useEffect(() => {
12
+ const checkApplePay = async () => {
13
+ const digitalWalletEnabled = await SQIPApplePay.canUseApplePay();
14
+ setHasApplePay(digitalWalletEnabled);
15
+ };
16
+
17
+ checkApplePay();
18
+ }, []);
19
+
20
+ // Handle Apple Pay Nonce Request Success
21
+ const onApplePayNonceRequestSuccess = useCallback(async (cardDetails) => {
22
+ try {
23
+ // Perform operations with cardDetails
24
+ // await chargeCard(cardDetails);
25
+
26
+ Alert.show({ title: JSON.stringify(cardDetails) });
27
+
28
+ // Close the Apple Pay sheet with success
29
+ await SQIPApplePay.completeApplePayAuthorization(true);
30
+ } catch (ex: any) {
31
+ await SQIPApplePay.completeApplePayAuthorization(false, ex.message);
32
+ }
33
+ }, []);
34
+
35
+ // Handle Apple Pay Nonce Request Failure
36
+ const onApplePayNonceRequestFailure = useCallback(async (errorInfo) => {
37
+ Alert.show({
38
+ title: errorInfo.message,
39
+ description: errorInfo.debugMessage,
40
+ });
41
+ await SQIPApplePay.completeApplePayAuthorization(false, errorInfo.message);
42
+ }, []);
43
+
44
+ // Handle Apple Pay Entry Completion
45
+ const onApplePayEntryComplete = useCallback(() => {
46
+ setIsProcessing(false);
47
+ }, []);
48
+
49
+ // Start Digital Wallet Flow
50
+ const startApplePay = useCallback(async () => {
51
+ if (Platform.OS === 'ios') {
52
+ if (hasApplePay) {
53
+ const applePayConfig = {
54
+ price: '0.1',
55
+ summaryLabel: 'Test Item',
56
+ countryCode: 'US',
57
+ currencyCode: 'USD',
58
+ paymentType: 2,
59
+ };
60
+ try {
61
+ setIsProcessing(true);
62
+ await SQIPApplePay.requestApplePayNonce(
63
+ applePayConfig,
64
+ onApplePayNonceRequestSuccess,
65
+ onApplePayNonceRequestFailure,
66
+ onApplePayEntryComplete
67
+ );
68
+ } catch (ex) {
69
+ setIsProcessing(false);
70
+ // Handle InAppPaymentsException
71
+ }
72
+ }
73
+ } else if (Platform.OS === 'android') {
74
+ // Android-specific digital wallet logic
75
+ }
76
+ }, [
77
+ onApplePayNonceRequestSuccess,
78
+ onApplePayNonceRequestFailure,
79
+ onApplePayEntryComplete,
80
+ hasApplePay,
81
+ ]);
82
+
83
+ return {
84
+ hasApplePay,
85
+ isProcessing,
86
+ startApplePay,
87
+ };
88
+ };