@akinon/pz-masterpass-rest 1.118.0-rc.8 → 2.0.0-beta.12
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 +32 -0
- package/README.md +0 -10
- package/package.json +1 -6
- package/src/components/card-list.tsx +18 -18
- package/src/components/confirmation-modal.tsx +1 -1
- package/src/components/credit-card-form.tsx +15 -15
- package/src/components/installment-list.tsx +16 -16
- package/src/components/masterpass-security-info.tsx +7 -3
- package/src/components/otp-modal.tsx +9 -99
- package/src/components/payment-method-selector.tsx +20 -20
- package/src/hooks/useMasterpassAccount.ts +38 -84
- package/src/hooks/useMasterpassPayment.ts +123 -248
- package/src/hooks/useMasterpassToken.ts +16 -61
- package/src/redux/api.ts +7 -16
- package/src/redux/reducer.ts +0 -14
- package/src/services/account.ts +64 -73
- package/src/services/payment.ts +93 -125
- package/src/services/verify.ts +44 -44
- package/src/types/account.types.ts +1 -0
- package/src/types/custom-render.types.ts +2 -22
- package/src/types/payment.types.ts +0 -14
- package/src/utils/payment-constants.ts +3 -3
- package/src/utils/payment-utils.ts +4 -20
- package/src/utils/response-handler.ts +6 -35
- package/src/utils/validation-schemas.ts +1 -1
- package/src/views/masterpass-rest-option.tsx +30 -55
- package/assets/masterpass-javascript-sdk-web.min.js +0 -1
- package/src/components/information-modal.tsx +0 -139
- package/src/utils/session-handler.ts +0 -60
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# @akinon/pz-masterpass-rest
|
|
2
|
+
|
|
3
|
+
## 1.117.0
|
|
4
|
+
|
|
5
|
+
## 1.116.0
|
|
6
|
+
|
|
7
|
+
## 1.115.0
|
|
8
|
+
|
|
9
|
+
## 1.114.0
|
|
10
|
+
|
|
11
|
+
## 1.113.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- cacc627: ZERO-3847: mp rest performance enhancements
|
|
16
|
+
- 10dcaae: ZERO-3847: move PaymentMethodSelectorProps to custom-render.types
|
|
17
|
+
- 78f1026: ZERO-3847: add sdkUrl and environment props for SDK URL configuration
|
|
18
|
+
- 2862d29: ZERO-3847: make CVV input optional in saved card list
|
|
19
|
+
- 3a25ab0: ZERO-3847: add fullrender support
|
|
20
|
+
- 36c7a51: ZERO-3847: merge main
|
|
21
|
+
- 4412917: ZERO-3487: masterpass rest ux guideline adjustments
|
|
22
|
+
- 4fa9202: ZERO-3847: add mp logo to card deletion modal
|
|
23
|
+
- 08f1f39: ZERO-3847: add resendOtp type and consolidate component props
|
|
24
|
+
|
|
25
|
+
## 1.112.0
|
|
26
|
+
|
|
27
|
+
## 1.111.0
|
|
28
|
+
|
|
29
|
+
### Minor Changes
|
|
30
|
+
|
|
31
|
+
- c026300: ZERO-3833: fix masterpass rest conflicts
|
|
32
|
+
- 02a1caf: ZERO-3833: use installment count number insteadof pk
|
package/README.md
CHANGED
|
@@ -10,16 +10,6 @@ Install the plugin using the Project Zero CLI:
|
|
|
10
10
|
npx @akinon/projectzero@latest --plugins
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
## SDK Setup
|
|
14
|
-
|
|
15
|
-
After installing the package, you need to copy the Masterpass JavaScript SDK to your project's public folder:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
cp node_modules/@akinon/pz-masterpass-rest/assets/masterpass-javascript-sdk-web.min.js public/
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
The SDK file must be accessible at `/masterpass-javascript-sdk-web.min.js` in your application.
|
|
22
|
-
|
|
23
13
|
## Basic Usage
|
|
24
14
|
|
|
25
15
|
Here's a simple example of how to use the Masterpass REST component:
|
package/package.json
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/pz-masterpass-rest",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.12",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"types": "src/index.d.ts",
|
|
6
6
|
"description": "Modern React-based Masterpass REST API integration package for ProjectZero e-commerce platform",
|
|
7
|
-
"files": [
|
|
8
|
-
"src",
|
|
9
|
-
"assets",
|
|
10
|
-
"README.md"
|
|
11
|
-
],
|
|
12
7
|
"keywords": [
|
|
13
8
|
"masterpass",
|
|
14
9
|
"payment",
|
|
@@ -35,8 +35,8 @@ const CardList: React.FC<CardListProps> = ({
|
|
|
35
35
|
key={card.uniqueCardNumber}
|
|
36
36
|
className={`border transition-all cursor-pointer ${
|
|
37
37
|
isSelected
|
|
38
|
-
? 'border-
|
|
39
|
-
: 'border-
|
|
38
|
+
? 'border-primary bg-primary/5'
|
|
39
|
+
: 'border-gray-200 hover:border-gray-300'
|
|
40
40
|
}`}
|
|
41
41
|
onClick={() => onCardSelect(card)}
|
|
42
42
|
>
|
|
@@ -48,13 +48,13 @@ const CardList: React.FC<CardListProps> = ({
|
|
|
48
48
|
w-4 h-4 border-2 rounded-full flex items-center justify-center
|
|
49
49
|
${
|
|
50
50
|
isSelected
|
|
51
|
-
? 'border-
|
|
52
|
-
: 'border-
|
|
51
|
+
? 'border-primary bg-primary'
|
|
52
|
+
: 'border-gray-300'
|
|
53
53
|
}
|
|
54
54
|
`}
|
|
55
55
|
>
|
|
56
56
|
{isSelected && (
|
|
57
|
-
<div className="w-2 h-2 bg-
|
|
57
|
+
<div className="w-2 h-2 bg-white rounded-full"></div>
|
|
58
58
|
)}
|
|
59
59
|
</div>
|
|
60
60
|
|
|
@@ -64,21 +64,21 @@ const CardList: React.FC<CardListProps> = ({
|
|
|
64
64
|
className="w-8 h-6 object-contain"
|
|
65
65
|
/>
|
|
66
66
|
<div>
|
|
67
|
-
<div className="font-medium text-
|
|
67
|
+
<div className="font-medium text-gray-900">
|
|
68
68
|
{card.maskedCardNumber}
|
|
69
69
|
</div>
|
|
70
|
-
<div className="text-sm text-
|
|
70
|
+
<div className="text-sm text-gray-500">{card.cardAlias}</div>
|
|
71
71
|
<div className="flex items-center space-x-2 mt-1">
|
|
72
72
|
<span className="text-xs px-2 py-1 bg-gray-100">
|
|
73
73
|
{card.cardType}
|
|
74
74
|
</span>
|
|
75
75
|
{card.isDefaultCard && (
|
|
76
|
-
<span className="text-xs px-2 py-1 bg-
|
|
76
|
+
<span className="text-xs px-2 py-1 bg-primary/10 text-primary">
|
|
77
77
|
{texts.defaultCardText}
|
|
78
78
|
</span>
|
|
79
79
|
)}
|
|
80
80
|
{card.expireSoon && (
|
|
81
|
-
<span className="text-xs px-2 py-1 bg-
|
|
81
|
+
<span className="text-xs px-2 py-1 bg-warning/10 text-warning">
|
|
82
82
|
{texts.expiresSoonText}
|
|
83
83
|
</span>
|
|
84
84
|
)}
|
|
@@ -88,7 +88,7 @@ const CardList: React.FC<CardListProps> = ({
|
|
|
88
88
|
|
|
89
89
|
{onRemove && (
|
|
90
90
|
<button
|
|
91
|
-
className="p-2 text-
|
|
91
|
+
className="p-2 text-gray-400 hover:text-red-500 transition-colors disabled:opacity-50 bg-gray-100 rounded-full"
|
|
92
92
|
disabled={removingCardId === card.uniqueCardNumber}
|
|
93
93
|
onClick={(e) => {
|
|
94
94
|
e.stopPropagation();
|
|
@@ -96,7 +96,7 @@ const CardList: React.FC<CardListProps> = ({
|
|
|
96
96
|
}}
|
|
97
97
|
>
|
|
98
98
|
{removingCardId === card.uniqueCardNumber ? (
|
|
99
|
-
<div className="animate-spin h-5 w-5 border-b-2 border-
|
|
99
|
+
<div className="animate-spin h-5 w-5 border-b-2 border-red-500"></div>
|
|
100
100
|
) : (
|
|
101
101
|
<svg
|
|
102
102
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -108,7 +108,7 @@ const CardList: React.FC<CardListProps> = ({
|
|
|
108
108
|
strokeWidth="2"
|
|
109
109
|
strokeLinecap="round"
|
|
110
110
|
strokeLinejoin="round"
|
|
111
|
-
className="text-
|
|
111
|
+
className="text-gray-600"
|
|
112
112
|
>
|
|
113
113
|
<path d="M3 6h18" />
|
|
114
114
|
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
|
|
@@ -121,17 +121,17 @@ const CardList: React.FC<CardListProps> = ({
|
|
|
121
121
|
|
|
122
122
|
{isSelected && cvcRequired && (
|
|
123
123
|
<div
|
|
124
|
-
className="border-t border-
|
|
124
|
+
className="border-t border-gray-200 p-4 bg-gray-50"
|
|
125
125
|
onClick={(e) => e.stopPropagation()}
|
|
126
126
|
>
|
|
127
127
|
<div className="flex items-center gap-4">
|
|
128
128
|
<div className="flex-1">
|
|
129
129
|
<label
|
|
130
130
|
htmlFor={`cvc-${card.uniqueCardNumber}`}
|
|
131
|
-
className="block text-sm font-medium text-
|
|
131
|
+
className="block text-sm font-medium text-gray-700 mb-2"
|
|
132
132
|
>
|
|
133
133
|
{texts.cvcCardLabel}
|
|
134
|
-
<span className="text-
|
|
134
|
+
<span className="text-red-500"> *</span>
|
|
135
135
|
</label>
|
|
136
136
|
<Input
|
|
137
137
|
id={`cvc-${card.uniqueCardNumber}`}
|
|
@@ -148,7 +148,7 @@ const CardList: React.FC<CardListProps> = ({
|
|
|
148
148
|
maxLength={expectedCvcLength}
|
|
149
149
|
className="w-32"
|
|
150
150
|
/>
|
|
151
|
-
<p className="text-xs text-
|
|
151
|
+
<p className="text-xs text-gray-500 mt-1">
|
|
152
152
|
{texts.cvcCardHelpText
|
|
153
153
|
?.replace('{length}', expectedCvcLength.toString())
|
|
154
154
|
.replace(
|
|
@@ -159,11 +159,11 @@ const CardList: React.FC<CardListProps> = ({
|
|
|
159
159
|
)}
|
|
160
160
|
</p>
|
|
161
161
|
</div>
|
|
162
|
-
<div className="flex items-center text-sm text-
|
|
162
|
+
<div className="flex items-center text-sm text-gray-600">
|
|
163
163
|
<Icon
|
|
164
164
|
name="shield-check"
|
|
165
165
|
size={16}
|
|
166
|
-
className="mr-1 text-
|
|
166
|
+
className="mr-1 text-green-600"
|
|
167
167
|
/>
|
|
168
168
|
<span>{texts.secureCardText}</span>
|
|
169
169
|
</div>
|
|
@@ -27,7 +27,7 @@ const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
|
|
|
27
27
|
<div className="flex gap-3 justify-center">
|
|
28
28
|
<Button
|
|
29
29
|
appearance="outlined"
|
|
30
|
-
className="px-6 py-3 h-auto hover:bg-
|
|
30
|
+
className="px-6 py-3 h-auto hover:bg-gray-50 transition-colors"
|
|
31
31
|
onClick={onClose}
|
|
32
32
|
disabled={isLoading}
|
|
33
33
|
>
|
|
@@ -146,17 +146,17 @@ const CreditCardForm: React.FC<CreditCardFormProps> = ({
|
|
|
146
146
|
const expectedCVVLength = getCvcLength(cardType);
|
|
147
147
|
|
|
148
148
|
return (
|
|
149
|
-
<div className="bg-
|
|
150
|
-
<div className="flex justify-start items-center border-b border-
|
|
149
|
+
<div className="bg-white border border-gray-200 shadow-sm">
|
|
150
|
+
<div className="flex justify-start items-center border-b border-gray-200 px-4 py-3 sm:px-6 sm:py-4">
|
|
151
151
|
<div className="flex items-center gap-2">
|
|
152
|
-
<Icon name="credit-card" size={20} className="text-
|
|
153
|
-
<span className="text-
|
|
152
|
+
<Icon name="credit-card" size={20} className="text-gray-600" />
|
|
153
|
+
<span className="text-black-800 text-lg font-medium sm:text-xl">
|
|
154
154
|
{texts.creditCardInfoTitle}
|
|
155
155
|
</span>
|
|
156
156
|
{isBinChecking && (
|
|
157
157
|
<div className="flex items-center gap-2 ml-auto">
|
|
158
158
|
<LoaderSpinner className="w-4 h-4" />
|
|
159
|
-
<span className="text-sm text-
|
|
159
|
+
<span className="text-sm text-gray-500">
|
|
160
160
|
{texts.checkingCardText}
|
|
161
161
|
</span>
|
|
162
162
|
</div>
|
|
@@ -169,7 +169,7 @@ const CreditCardForm: React.FC<CreditCardFormProps> = ({
|
|
|
169
169
|
<div className="space-y-2">
|
|
170
170
|
<label
|
|
171
171
|
htmlFor="cardNumber"
|
|
172
|
-
className="block text-sm font-medium text-
|
|
172
|
+
className="block text-sm font-medium text-gray-700"
|
|
173
173
|
>
|
|
174
174
|
{texts.cardNumberLabel}
|
|
175
175
|
</label>
|
|
@@ -194,7 +194,7 @@ const CreditCardForm: React.FC<CreditCardFormProps> = ({
|
|
|
194
194
|
errors.cardNumber && '-translate-y-full'
|
|
195
195
|
)}
|
|
196
196
|
>
|
|
197
|
-
<span className="flex text-sm font-medium text-
|
|
197
|
+
<span className="flex text-sm font-medium text-gray-600 bg-gray-100 px-2 py-1">
|
|
198
198
|
<img
|
|
199
199
|
src={
|
|
200
200
|
getCardIcon(watchedCardNumber.replace(/\D/g, '')).src
|
|
@@ -211,7 +211,7 @@ const CreditCardForm: React.FC<CreditCardFormProps> = ({
|
|
|
211
211
|
<div className="space-y-2">
|
|
212
212
|
<label
|
|
213
213
|
htmlFor="cardholderName"
|
|
214
|
-
className="block text-sm font-medium text-
|
|
214
|
+
className="block text-sm font-medium text-gray-700"
|
|
215
215
|
>
|
|
216
216
|
{texts.cardholderNameLabel}
|
|
217
217
|
</label>
|
|
@@ -228,7 +228,7 @@ const CreditCardForm: React.FC<CreditCardFormProps> = ({
|
|
|
228
228
|
<div className="space-y-2">
|
|
229
229
|
<label
|
|
230
230
|
htmlFor="expiryDate"
|
|
231
|
-
className="block text-sm font-medium text-
|
|
231
|
+
className="block text-sm font-medium text-gray-700"
|
|
232
232
|
>
|
|
233
233
|
{texts.expiryDateLabel}
|
|
234
234
|
</label>
|
|
@@ -248,7 +248,7 @@ const CreditCardForm: React.FC<CreditCardFormProps> = ({
|
|
|
248
248
|
<div className="space-y-2">
|
|
249
249
|
<label
|
|
250
250
|
htmlFor="cvv"
|
|
251
|
-
className="block text-sm font-medium text-
|
|
251
|
+
className="block text-sm font-medium text-gray-700"
|
|
252
252
|
>
|
|
253
253
|
{texts.cvcLabel}
|
|
254
254
|
</label>
|
|
@@ -278,18 +278,18 @@ const CreditCardForm: React.FC<CreditCardFormProps> = ({
|
|
|
278
278
|
{...register('saveCard')}
|
|
279
279
|
id="saveCard"
|
|
280
280
|
type="checkbox"
|
|
281
|
-
className="h-4 w-4 mt-0.5 text-
|
|
281
|
+
className="h-4 w-4 mt-0.5 text-primary focus:ring-primary border-gray-300"
|
|
282
282
|
/>
|
|
283
283
|
<label
|
|
284
284
|
htmlFor="saveCard"
|
|
285
|
-
className="text-sm font-medium text-
|
|
285
|
+
className="text-sm font-medium text-gray-700 cursor-pointer"
|
|
286
286
|
>
|
|
287
287
|
{texts.saveCardTermsPrefix}{' '}
|
|
288
288
|
<a
|
|
289
289
|
href="https://www.masterpassturkiye.com/terms-and-conditions"
|
|
290
290
|
target="_blank"
|
|
291
291
|
rel="noopener noreferrer"
|
|
292
|
-
className="text-
|
|
292
|
+
className="text-primary underline hover:text-primary-dark"
|
|
293
293
|
>
|
|
294
294
|
{texts.saveCardTermsLink}
|
|
295
295
|
</a>
|
|
@@ -301,7 +301,7 @@ const CreditCardForm: React.FC<CreditCardFormProps> = ({
|
|
|
301
301
|
<div className="space-y-2 pt-2">
|
|
302
302
|
<label
|
|
303
303
|
htmlFor="cardAlias"
|
|
304
|
-
className="block text-sm font-medium text-
|
|
304
|
+
className="block text-sm font-medium text-gray-700"
|
|
305
305
|
>
|
|
306
306
|
{texts.cardAliasLabel}
|
|
307
307
|
</label>
|
|
@@ -320,7 +320,7 @@ const CreditCardForm: React.FC<CreditCardFormProps> = ({
|
|
|
320
320
|
)}
|
|
321
321
|
|
|
322
322
|
{watchedSaveCard && (
|
|
323
|
-
<div className="flex flex-col sm:flex-row gap-3 pt-6 border-t border-
|
|
323
|
+
<div className="flex flex-col sm:flex-row gap-3 pt-6 border-t border-gray-200">
|
|
324
324
|
<Button
|
|
325
325
|
type="submit"
|
|
326
326
|
className="flex-1 group inline-flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed"
|
|
@@ -13,11 +13,11 @@ const InstallmentList: React.FC<InstallmentListProps> = ({
|
|
|
13
13
|
texts
|
|
14
14
|
}) => {
|
|
15
15
|
return (
|
|
16
|
-
<div className="bg-white border border-
|
|
17
|
-
<div className="border-b border-
|
|
16
|
+
<div className="bg-white border border-gray-200 shadow-sm">
|
|
17
|
+
<div className="border-b border-gray-200 px-4 py-3 sm:px-6 sm:py-4">
|
|
18
18
|
<div className="flex items-center gap-2">
|
|
19
19
|
<Icon name="credit-card" size={20} className="text-gray-600" />
|
|
20
|
-
<span className="text-
|
|
20
|
+
<span className="text-black-800 text-lg font-medium sm:text-xl">
|
|
21
21
|
{texts.installmentOptionsText}
|
|
22
22
|
</span>
|
|
23
23
|
</div>
|
|
@@ -26,10 +26,10 @@ const InstallmentList: React.FC<InstallmentListProps> = ({
|
|
|
26
26
|
<div className="p-4 sm:p-6">
|
|
27
27
|
{/* Card Type Display */}
|
|
28
28
|
{cardType && (
|
|
29
|
-
<div className="mb-4 p-3 bg-
|
|
29
|
+
<div className="mb-4 p-3 bg-gray-50 border border-gray-200">
|
|
30
30
|
<div className="flex items-center gap-2">
|
|
31
31
|
<Icon name="credit-card" size={16} className="text-gray-600" />
|
|
32
|
-
<span className="text-sm font-medium text-
|
|
32
|
+
<span className="text-sm font-medium text-gray-700">
|
|
33
33
|
{texts.cardTypeLabel}
|
|
34
34
|
</span>
|
|
35
35
|
<img
|
|
@@ -55,8 +55,8 @@ const InstallmentList: React.FC<InstallmentListProps> = ({
|
|
|
55
55
|
relative flex items-center justify-between p-3 border transition-all cursor-pointer
|
|
56
56
|
${
|
|
57
57
|
isSelected
|
|
58
|
-
? 'border-
|
|
59
|
-
: 'border-
|
|
58
|
+
? 'border-primary bg-primary/5'
|
|
59
|
+
: 'border-gray-200 hover:border-gray-300'
|
|
60
60
|
}
|
|
61
61
|
`}
|
|
62
62
|
>
|
|
@@ -66,13 +66,13 @@ const InstallmentList: React.FC<InstallmentListProps> = ({
|
|
|
66
66
|
w-4 h-4 border-2 rounded-full flex items-center justify-center
|
|
67
67
|
${
|
|
68
68
|
isSelected
|
|
69
|
-
? 'border-
|
|
70
|
-
: 'border-
|
|
69
|
+
? 'border-primary bg-primary'
|
|
70
|
+
: 'border-gray-300'
|
|
71
71
|
}
|
|
72
72
|
`}
|
|
73
73
|
>
|
|
74
74
|
{isSelected && (
|
|
75
|
-
<div className="w-2 h-2 bg-
|
|
75
|
+
<div className="w-2 h-2 bg-white rounded-full"></div>
|
|
76
76
|
)}
|
|
77
77
|
</div>
|
|
78
78
|
|
|
@@ -80,7 +80,7 @@ const InstallmentList: React.FC<InstallmentListProps> = ({
|
|
|
80
80
|
<div className="flex-1 ml-3">
|
|
81
81
|
<div className="flex items-center justify-between">
|
|
82
82
|
<div>
|
|
83
|
-
<span className="font-medium text-
|
|
83
|
+
<span className="font-medium text-gray-900">
|
|
84
84
|
{installment.installment_count === 1
|
|
85
85
|
? texts.singlePaymentText
|
|
86
86
|
: texts.installmentsText?.replace(
|
|
@@ -89,17 +89,17 @@ const InstallmentList: React.FC<InstallmentListProps> = ({
|
|
|
89
89
|
)}
|
|
90
90
|
</span>
|
|
91
91
|
{isNoInterest && (
|
|
92
|
-
<span className="text-xs bg-
|
|
92
|
+
<span className="text-xs bg-green-100 text-green-700 px-2 py-1 ml-2">
|
|
93
93
|
{texts.noInterestText}
|
|
94
94
|
</span>
|
|
95
95
|
)}
|
|
96
96
|
</div>
|
|
97
97
|
<div className="text-right">
|
|
98
|
-
<div className="font-semibold text-
|
|
98
|
+
<div className="font-semibold text-gray-900">
|
|
99
99
|
{installment.price_with_accrued_interest}
|
|
100
100
|
</div>
|
|
101
101
|
{installment.installment_count > 1 && (
|
|
102
|
-
<div className="text-sm text-
|
|
102
|
+
<div className="text-sm text-gray-500">
|
|
103
103
|
{installment.monthly_price_with_accrued_interest}
|
|
104
104
|
{texts.perMonthText}
|
|
105
105
|
</div>
|
|
@@ -114,7 +114,7 @@ const InstallmentList: React.FC<InstallmentListProps> = ({
|
|
|
114
114
|
|
|
115
115
|
{/* Payment Button */}
|
|
116
116
|
{selectedInstallment && onProceedToPayment && (
|
|
117
|
-
<div className="pt-4 border-t border-
|
|
117
|
+
<div className="pt-4 border-t border-gray-200">
|
|
118
118
|
<Button
|
|
119
119
|
onClick={onProceedToPayment}
|
|
120
120
|
disabled={isLoading || paymentLoading}
|
|
@@ -122,7 +122,7 @@ const InstallmentList: React.FC<InstallmentListProps> = ({
|
|
|
122
122
|
>
|
|
123
123
|
{paymentLoading ? (
|
|
124
124
|
<div className="flex items-center justify-center">
|
|
125
|
-
<div className="animate-spin h-4 w-4 border-b-2 border-
|
|
125
|
+
<div className="animate-spin h-4 w-4 border-b-2 border-white"></div>
|
|
126
126
|
<span className="ml-2">{texts.processingPaymentText}</span>
|
|
127
127
|
</div>
|
|
128
128
|
) : (
|
|
@@ -11,14 +11,18 @@ const MasterpassSecurityInfo: React.FC<MasterpassSecurityInfoProps> = ({
|
|
|
11
11
|
}) => {
|
|
12
12
|
return (
|
|
13
13
|
<div className="flex flex-col items-center gap-2">
|
|
14
|
-
<img
|
|
15
|
-
|
|
14
|
+
<img
|
|
15
|
+
src={mpBlackLogo.src}
|
|
16
|
+
alt="Masterpass"
|
|
17
|
+
className="h-6"
|
|
18
|
+
/>
|
|
19
|
+
<p className="text-xs text-center text-gray-600">
|
|
16
20
|
{texts.masterpassSecurityPrefix}{' '}
|
|
17
21
|
<a
|
|
18
22
|
href="https://www.mastercard.com.tr/tr-tr/tuketiciler/odeme-secenekleri/masterpass.html"
|
|
19
23
|
target="_blank"
|
|
20
24
|
rel="noopener noreferrer"
|
|
21
|
-
className="text-
|
|
25
|
+
className="text-primary underline hover:text-primary-dark font-medium"
|
|
22
26
|
>
|
|
23
27
|
{texts.masterpassSecurityLink}
|
|
24
28
|
</a>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { Modal, Button, Input } from '@akinon/next/components';
|
|
3
3
|
import { LoaderSpinner } from '@akinon/next/components';
|
|
4
|
-
import { useAppSelector } from '@akinon/next/redux/hooks';
|
|
5
4
|
import type { OTPModalProps } from '../types/custom-render.types';
|
|
6
5
|
import mpBlackLogo from '../assets/img/masterpass-black-logo.png';
|
|
7
6
|
import { handleResendOtp } from '../utils/response-handler';
|
|
@@ -12,11 +11,9 @@ const OTPModal: React.FC<OTPModalProps> = ({
|
|
|
12
11
|
onClose,
|
|
13
12
|
onSubmit,
|
|
14
13
|
type,
|
|
15
|
-
responseCode,
|
|
16
14
|
description,
|
|
17
15
|
texts
|
|
18
16
|
}) => {
|
|
19
|
-
const { token, tokenData } = useAppSelector((state) => state.masterpassRest);
|
|
20
17
|
const [otp, setOtp] = useState('');
|
|
21
18
|
const [isLoading, setIsLoading] = useState(false);
|
|
22
19
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -24,13 +21,6 @@ const OTPModal: React.FC<OTPModalProps> = ({
|
|
|
24
21
|
const [canResend, setCanResend] = useState(false);
|
|
25
22
|
const [remainingTime, setRemainingTime] = useState(90);
|
|
26
23
|
|
|
27
|
-
useEffect(() => {
|
|
28
|
-
if (!open) {
|
|
29
|
-
setError(null);
|
|
30
|
-
setOtp('');
|
|
31
|
-
}
|
|
32
|
-
}, [open]);
|
|
33
|
-
|
|
34
24
|
useEffect(() => {
|
|
35
25
|
if (open && type === 'OTP') {
|
|
36
26
|
setRemainingTime(90);
|
|
@@ -48,16 +38,14 @@ const OTPModal: React.FC<OTPModalProps> = ({
|
|
|
48
38
|
|
|
49
39
|
return () => clearInterval(timer);
|
|
50
40
|
}
|
|
51
|
-
}, [open, type
|
|
41
|
+
}, [open, type]);
|
|
52
42
|
|
|
53
43
|
const handleSubmit = async () => {
|
|
54
44
|
setError(null);
|
|
55
45
|
setIsLoading(true);
|
|
56
46
|
try {
|
|
57
47
|
const result = await onSubmit(otp);
|
|
58
|
-
if (result?.
|
|
59
|
-
setOtp('');
|
|
60
|
-
} else if (result?.message) {
|
|
48
|
+
if (result?.message) {
|
|
61
49
|
setError(result.message);
|
|
62
50
|
}
|
|
63
51
|
} catch (error) {
|
|
@@ -71,7 +59,7 @@ const OTPModal: React.FC<OTPModalProps> = ({
|
|
|
71
59
|
setError(null);
|
|
72
60
|
setIsResending(true);
|
|
73
61
|
try {
|
|
74
|
-
const result = await handleResendOtp(
|
|
62
|
+
const result = await handleResendOtp();
|
|
75
63
|
if (result.success) {
|
|
76
64
|
setRemainingTime(90);
|
|
77
65
|
setCanResend(false);
|
|
@@ -102,47 +90,7 @@ const OTPModal: React.FC<OTPModalProps> = ({
|
|
|
102
90
|
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
|
103
91
|
};
|
|
104
92
|
|
|
105
|
-
const getContentByResponseCode = () => {
|
|
106
|
-
switch (responseCode) {
|
|
107
|
-
case '5000':
|
|
108
|
-
return {
|
|
109
|
-
title: texts.rtaVerificationTitle,
|
|
110
|
-
description: texts.rtaVerificationDescription,
|
|
111
|
-
placeholder: texts.rtaVerificationPlaceholder,
|
|
112
|
-
helperText: null
|
|
113
|
-
};
|
|
114
|
-
case '5001':
|
|
115
|
-
return {
|
|
116
|
-
title: texts.bankOtpVerificationTitle,
|
|
117
|
-
description: texts.bankOtpVerificationDescription,
|
|
118
|
-
placeholder: texts.bankOtpVerificationPlaceholder,
|
|
119
|
-
helperText: null
|
|
120
|
-
};
|
|
121
|
-
case '5008':
|
|
122
|
-
return {
|
|
123
|
-
title: texts.phoneVerificationTitle,
|
|
124
|
-
description: texts.phoneVerificationDescription,
|
|
125
|
-
placeholder: texts.phoneVerificationPlaceholder,
|
|
126
|
-
helperText: null
|
|
127
|
-
};
|
|
128
|
-
case '5013':
|
|
129
|
-
return {
|
|
130
|
-
title: texts.cvvVerificationTitle,
|
|
131
|
-
description: texts.cvvVerificationDescription,
|
|
132
|
-
placeholder: texts.cvvVerificationPlaceholder,
|
|
133
|
-
helperText: texts.cvvVerificationHelperText
|
|
134
|
-
};
|
|
135
|
-
default:
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
|
|
140
93
|
const getDescription = () => {
|
|
141
|
-
const responseCodeContent = getContentByResponseCode();
|
|
142
|
-
if (responseCodeContent) {
|
|
143
|
-
return responseCodeContent.description;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
94
|
if (description) {
|
|
147
95
|
return description;
|
|
148
96
|
}
|
|
@@ -156,40 +104,15 @@ const OTPModal: React.FC<OTPModalProps> = ({
|
|
|
156
104
|
}
|
|
157
105
|
};
|
|
158
106
|
|
|
159
|
-
const
|
|
160
|
-
const responseCodeContent = getContentByResponseCode();
|
|
161
|
-
if (responseCodeContent) {
|
|
162
|
-
return responseCodeContent.title;
|
|
163
|
-
}
|
|
164
|
-
|
|
107
|
+
const getMaxLength = () => {
|
|
165
108
|
switch (type) {
|
|
166
109
|
case 'RTA':
|
|
167
|
-
return
|
|
110
|
+
return 3;
|
|
168
111
|
case 'CVV':
|
|
169
|
-
return
|
|
112
|
+
return 3;
|
|
170
113
|
default:
|
|
171
|
-
return
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
const getPlaceholder = () => {
|
|
176
|
-
const responseCodeContent = getContentByResponseCode();
|
|
177
|
-
if (responseCodeContent) {
|
|
178
|
-
return responseCodeContent.placeholder;
|
|
114
|
+
return 6;
|
|
179
115
|
}
|
|
180
|
-
return '';
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const getHelperText = () => {
|
|
184
|
-
const responseCodeContent = getContentByResponseCode();
|
|
185
|
-
return responseCodeContent?.helperText || null;
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const getMaxLength = () => {
|
|
189
|
-
if (responseCode === '5013' || type === 'CVV') {
|
|
190
|
-
return 4;
|
|
191
|
-
}
|
|
192
|
-
return 6;
|
|
193
116
|
};
|
|
194
117
|
|
|
195
118
|
return (
|
|
@@ -203,14 +126,7 @@ const OTPModal: React.FC<OTPModalProps> = ({
|
|
|
203
126
|
className="w-full sm:w-[28rem] max-h-[90vh] overflow-y-auto"
|
|
204
127
|
>
|
|
205
128
|
<div className="px-6">
|
|
206
|
-
{
|
|
207
|
-
<h3 className="text-center mt-4 text-lg font-semibold">
|
|
208
|
-
{getTitle()}
|
|
209
|
-
</h3>
|
|
210
|
-
)}
|
|
211
|
-
<p className="text-center mt-2 text-sm text-gray-600">
|
|
212
|
-
{getDescription()}
|
|
213
|
-
</p>
|
|
129
|
+
<p className="text-center mt-4 text-lg">{getDescription()}</p>
|
|
214
130
|
<div className="flex flex-col gap-3 p-5 w-3/4 m-auto">
|
|
215
131
|
<Input
|
|
216
132
|
type="text"
|
|
@@ -225,18 +141,12 @@ const OTPModal: React.FC<OTPModalProps> = ({
|
|
|
225
141
|
pattern="[0-9]*"
|
|
226
142
|
inputMode="numeric"
|
|
227
143
|
className="text-center"
|
|
228
|
-
placeholder={getPlaceholder()}
|
|
229
144
|
/>
|
|
230
|
-
{getHelperText() && (
|
|
231
|
-
<p className="text-xs text-gray-500 text-center">
|
|
232
|
-
{getHelperText()}
|
|
233
|
-
</p>
|
|
234
|
-
)}
|
|
235
145
|
{error && <p className="text-error text-xs text-center">{error}</p>}
|
|
236
146
|
<Button
|
|
237
147
|
className="py-3 h-auto"
|
|
238
148
|
onClick={handleSubmit}
|
|
239
|
-
disabled={isLoading || otp.length
|
|
149
|
+
disabled={isLoading || otp.length !== getMaxLength()}
|
|
240
150
|
>
|
|
241
151
|
{isLoading ? (
|
|
242
152
|
<LoaderSpinner className="w-4 h-4" />
|