@akinon/pz-masterpass-rest 2.0.16 → 2.0.17-rc.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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # @akinon/pz-masterpass-rest
2
2
 
3
+ ## 2.0.17-rc.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 823a4449: ZERO-4193: Upgrade TypeScript to version 5.2.2 across multiple packages
8
+ - 5da13fff: ZERO-4358: fix masterpass-rest guest checkout flow
9
+ - fb4aa9b4: ZERO-4333: update maxLength for cardNumber input and enhance button styling in installment list
10
+ - d51fa68e: ZERO-4399: add card rewards to pz-masterpass-rest
11
+ - 63a72000: ZERO-3895: enhance payment processing with additional fields support
12
+
3
13
  ## 2.0.16
4
14
 
5
15
  ## 2.0.15
package/docs/USAGE.md CHANGED
@@ -15,6 +15,7 @@
15
15
  - [LinkModal](#5-linkmodal)
16
16
  - [OTPModal](#6-otpmodal)
17
17
  - [ConfirmationModal](#7-confirmationmodal)
18
+ - [RewardSelectionModal](#7b-rewardselectionmodal)
18
19
  - [ErrorDisplay](#8-errordisplay)
19
20
  - [LoadingState](#9-loadingstate)
20
21
  - [EmptyState](#10-emptystate)
@@ -76,6 +77,7 @@ export default MasterpassRest
76
77
  | `environment` | `'production' \| 'development'` | `undefined` | SDK environment |
77
78
  | `texts` | `MasterpassRestOptionTexts` | `defaultTexts` | UI text overrides for localization |
78
79
  | `customRender` | `MasterpassRestOptionCustomRender` | `undefined` | Custom render functions |
80
+ | `enableRewards` | `boolean` | `false` | Opt in to the card rewards (FBB/BNS) feature. Requires backend support for `MasterpassRestRewardListPage` and `MasterpassRestRewardSelectionPage`. |
79
81
 
80
82
  ```tsx
81
83
  <PluginModule
@@ -90,6 +92,28 @@ export default MasterpassRest
90
92
  />
91
93
  ```
92
94
 
95
+ ### Enabling the Rewards feature
96
+
97
+ The card rewards feature (FBB / BNS query + selection) is **disabled by default** to keep existing integrations unaffected. To opt in:
98
+
99
+ ```tsx
100
+ <MasterpassRestOption
101
+ locale="tr"
102
+ currency="try"
103
+ enableRewards
104
+ />
105
+ ```
106
+
107
+ When `enableRewards` is `true`:
108
+ - After a saved card is selected, the package automatically calls `MasterpassRestRewardListPage` to fetch available rewards.
109
+ - A "Use Rewards" CTA appears inline on the selected card (default `CardList`).
110
+ - A modal is mounted at the bottom of the view for selection (`RewardSelectionModal`).
111
+ - On confirm, the selection is sent to `MasterpassRestRewardSelectionPage`. The chosen rewards are then forwarded to Masterpass via `additional_fields.rewardList` server-side during `prepareMasterpassOrder` — no frontend wiring needed.
112
+
113
+ Both backend pages must exist on your environment. If they don't, the package silently falls back (no rewards shown, no UI errors), but you'll see failed network requests in devtools.
114
+
115
+ See [RewardSelectionModal](#7b-rewardselectionmodal) for custom render options including a no-modal inline pattern.
116
+
93
117
  ---
94
118
 
95
119
  ## Custom Rendering
@@ -107,6 +131,7 @@ type MasterpassRestOptionCustomRender = {
107
131
  linkModal?: (props: LinkModalProps) => ReactElement
108
132
  otpModal?: (props: OTPModalProps) => ReactElement
109
133
  confirmationModal?: (props: ConfirmationModalProps) => ReactElement
134
+ rewardSelectionModal?: (props: RewardSelectionModalProps) => ReactElement
110
135
  errorDisplay?: (props: ErrorDisplayProps) => ReactElement
111
136
  loadingState?: (props: LoadingStateProps) => ReactElement
112
137
  emptyState?: (props: EmptyStateProps) => ReactElement
@@ -171,35 +196,58 @@ Displays the user's saved cards with selection, CVC input, and remove functional
171
196
 
172
197
  ```typescript
173
198
  type CardListProps = {
174
- cards: any[] // Array of CardModel objects
175
- onCardSelect: (card: any) => void // Called when a card is selected
176
- selectedCard?: any | null // Currently selected card
177
- onRemove?: (card: any) => void // Called to initiate card removal
178
- removingCardId?: string | null // Card being removed (shows loader)
179
- cvc?: string // Current CVC input value
180
- onCvcChange?: (cvc: string) => void // CVC input change handler
181
- cvcRequired?: boolean // Whether CVC field is shown
199
+ cards: any[]
200
+ onCardSelect: (card: any) => void
201
+ selectedCard?: any | null
202
+ onRemove?: (card: any) => void
203
+ removingCardId?: string | null
204
+ cvc?: string
205
+ onCvcChange?: (cvc: string) => void
206
+ cvcRequired?: boolean
207
+
208
+ availableRewards?: RewardItem[]
209
+ selectedRewards?: RewardItem[]
210
+ isLoadingRewards?: boolean
211
+ isConfirmingRewards?: boolean
212
+ onOpenRewardModal?: () => void
213
+ onConfirmRewards?: (selected: RewardItem[]) => Promise<void> | void
214
+ rewardCurrency?: string
215
+ rewardPayableAmount?: string | number | null
216
+
182
217
  texts: MasterpassRestOptionTexts
183
218
  }
184
219
  ```
185
220
 
221
+ `cards`, `onCardSelect`, `selectedCard`, `onRemove`, `removingCardId`, `cvc`, `onCvcChange`, `cvcRequired` are the standard card props. The reward props are only populated when `enableRewards` is set on `MasterpassRestOption`; when disabled they are `undefined` and existing card-list themes keep working unchanged.
222
+
223
+ Reward prop behavior:
224
+ - `availableRewards` — list returned by `MasterpassRestRewardListPage` for the currently selected card.
225
+ - `selectedRewards` — what the user has confirmed so far (persisted in Redux).
226
+ - `isLoadingRewards` — true while the list is being fetched.
227
+ - `isConfirmingRewards` — true while the selection is being posted.
228
+ - `onOpenRewardModal` — opens the default `RewardSelectionModal`.
229
+ - `onConfirmRewards` — bypasses the modal entirely; posts a selection directly. The cumulative cap (special → general) is applied inside before the network call.
230
+ - `rewardCurrency` — display-only ISO code, e.g. `'TRY'`.
231
+ - `rewardPayableAmount` — the order's unpaid amount as a string. Pass this to `getCappedRewardTotal(selected, rewardPayableAmount)` if you render the redeemable total in your inline picker.
232
+
186
233
  **CardModel structure (each item in `cards` array):**
187
234
 
188
235
  ```typescript
189
236
  interface CardModel {
190
- cardAlias: string // 'My Visa Card'
191
- maskedCardNumber: string // '534261******1234'
192
- uniqueCardNumber: string // Unique identifier
237
+ cardAlias: string
238
+ maskedCardNumber: string
239
+ uniqueCardNumber: string
193
240
  cardType: 'Credit' | 'Debit' | 'Unknown' | ''
194
- cardBin: string // First 6 digits
241
+ cardBin: string
195
242
  isDefaultCard: boolean
196
243
  expireSoon: boolean
197
244
  isExpired: boolean
198
245
  cardValidationType: 'OTP' | 'RTA' | '_3D' | 'Unknown' | ''
199
- // ...more fields
200
246
  }
201
247
  ```
202
248
 
249
+ `cardAlias` is the user-friendly name (e.g. `"My Visa Card"`); `cardBin` is the first 6 digits; `uniqueCardNumber` is the stable identifier. More fields are available — see `account.types.ts`.
250
+
203
251
  **Example:**
204
252
 
205
253
  ```tsx
@@ -234,7 +282,6 @@ customRender: {
234
282
  </button>
235
283
  </div>
236
284
 
237
- {/* CVC input for selected card */}
238
285
  {selectedCard?.uniqueCardNumber === card.uniqueCardNumber && (
239
286
  <input
240
287
  type="text"
@@ -597,6 +644,105 @@ customRender: {
597
644
 
598
645
  ---
599
646
 
647
+ ### 7b. RewardSelectionModal
648
+
649
+ Modal that lets the customer pick which card rewards (FBB / BNS) to apply. Only mounted when `enableRewards={true}` is passed to `MasterpassRestOption`. Requires backend support for `MasterpassRestRewardListPage` and `MasterpassRestRewardSelectionPage`.
650
+
651
+ **Props:**
652
+
653
+ ```typescript
654
+ type RewardSelectionModalProps = {
655
+ open: boolean
656
+ onClose: () => void
657
+ onConfirm: (selected: RewardItem[]) => Promise<void> | void
658
+ rewards: RewardItem[]
659
+ selectedRewards: RewardItem[]
660
+ isLoading?: boolean
661
+ currency?: string
662
+ payableAmount?: string | number | null
663
+ texts: MasterpassRestOptionTexts
664
+ }
665
+
666
+ type RewardItem = {
667
+ type: 'special' | 'general'
668
+ amount: number | string
669
+ name?: 'FBB' | 'BNS'
670
+ }
671
+ ```
672
+
673
+ `RewardItem.type` is the category — `'special'` maps to `FBB`, `'general'` maps to `BNS`. `amount` is what the backend currently returns (string like `"180.00"`); both `string` and `number` are accepted. `name` is the optional raw code.
674
+
675
+ #### Cumulative cap behavior
676
+
677
+ When `payableAmount` is provided, the modal shows the **actual amount that will be redeemed**, not the full reward value. The cap is cumulative across both categories with a fixed priority — `special` first, then `general`:
678
+
679
+ ```
680
+ payable = 100.00
681
+ special reward = 180.00 → 100.00 used (capped, remaining = 0)
682
+ general reward = 50.00 → 0.00 used (no budget left)
683
+ ```
684
+
685
+ When a reward is selected and capped, the modal:
686
+ - Shows the full reward amount struck through.
687
+ - Displays a green note below it using `texts.rewardCappedNoticeText` with `{amount}` replaced by the actual redeemed value.
688
+
689
+ `confirmRewards` applies the same cap before sending the selection to `MasterpassRestRewardSelectionPage`, so the backend payload is always `special + general ≤ payable`.
690
+
691
+ **Example — override with your own modal:**
692
+
693
+ ```tsx
694
+ {
695
+ customRender: {
696
+ rewardSelectionModal: ({ open, onClose, onConfirm, rewards, ...props }) => (
697
+ <MyRewardModal
698
+ isOpen={open}
699
+ onDismiss={onClose}
700
+ onApply={onConfirm}
701
+ items={rewards}
702
+ {...props}
703
+ />
704
+ )
705
+ }
706
+ }
707
+ ```
708
+
709
+ **Example — render rewards inline (no modal at all) via CardList:**
710
+
711
+ Brands that prefer an inline picker instead of a modal can use the new reward props on `CardListProps`:
712
+
713
+ ```tsx
714
+ {
715
+ customRender: {
716
+ cardList: (props) => (
717
+ <MyCardList
718
+ cards={props.cards}
719
+ onCardSelect={props.onCardSelect}
720
+ selectedCard={props.selectedCard}
721
+ renderRewards={(card) =>
722
+ props.availableRewards?.length ? (
723
+ <InlineRewardPicker
724
+ rewards={props.availableRewards}
725
+ selected={props.selectedRewards ?? []}
726
+ loading={props.isLoadingRewards}
727
+ confirming={props.isConfirmingRewards}
728
+ payableAmount={props.rewardPayableAmount}
729
+ onConfirm={props.onConfirmRewards}
730
+ />
731
+ ) : null
732
+ }
733
+ {...props}
734
+ />
735
+ )
736
+ }
737
+ }
738
+ ```
739
+
740
+ `onConfirmRewards` bypasses the default modal entirely — your inline picker calls it directly with the user's selection, the package applies the cumulative cap and posts to `MasterpassRestRewardSelectionPage`. For showing the capped redeemable total in your inline picker, use `getCappedRewardTotal(selected, props.rewardPayableAmount)` from `'@akinon/pz-masterpass-rest'`.
741
+
742
+ When you go this route, leave `customRender.rewardSelectionModal` unset — the default modal still won't show because your CardList handles selection via `onConfirmRewards` directly.
743
+
744
+ ---
745
+
600
746
  ### 8. ErrorDisplay
601
747
 
602
748
  Shown at the bottom of the payment area when an error occurs.
@@ -733,16 +879,21 @@ type MasterpassRestOptionRenderProps = {
733
879
  handleOTPSubmit: (otp: string) => Promise<{ success: boolean; message?: string }>
734
880
  onCloseError: () => void
735
881
 
736
- // Loading States
737
882
  isCheckoutLoading: boolean
738
883
  isInstallmentLoading: boolean
739
884
  isPrepareLoading: boolean
740
885
  isFinalizeLoading: boolean
886
+ isProcessingPayment: boolean
887
+ isRewardsQueryLoading: boolean
888
+ isRewardsSelectLoading: boolean
889
+
890
+ openRewardModal: () => void
891
+ closeRewardModal: () => void
892
+ handleConfirmRewards: (selected: RewardItem[]) => Promise<void>
893
+ payableAmount: string | null
741
894
 
742
- // Texts
743
895
  texts: MasterpassRestOptionTexts
744
896
 
745
- // Default Components (use these to avoid reimplementing everything)
746
897
  components: {
747
898
  PaymentMethodSelector: React.ComponentType<PaymentMethodSelectorProps>
748
899
  CardList: React.ComponentType<CardListProps>
@@ -751,10 +902,75 @@ type MasterpassRestOptionRenderProps = {
751
902
  LinkModal: React.ComponentType<LinkModalProps>
752
903
  OTPModal: React.ComponentType<OTPModalProps>
753
904
  ConfirmationModal: React.ComponentType<ConfirmationModalProps>
905
+ RewardSelectionModal: React.ComponentType<RewardSelectionModalProps>
754
906
  }
755
907
  }
756
908
  ```
757
909
 
910
+ `isProcessingPayment` is the orchestration-level flag that stays `true` for the entire payment flow (refresh → prepare → Masterpass SDK → finalize). If your `fullRender` exposes a "Proceed to Payment" button, OR it together with `isPrepareLoading` and `isFinalizeLoading` and pass that to your `InstallmentList.paymentLoading` — otherwise the button briefly re-enables between the prepare and finalize steps and double-clicks slip through.
911
+
912
+ #### Opting in to rewards from `fullRender`
913
+
914
+ If your `fullRender` replaces the package's default view, it also bypasses the default `RewardSelectionModal` mount and the inline reward CTA on the default `CardList`. To enable rewards in a `fullRender` integration:
915
+
916
+ 1. Pass `enableRewards={true}` on `MasterpassRestOption` so the package auto-fetches rewards on card selection and exposes the modal helpers via render props.
917
+ 2. Mount `components.RewardSelectionModal` alongside your other modals, wired to `modalState.showRewardModal`, `closeRewardModal`, `handleConfirmRewards`, `paymentState.availableRewards`, `paymentState.selectedRewards`, `isRewardsSelectLoading`, and `payableAmount`.
918
+ 3. In whichever component renders the saved-card list, expose an "Use Rewards" trigger that calls `openRewardModal` once `paymentState.availableRewards.length > 0`. If you use `components.CardList`, pass the reward props through (`availableRewards`, `selectedRewards`, `isLoadingRewards`, `onOpenRewardModal`, `rewardCurrency`, `rewardPayableAmount`) and it will render the inline CTA for you.
919
+
920
+ Minimal sketch:
921
+
922
+ ```tsx
923
+ <MasterpassRestOption
924
+ enableRewards
925
+ customRender={{
926
+ fullRender: (props) => {
927
+ const {
928
+ paymentState,
929
+ modalState,
930
+ components: { CardList, RewardSelectionModal },
931
+ openRewardModal,
932
+ closeRewardModal,
933
+ handleConfirmRewards,
934
+ isRewardsQueryLoading,
935
+ isRewardsSelectLoading,
936
+ payableAmount,
937
+ texts
938
+ } = props
939
+
940
+ return (
941
+ <>
942
+ <CardList
943
+ {...cardListProps}
944
+ availableRewards={paymentState.availableRewards}
945
+ selectedRewards={paymentState.selectedRewards}
946
+ isLoadingRewards={paymentState.isLoadingRewards || isRewardsQueryLoading}
947
+ isConfirmingRewards={isRewardsSelectLoading}
948
+ onOpenRewardModal={openRewardModal}
949
+ rewardCurrency="TRY"
950
+ rewardPayableAmount={payableAmount}
951
+ texts={texts}
952
+ />
953
+
954
+ <RewardSelectionModal
955
+ open={modalState.showRewardModal}
956
+ onClose={closeRewardModal}
957
+ onConfirm={handleConfirmRewards}
958
+ rewards={paymentState.availableRewards}
959
+ selectedRewards={paymentState.selectedRewards}
960
+ isLoading={isRewardsSelectLoading}
961
+ currency="TRY"
962
+ payableAmount={payableAmount}
963
+ texts={texts}
964
+ />
965
+ </>
966
+ )
967
+ }
968
+ }}
969
+ />
970
+ ```
971
+
972
+ Without `enableRewards`, the package does not call `MasterpassRestRewardListPage`, no reward state is populated, and the helpers stay no-ops — existing `fullRender` brands keep behaving identically until they explicitly opt in.
973
+
758
974
  **Example - Custom layout with default components:**
759
975
 
760
976
  ```tsx
@@ -784,6 +1000,7 @@ customRender: {
784
1000
  isInstallmentLoading,
785
1001
  isPrepareLoading,
786
1002
  isFinalizeLoading,
1003
+ isProcessingPayment,
787
1004
  texts,
788
1005
  components
789
1006
  } = props
@@ -840,14 +1057,13 @@ customRender: {
840
1057
  selectedInstallment={paymentState.selectedInstallment}
841
1058
  isLoading={isInstallmentLoading || isPrepareLoading}
842
1059
  onProceedToPayment={handleProceedToPayment}
843
- paymentLoading={isFinalizeLoading}
1060
+ paymentLoading={isProcessingPayment || isPrepareLoading || isFinalizeLoading}
844
1061
  texts={texts}
845
1062
  />
846
1063
  )}
847
1064
  </div>
848
1065
  </div>
849
1066
 
850
- {/* Modals - use default components */}
851
1067
  <LinkModal
852
1068
  open={modalState?.showLinkModal ?? false}
853
1069
  onClose={() => updateModalState({ showLinkModal: false })}
@@ -880,14 +1096,12 @@ All user-facing strings can be overridden via the `texts` prop. Pass only the ke
880
1096
  locale: 'tr',
881
1097
  currency: 'try',
882
1098
  texts: {
883
- // Titles
884
1099
  title: 'Masterpass ile Ode',
885
1100
  selectCardTitle: 'Kart Secin',
886
1101
  newCardTitle: 'Yeni Kart ile Ode',
887
1102
  installmentOptionsTitle: 'Taksit Secenekleri',
888
1103
  enterCardDetailsTitle: 'Kart Bilgilerini Girin',
889
1104
 
890
- // Form labels
891
1105
  cardNumberLabel: 'Kart Numarasi',
892
1106
  cardholderNameLabel: 'Kart Uzerindeki Isim',
893
1107
  expiryDateLabel: 'Son Kullanma Tarihi',
@@ -895,43 +1109,50 @@ All user-facing strings can be overridden via the `texts` prop. Pass only the ke
895
1109
  saveCardLabel: 'Bu karti gelecek odemeler icin kaydet',
896
1110
  addCardButton: 'Kart Ekle',
897
1111
 
898
- // Payment method
899
1112
  savedCardsText: 'Kayitli Kartlar',
900
1113
  newCardText: 'Yeni Kart',
901
1114
 
902
- // Installments
903
1115
  singlePaymentText: 'Tek Cekim',
904
- installmentsText: '{count} Taksit', // {count} is replaced dynamically
1116
+ installmentsText: '{count} Taksit',
905
1117
  noInterestText: 'Faizsiz',
906
1118
  proceedToPaymentText: 'Odemeye Devam Et',
907
1119
  processingPaymentText: 'Odeme Isleniyor...',
908
1120
 
909
- // Card list
910
1121
  defaultCardText: 'Varsayilan',
911
1122
  expiresSoonText: 'Suresi Yakinda Dolacak',
912
1123
 
913
- // Modals
914
1124
  linkModalDescription: '<PHONE_NUMBER> numarasi ile Masterpass hesabinizdaki kartlari gormek ister misiniz?',
915
1125
  linkAccountButton: 'Evet, istiyorum',
916
1126
  linkModalCancelButton: 'Hayir, istemiyorum',
917
1127
  removeCardMessage: '<{cardAlias}> kartini Masterpass altyapisindan silmek istediginize emin misiniz?',
918
1128
 
919
- // Verification
1129
+ rewardOpenButtonText: 'Puanlari Kullan',
1130
+ rewardModalTitle: 'Kart Puanlarini Kullan',
1131
+ rewardModalDescription: 'Bu siparise uygulamak istediginiz puanlari secin.',
1132
+ rewardModalEmptyMessage: 'Bu kart icin uygun puan yok.',
1133
+ rewardModalConfirmText: 'Uygula',
1134
+ rewardModalCancelText: 'Vazgec',
1135
+ rewardModalLoadingText: 'Uygulaniyor...',
1136
+ rewardSelectedSummaryText: '{count} puan uygulandi',
1137
+ rewardAvailableSummaryText: '{count} puan mevcut',
1138
+ rewardCategorySpecialText: 'Ozel Puanlar',
1139
+ rewardCategoryGeneralText: 'Genel Puanlar',
1140
+ rewardCappedNoticeText: '{amount} kullanilacak',
1141
+ rewardFailedToLoadText: 'Puanlar yuklenemedi',
1142
+ rewardFailedToSelectText: 'Puanlar uygulanamadi',
1143
+
920
1144
  rtaVerificationTitle: 'Guvenlik Dogrulamasi',
921
1145
  bankOtpVerificationTitle: 'Banka Dogrulamasi',
922
1146
  cvvVerificationTitle: 'CVV Dogrulamasi',
923
1147
  verifyButton: 'Dogrula',
924
1148
 
925
- // Errors
926
1149
  paymentErrorTitle: 'Odeme Hatasi',
927
1150
  cardNumberRequiredText: 'Kart numarasi zorunludur',
928
1151
  cardNumberInvalidText: 'Gecerli bir kart numarasi girin',
929
1152
 
930
- // Loading
931
1153
  loadingMessage: 'Odeme sistemi hazirlaniyor...',
932
1154
  scriptLoadingMessage: 'Odeme altyapisi yukleniyor...',
933
1155
 
934
- // Session
935
1156
  sessionExpiredTitle: 'Oturum Suresi Doldu',
936
1157
  sessionExpiredMessage: 'Oturumunuz zaman asimina ugradi. Devam etmek icin islemi yeniden baslatin.',
937
1158
  sessionExpiredButton: 'Yeniden Basla'
@@ -951,6 +1172,9 @@ Some text keys support placeholders:
951
1172
  | `removeCardMessage` | `{cardAlias}` | Name of the card being removed |
952
1173
  | `cvcHelpText` | `{length}`, `{side}` | CVC digit count and card side |
953
1174
  | `linkModalDescription` | `<PHONE_NUMBER>` | User's phone number (auto-replaced by SDK) |
1175
+ | `rewardSelectedSummaryText` | `{count}` | Number of selected rewards |
1176
+ | `rewardAvailableSummaryText` | `{count}` | Number of available rewards for the selected card |
1177
+ | `rewardCappedNoticeText` | `{amount}` | Capped redeemable amount (e.g. `"100.00 TRY"`) |
954
1178
 
955
1179
  ---
956
1180
 
@@ -1012,53 +1236,101 @@ const {
1012
1236
 
1013
1237
  ### `useMasterpassPayment`
1014
1238
 
1015
- Handles the payment processing flow.
1239
+ Handles the payment processing flow, including the rewards opt-in.
1016
1240
 
1017
1241
  ```typescript
1018
1242
  import { useMasterpassPayment } from '@akinon/pz-masterpass-rest'
1019
1243
 
1020
1244
  const {
1021
- paymentState, // selectedCard, selectedInstallment, installments, etc.
1245
+ paymentState,
1022
1246
  isCheckoutLoading,
1023
1247
  isInstallmentLoading,
1024
1248
  isPrepareLoading,
1025
1249
  isFinalizeLoading,
1026
- handleCardSelect, // Select a saved card (triggers BIN check)
1250
+ isRewardsQueryLoading,
1251
+ isRewardsSelectLoading,
1252
+ payableAmount,
1253
+ handleCardSelect,
1027
1254
  handleInstallmentSelect,
1028
- processPayment, // Stored card payment
1029
- processDirectPayment // New card payment
1030
- } = useMasterpassPayment()
1255
+ processPayment,
1256
+ processDirectPayment,
1257
+ fetchRewardsForCard,
1258
+ openRewardModal,
1259
+ closeRewardModal,
1260
+ confirmRewards
1261
+ } = useMasterpassPayment({ enableRewards: true })
1031
1262
  ```
1032
1263
 
1264
+ `paymentState` carries the live selection state (`selectedCard`, `selectedInstallment`, `installments`, plus `availableRewards`, `selectedRewards`, `isLoadingRewards` when rewards are enabled).
1265
+
1266
+ `useMasterpassPayment` accepts a single options arg `{ enableRewards?: boolean }` — default `false`. When `false`, `handleCardSelect` skips the reward fetch, and the reward state stays empty. Pass `true` only when you are also using `<MasterpassRestOption enableRewards />` (or rendering the rewards UI yourself); otherwise the package never hits `MasterpassRestRewardListPage`.
1267
+
1268
+ `payableAmount` is read from `state.checkout.preOrder.unpaid_amount` (falling back to `total_amount_with_interest`). Pass it to `getCappedRewardTotal` / `getCappedRewardAmounts` if you are building a custom rewards UI.
1269
+
1270
+ `confirmRewards(selected)` applies the cumulative cap (`special` first, then `general`) and posts to `MasterpassRestRewardSelectionPage`. It returns `{ success: true }` on success or `{ success: false, message }` on failure.
1271
+
1033
1272
  ---
1034
1273
 
1035
1274
  ## Exported Utilities
1036
1275
 
1037
1276
  ```typescript
1038
1277
  import {
1039
- // Card utilities
1040
- formatCardNumber, // '5342610000001234' -> '5342 6100 0000 1234'
1041
- formatExpiryDate, // '1227' -> '12/27'
1042
- formatCVV, // Strips non-digits, max 4 chars
1043
- detectCardType, // Returns 'visa' | 'mastercard' | 'amex' | 'troy' | 'discover' | 'unknown'
1044
- getCvcLength, // Returns 3 (or 4 for Amex)
1045
- maskCardNumber, // '5342610000001234' -> '****1234'
1046
- validateCardNumber, // Luhn algorithm validation
1047
- getCardBIN, // Returns first 6 digits
1048
- getCardIcon, // Returns logo image object by BIN
1049
-
1050
- // Payment utilities
1051
- isTokenExpired, // Check JWT token expiration
1052
- getTransactionType, // Returns 'PURCHASE' or 'PURCHASE_3D'
1053
- formatAmountForPayment, // '1500.00' -> '150000'
1054
- createPaymentRequest, // Builds stored card payment request
1055
- createDirectPaymentRequest, // Builds new card payment request
1056
-
1057
- // Validation
1058
- createCreditCardFormSchema // Returns Yup validation schema
1278
+ formatCardNumber,
1279
+ formatExpiryDate,
1280
+ formatCVV,
1281
+ detectCardType,
1282
+ getCvcLength,
1283
+ maskCardNumber,
1284
+ validateCardNumber,
1285
+ getCardBIN,
1286
+ getCardIcon,
1287
+
1288
+ isTokenExpired,
1289
+ getTransactionType,
1290
+ formatAmountForPayment,
1291
+ createPaymentRequest,
1292
+ createDirectPaymentRequest,
1293
+
1294
+ parseRewardAmount,
1295
+ getCappedRewardAmounts,
1296
+ getCappedRewardTotal,
1297
+ REWARD_PRIORITY,
1298
+
1299
+ createCreditCardFormSchema
1059
1300
  } from '@akinon/pz-masterpass-rest'
1060
1301
  ```
1061
1302
 
1303
+ **Card utilities** — `formatCardNumber` (`'5342610000001234'` → `'5342 6100 0000 1234'`), `formatExpiryDate` (`'1227'` → `'12/27'`), `formatCVV` (strips non-digits, max 4 chars), `detectCardType` (returns `'visa' | 'mastercard' | 'amex' | 'troy' | 'discover' | 'unknown'`), `getCvcLength` (returns `3`, or `4` for Amex), `maskCardNumber` (`'5342610000001234'` → `'****1234'`), `validateCardNumber` (Luhn), `getCardBIN` (first 6 digits), `getCardIcon` (logo image object by BIN).
1304
+
1305
+ **Payment utilities** — `isTokenExpired` checks the JWT expiry, `getTransactionType` returns `'PURCHASE'` / `'PURCHASE_3D'`, `formatAmountForPayment` (`'1500.00'` → `'150000'`), `createPaymentRequest` / `createDirectPaymentRequest` build the SDK request body.
1306
+
1307
+ **Reward utilities** — used internally and exported for custom UIs:
1308
+
1309
+ ```typescript
1310
+ parseRewardAmount(amount: number | string): number
1311
+
1312
+ getCappedRewardAmounts(
1313
+ selected: RewardItem[],
1314
+ payableAmount?: string | number | null
1315
+ ): Record<'special' | 'general', number>
1316
+
1317
+ getCappedRewardTotal(
1318
+ selected: RewardItem[],
1319
+ payableAmount?: string | number | null
1320
+ ): number
1321
+
1322
+ REWARD_PRIORITY: ['special', 'general']
1323
+ ```
1324
+
1325
+ - `parseRewardAmount` safely parses backend string amounts (e.g. `"180.00"`) to a number; returns `0` for invalid input.
1326
+ - `getCappedRewardAmounts` applies the cumulative cap (`special` first, then `general`) and returns the actual amounts that will be redeemed per category.
1327
+ - `getCappedRewardTotal` is the sum of the capped amounts — use this when rendering a "X TRY will be applied" total.
1328
+ - `REWARD_PRIORITY` is the fixed order in which the cap is applied. Exported so custom UIs can iterate in the same order.
1329
+
1330
+ `payableAmount` is the order's unpaid amount. If `null` / `undefined` / unparseable, no cap is applied (returns the full reward amounts) — this matches the behavior in the package and is safe as a fallback.
1331
+
1332
+ **Validation** — `createCreditCardFormSchema` returns the Yup schema used by the default `CreditCardForm`.
1333
+
1062
1334
  ---
1063
1335
 
1064
1336
  ## Type Definitions
@@ -1067,7 +1339,6 @@ All types are exported and can be imported for use in custom components:
1067
1339
 
1068
1340
  ```typescript
1069
1341
  import type {
1070
- // Component props
1071
1342
  PaymentMethodSelectorProps,
1072
1343
  CardListProps,
1073
1344
  CreditCardFormProps,
@@ -1075,6 +1346,7 @@ import type {
1075
1346
  LinkModalProps,
1076
1347
  OTPModalProps,
1077
1348
  ConfirmationModalProps,
1349
+ RewardSelectionModalProps,
1078
1350
  ErrorDisplayProps,
1079
1351
  LoadingStateProps,
1080
1352
  EmptyStateProps,
@@ -1082,7 +1354,6 @@ import type {
1082
1354
  MasterpassRestOptionCustomRender,
1083
1355
  MasterpassRestOptionTexts,
1084
1356
 
1085
- // Data types
1086
1357
  CardModel,
1087
1358
  CardType,
1088
1359
  Installment,
@@ -1092,21 +1363,21 @@ import type {
1092
1363
  OTPType,
1093
1364
  TransactionType,
1094
1365
  InformationModalData,
1366
+ RewardItem,
1367
+ RewardName,
1368
+ RewardCategory,
1095
1369
 
1096
- // Account types
1097
1370
  AccountAccessRequest,
1098
1371
  AccountAccessResponse,
1099
1372
  AccountAccessSuccessResponse,
1100
1373
  AccountAccessErrorResponse,
1101
1374
  CardResponse,
1102
1375
 
1103
- // Payment types
1104
1376
  PaymentProcessRequest,
1105
1377
  DirectPaymentRequest,
1106
1378
  DirectPaymentResponse,
1107
1379
  MPResponse,
1108
1380
 
1109
- // Environment
1110
1381
  MasterpassEnvironment
1111
1382
  } from '@akinon/pz-masterpass-rest'
1112
1383
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/pz-masterpass-rest",
3
- "version": "2.0.16",
3
+ "version": "2.0.17-rc.0",
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",
@@ -31,6 +31,6 @@
31
31
  "devDependencies": {
32
32
  "@types/react": "18.2.27",
33
33
  "@types/react-dom": "18.2.12",
34
- "typescript": "5.2.2"
34
+ "typescript": "^5.2.2"
35
35
  }
36
36
  }