@akinon/pz-masterpass-rest 2.0.33-beta.0 → 2.0.33

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.
@@ -1,6 +1,7 @@
1
- import React, { useState } from 'react';
1
+ import React, { useMemo, useState } from 'react';
2
2
  import { Input, Icon } from '@akinon/next/components';
3
3
  import { detectCardType, getCardIcon, getCvcLength } from '../utils/card-utils';
4
+ import { getCappedRewardTotal } from '../utils/reward-utils';
4
5
  import type { CardListProps } from '../types/custom-render.types';
5
6
  import MasterpassSecurityInfo from './masterpass-security-info';
6
7
 
@@ -13,6 +14,12 @@ const CardList: React.FC<CardListProps> = ({
13
14
  cvc = '',
14
15
  onCvcChange,
15
16
  cvcRequired = false,
17
+ availableRewards = [],
18
+ selectedRewards = [],
19
+ isLoadingRewards = false,
20
+ onOpenRewardModal,
21
+ rewardCurrency,
22
+ rewardPayableAmount,
16
23
  texts
17
24
  }) => {
18
25
  const [localCvc, setLocalCvc] = useState(cvc);
@@ -23,9 +30,21 @@ const CardList: React.FC<CardListProps> = ({
23
30
  onCvcChange?.(numericValue);
24
31
  };
25
32
 
33
+ const orderedCards = useMemo(() => {
34
+ if (!selectedCard) return cards;
35
+ const idx = cards.findIndex(
36
+ (c) => c.uniqueCardNumber === selectedCard.uniqueCardNumber
37
+ );
38
+ if (idx <= 0) return cards;
39
+ const reordered = [...cards];
40
+ const [picked] = reordered.splice(idx, 1);
41
+ reordered.unshift(picked);
42
+ return reordered;
43
+ }, [cards, selectedCard]);
44
+
26
45
  return (
27
46
  <div className="flex flex-col gap-4">
28
- {cards.map((card) => {
47
+ {orderedCards.map((card) => {
29
48
  const isSelected =
30
49
  selectedCard?.uniqueCardNumber === card.uniqueCardNumber;
31
50
  const expectedCvcLength = getCvcLength(detectCardType(card.cardBin));
@@ -119,6 +138,57 @@ const CardList: React.FC<CardListProps> = ({
119
138
  )}
120
139
  </div>
121
140
 
141
+ {isSelected &&
142
+ (isLoadingRewards || availableRewards.length > 0) && (
143
+ <div
144
+ className="border-t border-[#eeeeee] p-4 bg-[#fafafa] flex items-center justify-between gap-3"
145
+ onClick={(e) => e.stopPropagation()}
146
+ >
147
+ <div className="flex items-center gap-2 min-w-0">
148
+ <Icon name="gift" size={16} className="text-[#000000]" />
149
+ <div className="min-w-0">
150
+ <p className="text-sm font-medium text-[#4a4f54] truncate">
151
+ {texts.rewardOpenButtonText}
152
+ </p>
153
+ {selectedRewards.length > 0 ? (
154
+ <p className="text-xs text-[#00a63d] truncate">
155
+ {(texts.rewardSelectedSummaryText || '').replace(
156
+ '{count}',
157
+ String(selectedRewards.length)
158
+ )}
159
+ {' · '}
160
+ {getCappedRewardTotal(
161
+ selectedRewards,
162
+ rewardPayableAmount
163
+ ).toFixed(2)}{' '}
164
+ {(rewardCurrency || '').toUpperCase()}
165
+ </p>
166
+ ) : (
167
+ <p className="text-xs text-[#9d9d9d] truncate">
168
+ {(texts.rewardAvailableSummaryText || '').replace(
169
+ '{count}',
170
+ String(availableRewards.length)
171
+ )}
172
+ </p>
173
+ )}
174
+ </div>
175
+ </div>
176
+ <button
177
+ type="button"
178
+ onClick={(e) => {
179
+ e.stopPropagation();
180
+ onOpenRewardModal?.();
181
+ }}
182
+ disabled={isLoadingRewards}
183
+ className="px-3 py-1.5 text-xs border border-[#000000] text-[#000000] hover:bg-[#000000] hover:text-white transition-colors disabled:opacity-50 shrink-0"
184
+ >
185
+ {selectedRewards.length > 0
186
+ ? texts.rewardModalConfirmText
187
+ : texts.rewardOpenButtonText}
188
+ </button>
189
+ </div>
190
+ )}
191
+
122
192
  {isSelected && cvcRequired && (
123
193
  <div
124
194
  className="border-t border-[#eeeeee] p-4 bg-[#f7f7f7]"
@@ -0,0 +1,194 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { Modal, Button } from '@akinon/next/components';
3
+ import { RewardSelectionModalProps } from '../types/custom-render.types';
4
+ import type { RewardItem, RewardCategory } from '../types/payment.types';
5
+ import { parseRewardAmount, getCappedRewardAmounts } from '../utils/reward-utils';
6
+
7
+ const formatAmount = (amount: number | string, currency?: string) => {
8
+ const formatted = parseRewardAmount(amount).toFixed(2);
9
+ return currency ? `${formatted} ${currency.toUpperCase()}` : formatted;
10
+ };
11
+
12
+ const isSameReward = (a: RewardItem, b: RewardItem) => a.type === b.type;
13
+
14
+ const RewardSelectionModal: React.FC<RewardSelectionModalProps> = ({
15
+ open,
16
+ onClose,
17
+ onConfirm,
18
+ rewards,
19
+ selectedRewards,
20
+ isLoading = false,
21
+ currency,
22
+ payableAmount,
23
+ texts
24
+ }) => {
25
+ const [draft, setDraft] = useState<RewardItem[]>(selectedRewards);
26
+
27
+ useEffect(() => {
28
+ if (open) {
29
+ setDraft(selectedRewards);
30
+ }
31
+ }, [open, selectedRewards]);
32
+
33
+ const cappedAmounts = useMemo(
34
+ () => getCappedRewardAmounts(draft, payableAmount),
35
+ [draft, payableAmount]
36
+ );
37
+
38
+ const grouped = useMemo(() => {
39
+ const map: Record<RewardCategory, RewardItem[]> = {
40
+ special: [],
41
+ general: []
42
+ };
43
+ rewards.forEach((reward) => {
44
+ if (map[reward.type]) {
45
+ map[reward.type].push(reward);
46
+ }
47
+ });
48
+ return map;
49
+ }, [rewards]);
50
+
51
+ const toggleReward = (reward: RewardItem) => {
52
+ setDraft((current) => {
53
+ const exists = current.some((item) => isSameReward(item, reward));
54
+ if (exists) {
55
+ return current.filter((item) => !isSameReward(item, reward));
56
+ }
57
+ const withoutSameType = current.filter(
58
+ (item) => item.type !== reward.type
59
+ );
60
+ return [...withoutSameType, reward];
61
+ });
62
+ };
63
+
64
+ const isSelected = (reward: RewardItem) =>
65
+ draft.some((item) => isSameReward(item, reward));
66
+
67
+ const categoryLabel = (category: RewardCategory) =>
68
+ category === 'special'
69
+ ? texts.rewardCategorySpecialText
70
+ : texts.rewardCategoryGeneralText;
71
+
72
+ const handleConfirm = async () => {
73
+ await onConfirm(draft);
74
+ };
75
+
76
+ const hasRewards = rewards.length > 0;
77
+
78
+ return (
79
+ <Modal
80
+ portalId="masterpass-reward-modal"
81
+ open={open}
82
+ setOpen={onClose}
83
+ title={texts.rewardModalTitle}
84
+ className="w-full sm:w-[32rem] max-h-[90vh] overflow-y-auto"
85
+ >
86
+ <div className="px-6 pt-4 pb-6">
87
+ {texts.rewardModalDescription && (
88
+ <p className="text-sm text-gray-600 mb-4">
89
+ {texts.rewardModalDescription}
90
+ </p>
91
+ )}
92
+
93
+ {!hasRewards ? (
94
+ <p className="text-center text-gray-500 py-8">
95
+ {texts.rewardModalEmptyMessage}
96
+ </p>
97
+ ) : (
98
+ <div className="space-y-5">
99
+ {(Object.keys(grouped) as RewardCategory[])
100
+ .filter((category) => grouped[category].length > 0)
101
+ .map((category) => (
102
+ <div key={category}>
103
+ <h3 className="text-sm font-semibold text-gray-700 mb-2">
104
+ {categoryLabel(category)}
105
+ </h3>
106
+ <ul className="space-y-2">
107
+ {grouped[category].map((reward) => {
108
+ const selected = isSelected(reward);
109
+ const fullValue = parseRewardAmount(reward.amount);
110
+ const capped = cappedAmounts[reward.type];
111
+ const isCapped = selected && capped < fullValue;
112
+ return (
113
+ <li key={`${reward.type}-${reward.name ?? ''}`}>
114
+ <label
115
+ className={`flex items-center justify-between gap-3 px-3 py-3 border cursor-pointer transition-colors ${
116
+ selected
117
+ ? 'border-primary bg-primary/5'
118
+ : 'border-gray-200 hover:bg-gray-50'
119
+ }`}
120
+ >
121
+ <div className="flex items-center gap-3">
122
+ <input
123
+ type="checkbox"
124
+ className="h-4 w-4"
125
+ checked={selected}
126
+ onChange={() => toggleReward(reward)}
127
+ disabled={isLoading}
128
+ />
129
+ <div>
130
+ <p className="font-medium">
131
+ {reward.name || categoryLabel(reward.type)}
132
+ </p>
133
+ {reward.name && (
134
+ <p className="text-xs text-gray-500">
135
+ {categoryLabel(reward.type)}
136
+ </p>
137
+ )}
138
+ </div>
139
+ </div>
140
+ <div className="text-right">
141
+ <span
142
+ className={`font-semibold ${
143
+ isCapped
144
+ ? 'text-gray-400 line-through text-sm'
145
+ : ''
146
+ }`}
147
+ >
148
+ {formatAmount(reward.amount, currency)}
149
+ </span>
150
+ {isCapped && (
151
+ <p className="text-xs text-[#00a63d] font-medium mt-0.5">
152
+ {(texts.rewardCappedNoticeText || '').replace(
153
+ '{amount}',
154
+ formatAmount(capped, currency)
155
+ )}
156
+ </p>
157
+ )}
158
+ </div>
159
+ </label>
160
+ </li>
161
+ );
162
+ })}
163
+ </ul>
164
+ </div>
165
+ ))}
166
+ </div>
167
+ )}
168
+
169
+ <div className="flex gap-3 justify-end mt-6">
170
+ <Button
171
+ appearance="outlined"
172
+ className="px-5 py-3 h-auto"
173
+ onClick={onClose}
174
+ disabled={isLoading}
175
+ >
176
+ {texts.rewardModalCancelText}
177
+ </Button>
178
+ <Button
179
+ appearance="filled"
180
+ className="px-5 py-3 h-auto"
181
+ onClick={handleConfirm}
182
+ disabled={isLoading || !hasRewards}
183
+ >
184
+ {isLoading
185
+ ? texts.rewardModalLoadingText
186
+ : texts.rewardModalConfirmText}
187
+ </Button>
188
+ </div>
189
+ </div>
190
+ </Modal>
191
+ );
192
+ };
193
+
194
+ export default RewardSelectionModal;
@@ -5,7 +5,9 @@ import {
5
5
  useSetMasterpassRestBinNumberMutation,
6
6
  useCheckoutMasterpassInstallmentMutation,
7
7
  usePrepareMasterpassOrderMutation,
8
- useFinalizeMasterpassOrderMutation
8
+ useFinalizeMasterpassOrderMutation,
9
+ useQueryMasterpassRewardsMutation,
10
+ useSelectMasterpassRewardsMutation
9
11
  } from '../redux/api';
10
12
  import {
11
13
  updatePaymentState,
@@ -14,7 +16,12 @@ import {
14
16
  setInstallments,
15
17
  setCardType,
16
18
  setOrderData,
17
- setOrderCompleted
19
+ setOrderCompleted,
20
+ setAvailableRewards,
21
+ setSelectedRewards,
22
+ setLoadingRewards,
23
+ setShowRewardModal,
24
+ clearRewards
18
25
  } from '../redux/reducer';
19
26
  import { useMasterpassToken } from './useMasterpassToken';
20
27
  import { PaymentService } from '../services/payment';
@@ -24,8 +31,9 @@ import {
24
31
  getTransactionType
25
32
  } from '../utils/payment-utils';
26
33
  import { handleMasterpassResponse } from '../utils/response-handler';
27
- import type { Installment } from '../types/payment.types';
34
+ import type { Installment, RewardItem } from '../types/payment.types';
28
35
  import type { CardModel } from '../types/account.types';
36
+ import { getCappedRewardAmounts } from '../utils/reward-utils';
29
37
 
30
38
  export type PaymentResult = {
31
39
  success: boolean;
@@ -35,7 +43,14 @@ export type PaymentResult = {
35
43
  message?: any;
36
44
  };
37
45
 
38
- export const useMasterpassPayment = () => {
46
+ export interface UseMasterpassPaymentOptions {
47
+ enableRewards?: boolean;
48
+ }
49
+
50
+ export const useMasterpassPayment = (
51
+ options: UseMasterpassPaymentOptions = {}
52
+ ) => {
53
+ const { enableRewards = false } = options;
39
54
  const dispatch = useDispatch();
40
55
  const {
41
56
  paymentState,
@@ -46,6 +61,13 @@ export const useMasterpassPayment = () => {
46
61
  tokenData
47
62
  } = useAppSelector((state) => state.masterpassRest);
48
63
 
64
+ const payableAmount = useAppSelector(
65
+ (state) =>
66
+ state.checkout?.preOrder?.unpaid_amount ??
67
+ state.checkout?.preOrder?.total_amount_with_interest ??
68
+ null
69
+ );
70
+
49
71
  const { refetch: refetchToken } = useMasterpassToken({
50
72
  useThreeD: paymentState.useThreeD
51
73
  });
@@ -62,6 +84,12 @@ export const useMasterpassPayment = () => {
62
84
  const [finalizeMasterpassOrder, { isLoading: isFinalizeLoading }] =
63
85
  useFinalizeMasterpassOrderMutation();
64
86
 
87
+ const [queryMasterpassRewards, { isLoading: isRewardsQueryLoading }] =
88
+ useQueryMasterpassRewardsMutation();
89
+
90
+ const [selectMasterpassRewards, { isLoading: isRewardsSelectLoading }] =
91
+ useSelectMasterpassRewardsMutation();
92
+
65
93
  const refreshTokenForPayment = useCallback(async () => {
66
94
  const result = await refetchToken({ skipReset: true });
67
95
  if (result?.data?.token) {
@@ -97,12 +125,38 @@ export const useMasterpassPayment = () => {
97
125
  [checkoutMasterpassInstallment, dispatch]
98
126
  );
99
127
 
128
+ const fetchRewardsForCard = useCallback(
129
+ async (card: CardModel) => {
130
+ if (!card?.cardAlias) return;
131
+
132
+ dispatch(setLoadingRewards(true));
133
+
134
+ try {
135
+ const result = await queryMasterpassRewards({
136
+ card_alias: card.cardAlias
137
+ }).unwrap();
138
+
139
+ const rewards =
140
+ result.context_list?.[0]?.page_context?.rewards || [];
141
+ dispatch(setAvailableRewards(rewards));
142
+ } catch (error) {
143
+ dispatch(setAvailableRewards([]));
144
+ } finally {
145
+ dispatch(setLoadingRewards(false));
146
+ }
147
+ },
148
+ [queryMasterpassRewards, dispatch]
149
+ );
150
+
100
151
  const handleCardSelect = useCallback(
101
152
  async (card: CardModel) => {
102
153
  dispatch(setSelectedCard(card));
103
154
  dispatch(setInstallments([]));
104
155
  dispatch(setSelectedInstallment(null));
105
156
  dispatch(setCardType(null));
157
+ if (enableRewards) {
158
+ dispatch(clearRewards());
159
+ }
106
160
 
107
161
  try {
108
162
  const result = await setMasterpassRestBinNumber({
@@ -119,11 +173,53 @@ export const useMasterpassPayment = () => {
119
173
  await handleInstallmentSelect(pageContext.installments[0]);
120
174
  }
121
175
  }
176
+
177
+ if (enableRewards) {
178
+ void fetchRewardsForCard(card);
179
+ }
122
180
  } catch (error) {
123
181
  // Silently handle errors (e.g., mutation aborted due to rapid selection)
124
182
  }
125
183
  },
126
- [setMasterpassRestBinNumber, handleInstallmentSelect, dispatch]
184
+ [
185
+ enableRewards,
186
+ setMasterpassRestBinNumber,
187
+ handleInstallmentSelect,
188
+ fetchRewardsForCard,
189
+ dispatch
190
+ ]
191
+ );
192
+
193
+ const openRewardModal = useCallback(() => {
194
+ dispatch(setShowRewardModal(true));
195
+ }, [dispatch]);
196
+
197
+ const closeRewardModal = useCallback(() => {
198
+ dispatch(setShowRewardModal(false));
199
+ }, [dispatch]);
200
+
201
+ const confirmRewards = useCallback(
202
+ async (selected: RewardItem[]) => {
203
+ const capped = getCappedRewardAmounts(selected, payableAmount);
204
+ const special = capped.special.toFixed(2);
205
+ const general = capped.general.toFixed(2);
206
+
207
+ try {
208
+ await selectMasterpassRewards({ special, general }).unwrap();
209
+ dispatch(setSelectedRewards(selected));
210
+ dispatch(setShowRewardModal(false));
211
+ return { success: true };
212
+ } catch (error) {
213
+ return {
214
+ success: false,
215
+ message:
216
+ error instanceof Error
217
+ ? error.message
218
+ : 'Failed to apply rewards'
219
+ };
220
+ }
221
+ },
222
+ [selectMasterpassRewards, payableAmount, dispatch]
127
223
  );
128
224
 
129
225
  const processPayment = useCallback(async (): Promise<PaymentResult> => {
@@ -346,11 +442,18 @@ export const useMasterpassPayment = () => {
346
442
  isInstallmentLoading,
347
443
  isPrepareLoading,
348
444
  isFinalizeLoading,
445
+ isRewardsQueryLoading,
446
+ isRewardsSelectLoading,
447
+ payableAmount,
349
448
 
350
449
  updatePaymentState: updatePaymentStateAction,
351
450
  handleCardSelect,
352
451
  handleInstallmentSelect,
353
452
  processPayment,
354
- processDirectPayment
453
+ processDirectPayment,
454
+ fetchRewardsForCard,
455
+ openRewardModal,
456
+ closeRewardModal,
457
+ confirmRewards
355
458
  };
356
459
  };
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export { default as MasterpassRestOption } from './views/masterpass-rest-option'
4
4
 
5
5
  export { default as CreditCardForm } from './components/credit-card-form';
6
6
  export { default as PaymentMethodSelector } from './components/payment-method-selector';
7
+ export { default as RewardSelectionModal } from './components/reward-selection-modal';
7
8
 
8
9
  export { default as masterpassRestReducer } from './redux/reducer';
9
10
 
@@ -14,6 +15,7 @@ export { useMasterpassScript } from './hooks/useMasterpassScript';
14
15
 
15
16
  export * from './utils/payment-constants';
16
17
  export * from './utils/payment-utils';
18
+ export * from './utils/reward-utils';
17
19
  export * from './utils/card-utils';
18
20
  export * from './utils/validation-schemas';
19
21
  export * from './utils/masterpass-sdk';
package/src/redux/api.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { api } from '@akinon/next/data/client/api';
2
2
  import { buildClientRequestUrl } from '@akinon/next/utils';
3
3
  import { setPaymentStepBusy } from '@akinon/next/redux/reducers/checkout';
4
+ import type { RewardItem } from '../types/payment.types';
4
5
 
5
6
  export interface MasterpassRestTokenRequest {
6
7
  three_d?: boolean;
@@ -97,6 +98,39 @@ interface CardType {
97
98
  logo: string;
98
99
  }
99
100
 
101
+ export interface QueryMasterpassRewardsRequest {
102
+ card_alias: string;
103
+ }
104
+
105
+ export interface QueryMasterpassRewardsResponse {
106
+ context_list: Array<{
107
+ page_context: {
108
+ rewards: RewardItem[];
109
+ };
110
+ page_name: string;
111
+ page_slug: string;
112
+ }>;
113
+ template_name: string;
114
+ errors: any;
115
+ pre_order: any;
116
+ }
117
+
118
+ export interface SelectMasterpassRewardsRequest {
119
+ special: string;
120
+ general: string;
121
+ }
122
+
123
+ export interface SelectMasterpassRewardsResponse {
124
+ context_list: Array<{
125
+ page_context: any;
126
+ page_name: string;
127
+ page_slug: string;
128
+ }>;
129
+ template_name: string;
130
+ errors: any;
131
+ pre_order: any;
132
+ }
133
+
100
134
  export interface SetMasterpassRestBinNumberResponse {
101
135
  context_list: Array<{
102
136
  page_context: {
@@ -202,6 +236,51 @@ export const masterpassRestApi = api.injectEndpoints({
202
236
  dispatch(setPaymentStepBusy(false));
203
237
  }
204
238
  }),
239
+ queryMasterpassRewards: build.mutation<
240
+ QueryMasterpassRewardsResponse,
241
+ QueryMasterpassRewardsRequest
242
+ >({
243
+ query: (arg) => ({
244
+ url: buildClientRequestUrl('/orders/checkout/', {
245
+ useFormData: true
246
+ }),
247
+ method: 'POST',
248
+ params: {
249
+ page: 'MasterpassRestRewardListPage'
250
+ },
251
+ body: {
252
+ card_alias: arg.card_alias
253
+ }
254
+ }),
255
+ async onQueryStarted(_, { dispatch, queryFulfilled }) {
256
+ dispatch(setPaymentStepBusy(true));
257
+ await queryFulfilled;
258
+ dispatch(setPaymentStepBusy(false));
259
+ }
260
+ }),
261
+ selectMasterpassRewards: build.mutation<
262
+ SelectMasterpassRewardsResponse,
263
+ SelectMasterpassRewardsRequest
264
+ >({
265
+ query: (arg) => ({
266
+ url: buildClientRequestUrl('/orders/checkout/', {
267
+ useFormData: true
268
+ }),
269
+ method: 'POST',
270
+ params: {
271
+ page: 'MasterpassRestRewardSelectionPage'
272
+ },
273
+ body: {
274
+ special: arg.special,
275
+ general: arg.general
276
+ }
277
+ }),
278
+ async onQueryStarted(_, { dispatch, queryFulfilled }) {
279
+ dispatch(setPaymentStepBusy(true));
280
+ await queryFulfilled;
281
+ dispatch(setPaymentStepBusy(false));
282
+ }
283
+ }),
205
284
  finalizeMasterpassOrder: build.mutation<
206
285
  FinalizeMasterpassOrderResponse,
207
286
  FinalizeMasterpassOrderRequest
@@ -239,5 +318,7 @@ export const {
239
318
  useSetMasterpassRestBinNumberMutation,
240
319
  useCheckoutMasterpassInstallmentMutation,
241
320
  usePrepareMasterpassOrderMutation,
242
- useFinalizeMasterpassOrderMutation
321
+ useFinalizeMasterpassOrderMutation,
322
+ useQueryMasterpassRewardsMutation,
323
+ useSelectMasterpassRewardsMutation
243
324
  } = masterpassRestApi;
@@ -5,7 +5,8 @@ import {
5
5
  ModalState,
6
6
  Installment,
7
7
  CardType,
8
- OrderData
8
+ OrderData,
9
+ RewardItem
9
10
  } from '../types/payment.types';
10
11
  import { CreditCardFormData } from '../utils/validation-schemas';
11
12
 
@@ -68,13 +69,17 @@ const initialState: MasterpassRestState = {
68
69
  orderData: null,
69
70
  orderCompleted: false,
70
71
  cvc: '',
71
- useThreeD: true
72
+ useThreeD: true,
73
+ availableRewards: [],
74
+ selectedRewards: [],
75
+ isLoadingRewards: false
72
76
  },
73
77
  modalState: {
74
78
  showLinkModal: false,
75
79
  showOTPModal: false,
76
80
  show3DSecureModal: false,
77
81
  showInformationModal: false,
82
+ showRewardModal: false,
78
83
  informationModalData: null,
79
84
  otpType: 'OTP',
80
85
  verificationData: null,
@@ -169,6 +174,24 @@ const masterpassRestSlice = createSlice({
169
174
  setOrderCompleted: (state, action: PayloadAction<boolean>) => {
170
175
  state.paymentState.orderCompleted = action.payload;
171
176
  },
177
+ setAvailableRewards: (state, action: PayloadAction<RewardItem[]>) => {
178
+ state.paymentState.availableRewards = action.payload;
179
+ },
180
+ setSelectedRewards: (state, action: PayloadAction<RewardItem[]>) => {
181
+ state.paymentState.selectedRewards = action.payload;
182
+ },
183
+ setLoadingRewards: (state, action: PayloadAction<boolean>) => {
184
+ state.paymentState.isLoadingRewards = action.payload;
185
+ },
186
+ setShowRewardModal: (state, action: PayloadAction<boolean>) => {
187
+ state.modalState.showRewardModal = action.payload;
188
+ },
189
+ clearRewards: (state) => {
190
+ state.paymentState.availableRewards = [];
191
+ state.paymentState.selectedRewards = [];
192
+ state.paymentState.isLoadingRewards = false;
193
+ state.modalState.showRewardModal = false;
194
+ },
172
195
  updateModalState: (state, action: PayloadAction<Partial<ModalState>>) => {
173
196
  state.modalState = { ...state.modalState, ...action.payload };
174
197
  },
@@ -234,13 +257,17 @@ const masterpassRestSlice = createSlice({
234
257
  orderData: null,
235
258
  orderCompleted: false,
236
259
  cvc: '',
237
- useThreeD: true
260
+ useThreeD: true,
261
+ availableRewards: [],
262
+ selectedRewards: [],
263
+ isLoadingRewards: false
238
264
  };
239
265
  state.modalState = {
240
266
  showLinkModal: false,
241
267
  showOTPModal: false,
242
268
  show3DSecureModal: false,
243
269
  showInformationModal: false,
270
+ showRewardModal: false,
244
271
  informationModalData: null,
245
272
  otpType: 'OTP',
246
273
  verificationData: null,
@@ -276,6 +303,11 @@ export const {
276
303
  setOrderData,
277
304
  setCvc,
278
305
  setOrderCompleted,
306
+ setAvailableRewards,
307
+ setSelectedRewards,
308
+ setLoadingRewards,
309
+ setShowRewardModal,
310
+ clearRewards,
279
311
  updateModalState,
280
312
  setShowLinkModal,
281
313
  setShowOTPModal,