@akinon/pz-masterpass 1.53.0 → 1.54.0
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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/redux/reducer.ts +8 -2
- package/src/utils/index.ts +7 -1
- package/src/views/card-list/index.tsx +108 -37
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
package/src/redux/reducer.ts
CHANGED
|
@@ -29,6 +29,7 @@ export interface MasterpassState {
|
|
|
29
29
|
additionalParams?: {
|
|
30
30
|
[key: string]: string;
|
|
31
31
|
};
|
|
32
|
+
cvcRequired: boolean;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
const initialState: MasterpassState = {
|
|
@@ -41,7 +42,8 @@ const initialState: MasterpassState = {
|
|
|
41
42
|
deletion: {
|
|
42
43
|
isModalVisible: false
|
|
43
44
|
},
|
|
44
|
-
additionalParams: {}
|
|
45
|
+
additionalParams: {},
|
|
46
|
+
cvcRequired: false
|
|
45
47
|
};
|
|
46
48
|
|
|
47
49
|
const rootSlice = createSlice({
|
|
@@ -111,6 +113,9 @@ const rootSlice = createSlice({
|
|
|
111
113
|
{ payload }: PayloadAction<{ [key: string]: any }>
|
|
112
114
|
) => {
|
|
113
115
|
state.additionalParams = payload;
|
|
116
|
+
},
|
|
117
|
+
setCvcRequired: (state, { payload }: PayloadAction<boolean>) => {
|
|
118
|
+
state.cvcRequired = payload;
|
|
114
119
|
}
|
|
115
120
|
}
|
|
116
121
|
});
|
|
@@ -131,7 +136,8 @@ export const {
|
|
|
131
136
|
setOtpResponse,
|
|
132
137
|
setDeletionModalVisible,
|
|
133
138
|
setDeletionCardAliasName,
|
|
134
|
-
setAdditionalParams
|
|
139
|
+
setAdditionalParams,
|
|
140
|
+
setCvcRequired
|
|
135
141
|
} = rootSlice.actions;
|
|
136
142
|
|
|
137
143
|
export default rootSlice.reducer;
|
package/src/utils/index.ts
CHANGED
|
@@ -80,7 +80,8 @@ export const buildPurchaseForm = async ({
|
|
|
80
80
|
amount,
|
|
81
81
|
additionalParams,
|
|
82
82
|
language = 'eng',
|
|
83
|
-
installmentCount
|
|
83
|
+
installmentCount,
|
|
84
|
+
cardCvc
|
|
84
85
|
}: {
|
|
85
86
|
token: string;
|
|
86
87
|
orderNo: string;
|
|
@@ -94,6 +95,7 @@ export const buildPurchaseForm = async ({
|
|
|
94
95
|
};
|
|
95
96
|
language: string;
|
|
96
97
|
installmentCount: number;
|
|
98
|
+
cardCvc?: string;
|
|
97
99
|
}) => {
|
|
98
100
|
const session = await getSession();
|
|
99
101
|
|
|
@@ -124,6 +126,10 @@ export const buildPurchaseForm = async ({
|
|
|
124
126
|
{ name: 'cardAliasName', value: selectedCard?.Name ?? '' }
|
|
125
127
|
];
|
|
126
128
|
|
|
129
|
+
if (cardCvc) {
|
|
130
|
+
fields.push({ name: 'cvc', value: cardCvc });
|
|
131
|
+
}
|
|
132
|
+
|
|
127
133
|
const form = formCreator({
|
|
128
134
|
id: 'mp-purchase-form',
|
|
129
135
|
fields
|
|
@@ -15,10 +15,22 @@ import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
|
15
15
|
import { useCards } from '../../hooks/use-cards';
|
|
16
16
|
import { useDeleteCard } from '../../hooks/use-delete-card';
|
|
17
17
|
import { useSetBinNumberMutation } from '@akinon/next/data/client/checkout';
|
|
18
|
-
import { Button, Image } from '@akinon/next/components';
|
|
18
|
+
import { Button, Icon, Image, Input } from '@akinon/next/components';
|
|
19
19
|
import { twMerge } from 'tailwind-merge';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
setCvcRequired,
|
|
22
|
+
setIsDirectPurchase,
|
|
23
|
+
setSelectedCard
|
|
24
|
+
} from '../../redux/reducer';
|
|
21
25
|
import { setInstallmentOptions } from '@akinon/next/redux/reducers/checkout';
|
|
26
|
+
import clsx from 'clsx';
|
|
27
|
+
import {
|
|
28
|
+
Control,
|
|
29
|
+
FieldError,
|
|
30
|
+
UseFormClearErrors,
|
|
31
|
+
UseFormRegister,
|
|
32
|
+
UseFormSetValue
|
|
33
|
+
} from 'react-hook-form';
|
|
22
34
|
|
|
23
35
|
const cardImages = {
|
|
24
36
|
amex,
|
|
@@ -31,22 +43,31 @@ const cardImages = {
|
|
|
31
43
|
const defaultTranslations = {
|
|
32
44
|
title: 'Select a card to pay with',
|
|
33
45
|
pay_with_new_card: 'Pay with a new card',
|
|
34
|
-
retryFetchCards: 'Retry Fetching Cards'
|
|
46
|
+
retryFetchCards: 'Retry Fetching Cards',
|
|
47
|
+
security_code: 'Security Code',
|
|
48
|
+
security_code_info: 'What’s CVC?'
|
|
35
49
|
};
|
|
36
50
|
|
|
37
51
|
export interface MasterpassCardListProps {
|
|
38
52
|
className?: string;
|
|
39
53
|
translations?: typeof defaultTranslations;
|
|
54
|
+
form?: {
|
|
55
|
+
clearErrors: UseFormClearErrors<any>;
|
|
56
|
+
control: Control<any>;
|
|
57
|
+
errors: Record<string, FieldError>;
|
|
58
|
+
register: UseFormRegister<any>;
|
|
59
|
+
setFormValue: UseFormSetValue<any>;
|
|
60
|
+
};
|
|
40
61
|
}
|
|
41
62
|
|
|
42
63
|
export const MasterpassCardList = ({
|
|
43
64
|
className,
|
|
44
|
-
translations
|
|
65
|
+
translations,
|
|
66
|
+
form
|
|
45
67
|
}: MasterpassCardListProps) => {
|
|
46
68
|
const { preOrder } = useAppSelector((state) => state.checkout);
|
|
47
|
-
const { accountStatus, isDirectPurchase, selectedCard } =
|
|
48
|
-
(state) => state.masterpass
|
|
49
|
-
);
|
|
69
|
+
const { accountStatus, isDirectPurchase, selectedCard, cvcRequired } =
|
|
70
|
+
useAppSelector((state) => state.masterpass);
|
|
50
71
|
const [setMasterpassBinNumber] = useSetBinNumberMutation();
|
|
51
72
|
const { cards, loading, error, refreshCards } = useCards({ skipData: false });
|
|
52
73
|
const { DeleteButton } = useDeleteCard();
|
|
@@ -121,40 +142,90 @@ export const MasterpassCardList = ({
|
|
|
121
142
|
return (
|
|
122
143
|
<li
|
|
123
144
|
key={card.UniqueId}
|
|
124
|
-
className="p-4 mb-2 border-2 border-gray-200 flex
|
|
145
|
+
className="p-4 mb-2 border-2 border-gray-200 flex flex-col space-x-2 cursor-pointer"
|
|
125
146
|
onClick={async () => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
147
|
+
if (selectedCard?.UniqueId !== card.UniqueId) {
|
|
148
|
+
await setMasterpassBinNumber(
|
|
149
|
+
card.Value1.substring(0, 6)
|
|
150
|
+
).unwrap();
|
|
151
|
+
|
|
152
|
+
dispatch(setSelectedCard(card));
|
|
153
|
+
dispatch(setCvcRequired(false));
|
|
154
|
+
|
|
155
|
+
if (form) {
|
|
156
|
+
form.setFormValue('card_cvv', '');
|
|
157
|
+
form.clearErrors('card_cvv');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
131
160
|
}}
|
|
132
161
|
>
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
<label
|
|
143
|
-
htmlFor={card.UniqueId}
|
|
144
|
-
className="flex flex-col w-full cursor-pointer md:flex-row md:items-center md:justify-between"
|
|
145
|
-
>
|
|
146
|
-
<p className="w-full lg:w-1/3">{card.Name}</p>
|
|
147
|
-
<p className="w-full text-[10px] lg:w-1/3">{card.Value1}</p>
|
|
148
|
-
<Image
|
|
149
|
-
className="w-8 h-6 object-contain flex items-center justify-center mr-4"
|
|
150
|
-
width={50}
|
|
151
|
-
height={50}
|
|
152
|
-
src={cardImages[getCreditCardType(card.Value1)].src}
|
|
153
|
-
alt={card.Name}
|
|
162
|
+
<div className="flex justify-between items-center">
|
|
163
|
+
<input
|
|
164
|
+
name="masterpass-card"
|
|
165
|
+
type="radio"
|
|
166
|
+
checked={selectedCard?.UniqueId === card.UniqueId}
|
|
167
|
+
value={card.Name}
|
|
168
|
+
id={card.UniqueId}
|
|
169
|
+
className="mr-2"
|
|
170
|
+
onChange={(e) => e.preventDefault()}
|
|
154
171
|
/>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
172
|
+
<label
|
|
173
|
+
htmlFor={card.UniqueId}
|
|
174
|
+
className="flex flex-col w-full cursor-pointer md:flex-row md:items-center md:justify-between"
|
|
175
|
+
>
|
|
176
|
+
<p className="w-full lg:w-1/3">{card.Name}</p>
|
|
177
|
+
<p className="w-full text-[10px] lg:w-1/3">{card.Value1}</p>
|
|
178
|
+
<Image
|
|
179
|
+
className="w-8 h-6 object-contain flex items-center justify-center mr-4"
|
|
180
|
+
width={50}
|
|
181
|
+
height={50}
|
|
182
|
+
src={cardImages[getCreditCardType(card.Value1)].src}
|
|
183
|
+
alt={card.Name}
|
|
184
|
+
/>
|
|
185
|
+
</label>
|
|
186
|
+
|
|
187
|
+
<DeleteButton cardAliasName={card.Name} />
|
|
188
|
+
</div>
|
|
189
|
+
{selectedCard?.UniqueId === card.UniqueId && form && cvcRequired && (
|
|
190
|
+
<div
|
|
191
|
+
className={twMerge(
|
|
192
|
+
clsx('flex items-center justify-start mt-2', {
|
|
193
|
+
'items-baseline': form.errors.card_cvv
|
|
194
|
+
})
|
|
195
|
+
)}
|
|
196
|
+
>
|
|
197
|
+
<label
|
|
198
|
+
className="text-xs text-black-400 mr-1.5"
|
|
199
|
+
htmlFor="card_cvv"
|
|
200
|
+
>
|
|
201
|
+
{translations?.security_code ??
|
|
202
|
+
defaultTranslations.security_code}
|
|
203
|
+
</label>
|
|
204
|
+
<Input
|
|
205
|
+
format="###"
|
|
206
|
+
mask="_"
|
|
207
|
+
control={form.control}
|
|
208
|
+
allowEmptyFormatting={true}
|
|
209
|
+
{...form.register('card_cvv')}
|
|
210
|
+
error={form.errors.card_cvv}
|
|
211
|
+
/>
|
|
212
|
+
<div className="group relative flex items-center justify-start text-gray-600 cursor-pointer ml-2 transition-all hover:text-secondary">
|
|
213
|
+
<span className="text-xs underline">
|
|
214
|
+
{translations?.security_code_info ??
|
|
215
|
+
defaultTranslations.security_code_info}
|
|
216
|
+
</span>
|
|
217
|
+
<Icon name="cvc" size={16} className="leading-none ml-2" />
|
|
218
|
+
<div className="hidden group-hover:block absolute right-0 bottom-5 w-[11rem] lg:w-[21rem] lg:left-auto lg:right-auto border-2">
|
|
219
|
+
<Image
|
|
220
|
+
src="/cvv.jpg"
|
|
221
|
+
alt="Cvv"
|
|
222
|
+
width={385}
|
|
223
|
+
height={262}
|
|
224
|
+
/>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
)}
|
|
158
229
|
</li>
|
|
159
230
|
);
|
|
160
231
|
})}
|