Skip to main content
This guide explains how to add 3DS Authentication to your native mobile integration. By using the native 3DS flow, you handle the “Identify” and “Challenge” phases directly within your app, providing a seamless experience that avoids browser redirects and improves conversion rates.

Prerequisites

This guide assumes you have already implemented the basic vaulting flow. If you haven’t done that yet, please start with the native vaulting guide. You should already have:
  1. The Gr4vy SDK installed in your project.
  2. An initialized Gr4vy instance.
  3. A backend endpoint that generates a Checkout Session.

Implementation

To add 3DS support, you use the standard tokenize method (the vaulting method) and set the authenticate parameter to true. This tells the SDK to automatically manage the 3DS lifecycle: it performs device fingerprinting (Identify) and presents a native challenge UI (Challenge) if required by the card issuer.

Update SDK logic

/// Create card data
let cardData = Gr4vyCardData(
    paymentMethod: .card(CardPaymentMethod(
        number: "4111111111111111",
        expirationDate: "12/25",
        securityCode: "123"
    ))
)

// Tokenize with 3DS authentication (async/await)
do {
    let result = try await gr4vy.tokenize(
        checkoutSessionId: "session_123",
        cardData: cardData,
        sdkMaxTimeoutMinutes: 5,
        authenticate: true
    )

    if result.tokenized {
        print("Payment method tokenized successfully")

        // Check authentication details
        if let auth = result.authentication {
            print("3DS attempted: \(auth.attempted)")
            print("Authentication type: \(auth.type ?? "N/A")")
            print("Transaction status: \(auth.transactionStatus ?? "N/A")")

            if auth.hasCancelled {
                print("User cancelled authentication")
            }
            if auth.hasTimedOut {
                print("Authentication timed out")
            }
        }
    }
} catch {
    print("Error tokenizing payment method: \(error)")
}

// With explicit view controller (async/await)
do {
    let result = try await gr4vy.tokenize(
        checkoutSessionId: "session_123",
        cardData: cardData,
        viewController: self, // Explicitly provide the presenting view controller
        sdkMaxTimeoutMinutes: 5,
        authenticate: true
    )
    print("Tokenization complete: \(result.tokenized)")
} catch {
    print("Error: \(error)")
}

// Completion handler variant
gr4vy.tokenize(
    checkoutSessionId: "session_123",
    cardData: cardData,
    sdkMaxTimeoutMinutes: 5,
    authenticate: true
) { result in
    switch result {
    case .success(let tokenizeResult):
        if tokenizeResult.tokenized {
            print("Payment method tokenized successfully")
            if let auth = tokenizeResult.authentication {
                print("Transaction status: \(auth.transactionStatus ?? "N/A")")
            }
        }
    case .failure(let error):
        print("Error tokenizing payment method: \(error)")
    }
}

Backend Authorization

Once the SDK returns a successful result, the 3DS verification data is securely stored within the Checkout Session. When your app notifies your backend, create the transaction by referencing the session ID in the payment_method object. The system automatically detects the 3DS data and ensures it is passed to your payment processor to satisfy SCA requirements.
POST /transactions
{

    "amount": 1800,
    "currency": "EUR",
    "country": "DE",
    "intent": "capture",
    "payment_method": {
        "method": "checkout-session",
        "id": "[checkout-session-id]"
    },
    ...
}

Handling failed authentication

If a customer fails or cancels their 3DS authentication, you have the option to still process the payment using the vaulted card data. This is useful for scenarios where:
  • The customer wants to retry with a different payment method later
  • You want to reduce friction by allowing offline card storage
  • Your business model supports processing payments without 3DS authentication (subject to regional regulations and processor rules)

Theming

When a 3DS challenge is required, the Gr4vy SDK presents a native interface provided by the card issuer. You can customize the appearance of these screens to ensure they match your app’s branding and provide a consistent user experience. Customization is handled via a uiCustomization object passed during the SDK initialization. This allows you to define colors, fonts, and button styles that are applied to the 3DS challenge modals.
  • Text: Colors and font sizes for headings, body text, and input fields.
  • Buttons: Background colors, text colors, and corner radii for “Continue” and “Cancel” actions.
  • Navigation Bar: Background and title colors for the header.
  • Input Fields: Border colors and focus states.
// Create toolbar customization
let toolbar = Gr4vyThreeDSToolbarCustomization(
    textColorHex: "#FFFFFF",
    backgroundColorHex: "#007AFF",
    headerText: "Secure Verification",
    buttonText: "Cancel"
)

// Create button customizations
let submitButton = Gr4vyThreeDSButtonCustomization(
    textFontSize: 16,
    textColorHex: "#FFFFFF",
    backgroundColorHex: "#007AFF",
    cornerRadius: 8
)

let cancelButton = Gr4vyThreeDSButtonCustomization(
    textFontSize: 16,
    textColorHex: "#007AFF",
    backgroundColorHex: "#F0F0F0",
    cornerRadius: 8
)

// Create label customization
let label = Gr4vyThreeDSLabelCustomization(
    textFontSize: 14,
    textColorHex: "#333333",
    headingTextFontSize: 18,
    headingTextColorHex: "#000000"
)

// Create text box customization
let textBox = Gr4vyThreeDSTextBoxCustomization(
    textFontSize: 16,
    textColorHex: "#000000",
    borderWidth: 1,
    borderColorHex: "#CCCCCC",
    cornerRadius: 4
)

// Combine into UI customization
let uiCustomization = Gr4vyThreeDSUiCustomization(
    label: label,
    toolbar: toolbar,
    textBox: textBox,
    buttons: [
        .submit: submitButton,
        .cancel: cancelButton
    ]
)

// Use with tokenization
let result = try await gr4vy.tokenize(
    checkoutSessionId: "session_123",
    cardData: cardData,
    authenticate: true,
    uiCustomization: Gr4vyThreeDSUiCustomizationMap(default: uiCustomization)
)
All color values should be provided as hexadecimal strings (for example, #007AFF).

Testing and validation

To test your implementation, use the 3DS Test Cards defined here in your sandbox environment.
  • Frictionless Flow: Use a card that is configured to skip the challenge phase. The SDK can return success immediately after the authentication call.
  • Challenge Flow: Use a card that requires an SMS or Biometric challenge. The SDK can automatically present the native modal for you to complete.
Use these card numbers and scenarios to test your native 3DS implementation in the Gr4vy Sandbox environment. These cards allow you to simulate various outcomes, including frictionless authentication, mandatory challenges, and specific failure states.

Testing scenarios

Visa

Card NumberExpected 3DS Outcome
4000 0000 0000 0010Frictionless Success: Authenticated without user interaction.
4000 0000 0000 0028Challenge Required: Triggers a native 3DS challenge UI.
4000 0000 0000 0044Authentication Failed: Simulates a “Not Authenticated” result.
4000 0000 0000 1042Rejected: The authentication request was explicitly rejected.

Mastercard

Card NumberExpected 3DS Outcome
5100 0000 0000 0010Challenge Required: Triggers a native 3DS challenge UI.
5200 0000 0000 2052Frictionless Success: Authenticated without user interaction.
5200 0000 0000 1039Unavailable: Simulates a scenario where 3DS is not available for the card.
5100 0000 0000 0065Suspected Fraud: Frictionless failure due to suspected fraud.

American Express

Card NumberExpected 3DS Outcome
3415 0209 8634 895Frictionless Success: Authenticated without user interaction.
3486 3826 7931 507Challenge Required: Triggers a native 3DS challenge UI.
3456 9539 9207 589Authentication Failed: Frictionless failure.

Challenge screen inputs

When a challenge is triggered, use the following values to simulate different transaction results within the native UI.

OTP (One-Time Password)

When the challenge screen asks for a code, enter one of the following:
OTP CodeResulting Transaction StatusDescription
1234YSuccess / Authenticated.
1111NNot Authenticated.
2222RRejected.
3333UUnavailable / Unable to complete.

Selection options

Some challenges may present a list of options (for example, “Select your favorite city”). Single-select
  • Paris or Nice: Returns Success (Y).
Multi-select
  • Paris & Lyon: Returns Success (Y).
  • Toulouse & Lyon: Returns Success (Y).

Best practices for testing

  1. Verify UI transitions: Ensure your app correctly transitions from the card entry screen to the native 3DS challenge modal and back.
  2. Test error states: Use the “Rejected” or “Failed” cards to ensure your app displays helpful messaging to the user when a card cannot be verified.
  3. Check Backend Session: After a successful test, verify that the checkout_session_id on your server reflects the successful authentication before attempting the final transaction.