@kiosinc/commons-rn 0.1.95 → 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.
- package/lib/commonjs/assets/icons/apple_pay.svg +8 -0
- package/lib/commonjs/localization/translations/en.json +6 -1
- package/lib/commonjs/screens/SavedCards/CardRow.js +35 -21
- package/lib/commonjs/screens/SavedCards/CardRow.js.map +1 -1
- package/lib/commonjs/screens/SavedCards/SavedCards.js +61 -44
- package/lib/commonjs/screens/SavedCards/SavedCards.js.map +1 -1
- package/lib/commonjs/screens/SavedCards/useApplePay.js +85 -0
- package/lib/commonjs/screens/SavedCards/useApplePay.js.map +1 -0
- package/lib/module/assets/icons/apple_pay.svg +8 -0
- package/lib/module/localization/translations/en.json +6 -1
- package/lib/module/screens/SavedCards/CardRow.js +35 -21
- package/lib/module/screens/SavedCards/CardRow.js.map +1 -1
- package/lib/module/screens/SavedCards/SavedCards.js +62 -45
- package/lib/module/screens/SavedCards/SavedCards.js.map +1 -1
- package/lib/module/screens/SavedCards/useApplePay.js +77 -0
- package/lib/module/screens/SavedCards/useApplePay.js.map +1 -0
- package/lib/typescript/src/localization/index.d.ts +5 -0
- package/lib/typescript/src/localization/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/SavedCards/CardRow.d.ts +5 -4
- package/lib/typescript/src/screens/SavedCards/CardRow.d.ts.map +1 -1
- package/lib/typescript/src/screens/SavedCards/SavedCards.d.ts +3 -2
- package/lib/typescript/src/screens/SavedCards/SavedCards.d.ts.map +1 -1
- package/lib/typescript/src/screens/SavedCards/useApplePay.d.ts +6 -0
- package/lib/typescript/src/screens/SavedCards/useApplePay.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/assets/icons/apple_pay.svg +8 -0
- package/src/localization/translations/en.json +6 -1
- package/src/screens/SavedCards/CardRow.tsx +52 -31
- package/src/screens/SavedCards/SavedCards.tsx +71 -61
- 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
|
|
17
|
-
onDelete
|
|
18
|
-
onSelect
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
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
|
|
77
|
+
value={card?.linkedObjectId || 'apple-pay'}
|
|
70
78
|
status={isSelected ? 'checked' : 'unchecked'}
|
|
71
79
|
onPress={() => {
|
|
72
|
-
onSelect(card
|
|
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
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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,
|
|
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
|
-
|
|
47
|
+
setSelectedPaymentId(customer.paymentId);
|
|
44
48
|
}
|
|
45
|
-
}, [customer?.paymentId
|
|
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
|
|
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
|
|
67
|
-
cards.length
|
|
75
|
+
const handleUseCard = () => {
|
|
76
|
+
if (!cards.length && !(hasApplePay && Platform.OS === 'ios')) {
|
|
77
|
+
Alert.show({ title: t('commons.noPaymentMethods') });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
68
80
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
if (!selectedPaymentId) {
|
|
82
|
+
Alert.show({ title: t('commons.selectPaymentMethod') });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
72
85
|
|
|
73
|
-
|
|
74
|
-
|
|
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 ===
|
|
90
|
+
(card: any) => card.linkedObjectId === selectedPaymentId
|
|
91
91
|
);
|
|
92
|
+
|
|
92
93
|
if (selectedCard) {
|
|
93
94
|
onCheckout?.(selectedCard);
|
|
94
95
|
} else {
|
|
95
|
-
Alert.show({ title: '
|
|
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={
|
|
119
|
+
<RefreshControl refreshing={isFetching} onRefresh={refetch} />
|
|
110
120
|
}
|
|
111
|
-
data={
|
|
121
|
+
data={paymentMethods}
|
|
112
122
|
showsVerticalScrollIndicator={false}
|
|
113
|
-
renderItem={({ item: card
|
|
123
|
+
renderItem={({ item: card }) => (
|
|
114
124
|
<CardRow
|
|
115
125
|
isDisabled={disableCardLoading}
|
|
116
126
|
isSelected={selectedPaymentId === card.linkedObjectId}
|
|
117
127
|
isSelectable={!!onCheckout}
|
|
118
|
-
|
|
128
|
+
isApplePay={card.isApplePay}
|
|
119
129
|
card={card}
|
|
120
130
|
onDelete={(id: string) => {
|
|
121
|
-
|
|
131
|
+
if (!card.isApplePay) {
|
|
132
|
+
disableCard(id);
|
|
133
|
+
}
|
|
122
134
|
}}
|
|
123
135
|
onSelect={(linkedObjectId: string) => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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=
|
|
161
|
+
pt="16"
|
|
150
162
|
loading={saveCardLoading}
|
|
151
|
-
onPress={
|
|
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=
|
|
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
|
-
{
|
|
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
|
+
};
|