Skip to main content

When and Why to Customize Text

Customize checkout form text to match your brand voice, provide clearer user guidance, and support international customers. Text customization helps create a cohesive experience that aligns with your existing user interface patterns.

Complete Control

Override labels, buttons, validation messages, and status notifications.

Multi-Language Support

30+ built-in translations with automatic browser language detection.

RTL Language Support

Automatic right-to-left layout adjustment for Arabic and other RTL languages.

Partial Overrides

Change only specific text elements while keeping default translations for others.

Implement Language Detection

The PayNext SDK automatically detects the user’s preferred language from browser settings:
Provide the locale and translate properties on the object you pass into checkout.mount('element-id', { ...config }) after creating the instance with new PayNextCheckout(), as shown in Mount the Checkout. The snippets below focus on those configuration objects.
import { useState } from 'react'
import { type PayNextConfig } from '@paynext/sdk'

const defaultConfig: PayNextConfig = {
  /* other options */
  // locale omitted: automatic detection
}

const spanishConfig: PayNextConfig = {
  ...defaultConfig,
  locale: 'es', // Manual override
}

// Dynamic language switching (React example)
const [currentLocale, setCurrentLocale] = useState(
  navigator.language.split('-')[0]
)

const dynamicConfig: PayNextConfig = {
  ...defaultConfig,
  locale: currentLocale,
}

Override the Global Error Banner

The SDK reads errorMessageText from the same configuration object you pass into checkout.mount(...). That value flows straight into the checkout state and overrides the localized fallback string shown in the global error banner.
this.core.state.checkout.setState({
  clientToken: config.clientToken,
  environment: config.environment,
  apiVersion: config.apiVersion,
  errorMessageText: config.errorMessageText,
})
The banner itself respects that override when the alert component is created:
if (customErrorMessage) {
  this.message.textContent = customErrorMessage
  this.hasCustomMessage = true
  this.hasConfigMessage = true
} else {
  this.message.textContent = translate.messages().status.error
}
Provide your copy on the config object to replace the default message with brand-specific instructions or support details.

Customize Text (Examples)

import { PayNextCheckout, type PayNextConfig, type CheckoutTranslate, type DeepPartial } from '@paynext/sdk'

const customTranslations: DeepPartial<CheckoutTranslate> = {
  card: {
    required: 'Please fill in every required field.',
    pay: { button: 'Complete purchase' },
    compact: { button: 'Pay with card' }
  },
  status: {
    error: 'Unable to process payment. Please try again.',
    invalidSession: 'Your session expired. Refresh and try again.',
    authenticationFailed: 'Your payment method authentication was unsuccessful. Please try submitting the payment again and complete the authentication process, or use a different payment method.'
  }
}

export async function mountCustomTranslations(
  containerId: string,
  baseConfig: PayNextConfig
) {
  const checkout = new PayNextCheckout()
  await checkout.mount(containerId, {
    ...baseConfig,
    translate: customTranslations,
  })
  return checkout
}
You can pass a partial object. Any missing strings fall back to built-in translations.

Use the CheckoutTranslate Interface

The CheckoutTranslate interface defines all customizable text elements:
card
CheckoutTranslate['card']
Text content for card payment form elements.
status
CheckoutTranslate['status']
Status messages for payment completion and errors.
errors
CheckoutTranslate['errors']
Optional processor error overrides that map gateway decline codes to custom copy.

3DS Authentication Failures

When the 3DS (3D Secure) authentication flow encounters an error, the checkout surfaces the authenticationFailed status message. The payment status payload contains a decline_code that distinguishes between:
  • payment_attempt_authentication_cancelled: Customer explicitly closed the challenge window, abandoned it before timeout, or the challenge window was left open beyond the 10-minute timeout
  • payment_attempt_authentication_failed: Issuer rejected the authentication attempt (wrong code, failed biometric verification, issuer decline, etc.).
Use the status_reason.decline_code in your payment status callbacks to customize messaging based on the failure type. See the 3D Secure guide for complete API response examples and detailed explanations of each scenario.
Workflows can still block a payment after 3DS. In that case the SDK receives status: "blocked" with the default “The payment was blocked by the workflow settings.” copy—branch on this separately from regular declined/failed outcomes.

Supported Languages

For locales with regional variants (e.g., pt-BR), the PayNext SDK falls back to the base language when a region-specific string is not provided.

Apply Best Practices

Right-to-Left (RTL) Support

RTL languages automatically receive proper layout and text direction:
// When mounting (see Getting Started "Mount the Checkout" section):
const checkout = new PayNextCheckout()
await checkout.mount('checkout-container', {
  ...config,
  locale: 'ar', // Arabic
})
Refer to Mount the Checkout for the complete mounting flow.
No extra configuration is required. Provide locale="ar" (or let the browser detection pick it up) and the PayNext SDK will render RTL.

Multi-Language Support

Handle multiple languages dynamically with language switching:
custom component
import { useEffect, useState } from 'react'
import { PayNextCheckout, type DeepPartial, type CheckoutTranslate, type PayNextConfig } from '@paynext/sdk'

const translations: Record<string, DeepPartial<CheckoutTranslate>> = {
  en: {
    card: {
      required: 'Enter all required card details.',
      pay: { button: 'Pay Now' }
    },
    status: {
      error: 'Something went wrong. Try again.',
      invalidSession: 'Session expired. Refresh to continue.'
    }
  },
  es: {
    card: {
      required: 'Completa todos los campos obligatorios.',
      pay: { button: 'Pagar Ahora' }
    },
    status: {
      error: 'No se pudo procesar el pago. Inténtalo de nuevo.',
      invalidSession: 'La sesión expiró. Actualiza para continuar.'
    }
  },
  fr: {
    card: {
      required: 'Veuillez remplir tous les champs obligatoires.',
      pay: { button: 'Payer Maintenant' }
    },
    status: {
      error: 'Impossible de traiter le paiement. Réessayez.',
      invalidSession: 'La session a expiré. Actualisez pour continuer.'
    }
  },
  de: {
    card: {
      required: 'Bitte füllen Sie alle Pflichtfelder aus.',
      pay: { button: 'Jetzt Bezahlen' }
    },
    status: {
      error: 'Zahlung fehlgeschlagen. Bitte versuchen Sie es erneut.',
      invalidSession: 'Sitzung abgelaufen. Bitte aktualisieren.'
    }
  }
}

const clientToken = 'your-client-token' // Replace with token from your backend
const baseConfig: PayNextConfig = {
  clientToken,
  environment: 'sandbox',
  apiVersion: '1.0.0',
  /* other options */
}

const MultiLanguageCheckout = () => {
  const [locale, setLocale] = useState('en')
  const containerId = 'paynext-checkout'

  useEffect(() => {
    const checkout = new PayNextCheckout()
    checkout
      .mount(containerId, {
        ...baseConfig,
        locale,
        translate: translations[locale],
      })
      .catch(console.error)

    return () => {
      checkout.unmount().catch(console.error)
    }
  }, [locale])

  return (
    <div>
      <select
        value={locale}
        onChange={(event) => setLocale(event.target.value)}
        className="mb-4 p-2 border rounded"
      >
        <option value="en">🇺🇸 English</option>
        <option value="es">🇪🇸 Español</option>
        <option value="fr">🇫🇷 Français</option>
        <option value="de">🇩🇪 Deutsch</option>
      </select>

      <div id={containerId} />
    </div>
  )
}

export default MultiLanguageCheckout
The SDK will automatically use built-in translations for any text you don’t customize, ensuring complete language coverage.
Always provide fallbacks for missing translations to prevent broken user experiences in production.

Text Quality Guidelines

Write clear, actionable text that helps users complete their payment: Error messages:
  • Explain what went wrong and how to fix it
  • Use positive, helpful language instead of blame
  • Provide specific guidance when possible
  • Keep messages concise but complete
Button labels:
  • Use action-oriented language (“Complete Purchase” vs “Submit”)
  • Match your site’s existing button patterns
  • Consider cultural preferences for different markets
  • Test different labels to optimize conversion

Fallback Strategy

Implement proper fallbacks for missing translations:
  • Partial overrides: Unspecified strings use built-in translations
  • Locale fallbacks: Regional variants fall back to the base language
  • Default language: System falls back to English if locale is unsupported
  • Error handling: Graceful degradation if translation loading fails

Common Pitfalls

Avoid these text customization mistakes:
  • Inconsistent terminology across your application and checkout form
  • Overly long text that breaks mobile layouts
  • Missing context in error messages that confuses users
  • Cultural insensitivity when translating for different markets
  • Technical jargon that doesn’t match your brand voice

Always test custom text across different screen sizes and with actual users from your target markets.