Documentation Index

Fetch the complete documentation index at: https://docs.monri.com/llms.txt

Use this file to discover all available pages before exploring further.

Card component

Prev Next

Click to Pay — card component

The card component renders card input fields (PAN, expiry, CVV) and Click to Pay in a single iframe. Use it when you do not have your own card form — the component provides everything.

If you already have your own card form and only want to add Click to Pay alongside it, use c2p-extension instead. For shared concepts (enablement, states, accessibility), see the Click to Pay overview.

Prerequisites

Include the Components script and add a container element:

<script src="https://ipgtest.monri.com/dist/components.js"></script>
<div id="card-element"></div>

Integration

// Initialize Monri
const monri = Monri('your_authenticity_token', {
  locale: 'en' // or 'hr', 'de', etc.
});

// Create a components instance
const components = monri.components({
  clientSecret: 'your_client_secret'
});

// Create the card component with Click to Pay
const card = components.create("card", {
  clientSecret: 'your_client_secret',
  locale: 'en',
  environment: 'test',               // 'test' or 'prod'

  // Click to Pay configuration (optional — auto-detected from the merchant's backend settings)
  clickToPay: {
    chPhone: '+385991234567',        // Customer phone (optional)
    chEmail: 'customer@example.com'  // Customer email (optional)
  },

  // Light theme styling (optional)
  style: {
    customComponents: {
      backgroundColor: '#ffffff',
      textColor: '#333333',
      primaryColor: '#8132A7',
      borderColor: '#e0e0e0',
      surfaceColor: 'rgba(0, 0, 0, .02)',
      radioButtonBackgroundColor: 'rgba(0, 0, 0, .02)',
      radioButtonSelectedBackgroundColor: '#FCF5FF',
      secondaryTextColor: '#575757',
      successColor: '#4caf50',
      errorColor: '#f44336',
      errorBackgroundColor: 'rgba(244, 67, 54, 0.1)',
      successBorderColor: '2px solid #4caf50',
      errorBorderColor: '2px solid #f44336'
    }
  },

  // Dark theme styling (optional)
  darkThemeStyle: {
    customComponents: {
      backgroundColor: 'rgba(0, 0, 0, 1)',
      textColor: '#ffffff',
      primaryColor: '#FF66FF',
      borderColor: '#444444',
      surfaceColor: 'rgba(255, 255, 255, .05)',
      radioButtonBackgroundColor: 'rgba(255, 255, 255, .05)',
      radioButtonSelectedBackgroundColor: '#1A001F',
      secondaryTextColor: '#FFFFFF',
      successColor: '#81c784',
      errorColor: '#e57373',
      errorBackgroundColor: 'rgba(229, 115, 115, 0.1)',
      successBorderColor: '2px solid #81c784',
      errorBorderColor: '2px solid #e57373'
    }
  }
});

// Mount the component
card.mount("card-element");

Click to Pay configuration

The entire clickToPay object is optional. When omitted, Click to Pay availability is auto-detected from the merchant's backend configuration (mastercard_c2p_active flag in the transaction hash). If the merchant has Click to Pay enabled, it activates automatically — even without passing clickToPay. All fields within the object are also optional.

Note: identity lookup is triggered as soon as either chEmail or chPhone is provided — both are not required. If the first identity finds no saved cards, the lookup automatically retries when the second identity is provided.

Property Type Required Description
chPhone string No Customer phone number (E.164 format) — used for identity lookup
chEmail string No Customer email address — used for identity lookup
isHighContrast 'true' | 'false' No High-contrast mode for accessibility
dyslexicFont 'true' | 'false' No Dyslexia-friendly font
fontSize 'normal' | 'large' | 'larger' No Font-size scaling

Minimal integration (Click to Pay auto-detected, no customer data pre-filled):

const card = components.create("card", {
  clientSecret: 'your_client_secret',
  environment: 'test'
});

Styling properties

Both style.customComponents and darkThemeStyle.customComponents accept:

Property Type Description
backgroundColor string Main background color
textColor string Primary text color
primaryColor string Brand/accent color
borderColor string Border color for inputs
surfaceColor string Surface/card background
radioButtonBackgroundColor string Radio button background
radioButtonSelectedBackgroundColor string Selected radio button background
secondaryTextColor string Secondary text color
successColor string Success state color
errorColor string Error state color
errorBackgroundColor string Error background color
successBorderColor string Success border style (e.g. '2px solid #4caf50')
errorBorderColor string Error border style (e.g. '2px solid #f44336')

Layout & sizing

Viewport breakpoints

The component detects the parent window's viewport width (not the iframe's own width) and applies responsive behaviour:

Viewport Parent width CSS class on iframe body
Mobile <= 575px viewport-mobile
Tablet 576px – 760px viewport-tablet
Desktop > 760px viewport-desktop

Width

The card iframe uses width: 1px; min-width: 100% — it fills its parent container. The Click to Pay content inside also fills 100% width. Size the parent <div> to control the component's width. All states render at 100% width on all viewports.

Height

The iframe height adjusts dynamically per state. Base heights (normal font size):

State Mobile (<=575px) Desktop (>575px) Notes
empty 220px 200px Card form only, Click to Pay hidden
loading 200px 180px Card form + loading spinner
consent ~290px+ ~260px+ Card form + consent text (dynamic)
card-list ~170px/card ~160px/card Per-card height, paginated at 3
otp-input 320px 310px +110–120px if validation channels shown
new-card ~280px+ ~260px+ Card form + consent (dynamic)
not-your-card ~240px+ ~240px+ Identity switch form (dynamic)

Heights scale proportionally with font size (large ~1.14×, larger ~1.29×). Error messages add ~54px+ when visible.

Events

State change

Fires when the Click to Pay UI transitions between states.

card.addChangeListener('state_change', (event) => {
  console.log('C2P State:', event.data.state);
});

Possible states:

State Description
'loading' The Click to Pay SDK is initializing
'empty' Initial state, or SDK failed / no identity found — the traditional card form is shown
'consent' New-user consent screen — appears when a valid card brand is entered in the PAN field; disappears if the PAN is cleared
'card-list' Returning shopper sees their saved cards
'otp-input' OTP verification screen
'new-card' Shopper is adding a new card — appears when a valid card brand is entered in the PAN field; disappears if the PAN is cleared
'not-your-card' Shopper is switching identity

Checkout data

Fires when a Click to Pay payment completes. Contains the v2/transaction result.

card.addChangeListener('checkout_data', (event) => {
  const { response } = event.data;
  if (response.error) {
    console.error('C2P payment failed:', response.error.message);
  } else {
    console.log('C2P payment result:', response.result);
  }
});
// checkout_data event payload (Click to Pay path)
type CheckoutDataPayload = {
  data: {
    response: {
      result: {
        status: string;   // 'approved' | 'declined' | 'action_required'
        id: string;
        action_required?: {
          redirect_to: string;
          acs_url: string;
        };
        payment_result?: {
          amount: number;
          currency: string;
        };
      } | null;
      error: { message: string } | null;
    };
  };
};

Installments

Installments are controlled by different flags depending on which state is active:

  • New-card / consent state — gated by merchant.installment_enabled (backend merchant setting)
  • Card-list state — gated by mastercard_c2p_settings.allow_installments (a separate backend setting inside the Click to Pay config)

Both paths also require showInstallmentsSelection: true in the component options and a /pan-info response returning more than one installment option.

New-card / consent state (shopper is typing a new PAN)

  1. Pass showInstallmentsSelection: true in the component options
  2. merchant.installment_enabled: true in the transaction hash. If no dropdown ever appears, installments are disabled for your merchant account — contact Monri support
  3. /pan-info returns more than one installment option for the entered PAN

When the shopper enters a PAN with more than 10 digits, the component calls /pan-info internally and renders a standard installments dropdown alongside the card form.

const card = components.create("card", {
  clientSecret: 'your_client_secret',
  environment: 'test',
  showInstallmentsSelection: true
});
card.addChangeListener('installments', (event) => {
  console.log('Selected installment:', event.data.selectedInstallment);
  // 1 = single payment, 2, 3, etc. = number of installments
});

The selected value is automatically included in the v2/transaction request (number_of_installments) when the shopper confirms payment.

Card-list state (returning shopper with saved cards)

  1. Pass showInstallmentsSelection: true in the component options
  2. mastercard_c2p_settings.allow_installments: true in the transaction hash (separate from merchant.installment_enabled)
  3. /pan-info returns more than one installment option for the selected card's BIN

Because a saved card has no PAN input, the component fetches /pan-info for all saved cards in parallel while the loading state is shown. When the shopper selects a card, the dropdown appears immediately using the pre-fetched data, and the selected value is included in the v2/transaction request automatically. Cards remain selectable regardless of BIN data availability; if there are no installment options, the payment proceeds as a single charge.

Not your card

Fires when the shopper wants to switch accounts or enter a card manually.

card.addChangeListener('not_your_card', (event) => {
  const { inputType, inputValue } = event.data;
  // inputType: 'email' | 'phone' | 'manual'
  // inputValue: string | null
});

Payment processing

Traditional card payment

When the shopper enters card details manually (not through Click to Pay), use confirmPayment:

monri.confirmPayment(card, {
  address: "123 Main Street",
  city: "New York",
  email: "customer@example.com",
  fullName: "John Doe",
  phone: "+1-555-123-4567",
  country: "US",
  zip: "10001",
  orderInfo: "Order #12345",
  language: 'en'
}).then((result) => {
  if (result.error) {
    console.error('Payment failed:', result.error);
  } else if (result.result.status === "approved") {
    console.log('Payment successful:', result.result);
  }
}).catch((error) => {
  console.error('Payment error:', error);
});
// confirmPayment result model
type ConfirmPaymentResult = {
  error: { message: string } | null;
  result: {
    status: string;   // 'approved' | 'declined' | 'action_required' | 'executed' | 'payment_method_required'
    id: string;
    action_required?: {
      redirect_to: string;
      acs_url: string;
    };
    payment_result?: {
      amount: number;
      currency: string;
    };
  } | null;
};

The checkoutActionCode in the Mastercard response indicates the outcome:

Action code Meaning
COMPLETE Checkout completed successfully
CANCEL Shopper cancelled the checkout
CHANGE_CARD Shopper wants to select a different card
ADD_CARD Shopper wants to add a new card

Runtime methods

card.setLanguage('hr');                  // update language
card.toggleTheme(true);                  // true = dark, false = light
card.toggleDyslexicFont(true);           // dyslexic font
card.toggleFontSize('large');            // 'normal' | 'large' | 'larger'
card.clientPhoneUpdate('+385991234567'); // update contact — re-runs identity lookup
card.clientEmailUpdate('new@example.com');
card.updateActiveBrand('mastercard');    // sync active card brand with the Click to Pay UI

Accessibility

Click to Pay includes comprehensive accessibility support:

  • High-contrast mode — set via isHighContrast or toggleTheme() at runtime
  • Dyslexic font — OpenDyslexic font family, via dyslexicFont or toggleDyslexicFont()
  • Font-size scalingnormal, large, larger, via fontSize or toggleFontSize()
  • Theme support — light and dark variants with automatic contrast adjustments
const card = components.create("card", {
  environment: 'test',
  clickToPay: {
    isHighContrast: localStorage.getItem('highContrast') ?? 'false',
    dyslexicFont: localStorage.getItem('dyslexicFont') ?? 'false',
    fontSize: localStorage.getItem('fontSizeState') ?? 'normal'
  }
});