@retter/rn-masterpass
v0.1.0
Published
React Native Masterpass SDK for retter.io
Keywords
Readme
@retter/rn-masterpass
React Native Masterpass Turkey SDK. A wrapper library to use the Masterpass Turkey JS library (designed for web) in React Native.
Two SDK generations live in this package:
MasterPassV2— wraps the new JSON/JWT based Masterpass web SDK (masterpass-javascript-sdk-web). Use this for new integrations. See the Masterpass V2 section.MasterPass(below) — wraps the legacy MFS client (mfs-client.min.js). Kept unchanged for existing integrations; documented in the sections that follow.
Installation
npm
npm install @retter/rn-masterpassyarn
yarn add @retter/rn-masterpassPeer Dependencies
This package requires the following peer dependencies:
npm install react react-native react-native-webview
# or
yarn add react react-native react-native-webviewRequirements:
react >= 16.8.0react-native >= 0.60.0react-native-webview >= 13.15.0
Quick Start
1. Create SDK Instance
import MasterPass, { MasterPassView } from '@retter/rn-masterpass';
const masterPass = MasterPass.getInstance({
token: 'your-token',
referenceNo: 'your-reference-no',
userId: 'user-phone-number',
sendSms: 'Y', // 'Y' or 'N'
sendSmsLanguage: 'tr', // 'tr' or 'en'
macroMerchantId: 'your-merchant-id',
clientIp: 'client-ip-address',
sdkUrl: 'https://masterpass.com/sdk.js',
clientId: 'your-client-id',
serviceUrl: 'https://masterpass.com/service',
});2. Render Component
import React from 'react';
import { View } from 'react-native';
import MasterPass, { MasterPassView } from '@retter/rn-masterpass';
const PaymentScreen = () => {
const masterPass = MasterPass.getInstance({
// ... config
});
return (
<View>
<MasterPassView
masterPass={masterPass}
onEvent={(data) => {
console.log('Event:', data);
}}
onRequest={async (message, body) => {
// Handle requests from web
return null;
}}
/>
</View>
);
};API Reference
MasterPass Class
getInstance(config: MasterPassTurkeyArgs): MasterPass
Creates a new SDK instance. Each call creates a new instance, allowing you to initialize with different tokens as needed.
Parameters:
interface MasterPassTurkeyArgs {
token: string; // Masterpass token
referenceNo: string; // Reference number
userId: string; // User phone number
sendSmsLanguage: string; // SMS language ('tr' or 'en')
sendSms: 'N' | 'Y'; // Send SMS
macroMerchantId: string; // Merchant ID
clientIp: string; // Client IP address
sdkUrl: string; // Masterpass SDK URL
clientId: string; // Client ID
serviceUrl: string; // Service URL
}updateConfig(config: Partial<MasterPassTurkeyArgs>): void
Dynamically updates the config. Changed values are automatically applied to forms in WebView.
masterPass.updateConfig({
token: 'new-token',
referenceNo: 'new-reference-no',
});registrationCheck(): Promise<RegistrationCheckResult>
Checks the user's Masterpass registration status.
Return Value:
interface RegistrationCheckResult {
result: boolean;
action: RegistrationCheckAction;
}
enum RegistrationCheckAction {
linkCards = 'link-cards', // Card linking required
listCards = 'list-cards', // Registered cards can be listed
showMpOption = 'show-mp-option' // Show Masterpass option
}Usage Example:
try {
const result = await masterPass.registrationCheck();
switch (result.action) {
case 'link-cards':
// Redirect to card linking flow
await masterPass.linkCards();
break;
case 'list-cards':
// List registered cards
const cards = await masterPass.listCards();
break;
case 'show-mp-option':
// Show Masterpass option
break;
}
} catch (error) {
console.error('Registration check failed:', error);
}linkCards(): Promise<OtpResult>
Links user's cards to Masterpass account.
Return Value:
interface OtpResult {
result: boolean;
action: OtpAction;
type: OtpType;
}
enum OtpAction {
verifyOtp = 'verify-otp', // OTP verification required
listCards = 'list-cards', // Cards successfully listed
redirect3D = 'redirect-3D' // 3D Secure redirect
}
enum OtpType {
bank = 'bank', // Bank OTP
masterPass = 'mp', // Masterpass OTP
mPin = 'mpin' // MPIN
}Usage Example:
try {
const result = await masterPass.linkCards();
if (result.action === 'verify-otp') {
// Show OTP screen
// Get OTP code from user
const otpCode = '123456';
await masterPass.verifyOtp(otpCode, result.type);
}
} catch (error) {
console.error('Link cards failed:', error);
}listCards(): Promise<CardResult>
Lists user's registered cards.
Return Value:
interface CardResult {
result: boolean;
action: CardAction;
cards: CardModel[];
}
enum CardAction {
hideMpOption = 'hide-mp-option', // Hide Masterpass option
purchase = 'purchase' // Purchase can be made
}
interface CardModel {
BankIca: string; // Bank ICA code
CardStatus: string; // Card status
IsMasterPassMember: 'Y' | 'N'; // Is Masterpass member?
Name: string; // Card name
ProductName: string; // Product name
UniqueId: string; // Unique ID
Value1: string; // Masked card number
}Usage Example:
try {
const result = await masterPass.listCards();
if (result.result && result.cards.length > 0) {
result.cards.forEach(card => {
console.log(`${card.Name}: ${card.Value1}`);
});
}
} catch (error) {
console.error('List cards failed:', error);
}deleteCard(cardName: string): Promise<DeleteCardResult>
Deletes a registered card.
Parameters:
cardName: string- Name of the card to delete
Return Value:
interface DeleteCardResult {
result: boolean;
}Usage Example:
try {
const result = await masterPass.deleteCard('My Card');
if (result.result) {
console.log('Card deleted successfully');
}
} catch (error) {
console.error('Delete card failed:', error);
}resendOtp(): Promise<OtpResult>
Resends the OTP code.
Usage Example:
try {
const result = await masterPass.resendOtp();
// OTP sent, notify user
} catch (error) {
console.error('Resend OTP failed:', error);
}verifyOtp(code: string, type: OtpType): Promise<OtpResult>
Verifies the OTP code.
Parameters:
code: string- OTP codetype: OtpType- OTP type ('bank', 'mp', 'mpin')
Usage Example:
import { OtpType } from '@retter/rn-masterpass';
try {
const result = await masterPass.verifyOtp('123456', OtpType.bank);
if (result.action === 'list-cards') {
// OTP verified, cards can be listed
const cards = await masterPass.listCards();
} else if (result.action === 'redirect-3D') {
// 3D Secure redirect
}
} catch (error) {
console.error('Verify OTP failed:', error);
}purchase(args: PurchaseArgs): Promise<PurchaseResult>
Performs a purchase transaction. Can purchase with a new card or registered card.
Purchase with New Card:
import { PurchaseNewArgs } from '@retter/rn-masterpass';
const purchaseArgs: PurchaseNewArgs = {
orderNo: 'ORDER123',
referenceNo: 'REF123',
amount: 100.50,
installmentCount: 1,
card: {
cardHolderName: 'John Doe',
number: '5555555555555555',
cvc: '123',
expMonth: 12,
expYear: 2025,
accountAliasName: 'My Card', // Optional: to save the card
},
additionalParameters: { // Optional
// Additional parameters
},
};
try {
const result = await masterPass.purchase(purchaseArgs);
if (result.action === 'verify-otp') {
// OTP verification required
await masterPass.verifyOtp('123456', result.type);
} else if (result.action === 'redirect-3D') {
// 3D Secure redirect
// Redirect to 3D Secure page with result.url
}
} catch (error) {
console.error('Purchase failed:', error);
}Purchase with Registered Card:
import { PurchaseExistingArgs } from '@retter/rn-masterpass';
const purchaseArgs: PurchaseExistingArgs = {
orderNo: 'ORDER123',
referenceNo: 'REF123',
amount: 100.50,
installmentCount: 1,
cardName: 'My Card', // Registered card name
token: 'optional-token', // Optional
additionalParameters: { // Optional
// Additional parameters
},
};
try {
const result = await masterPass.purchase(purchaseArgs);
// ...
} catch (error) {
console.error('Purchase failed:', error);
}Return Value:
interface PurchaseResult {
result: boolean;
action: PurchaseAction;
token: string;
purchaseType: PurchaseType;
type: OtpType;
url: string; // 3D Secure URL
}
enum PurchaseAction {
verifyOtp = 'verify-otp', // OTP verification required
redirect3D = 'redirect-3D' // 3D Secure redirect
}
enum PurchaseType {
NewCardRegistration = 0, // Payment with new card registration
WithRegisteredCard = 1, // Payment with registered card
DirectPayment = 2 // Direct payment
}setOnEvent(callback: (data: any) => void): void
Sets the event callback. Listens to events from WebView.
masterPass.setOnEvent((data) => {
console.log('Event received:', data);
if (data.status === 'ready') {
console.log('MasterPass is ready');
}
});setOnRequest(callback: (message: string, body: any) => Promise<any>): void
Handles requests from web.
masterPass.setOnRequest(async (message, body) => {
console.log('Request received:', message, body);
// Handle request and return result
return { success: true };
});MasterPassView Component
React component that manages the WebView.
Props:
interface MasterPassViewProps {
masterPass: MasterPass; // MasterPass instance
onEvent?: (data: any) => void; // Event callback
onRequest?: (message: string, body: any) => Promise<any>; // Request callback
style?: any; // Additional style (optional)
}Usage:
<MasterPassView
masterPass={masterPass}
onEvent={(data) => {
console.log('Event:', data);
}}
onRequest={async (message, body) => {
// Request handling
return null;
}}
style={{ opacity: 0 }} // WebView should be hidden
/>Complete Example
import React, { useState, useEffect } from 'react';
import { View, Text, Button, TextInput, Alert } from 'react-native';
import MasterPass, { MasterPassView, OtpType } from '@retter/rn-masterpass';
const PaymentScreen = () => {
const [masterPass] = useState(() =>
MasterPass.getInstance({
token: 'your-token',
referenceNo: 'REF123',
userId: '5551234567',
sendSms: 'Y',
sendSmsLanguage: 'tr',
macroMerchantId: 'MERCHANT123',
clientIp: '192.168.1.1',
sdkUrl: 'https://masterpass.com/sdk.js',
clientId: 'CLIENT123',
serviceUrl: 'https://masterpass.com/service',
})
);
const [cards, setCards] = useState([]);
const [otpCode, setOtpCode] = useState('');
const [showOtpInput, setShowOtpInput] = useState(false);
const [otpType, setOtpType] = useState<OtpType>(OtpType.bank);
useEffect(() => {
checkRegistration();
}, []);
const checkRegistration = async () => {
try {
const result = await masterPass.registrationCheck();
if (result.action === 'list-cards') {
await loadCards();
}
} catch (error) {
Alert.alert('Error', error.message);
}
};
const loadCards = async () => {
try {
const result = await masterPass.listCards();
if (result.result) {
setCards(result.cards);
}
} catch (error) {
Alert.alert('Error', error.message);
}
};
const handlePurchase = async (cardName?: string) => {
try {
const purchaseArgs = cardName
? {
orderNo: 'ORDER123',
referenceNo: 'REF123',
amount: 100.50,
installmentCount: 1,
cardName,
}
: {
orderNo: 'ORDER123',
referenceNo: 'REF123',
amount: 100.50,
installmentCount: 1,
card: {
cardHolderName: 'John Doe',
number: '5555555555555555',
cvc: '123',
expMonth: 12,
expYear: 2025,
},
};
const result = await masterPass.purchase(purchaseArgs);
if (result.action === 'verify-otp') {
setOtpType(result.type);
setShowOtpInput(true);
} else if (result.action === 'redirect-3D') {
// 3D Secure redirect
Alert.alert('3D Secure', `URL: ${result.url}`);
}
} catch (error) {
Alert.alert('Error', error.message);
}
};
const handleVerifyOtp = async () => {
try {
const result = await masterPass.verifyOtp(otpCode, otpType);
setShowOtpInput(false);
setOtpCode('');
if (result.action === 'list-cards') {
await loadCards();
}
} catch (error) {
Alert.alert('Error', error.message);
}
};
return (
<View style={{ flex: 1, padding: 20 }}>
<MasterPassView masterPass={masterPass} />
<Text style={{ fontSize: 20, marginBottom: 20 }}>Registered Cards</Text>
{cards.map((card) => (
<View key={card.UniqueId} style={{ marginBottom: 10 }}>
<Text>{card.Name}</Text>
<Text>{card.Value1}</Text>
<Button
title="Pay with This Card"
onPress={() => handlePurchase(card.Name)}
/>
</View>
))}
<Button
title="Pay with New Card"
onPress={() => handlePurchase()}
/>
{showOtpInput && (
<View style={{ marginTop: 20 }}>
<TextInput
placeholder="OTP Code"
value={otpCode}
onChangeText={setOtpCode}
keyboardType="numeric"
/>
<Button title="Verify" onPress={handleVerifyOtp} />
</View>
)}
</View>
);
};
export default PaymentScreen;Type Definitions
All type definitions are exported from the package:
import {
MasterPassTurkeyArgs,
RegistrationCheckResult,
RegistrationCheckAction,
OtpResult,
OtpAction,
OtpType,
CardResult,
CardAction,
CardModel,
DeleteCardResult,
PurchaseArgs,
PurchaseNewArgs,
PurchaseExistingArgs,
PurchaseResult,
PurchaseAction,
PurchaseType,
PurchaseCardModel,
} from '@retter/rn-masterpass';Notes
- WebView should be hidden (
opacity: 0). The MasterPassView component does this automatically. - The
getInstancemethod creates a new instance each time. This allows you to initialize with different tokens as needed (e.g., after fetching a token from your backend). - All methods return Promises, you can use async/await or
.then(). - Methods throw Errors in error cases, catch them with try-catch.
Masterpass V2
MasterPassV2 wraps the new Masterpass Turkey web SDK (JSON payloads + JWT auth, no jQuery/forms). It runs the SDK inside a hidden WebView and exposes a typed, promise-based API. The legacy MasterPass class is untouched — both can coexist in one app.
Key differences from V1:
- No
clientId/macroMerchantId/referenceNo— merchant identity travels in a JWT (accessToken) minted by your backend against the Masterpass merchant API. - Explicit
init()— no more throwawayregistrationCheck()to configure the SDK. - No
registrationCheck()— thelistCards()result drives the state machine (cards / no wallet / not linked). - Step-up URLs come ready-built —
returnUrl(andjwttokenfor the hosted-iframe flow) are appended by the library. - Built-in timeouts — a 60s bridge timeout (
BRIDGE_TIMEOUT) and a 30s SDK-load timeout (SDK_LOAD_TIMEOUT); no need for app-sidewithTimeoutwrappers. - Self-healing WebView — if the WebView reloads (renderer death, screen remount), the SDK is reconfigured automatically on the next
readysignal.
Quick Start (V2)
import { MasterPassV2, MasterPassView } from '@retter/rn-masterpass';
// 1. Create the instance with the access token from YOUR backend
// (raw value, e.g. "Bearer eyJ...")
const masterPass = MasterPassV2.getInstance({
accessToken: 'Bearer eyJ...',
endpoint: 'https://mp-test-sdk.masterpassturkiye.com',
sdkUrl: 'https://your-domain.com/masterpass/masterpass-v2-sdk-1.0.7.min.js',
returnUrl: 'https://your-domain.com/payment-results',
webBaseUrl: 'https://your-domain.com', // WebView origin (CORS)
terminalGroupId: 'your-terminal-group-id', // required for payments
acquirerIcaNumber: 'your-acquirer-ica', // required for payments
language: 'tr-TR',
});
// 2. Mount the hidden WebView (note the baseUrl prop!)
<MasterPassView
masterPass={masterPass}
baseUrl={masterPass.getWebBaseUrl()}
onEvent={async (event) => {
if (event.status === 'ready') {
// 3. Initialize the SDK, then drive UI off listCards()
await masterPass.init();
const result = await masterPass.listCards();
// result.action: 'list-cards' (+ cards) | 'show-mp-option' | 'link-cards'
} else if (event.status === 'sdk-load-failed') {
// SDK script could not be loaded (offline / wrong sdkUrl)
}
}}
/>V2 Configuration
| Field | Required | Description |
|---|---|---|
| accessToken | ✅ | Raw token from your backend ("Bearer eyJ..." — the Bearer prefix is stripped internally where needed) |
| endpoint | ✅ | Masterpass V2 API base URL (test: https://mp-test-sdk.masterpassturkiye.com) |
| sdkUrl | ✅ | HTTPS URL of the self-hosted masterpass-javascript-sdk-web bundle |
| returnUrl | ✅ | Landing page for 3DS (5010) and hosted-iframe (5009) returns |
| webBaseUrl | – | WebView baseUrl; gives the inline HTML a real origin so fetch/CORS work |
| terminalGroupId | for payments | Merchant acquiring config from Masterpass onboarding |
| acquirerIcaNumber | for payments | Merchant acquiring config from Masterpass onboarding |
| language | – | SDK language, must contain - (default tr-TR) |
V2 API
All methods return Promises and reject with Error & { code: string; correlationId?: string; response?: any }. Log correlationId on failures — Masterpass support tracks transactions by it.
| Method | Returns | Notes |
|---|---|---|
| init() | { claims } | Waits for the WebView ready signal, configures the SDK, decodes JWT claims. Call once after ready. |
| updateAccessToken(token) | void | Token refresh; safe to call before the page is ready (applied on next ready). |
| getSession() | { accessToken, jwt, claims } \| null | e.g. to send accessToken to your backend's commit call |
| listCards() | { action: 'list-cards', cards } / { action: 'show-mp-option' } / { action: 'link-cards' } | show-mp-option = no wallet (ACCOUNT_NOT_FOUND) — not an error; also returned after deleting the last card |
| addCard({ cardNumber, cardHolderName, expiryDate, cvv, accountAliasName, email? }) | V2FlowResult | expiryDate is YYMM |
| deleteCard(cardAlias) | { result: true } | cardAlias is the V2 card identity |
| linkToMerchant() | V2FlowResult | Links an existing wallet to your merchant |
| purchase({ orderNo, amount, installmentCount?, cardName? \| card? }) | V2FlowResult | amount in kuruş; cardName (= cardAlias) → saved-card payment, card {number, cvc, expMonth, expYear, cardHolderName} → direct payment |
| verifyOtp(code) | V2FlowResult | Mode-aware: routes to otpCode / validationCode, or replays the payment with cvc for a 5013 step-up on saved-card payments |
| resendOtp() | { result: boolean } | result: true when a new SMS was sent |
V2 Result Actions (V2FlowResult)
| action | Fields | What to do |
|---|---|---|
| 'purchase' | token | Payment succeeded — send token (+ getSession().accessToken) to your backend to commit. An empty token is never returned (rejected as EMPTY_TOKEN). |
| 'list-cards' | token? | addCard/link flow succeeded — refresh the card list |
| 'verify-otp' | type: 'otp' \| 'rta' \| 'cvv', message, step | Show a code-entry modal; call verifyOtp(code). step increments on chained step-ups (e.g. wallet OTP → bank OTP) — remount your modal (key={step}) and tell the user a new SMS arrived. |
| 'redirect-3D' | url | Open in a visible WebView; returnUrl is already appended. Detect completion by sniffing navigation to your return URL and reading its ?token= param. |
| 'redirect-iframe' | url | Hosted-iframe flow (5009); jwttoken + returnUrl already appended. Same completion handling as 3DS. |
Notable error codes: ACCOUNT_NOT_FOUND (no wallet — handled by listCards), CARD_ALREADY_EXISTS, EMPTY_TOKEN, FLOW_EXPIRED (step-up state lost — restart the flow), BRIDGE_TIMEOUT, SDK_LOAD_FAILED / SDK_LOAD_TIMEOUT, NOT_INITIALIZED (init() not called).
V2 Notes
- One chained flow per instance. Step-up state (OTP chains) lives inside each instance's WebView; never start a flow on one instance and verify it on another. If two screens need Masterpass at once (e.g. checkout + add-card), give each its own instance and
MasterPassView— that is fully supported. - Pass
baseUrl={masterPass.getWebBaseUrl()}toMasterPassView. Without it the inline HTML runs at a null origin and the SDK's fetch calls may fail CORS. - The SDK validates card numbers (Luhn) synchronously; invalid PANs reject with the SDK's own error message.
- Card objects are
{ cardAlias, maskedCardNumber }(e.g.540667******5403) — the first 6 characters of the masked PAN are the real BIN.
License
ISC
Support
For questions: GitHub Issues
