@akinon/pz-masterpass-rest 2.0.0-beta.12 → 2.0.0-beta.14

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/docs/USAGE.md ADDED
@@ -0,0 +1,1176 @@
1
+ # Masterpass REST - Usage Guide
2
+
3
+ ## Table of Contents
4
+
5
+ - [Installation](#installation)
6
+ - [SDK Setup](#sdk-setup)
7
+ - [Basic Usage](#basic-usage)
8
+ - [Props Reference](#props-reference)
9
+ - [Custom Rendering](#custom-rendering)
10
+ - [Available Custom Render Slots](#available-custom-render-slots)
11
+ - [PaymentMethodSelector](#1-paymentmethodselector)
12
+ - [CardList](#2-cardlist)
13
+ - [CreditCardForm](#3-creditcardform)
14
+ - [InstallmentList](#4-installmentlist)
15
+ - [LinkModal](#5-linkmodal)
16
+ - [OTPModal](#6-otpmodal)
17
+ - [ConfirmationModal](#7-confirmationmodal)
18
+ - [ErrorDisplay](#8-errordisplay)
19
+ - [LoadingState](#9-loadingstate)
20
+ - [EmptyState](#10-emptystate)
21
+ - [Full Render](#11-fullrender)
22
+ - [Text Customization](#text-customization)
23
+ - [Exported Hooks](#exported-hooks)
24
+ - [Exported Utilities](#exported-utilities)
25
+ - [Type Definitions](#type-definitions)
26
+ - [Payment Flow](#payment-flow)
27
+
28
+ ---
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ npx @akinon/projectzero@latest --plugins
34
+ ```
35
+
36
+ ## SDK Setup
37
+
38
+ Copy the Masterpass JavaScript SDK to your project's `public` folder:
39
+
40
+ ```bash
41
+ cp node_modules/@akinon/pz-masterpass-rest/assets/masterpass-javascript-sdk-web.min.js public/
42
+ ```
43
+
44
+ The SDK file must be accessible at `/masterpass-javascript-sdk-web.min.js` in your application.
45
+
46
+ ## Basic Usage
47
+
48
+ ```tsx
49
+ // src/views/checkout/steps/payment/options/masterpass-rest.tsx
50
+ import { useLocalization } from '@akinon/next/hooks'
51
+ import PluginModule, { Component } from '@akinon/next/components/plugin-module'
52
+
53
+ const MasterpassRest = () => {
54
+ const { locale, currency } = useLocalization()
55
+
56
+ return (
57
+ <PluginModule
58
+ component={Component.MasterpassRest}
59
+ props={{ locale, currency }}
60
+ />
61
+ )
62
+ }
63
+
64
+ export default MasterpassRest
65
+ ```
66
+
67
+ ## Props Reference
68
+
69
+ `MasterpassRestOption` is the main component. It accepts:
70
+
71
+ | Prop | Type | Default | Description |
72
+ |------|------|---------|-------------|
73
+ | `locale` | `string` | `'en'` | Language locale (`'en'`, `'tr'`) |
74
+ | `currency` | `string` | `'usd'` | Currency code (`'usd'`, `'try'`) |
75
+ | `sdkUrl` | `string` | `undefined` | Custom Masterpass SDK URL |
76
+ | `environment` | `'production' \| 'development'` | `undefined` | SDK environment |
77
+ | `texts` | `MasterpassRestOptionTexts` | `defaultTexts` | UI text overrides for localization |
78
+ | `customRender` | `MasterpassRestOptionCustomRender` | `undefined` | Custom render functions |
79
+
80
+ ```tsx
81
+ <PluginModule
82
+ component={Component.MasterpassRest}
83
+ props={{
84
+ locale: 'tr',
85
+ currency: 'try',
86
+ environment: 'production',
87
+ texts: { title: 'Masterpass ile Ode' },
88
+ customRender: { /* ... */ }
89
+ }}
90
+ />
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Custom Rendering
96
+
97
+ The `customRender` prop lets you override individual UI sections or the entire component. Each render function receives the same props as the default component, so you have full access to state and handlers.
98
+
99
+ ### Available Custom Render Slots
100
+
101
+ ```typescript
102
+ type MasterpassRestOptionCustomRender = {
103
+ paymentMethodSelector?: (props: PaymentMethodSelectorProps) => ReactElement
104
+ cardList?: (props: CardListProps) => ReactElement
105
+ creditCardForm?: (props: CreditCardFormProps) => ReactElement
106
+ installmentList?: (props: InstallmentListProps) => ReactElement
107
+ linkModal?: (props: LinkModalProps) => ReactElement
108
+ otpModal?: (props: OTPModalProps) => ReactElement
109
+ confirmationModal?: (props: ConfirmationModalProps) => ReactElement
110
+ errorDisplay?: (props: ErrorDisplayProps) => ReactElement
111
+ loadingState?: (props: LoadingStateProps) => ReactElement
112
+ emptyState?: (props: EmptyStateProps) => ReactElement
113
+ fullRender?: (props: MasterpassRestOptionRenderProps) => ReactElement
114
+ }
115
+ ```
116
+
117
+ ---
118
+
119
+ ### 1. PaymentMethodSelector
120
+
121
+ Toggles between saved cards and new card entry. Only rendered when the user has stored cards.
122
+
123
+ **Props:**
124
+
125
+ ```typescript
126
+ type PaymentMethodSelectorProps = {
127
+ cards: any[] // User's stored cards array
128
+ selectedMethod: 'stored_card' | 'new_card' // Currently active method
129
+ onMethodChange: (method: 'stored_card' | 'new_card') => void // Switch handler
130
+ texts: MasterpassRestOptionTexts // Merged text strings
131
+ }
132
+ ```
133
+
134
+ **Example:**
135
+
136
+ ```tsx
137
+ <PluginModule
138
+ component={Component.MasterpassRest}
139
+ props={{
140
+ locale,
141
+ currency,
142
+ customRender: {
143
+ paymentMethodSelector: ({ cards, selectedMethod, onMethodChange, texts }) => (
144
+ <div className="flex gap-4 border-b pb-4">
145
+ <button
146
+ className={selectedMethod === 'stored_card' ? 'font-bold border-b-2 border-primary' : 'text-gray-500'}
147
+ onClick={() => onMethodChange('stored_card')}
148
+ >
149
+ {texts.savedCardsText} ({cards.length})
150
+ </button>
151
+ <button
152
+ className={selectedMethod === 'new_card' ? 'font-bold border-b-2 border-primary' : 'text-gray-500'}
153
+ onClick={() => onMethodChange('new_card')}
154
+ >
155
+ {texts.newCardText}
156
+ </button>
157
+ </div>
158
+ )
159
+ }
160
+ }}
161
+ />
162
+ ```
163
+
164
+ ---
165
+
166
+ ### 2. CardList
167
+
168
+ Displays the user's saved cards with selection, CVC input, and remove functionality.
169
+
170
+ **Props:**
171
+
172
+ ```typescript
173
+ 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
182
+ texts: MasterpassRestOptionTexts
183
+ }
184
+ ```
185
+
186
+ **CardModel structure (each item in `cards` array):**
187
+
188
+ ```typescript
189
+ interface CardModel {
190
+ cardAlias: string // 'My Visa Card'
191
+ maskedCardNumber: string // '534261******1234'
192
+ uniqueCardNumber: string // Unique identifier
193
+ cardType: 'Credit' | 'Debit' | 'Unknown' | ''
194
+ cardBin: string // First 6 digits
195
+ isDefaultCard: boolean
196
+ expireSoon: boolean
197
+ isExpired: boolean
198
+ cardValidationType: 'OTP' | 'RTA' | '_3D' | 'Unknown' | ''
199
+ // ...more fields
200
+ }
201
+ ```
202
+
203
+ **Example:**
204
+
205
+ ```tsx
206
+ customRender: {
207
+ cardList: ({ cards, onCardSelect, selectedCard, onRemove, cvc, onCvcChange, texts }) => (
208
+ <div className="space-y-2">
209
+ {cards.map((card) => (
210
+ <div
211
+ key={card.uniqueCardNumber}
212
+ className={`p-4 border cursor-pointer ${
213
+ selectedCard?.uniqueCardNumber === card.uniqueCardNumber
214
+ ? 'border-primary bg-primary/5'
215
+ : 'border-gray-200'
216
+ }`}
217
+ onClick={() => onCardSelect(card)}
218
+ >
219
+ <div className="flex justify-between items-center">
220
+ <div>
221
+ <span className="font-medium">{card.cardAlias}</span>
222
+ <span className="ml-2 text-gray-500">{card.maskedCardNumber}</span>
223
+ {card.isDefaultCard && (
224
+ <span className="ml-2 text-xs bg-green-100 text-green-700 px-2 py-0.5">
225
+ {texts.defaultCardText}
226
+ </span>
227
+ )}
228
+ </div>
229
+ <button
230
+ onClick={(e) => { e.stopPropagation(); onRemove?.(card) }}
231
+ className="text-red-500 text-sm"
232
+ >
233
+ Remove
234
+ </button>
235
+ </div>
236
+
237
+ {/* CVC input for selected card */}
238
+ {selectedCard?.uniqueCardNumber === card.uniqueCardNumber && (
239
+ <input
240
+ type="text"
241
+ maxLength={4}
242
+ value={cvc || ''}
243
+ onChange={(e) => onCvcChange?.(e.target.value)}
244
+ placeholder={texts.cvcPlaceholder3}
245
+ className="mt-2 border px-2 py-1 w-20"
246
+ />
247
+ )}
248
+ </div>
249
+ ))}
250
+ </div>
251
+ )
252
+ }
253
+ ```
254
+
255
+ ---
256
+
257
+ ### 3. CreditCardForm
258
+
259
+ The new card entry form with validation, BIN checking, and optional card saving.
260
+
261
+ **Props:**
262
+
263
+ ```typescript
264
+ type CreditCardFormProps = {
265
+ onSaveCard?: (data: CreditCardFormData) => void // Submit handler
266
+ isLoading?: boolean // Disables form during API calls
267
+ showSaveOption?: boolean // Shows "save card" checkbox
268
+ initialValues?: Partial<CreditCardFormData> // Pre-fill form fields
269
+ onBinChange?: (bin: string) => Promise<void> // Called with first 6 digits for installment lookup
270
+ texts: MasterpassRestOptionTexts
271
+ }
272
+ ```
273
+
274
+ **CreditCardFormData (form submit payload):**
275
+
276
+ ```typescript
277
+ interface CreditCardFormData {
278
+ cardNumber: string // '5342610000001234'
279
+ cardholderName: string // 'John Doe'
280
+ expiryDate: string // '12/27'
281
+ cvv: string // '123'
282
+ saveCard: boolean // true
283
+ cardAlias: string // 'My Card'
284
+ }
285
+ ```
286
+
287
+ **Example:**
288
+
289
+ ```tsx
290
+ customRender: {
291
+ creditCardForm: ({ onSaveCard, isLoading, showSaveOption, onBinChange, texts }) => (
292
+ <MyCustomCardForm
293
+ onSubmit={(formData) => onSaveCard?.(formData)}
294
+ disabled={isLoading}
295
+ showSave={showSaveOption}
296
+ onCardNumberChange={(number) => {
297
+ // Trigger installment lookup when 6+ digits entered
298
+ const digits = number.replace(/\D/g, '')
299
+ if (digits.length >= 6) {
300
+ onBinChange?.(digits.substring(0, 6))
301
+ }
302
+ }}
303
+ labels={{
304
+ cardNumber: texts.cardNumberLabel,
305
+ name: texts.cardholderNameLabel,
306
+ expiry: texts.expiryDateLabel,
307
+ cvc: texts.cvcLabel,
308
+ submit: texts.addCardButton
309
+ }}
310
+ />
311
+ )
312
+ }
313
+ ```
314
+
315
+ > **Important:** When implementing a custom card form, you must call `onBinChange` with the first 6 digits of the card number to trigger installment options loading. The default form does this automatically.
316
+
317
+ ---
318
+
319
+ ### 4. InstallmentList
320
+
321
+ Shows available installment options after a card/BIN is selected, plus the "Proceed to Payment" button.
322
+
323
+ **Props:**
324
+
325
+ ```typescript
326
+ type InstallmentListProps = {
327
+ installments: Installment[] // Available installment options
328
+ cardType: CardType | null // Detected card type info
329
+ onInstallmentSelect: (installment: Installment) => void
330
+ selectedInstallment?: Installment | null
331
+ isLoading?: boolean // Loading installments or preparing order
332
+ onProceedToPayment?: () => void // Triggers payment flow
333
+ paymentLoading?: boolean // Payment in progress
334
+ texts: MasterpassRestOptionTexts
335
+ }
336
+ ```
337
+
338
+ **Installment structure:**
339
+
340
+ ```typescript
341
+ interface Installment {
342
+ pk: number
343
+ installment_count: number // 1 = single payment, 2+ = installments
344
+ label: string
345
+ price_with_accrued_interest: string // Total price (e.g. '1500.00')
346
+ monthly_price_with_accrued_interest: string // Monthly amount (e.g. '500.00')
347
+ }
348
+ ```
349
+
350
+ **CardType structure:**
351
+
352
+ ```typescript
353
+ interface CardType {
354
+ name: string // 'Visa'
355
+ slug: string // 'visa'
356
+ logo: string // URL to card logo
357
+ }
358
+ ```
359
+
360
+ **Example:**
361
+
362
+ ```tsx
363
+ customRender: {
364
+ installmentList: ({
365
+ installments,
366
+ cardType,
367
+ onInstallmentSelect,
368
+ selectedInstallment,
369
+ isLoading,
370
+ onProceedToPayment,
371
+ paymentLoading,
372
+ texts
373
+ }) => (
374
+ <div>
375
+ {cardType && (
376
+ <div className="mb-4 text-sm text-gray-500">
377
+ {texts.cardTypeLabel} {cardType.name}
378
+ </div>
379
+ )}
380
+
381
+ <div className="space-y-2">
382
+ {installments.map((inst) => (
383
+ <label
384
+ key={inst.pk}
385
+ className={`flex items-center justify-between p-3 border cursor-pointer ${
386
+ selectedInstallment?.pk === inst.pk ? 'border-primary' : 'border-gray-200'
387
+ }`}
388
+ >
389
+ <div className="flex items-center gap-2">
390
+ <input
391
+ type="radio"
392
+ checked={selectedInstallment?.pk === inst.pk}
393
+ onChange={() => onInstallmentSelect(inst)}
394
+ />
395
+ <span>
396
+ {inst.installment_count === 1
397
+ ? texts.singlePaymentText
398
+ : texts.installmentsText?.replace('{count}', String(inst.installment_count))}
399
+ </span>
400
+ </div>
401
+ <span className="font-medium">{inst.price_with_accrued_interest} TL</span>
402
+ </label>
403
+ ))}
404
+ </div>
405
+
406
+ <button
407
+ onClick={onProceedToPayment}
408
+ disabled={!selectedInstallment || isLoading || paymentLoading}
409
+ className="w-full mt-4 py-3 bg-primary text-white disabled:opacity-50"
410
+ >
411
+ {paymentLoading ? texts.processingPaymentText : texts.proceedToPaymentText}
412
+ </button>
413
+ </div>
414
+ )
415
+ }
416
+ ```
417
+
418
+ ---
419
+
420
+ ### 5. LinkModal
421
+
422
+ Shown when the user has a Masterpass account but it's not linked to the current merchant.
423
+
424
+ **Props:**
425
+
426
+ ```typescript
427
+ type LinkModalProps = {
428
+ open: boolean
429
+ onClose: () => void
430
+ onConfirm: () => void // Links account to merchant
431
+ texts: MasterpassRestOptionTexts
432
+ }
433
+ ```
434
+
435
+ **Example:**
436
+
437
+ ```tsx
438
+ customRender: {
439
+ linkModal: ({ open, onClose, onConfirm, texts }) => {
440
+ if (!open) return null
441
+
442
+ return (
443
+ <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
444
+ <div className="bg-white p-6 max-w-md">
445
+ <p className="mb-4">{texts.linkModalDescription}</p>
446
+ <div className="flex gap-3">
447
+ <button onClick={onConfirm} className="flex-1 bg-primary text-white py-2">
448
+ {texts.linkAccountButton}
449
+ </button>
450
+ <button onClick={onClose} className="flex-1 border py-2">
451
+ {texts.linkModalCancelButton}
452
+ </button>
453
+ </div>
454
+ </div>
455
+ </div>
456
+ )
457
+ }
458
+ }
459
+ ```
460
+
461
+ ---
462
+
463
+ ### 6. OTPModal
464
+
465
+ Handles 3 types of verification: RTA (security verification), OTP (bank SMS), and CVV.
466
+
467
+ **Props:**
468
+
469
+ ```typescript
470
+ type OTPModalProps = {
471
+ open: boolean
472
+ onClose: () => void
473
+ onSubmit: (otp: string) => Promise<{
474
+ success: boolean
475
+ requiresOTP?: boolean
476
+ message?: string
477
+ }>
478
+ type: 'RTA' | 'OTP' | 'CVV' // Determines labels/placeholders
479
+ responseCode?: string
480
+ description?: string
481
+ texts: MasterpassRestOptionTexts
482
+ }
483
+ ```
484
+
485
+ **Example:**
486
+
487
+ ```tsx
488
+ customRender: {
489
+ otpModal: ({ open, onClose, onSubmit, type, texts }) => {
490
+ if (!open) return null
491
+
492
+ const titles = {
493
+ RTA: texts.rtaVerificationTitle,
494
+ OTP: texts.bankOtpVerificationTitle,
495
+ CVV: texts.cvvVerificationTitle
496
+ }
497
+
498
+ const descriptions = {
499
+ RTA: texts.rtaVerificationDescription,
500
+ OTP: texts.bankOtpVerificationDescription,
501
+ CVV: texts.cvvVerificationDescription
502
+ }
503
+
504
+ let inputValue = ''
505
+
506
+ const handleSubmit = async () => {
507
+ const result = await onSubmit(inputValue)
508
+ if (!result.success && result.message) {
509
+ alert(result.message)
510
+ }
511
+ }
512
+
513
+ return (
514
+ <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
515
+ <div className="bg-white p-6 max-w-sm w-full">
516
+ <h3 className="font-bold text-lg mb-2">{titles[type]}</h3>
517
+ <p className="text-sm text-gray-600 mb-4">{descriptions[type]}</p>
518
+ <input
519
+ type="text"
520
+ maxLength={type === 'CVV' ? 4 : 6}
521
+ onChange={(e) => { inputValue = e.target.value }}
522
+ className="w-full border px-3 py-2 mb-4"
523
+ placeholder={
524
+ type === 'CVV'
525
+ ? texts.cvvVerificationPlaceholder
526
+ : texts.rtaVerificationPlaceholder
527
+ }
528
+ />
529
+ <div className="flex gap-3">
530
+ <button onClick={handleSubmit} className="flex-1 bg-primary text-white py-2">
531
+ {texts.verifyButton}
532
+ </button>
533
+ <button onClick={onClose} className="flex-1 border py-2">
534
+ {texts.otpModalCancelButton}
535
+ </button>
536
+ </div>
537
+ </div>
538
+ </div>
539
+ )
540
+ }
541
+ }
542
+ ```
543
+
544
+ ---
545
+
546
+ ### 7. ConfirmationModal
547
+
548
+ Used for card removal confirmation. Receives the card alias in the message.
549
+
550
+ **Props:**
551
+
552
+ ```typescript
553
+ type ConfirmationModalProps = {
554
+ open: boolean
555
+ onClose: () => void
556
+ onConfirm: () => void
557
+ title: React.ReactNode | string // Can be JSX (default: Masterpass logo image)
558
+ message: string // Pre-formatted with card alias
559
+ confirmText?: string
560
+ cancelText?: string
561
+ isLoading?: boolean
562
+ loadingText?: string
563
+ texts: MasterpassRestOptionTexts
564
+ }
565
+ ```
566
+
567
+ **Example:**
568
+
569
+ ```tsx
570
+ customRender: {
571
+ confirmationModal: ({ open, onClose, onConfirm, title, message, isLoading, texts }) => {
572
+ if (!open) return null
573
+
574
+ return (
575
+ <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
576
+ <div className="bg-white p-6 max-w-sm">
577
+ <div className="mb-4">{title}</div>
578
+ <p className="mb-4">{message}</p>
579
+ <div className="flex gap-3">
580
+ <button
581
+ onClick={onConfirm}
582
+ disabled={isLoading}
583
+ className="flex-1 bg-red-600 text-white py-2"
584
+ >
585
+ {isLoading ? texts.removeCardLoadingText : texts.removeCardConfirmText}
586
+ </button>
587
+ <button onClick={onClose} className="flex-1 border py-2">
588
+ {texts.removeCardCancelText}
589
+ </button>
590
+ </div>
591
+ </div>
592
+ </div>
593
+ )
594
+ }
595
+ }
596
+ ```
597
+
598
+ ---
599
+
600
+ ### 8. ErrorDisplay
601
+
602
+ Shown at the bottom of the payment area when an error occurs.
603
+
604
+ **Props:**
605
+
606
+ ```typescript
607
+ type ErrorDisplayProps = {
608
+ error: any
609
+ onDismiss: () => void
610
+ getErrorInfo: (error: any) => { message: string; type: string } | null
611
+ }
612
+ ```
613
+
614
+ **Example:**
615
+
616
+ ```tsx
617
+ customRender: {
618
+ errorDisplay: ({ error, onDismiss, getErrorInfo }) => {
619
+ const info = getErrorInfo(error)
620
+ if (!info) return null
621
+
622
+ return (
623
+ <div className="mt-4 p-4 bg-red-50 border border-red-200 flex justify-between items-start">
624
+ <div>
625
+ <h4 className="font-semibold text-red-800">{info.type}</h4>
626
+ <p className="text-sm text-red-700">{info.message}</p>
627
+ </div>
628
+ <button onClick={onDismiss} className="text-red-500 text-xl leading-none">&times;</button>
629
+ </div>
630
+ )
631
+ }
632
+ }
633
+ ```
634
+
635
+ ---
636
+
637
+ ### 9. LoadingState
638
+
639
+ Shown during initial token fetch and SDK loading.
640
+
641
+ **Props:**
642
+
643
+ ```typescript
644
+ type LoadingStateProps = {
645
+ message: string // Either texts.loadingMessage or texts.scriptLoadingMessage
646
+ }
647
+ ```
648
+
649
+ **Example:**
650
+
651
+ ```tsx
652
+ customRender: {
653
+ loadingState: ({ message }) => (
654
+ <div className="flex items-center justify-center h-48">
655
+ <div className="animate-pulse text-center">
656
+ <div className="w-12 h-12 border-4 border-primary border-t-transparent rounded-full animate-spin mx-auto" />
657
+ <p className="mt-3 text-gray-500">{message}</p>
658
+ </div>
659
+ </div>
660
+ )
661
+ }
662
+ ```
663
+
664
+ ---
665
+
666
+ ### 10. EmptyState
667
+
668
+ Shown in the installment area when no installments are available yet.
669
+
670
+ **Props:**
671
+
672
+ ```typescript
673
+ type EmptyStateProps = {
674
+ paymentMethod: 'stored_card' | 'new_card'
675
+ cardNumberLength: number // Number of digits entered (for new card)
676
+ isCheckoutLoading: boolean
677
+ }
678
+ ```
679
+
680
+ **Example:**
681
+
682
+ ```tsx
683
+ customRender: {
684
+ emptyState: ({ paymentMethod, cardNumberLength }) => (
685
+ <div className="flex items-center justify-center h-64 border-2 border-dashed border-gray-200">
686
+ <div className="text-center text-gray-400">
687
+ {paymentMethod === 'stored_card'
688
+ ? 'Select a card to view installment options'
689
+ : cardNumberLength < 6
690
+ ? `Enter ${6 - cardNumberLength} more digits to see installments`
691
+ : 'No installment options available'}
692
+ </div>
693
+ </div>
694
+ )
695
+ }
696
+ ```
697
+
698
+ ---
699
+
700
+ ### 11. FullRender
701
+
702
+ Takes over the entire component rendering. You get all state, handlers, and default components as props.
703
+
704
+ **Props:**
705
+
706
+ ```typescript
707
+ type MasterpassRestOptionRenderProps = {
708
+ // State
709
+ paymentMethod: 'stored_card' | 'new_card'
710
+ hasStoredCards: boolean
711
+ shouldShowDirectForm: boolean
712
+ cvc: string
713
+ error: any
714
+
715
+ // Data
716
+ accountData: AccountAccessSuccessResponse | null
717
+ accountStatus: any
718
+ modalState: ModalState
719
+ paymentState: PaymentState
720
+
721
+ // Handlers
722
+ handlePaymentMethodChange: (method: 'stored_card' | 'new_card') => void
723
+ handleCardSelect: (card: any) => Promise<void>
724
+ updateModalState: (updates: Partial<ModalState>) => void
725
+ handleRemoveCard: (card: any) => void
726
+ confirmRemoveCard: () => Promise<void>
727
+ handleCvcChange: (cvc: string) => void
728
+ handleSaveCard: (cardData: CreditCardFormData) => Promise<void>
729
+ handleBinChange: (bin: string) => Promise<void>
730
+ handleInstallmentSelect: (installment: Installment) => Promise<void>
731
+ handleProceedToPayment: () => Promise<void>
732
+ handleLinkConfirm: () => Promise<void>
733
+ handleOTPSubmit: (otp: string) => Promise<{ success: boolean; message?: string }>
734
+ onCloseError: () => void
735
+
736
+ // Loading States
737
+ isCheckoutLoading: boolean
738
+ isInstallmentLoading: boolean
739
+ isPrepareLoading: boolean
740
+ isFinalizeLoading: boolean
741
+
742
+ // Texts
743
+ texts: MasterpassRestOptionTexts
744
+
745
+ // Default Components (use these to avoid reimplementing everything)
746
+ components: {
747
+ PaymentMethodSelector: React.ComponentType<PaymentMethodSelectorProps>
748
+ CardList: React.ComponentType<CardListProps>
749
+ CreditCardForm: React.ComponentType<CreditCardFormProps>
750
+ InstallmentList: React.ComponentType<InstallmentListProps>
751
+ LinkModal: React.ComponentType<LinkModalProps>
752
+ OTPModal: React.ComponentType<OTPModalProps>
753
+ ConfirmationModal: React.ComponentType<ConfirmationModalProps>
754
+ }
755
+ }
756
+ ```
757
+
758
+ **Example - Custom layout with default components:**
759
+
760
+ ```tsx
761
+ customRender: {
762
+ fullRender: (props) => {
763
+ const {
764
+ paymentMethod,
765
+ hasStoredCards,
766
+ shouldShowDirectForm,
767
+ accountData,
768
+ paymentState,
769
+ modalState,
770
+ handlePaymentMethodChange,
771
+ handleCardSelect,
772
+ handleCvcChange,
773
+ handleSaveCard,
774
+ handleBinChange,
775
+ handleInstallmentSelect,
776
+ handleProceedToPayment,
777
+ handleLinkConfirm,
778
+ handleOTPSubmit,
779
+ handleRemoveCard,
780
+ confirmRemoveCard,
781
+ updateModalState,
782
+ cvc,
783
+ isCheckoutLoading,
784
+ isInstallmentLoading,
785
+ isPrepareLoading,
786
+ isFinalizeLoading,
787
+ texts,
788
+ components
789
+ } = props
790
+
791
+ const { CardList, CreditCardForm, InstallmentList, LinkModal, OTPModal } = components
792
+
793
+ return (
794
+ <div className="my-custom-layout">
795
+ {/* Your custom header */}
796
+ <h1 className="text-2xl mb-6">Secure Payment</h1>
797
+
798
+ {/* Use default components with custom layout */}
799
+ {hasStoredCards && !shouldShowDirectForm && (
800
+ <div className="tabs mb-4">
801
+ <button onClick={() => handlePaymentMethodChange('stored_card')}>
802
+ Saved Cards
803
+ </button>
804
+ <button onClick={() => handlePaymentMethodChange('new_card')}>
805
+ New Card
806
+ </button>
807
+ </div>
808
+ )}
809
+
810
+ <div className="flex gap-8">
811
+ <div className="flex-1">
812
+ {paymentMethod === 'stored_card' && hasStoredCards ? (
813
+ <CardList
814
+ cards={accountData?.result?.cards || []}
815
+ onCardSelect={handleCardSelect}
816
+ selectedCard={paymentState.selectedCard}
817
+ onRemove={handleRemoveCard}
818
+ removingCardId={modalState?.removingCardId}
819
+ cvc={cvc}
820
+ onCvcChange={handleCvcChange}
821
+ texts={texts}
822
+ />
823
+ ) : (
824
+ <CreditCardForm
825
+ onSaveCard={handleSaveCard}
826
+ isLoading={isCheckoutLoading}
827
+ showSaveOption={true}
828
+ onBinChange={handleBinChange}
829
+ texts={texts}
830
+ />
831
+ )}
832
+ </div>
833
+
834
+ <div className="flex-1">
835
+ {paymentState.installments.length > 0 && (
836
+ <InstallmentList
837
+ installments={paymentState.installments}
838
+ cardType={paymentState.cardType}
839
+ onInstallmentSelect={handleInstallmentSelect}
840
+ selectedInstallment={paymentState.selectedInstallment}
841
+ isLoading={isInstallmentLoading || isPrepareLoading}
842
+ onProceedToPayment={handleProceedToPayment}
843
+ paymentLoading={isFinalizeLoading}
844
+ texts={texts}
845
+ />
846
+ )}
847
+ </div>
848
+ </div>
849
+
850
+ {/* Modals - use default components */}
851
+ <LinkModal
852
+ open={modalState?.showLinkModal ?? false}
853
+ onClose={() => updateModalState({ showLinkModal: false })}
854
+ onConfirm={handleLinkConfirm}
855
+ texts={texts}
856
+ />
857
+ <OTPModal
858
+ open={modalState?.showOTPModal ?? false}
859
+ onClose={() => updateModalState({ showOTPModal: false })}
860
+ onSubmit={handleOTPSubmit}
861
+ type={modalState?.otpType ?? 'OTP'}
862
+ texts={texts}
863
+ />
864
+ </div>
865
+ )
866
+ }
867
+ }
868
+ ```
869
+
870
+ ---
871
+
872
+ ## Text Customization
873
+
874
+ All user-facing strings can be overridden via the `texts` prop. Pass only the keys you want to change; the rest will use defaults.
875
+
876
+ ```tsx
877
+ <PluginModule
878
+ component={Component.MasterpassRest}
879
+ props={{
880
+ locale: 'tr',
881
+ currency: 'try',
882
+ texts: {
883
+ // Titles
884
+ title: 'Masterpass ile Ode',
885
+ selectCardTitle: 'Kart Secin',
886
+ newCardTitle: 'Yeni Kart ile Ode',
887
+ installmentOptionsTitle: 'Taksit Secenekleri',
888
+ enterCardDetailsTitle: 'Kart Bilgilerini Girin',
889
+
890
+ // Form labels
891
+ cardNumberLabel: 'Kart Numarasi',
892
+ cardholderNameLabel: 'Kart Uzerindeki Isim',
893
+ expiryDateLabel: 'Son Kullanma Tarihi',
894
+ cvcLabel: 'CVC/CVV',
895
+ saveCardLabel: 'Bu karti gelecek odemeler icin kaydet',
896
+ addCardButton: 'Kart Ekle',
897
+
898
+ // Payment method
899
+ savedCardsText: 'Kayitli Kartlar',
900
+ newCardText: 'Yeni Kart',
901
+
902
+ // Installments
903
+ singlePaymentText: 'Tek Cekim',
904
+ installmentsText: '{count} Taksit', // {count} is replaced dynamically
905
+ noInterestText: 'Faizsiz',
906
+ proceedToPaymentText: 'Odemeye Devam Et',
907
+ processingPaymentText: 'Odeme Isleniyor...',
908
+
909
+ // Card list
910
+ defaultCardText: 'Varsayilan',
911
+ expiresSoonText: 'Suresi Yakinda Dolacak',
912
+
913
+ // Modals
914
+ linkModalDescription: '<PHONE_NUMBER> numarasi ile Masterpass hesabinizdaki kartlari gormek ister misiniz?',
915
+ linkAccountButton: 'Evet, istiyorum',
916
+ linkModalCancelButton: 'Hayir, istemiyorum',
917
+ removeCardMessage: '<{cardAlias}> kartini Masterpass altyapisindan silmek istediginize emin misiniz?',
918
+
919
+ // Verification
920
+ rtaVerificationTitle: 'Guvenlik Dogrulamasi',
921
+ bankOtpVerificationTitle: 'Banka Dogrulamasi',
922
+ cvvVerificationTitle: 'CVV Dogrulamasi',
923
+ verifyButton: 'Dogrula',
924
+
925
+ // Errors
926
+ paymentErrorTitle: 'Odeme Hatasi',
927
+ cardNumberRequiredText: 'Kart numarasi zorunludur',
928
+ cardNumberInvalidText: 'Gecerli bir kart numarasi girin',
929
+
930
+ // Loading
931
+ loadingMessage: 'Odeme sistemi hazirlaniyor...',
932
+ scriptLoadingMessage: 'Odeme altyapisi yukleniyor...',
933
+
934
+ // Session
935
+ sessionExpiredTitle: 'Oturum Suresi Doldu',
936
+ sessionExpiredMessage: 'Oturumunuz zaman asimina ugradi. Devam etmek icin islemi yeniden baslatin.',
937
+ sessionExpiredButton: 'Yeniden Basla'
938
+ }
939
+ }}
940
+ />
941
+ ```
942
+
943
+ ### Dynamic Text Placeholders
944
+
945
+ Some text keys support placeholders:
946
+
947
+ | Key | Placeholder | Description |
948
+ |-----|------------|-------------|
949
+ | `installmentsText` | `{count}` | Installment count |
950
+ | `savedCardsDescription` | `{count}` | Number of saved cards |
951
+ | `removeCardMessage` | `{cardAlias}` | Name of the card being removed |
952
+ | `cvcHelpText` | `{length}`, `{side}` | CVC digit count and card side |
953
+ | `linkModalDescription` | `<PHONE_NUMBER>` | User's phone number (auto-replaced by SDK) |
954
+
955
+ ---
956
+
957
+ ## Exported Hooks
958
+
959
+ These hooks can be imported independently for advanced use cases:
960
+
961
+ ### `useMasterpassScript`
962
+
963
+ Manages Masterpass SDK script loading and initialization.
964
+
965
+ ```typescript
966
+ import { useMasterpassScript } from '@akinon/pz-masterpass-rest'
967
+
968
+ const { isScriptLoaded, isMasterpassInitialized } = useMasterpassScript({
969
+ locale: 'tr',
970
+ sdkUrl: 'https://mp-sdk.masterpassturkiye.com',
971
+ environment: 'production',
972
+ onScriptLoaded: () => console.log('SDK ready'),
973
+ onScriptError: (error) => console.error('SDK failed', error)
974
+ })
975
+ ```
976
+
977
+ ### `useMasterpassToken`
978
+
979
+ Fetches and manages the JWT token for Masterpass API authentication.
980
+
981
+ ```typescript
982
+ import { useMasterpassToken } from '@akinon/pz-masterpass-rest'
983
+
984
+ const { token, tokenData, isLoading, error, refetch } = useMasterpassToken({
985
+ useThreeD: false,
986
+ onTokenReady: (data) => console.log('Token ready', data)
987
+ })
988
+
989
+ // Token data includes: MerchantId, AccountKey, UserId, Hash, exp, etc.
990
+ ```
991
+
992
+ ### `useMasterpassAccount`
993
+
994
+ Manages account access, linking, card operations, and OTP verification.
995
+
996
+ ```typescript
997
+ import { useMasterpassAccount } from '@akinon/pz-masterpass-rest'
998
+
999
+ const {
1000
+ accountData, // User's account with cards
1001
+ accountStatus, // Linked/unlinked/not found
1002
+ modalState, // All modal visibility states
1003
+ initializeAccount,
1004
+ refreshAccountData,
1005
+ handleLinkConfirm,
1006
+ handleOTPSubmit,
1007
+ handleRemoveCard,
1008
+ confirmRemoveCard,
1009
+ handleAddCard
1010
+ } = useMasterpassAccount()
1011
+ ```
1012
+
1013
+ ### `useMasterpassPayment`
1014
+
1015
+ Handles the payment processing flow.
1016
+
1017
+ ```typescript
1018
+ import { useMasterpassPayment } from '@akinon/pz-masterpass-rest'
1019
+
1020
+ const {
1021
+ paymentState, // selectedCard, selectedInstallment, installments, etc.
1022
+ isCheckoutLoading,
1023
+ isInstallmentLoading,
1024
+ isPrepareLoading,
1025
+ isFinalizeLoading,
1026
+ handleCardSelect, // Select a saved card (triggers BIN check)
1027
+ handleInstallmentSelect,
1028
+ processPayment, // Stored card payment
1029
+ processDirectPayment // New card payment
1030
+ } = useMasterpassPayment()
1031
+ ```
1032
+
1033
+ ---
1034
+
1035
+ ## Exported Utilities
1036
+
1037
+ ```typescript
1038
+ 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
1059
+ } from '@akinon/pz-masterpass-rest'
1060
+ ```
1061
+
1062
+ ---
1063
+
1064
+ ## Type Definitions
1065
+
1066
+ All types are exported and can be imported for use in custom components:
1067
+
1068
+ ```typescript
1069
+ import type {
1070
+ // Component props
1071
+ PaymentMethodSelectorProps,
1072
+ CardListProps,
1073
+ CreditCardFormProps,
1074
+ InstallmentListProps,
1075
+ LinkModalProps,
1076
+ OTPModalProps,
1077
+ ConfirmationModalProps,
1078
+ ErrorDisplayProps,
1079
+ LoadingStateProps,
1080
+ EmptyStateProps,
1081
+ MasterpassRestOptionRenderProps,
1082
+ MasterpassRestOptionCustomRender,
1083
+ MasterpassRestOptionTexts,
1084
+
1085
+ // Data types
1086
+ CardModel,
1087
+ CardType,
1088
+ Installment,
1089
+ PaymentState,
1090
+ ModalState,
1091
+ OrderData,
1092
+ OTPType,
1093
+ TransactionType,
1094
+ InformationModalData,
1095
+
1096
+ // Account types
1097
+ AccountAccessRequest,
1098
+ AccountAccessResponse,
1099
+ AccountAccessSuccessResponse,
1100
+ AccountAccessErrorResponse,
1101
+ CardResponse,
1102
+
1103
+ // Payment types
1104
+ PaymentProcessRequest,
1105
+ DirectPaymentRequest,
1106
+ DirectPaymentResponse,
1107
+ MPResponse,
1108
+
1109
+ // Environment
1110
+ MasterpassEnvironment
1111
+ } from '@akinon/pz-masterpass-rest'
1112
+ ```
1113
+
1114
+ ---
1115
+
1116
+ ## Payment Flow
1117
+
1118
+ ### Stored Card Payment
1119
+
1120
+ ```
1121
+ 1. Component mounts
1122
+ -> Token fetched from backend
1123
+ -> Masterpass SDK loaded
1124
+ -> Account access requested
1125
+
1126
+ 2. Account status determined:
1127
+ - Account found & linked -> Show saved cards
1128
+ - Account found & NOT linked -> Show LinkModal
1129
+ - Account not found -> Show new card form only
1130
+
1131
+ 3. User selects a saved card
1132
+ -> BIN check sent to backend
1133
+ -> Installment options returned
1134
+
1135
+ 4. User selects installment option
1136
+
1137
+ 5. User clicks "Proceed to Payment"
1138
+ -> Order prepared (gets order number)
1139
+ -> Payment request sent via Masterpass SDK
1140
+
1141
+ 6. Payment result:
1142
+ - Success -> Order finalized
1143
+ - 3D Secure required -> Redirect to bank page
1144
+ - OTP required -> Show OTP modal (RTA/OTP/CVV)
1145
+ - Error -> Show error display
1146
+ ```
1147
+
1148
+ ### New Card Payment
1149
+
1150
+ ```
1151
+ 1. User enters card number (6+ digits)
1152
+ -> BIN check sent automatically
1153
+ -> Installment options shown
1154
+
1155
+ 2. User fills remaining card fields
1156
+
1157
+ 3. User selects installment option
1158
+
1159
+ 4. User clicks "Proceed to Payment"
1160
+ -> Direct payment request via Masterpass SDK
1161
+
1162
+ 5. Same result handling as stored card flow
1163
+ ```
1164
+
1165
+ ### OTP Verification
1166
+
1167
+ ```
1168
+ 1. OTP modal shown with type-specific UI (RTA/OTP/CVV)
1169
+ 2. User enters verification code
1170
+ 3. Code submitted to Masterpass
1171
+ 4. Result:
1172
+ - Success -> Order finalized
1173
+ - Another OTP needed -> Modal updates
1174
+ - Session expired -> Information modal shown
1175
+ - 3D Secure needed -> Redirect
1176
+ ```