@akinon/pz-masterpass-rest 2.0.15 → 2.0.16-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 +10 -0
- package/docs/USAGE.md +332 -61
- package/package.json +2 -2
- package/src/components/card-list.tsx +72 -2
- package/src/components/credit-card-form.tsx +1 -1
- package/src/components/installment-list.tsx +1 -1
- package/src/components/otp-modal.tsx +12 -4
- package/src/components/reward-selection-modal.tsx +194 -0
- package/src/hooks/useMasterpassAccount.ts +21 -5
- package/src/hooks/useMasterpassPayment.ts +112 -13
- package/src/index.ts +2 -0
- package/src/redux/api.ts +82 -1
- package/src/redux/reducer.ts +35 -3
- package/src/types/custom-render.types.ts +47 -1
- package/src/types/payment.types.ts +13 -0
- package/src/utils/card-utils.ts +1 -1
- package/src/utils/reward-utils.ts +41 -0
- package/src/views/masterpass-rest-option.tsx +133 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @akinon/pz-masterpass-rest
|
|
2
2
|
|
|
3
|
+
## 2.0.16-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.15
|
|
4
14
|
|
|
5
15
|
## 2.0.14
|
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[]
|
|
175
|
-
onCardSelect: (card: any) => void
|
|
176
|
-
selectedCard?: any | null
|
|
177
|
-
onRemove?: (card: any) => void
|
|
178
|
-
removingCardId?: string | null
|
|
179
|
-
cvc?: string
|
|
180
|
-
onCvcChange?: (cvc: string) => void
|
|
181
|
-
cvcRequired?: boolean
|
|
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
|
|
191
|
-
maskedCardNumber: string
|
|
192
|
-
uniqueCardNumber: string
|
|
237
|
+
cardAlias: string
|
|
238
|
+
maskedCardNumber: string
|
|
239
|
+
uniqueCardNumber: string
|
|
193
240
|
cardType: 'Credit' | 'Debit' | 'Unknown' | ''
|
|
194
|
-
cardBin: string
|
|
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',
|
|
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
|
-
|
|
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,
|
|
1245
|
+
paymentState,
|
|
1022
1246
|
isCheckoutLoading,
|
|
1023
1247
|
isInstallmentLoading,
|
|
1024
1248
|
isPrepareLoading,
|
|
1025
1249
|
isFinalizeLoading,
|
|
1026
|
-
|
|
1250
|
+
isRewardsQueryLoading,
|
|
1251
|
+
isRewardsSelectLoading,
|
|
1252
|
+
payableAmount,
|
|
1253
|
+
handleCardSelect,
|
|
1027
1254
|
handleInstallmentSelect,
|
|
1028
|
-
processPayment,
|
|
1029
|
-
processDirectPayment
|
|
1030
|
-
|
|
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
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
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.
|
|
3
|
+
"version": "2.0.16-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
|
}
|