Are you looking for test card numbers?

Would you like to contact support?

Classic-integration icon

iOS 3D Secure 2 SDK

Support 3D Secure 2 authentication on your iOS app.

This page is for our classic Direct API (/authorise) integration. If you are integrating using our Checkout APIs, refer to the Native 3D Secure 2 for iOS documentation instead.
PSD2 compliance

If you are implementing 3D Secure to comply with PSD2 SCA, learn more from our comprehensive guide.

The Adyen 3DS2 iOS SDK gets the device fingerprint and presents the challenge to your shopper. From your server, you need to send a payment request, the encrypted device fingerprint, and the challenge result to Adyen.

In a full 3D Secure 2 implementation, a payment can go through either a frictionless or a challenge authentication flow before it can be authorised.

If you only want to perform a 3D Secure 2 authentication and then authorise the payment later, refer to the Authentication-only documentation.

How it works

Here's how a 3D Secure 2 challenge authentication flow works:

  1. Submit a payment request with the required 3D Secure 2 objects to start the authentication process. Build your implementation depending on the resultCode returned in the response. If the transaction is exempted from 3D Secure, you might get an Authorised result code.
  2. If you receive an IdentifyShopper resultCode, get the 3D Secure 2 device fingerprint and send the encrypted device fingerprint information to Adyen. After you send the device fingerprint, it's possible that you receive an AuthorisedresultCode in the response. This means that the 3D Secure 2 authentication was frictionless, and the payment was authorised.
  3. If you receive ChallengeShopperresultCode, this means that the issuer requires further shopper interaction and you need to present a challenge to the shopper.

The payment might also be routed to the 3D Secure 1 flow, based on issuer performance. This is indicated by a RedirectShopper resultCode. If you don't want to automatically fall back to 3D Secure 1, contact our Support Team.

To test your integration, see Testing 3D Secure 2.

Before you begin

Adyen 3DS2 iOS

Subscribe to our GitHub repository to get the latest updates and versions.

  1. If you haven't done so already, sign up for an Adyen test account.

  2. Get your API Key. Save a copy, because you'll need it for API calls you make to the Adyen payments platform.

  3. Install one of our Libraries to connect with the Adyen APIs.

  4. Contact our Support Team to make sure that 3D Secure is triggered in your app.

  5. Install the Adyen 3DS2 iOS SDK. You can install the SDK through CocoaPods, Carthage, or by manually installing the SDK as a dynamic or static framework.
  1. Add pod 'Adyen3DS2' to your Podfile.
  2. Run pod install.
  1. Add github "adyen/adyen-3ds2-ios" to your Cartfile.
  2. Run carthage update.
  3. Link the framework with your target as described in Carthage Readme.

Install manually as a dynamic framework

  1. In Xcode, drag the dynamic Adyen3DS2.framework to the Embedded Binaries section of the General settings tab. Select Copy items if needed when asked.

Install manually as a static framework

  1. In Xcode, select File > Add Files to....
  2. Select the static Adyen3DS2.framework and check Copy items if needed, then select Add.
  3. In Xcode, select File > Add Files to....
  4. Select Adyen3DS2.bundle inside Adyen3DS2.framework and check Copy items if needed, then select Add.

Step 1: Submit a payment request

Collect your shopper's card details then make a payment with a POST /authorise request. Include the threeDS2RequestData object to indicate that you are ready to accept 3D Secure 2 payments, specifying:

  • threeDS2RequestData.deviceChannelapp
  • threeDS2RequestData.messageVersion: Optional. Include this in the request if you want to use a specific version of 3D Secure 2, for example, 2.1.0. If you don't specify a version, we determine the version automatically. The API response will return the version used.
    To increase the likelihood of achieving a frictionless flow and higher authorisation rates, we also recommend that you send additional parameters.

Here is an example of how you would make a request for a 150 EUR purchase:

curl \
-H "X-API-key: [Your API Key here]" \
-H "Content-Type: application/json" \
-d '{
      "holderName":"Simon Hopper",
# Set your X-API-KEY with the API key from the Customer Area.
adyen =
adyen.api_key = "YOUR X-API-KEY"

response = adyen.authorise({
  "amount" => {
    "currency" => "EUR",
    "value" => 15000
  "merchantAccount" => "YOUR_MERCHANT_ACCOUNT",
  "reference" => "YOUR_ORDER_NUMBER",
  "threeDS2RequestData" => {
    "deviceChannel" => "app"
  "card" => {
    "cvc" => "737",
    "expiryMonth" => "03",
    "expiryYear" => "2030",
    "holderName" => "Simon Hopper",
    "number" => "4917610000000000"
// Set your X-API-KEY with the API key from the Customer Area.
Config config = new Config();
config.setApiKey("Your X-API-KEY"));
Client client = new Client(config);

Checkout checkout = new Checkout(client);
AuthoriseRequest authoriseRequest = new AuthoriseRequest();
Amount amount = new Amount();
authoriseRequest.setThreeDS2RequestData(new HashMap<String, String>());
authoriseRequest.putThreeDS2RequestData("deviceChannel", "app");
authoriseRequest.setCard(new HashMap<String, String>());
authoriseRequest.putCardItem("cvc", "737");
authoriseRequest.putCardItem("expiryMonth", "03");
authoriseRequest.putCardItem("expiryYear", "2030");
authoriseRequest.putCardItem("holderName", "Simon Hopper");
authoriseRequest.putCardItem("number", "4917610000000000");
authoriseResponse response = checkout.authorise(authoriseRequest);
// Set your X-API-KEY with the API key from the Customer Area.
$client = new \Adyen\Client();
$client->setXApiKey("YOUR X-API-KEY");
$service = new \Adyen\Service\Authorise($client);

$params = array(
  "amount" => array(
    "currency" => "EUR",
    "value" => 15000
  "reference" => "YOUR_ORDER_NUMBER",
  "threeDS2RequestData" => array(
    "deviceChannel" => "app"
  "card" => array(
    "cvc" => "737",
    "expiryMonth" => "03",
    "expiryYear" => "2030",
    "holderName" => "Simon Hopper",
    "number" => "4917610000000000"
  "merchantAccount" => "YOUR_MERCHANT_ACCOUNT"
$result = $service->authorise($params);
#Set your X-API-KEY with the API key from the Customer Area.
ady = Adyen.Adyen()
client = ady.client
client.xapikey = "YOUR X-API-KEY"

request = {}
request['amount'] = {
  "value": 15000,
  "currency": "EUR"
request['threeDS2RequestData] = {
  "deviceChannel" = "app"
request['reference'] = "YOUR_ORDER_NUMBER"
request['card'] = {
  "cvc": "737",
  "expiryMonth": "03",
  "expiryYear": "2030",
  "holderName": "Simon Hopper",
  "number": "4917610000000000"
request['merchantAccount'] = "YOUR_MERCHANT_ACCOUNT"
result = self.ady.checkout.authorise(request)
// Set your X-API-KEY with the API key from the Customer Area.
var client = new Client ("YOUR-X-API-KEY", Environment.Test);
var checkout = new Checkout(client);

var amount = new Model.Checkout.Amount("EUR", 15000);
var details = new Model.Checkout.DefaultCardDetails{
  Cvc = "737",
  ExpiryMonth = "03",
  ExpiryYear = "2030",
  HolderName= "Simon Hopper",
  Number = "4917610000000000"
var threeds2requestdata = new Model.Checkout.threeDS2RequestData{
  deviceChannel = "app"
var authoriseRequest = new Model.Checkout.AuthoriseRequest
  Reference = "YOUR_ORDER_NUMBER",
  Amount = amount,
  MerchantAccount = "YOUR_MERCHANT_ACCOUNT",
  Card = details,
  ThreeDS2RequestData = threeds2requestdata

var paymentResponse = checkout.Authorise(authoriseRequest); 

You receive a response containing a resultCode of:

  •  IdentifyShopper. This means that you need to perform the Identify the shopper flow. You also get the following parameters from the response, which you'll need to create a transaction in the SDK:
    • additionalData.threeds2.threeDS2DirectoryServerInformation.directoryServerId
    • additionalData.threeds2.threeDS2DirectoryServerInformation.publicKey
    • additionalData.threeds2.threeDS2ResponseData.messageVersion
      Alternatively, you can choose to prefetch the device fingerprinting keys.
To optimize authorization rates, Adyen's Authentication Engine routes each payment to either the 3D Secure 2 or the 3D Secure 1 flow, based on issuer performance. See 3D Secure fallback for more information.

For a complete list of resultCode values and the actions you need to take, see Result codes.

Here's an example of a response showing an IdentifyShopper result code:

Response with IdentifyShopper
    "additionalData": {
        "threeds2.threeDSServerTransID": "055fadfb-9fe4-4e70-99f0-9b8935bf1eb2",
        "threeds2.threeDS2DirectoryServerInformation.algorithm": "RSA",
        "threeds2.threeDS2ResponseData.messageVersion" : "2.1.0",
        "threeds2.threeDS2Token": "BQABAQC2LEWucYNwQRzhH9c1/hup5ZZa9HUehFbwdC86PAfhpGJ/+...",
        "threeds2.threeDS2DirectoryServerInformation.directoryServerId": "A000000003",
        "threeds2.threeDS2DirectoryServerInformation.publicKey": "eyJrdHkiOiJSU0EiLCJlIjoiQVFBQiIsIm4iOiI4VFBxZkFQ==..."
    "pspReference": "8835495304426403",
    "resultCode": "IdentifyShopper"

Step 2: Get the 3D Secure 2 device fingerprint

If your server receives IdentifyShopper resultCode, start the 3D Secure 2 device fingerprinting process.

  1. In your app, create an instance of  ADYServiceParameters passing the directoryServerId and the publicKey parameters from the /authorise response or from your prefetched cached keys.

    ADYServiceParameters *parameters = [ADYServiceParameters new];
    [parameters setDirectoryServerIdentifier:...]; // Retrieved from /authorise response.
    [parameters setDirectoryServerPublicKey:...]; // Retrieved from /authorise response.
  2. Use the ADYService class to create a transaction.

    When using Adyen 3DS2 iOS SDK version 2.2.0 and later, you must set transactionWithMessageVersion to the value of additionalData.threeds2.threeDS2ResponseData.messageVersion from the /authorise response.
    For more information about the latest version, refer to Migrating to 3DS2 SDK version 2.2.0.

    Keep a reference to your ADYTransaction instance until the transaction is complete.

    Here is how you would create a transaction, using the messageVersion.

    Create a transaction in the SDK
    [ADYService serviceWithParameters:parameters appearanceConfiguration:nil completionHandler:^(ADYService *service) {
        NSError *error = nil;
        // We've set threedsMessageVersion to the value of additionalData.threeds2.threeDS2ResponseData.messageVersion
        ADYTransaction *transaction = [service transactionWithMessageVersion:threedsMessageVersion error:&error];
        if (transaction) {
            ADYAuthenticationRequestParameters *authenticationRequestParameters = [transaction authenticationRequestParameters];
            // Submit the authenticationRequestParameters to /authorise3ds2.
        } else {
            // An error occurred.

  1. Get the transaction's authenticationRequestParameters  class and pass the contents to your server.
    You'll get the following information:

    • deviceInformation
    • SDKTransactionIdentifier
    • SDKApplicationIdentifier
    • SDKReferenceNumber
    • SDKEphemeralPublicKey
    • messageVersion

  2. From your server, make a POST /authorise3ds2 request to send the encrypted device fingerprint information to Adyen, specifying the threeDS2RequestData object containing:

    • sdkEncData: Device information
    • sdkTransID: SDK Transaction Identifier
    • sdkAppID: SDK Application Identifier
    • sdkReferenceNumber: SDK Reference Number
    • sdkEphemPubKey: Object that contains the SDK Ephemeral Public Key
    • messageVersion: 3D Secure 2 protocol version
    To increase the likelihood of achieving a frictionless flow and higher authorisation rates, we also recommend that you send additional parameters.
    Send device fingerprint to Adyen
    curl \
       -H "X-API-key: [Your API Key here]" \
       -H "Content-Type: application/json" \
       -d '{
             "merchantAccount": "YOUR_MERCHANT_ACCOUNT",
             "threeDS2RequestData": {
               "deviceChannel": "app",
               "sdkAppID": "9063b12c-fcde-43c7-b28e-8d0af5520e8a",
               "sdkEncData": "<device-fingerprint>",
               "sdkEphemPubKey": {
                 "crv": "P-256",
                 "kty": "EC",
                 "x": "LYImJkRzS92vogM6AUPCBhJ20VagSe8IL0Q9SdisUSo",
                 "y": "Rav4sKHnLUIUHVdyR4dyV7G2_EeAnuCn_6621ZhqZYU"
               "sdkReferenceNumber": "3DS_LOA_SDK_ADBV_739485_94783",
               "sdkTransID": "b60c9879-ac77-4918-a317-7b01c4317053/8Q==..",
               "messageVersion": "2.2.0"

    You receive a response containing a resultCode. Some of the possible values for resultCode are:

    • Authorised – Indicates that the 3D Secure 2 authentication was frictionless, and the payment authorisation was successfully completed. This means that you can already proceed with the delivery of goods and services. 
    • ChallengeShopper – The issuer has requested further shopper interaction. This means that you'll have to perform the challenge flow. You also get the following parameters from the response, which you'll need to start the challenge flow in the SDK:
      • threeds2.threeDS2ResponseData.threeDSServerTransID
      • threeds2.threeDS2ResponseData.acsTransID
      • threeds2.threeDS2ResponseData.acsReferenceNumber
      • threeds2.threeDS2ResponseData.acsSignedContent
      • threeds2.threeDS2Token

    For a complete list of resultCode values and the actions that you need to take, see Result codes.

    Here's an example of a challenge flow response:

    Response showing ChallengeShopper
        "additionalData": {
            "threeds2.threeDS2ResponseData.acsSignedContent": "eyJhbGciOiJQUzI1NiIsIngPVEFOQmdrcWhraUc5dtw4I-RBJ8_OUt8yIZEsoc...",
            "threeds2.threeDS2ResponseData.transStatus": "C",
            "threeds2.threeDS2ResponseData.acsChallengeMandated": "Y",
            "threeds2.threeDS2ResponseData.acsURL": "",
            "threeds2.threeDS2ResponseData.threeDSServerTransID": "930h2k09-1986-4hl2-800a-c8d7783918bf",
            "threeds2.threeDS2ResponseData.authenticationType": "01",
            "threeds2.threeDS2ResponseData.messageVersion": "2.1.0",
            "threeds2.threeDS2Token": "BQABAQC2LEWucYNwQRzhH9c1/hup5ZZa9HUehFbwdC86PAfhpGJ/+...",
            "threeds2.threeDS2ResponseData.acsTransID": "45e79886-e60c-4c6d-a962-7aa43d59b150",
            "threeds2.threeDS2ResponseData.acsReferenceNumber": "ADYEN-ACS-SIMULATOR"
        "pspReference": "8825495326513370",
        "resultCode": "ChallengeShopper"

Step 3: Present a challenge

If your server receives ChallengeShopper resultCode, this means that the issuer would like to perform additional checks in order to verify that the shopper is indeed the cardholder.

  1. Create an instance of ADYChallengeParameters and pass the following:

    • The values from the /authorise3ds2 response.
    • threeDSRequestorAppURL: Supported in 3D Secure version 2.2.0 and later. Your app's URL in {YOUR_CUSTOM_SCHEME}://{YOUR_CUSTOM_DOMAIN} format. In an out-of-band (OOB) authentication, the authenticating app uses this URL to call your app after an OOB authentication occurs. If you specified a previous version of 3D Secure in step 2, this parameter is ignored. For more information about the latest version, refer to Migrating to 3DS2 SDK version 2.2.0.

      Make sure to add the URL should to the app build settings. Go to Info.plist > URL Types. For more information, refer to the Apple Developer documentation.

    Create instance of ADYChallengeParameters
    NSDictionary *additionalData = ...; // Retrieved from Adyen.
    ADYChallengeParameters *parameters = [ADYChallengeParameters challengeParametersWithServerTransactionIdentifier:additionalData[@"threeds2.threeDS2ResponseData.threeDSServerTransID"]
                                                                                             threeDSRequestorAppURL: [NSURL URLWithString:@"{YOUR_CUSTOM_SCHEME}://{YOUR_CUSTOM_DOMAIN}"]
  2. Present the challenge.

    Perform the challenge flow
    [transaction performChallengeWithParameters:parameters completionHandler:^(ADYChallengeResult *result, NSError *error) {
        if (result) {
            NSString *transactionStatus = [result transactionStatus];
            // Submit the transactionStatus to /authorise3ds2.
        } else {
            // An error occurred.
  3. When the challenge flow has been completed, get the transactionStatus from the result and pass this to your server.

  4. From your server, make a POST /authorise3ds2 request specifying the transStatus generated by the SDK and the threeds2.threeDS2Token.

    Send challenge result
     curl \
      -H "X-API-key: [Your API Key here]" \
      -H "Content-Type: application/json" \
      -d '{
          "merchantAccount": "YOUR_MERCHANT_ACCOUNT",
          "threeDS2Result": {
            "transStatus": "Y"
        "threeDS2Token": "BQABAQC2LEWucYNwQRzhH9c1/hup5ZZa9HUehFbwdC86PAfhpGJ/+..."

    You receive Authorised as the resultCode if the payment was successful. 

            "additionalData": {
                "liabilityShift": "true",
                "authCode": "44402",
                "avsResult": "4 AVS not supported for this card type",
                "threeDOffered": "true",
                "refusalReasonRaw": "AUTHORISED",
                "authorisationMid": "1000",
                "acquirerAccountCode": "TestPmmAcquirerAccount",
                "cvcResult": "1 Matches",
                "avsResultRaw": "4",
                "threeDAuthenticated": "true",
                "cvcResultRaw": "M",
                "acquirerCode": "TestPmmAcquirer",
                "acquirerReference": "7CASOGMCCB4"
            "pspReference": "8825495331860022",
            "resultCode": "Authorised",
            "authCode": "44402"

Optional: Prefetch device fingerprinting keys

To enable this functionality, contact our Support Team.

You can opt to retrieve and cache 3D Secure device fingerprint keys for specific BIN ranges or card brands. When you cache the keys, you reduce the number of calls for each transaction because you can already perform the 3D Secure 2 device fingerprinting before making the payment.
To use cached keys for your authentication flow, you need to:

  1. Retrieve device fingerprinting keys once for each card brand or BIN and save the keys to your cache.
  2. Use the cached keys to perform 3D Secure 2 device fingerprinting and submit the result in a payment request.
  3. Present a challenge if required by the issuer.
Make sure to update your cache regularly to get the latest keys and to avoid getting your transactions refused.

Retrieve device fingerprinting keys

  1. Send a POST /get3dsAvailability request, providing your merchantAccount along with a cardNumber from a BIN range you want to cache, or a list of card brand names.
    Request with card number
    curl \
      -H "X-API-key: [Your API Key here]" \
      -H "Content-Type: application/json" \
      -d '{
    Response for card number
      "binDetails": {
        "issuerCountry": "PL"
      "dsPublicKeys": [
          "brand": "visa",
          "directoryServerId": "F013371337",
          "publicKey": "eyJrdHkiOiJSU0==.."
      "threeDS1Supported": true,
      "threeDS2CardRangeDetails": [
          "brandCode": "visa",
          "endRange": "491761000000",
          "startRange": "491761000000",
          "threeDS2Version": "2.1.0",
          "threeDSMethodURL": ""
      "threeDS2supported": true
    Request with card brand
    curl \
      -H "X-API-key: [Your API Key here]" \
      -H "Content-Type: application/json" \
      -d '{
          "brands" : ["visa","mc"]
    Response for card brands
  2. Save the values of the following parameters for the specific BIN range or brand:
    • dsPublicKeys.directoryServerId
    • dsPublicKeys.publicKey

Next, use the cached keys to create a transaction in the 3D Secure 2 SDK to get the device fingerprint.

UI customizations

The SDK inherits your app's theme to ensure the UI of the challenge flow fits your app's look and feel. If you require further UI customizations, the SDK provides some customization options through the ADYAppearanceConfiguration class. 

To customize the UI:

  • Create an instance of ADYAppearanceConfiguration, configure the UI elements that you want to customize, and pass the configuration instance when you initialize ADYService.
    For example, here is how you would change the color of the  Continue button to red and to change its corner radius:

    Customize Continue button
    ADYAppearanceConfiguration *appearanceConfiguration = [ADYAppearanceConfiguration new];
    [[appearanceConfiguration buttonAppearanceForButtonType:ADYAppearanceButtonTypeContinue] setBackgroundColor:[UIColor redColor]];
    [[appearanceConfiguration buttonAppearanceForButtonType:ADYAppearanceButtonTypeContinue] setTextColor:[UIColor whiteColor]];
    [[appearanceConfiguration buttonAppearanceForButtonType:ADYAppearanceButtonTypeContinue] setCornerRadius:3.0f];
    [ADYService transactionWithParameters:parameters appearanceConfiguration:appearanceConfiguration completionHandler:...];

Customizable UI elements

Refer to ADYAppearanceConfiguration class reference documentation for a complete list of customizable properties.

You can also customize the following UI elements used in the challenge flow:

Customizable elements Customizable properties
  • Font
  • Font color
  • Line height
  • Font
  • Font color
  • Line height
Info items
  • Font
  • Font color
  • Line height
  • Border color
Input fields
  • Font
  • Font color
  • Separator color
  • Keyboard appearance
Input fields header
  • Font
  • Font color
Navigation bar
  • Background color
  • Status bar style
  • Background color
Submit/Next/Continue button
  • Font
  • Text transform
  • Font color
  • Background color
  • Corner radius
Cancel button
  • Font
  • Text transform
  • Font color
Resend button
  • Font
  • Text transform
  • Text color

Testing 3D Secure 2

Before going live, use the following card numbers and credentials to test your integration.

We recommend testing each Card Type.

To test how your integration handles different 3D Secure 2 authentication scenarios, use our test card numbers.
All our test cards use the following expiry dates and security codes:

Expiry Date CVC/CVV CID
03/2030 737 7373

When prompted for 3D Secure 2 text challenges, use the following credentials:

  • For mobile, use password: 1234
  • For web, use password: password
Card Type Card Number
Cartes Bancaires 4035 5014 2814 6300
Maestro 1 5000 5500 0000 0029
Mastercard 5454 5454 5454 5454
Visa 4917 6100 0000 0000

1 This card doesn't require a CVC.
When you make a payment request with these cards, you'll receive the following result codes depending on your integration:

  • RedirectShopper: You'll receive this result code if you are using the Redirect authentication.
  • IdentifyShopper: You'll receive this result code if you are using the Native authentication.
  • ChallengeShopper: You might get this result code after you submit the 3D Secure 2 device fingerprinting result in a Native authentication, indicating a challenge flow.

To test the web-based flow where the device fingerprinting step is skipped (because the issuer's ACS has not configured a threeDSMethodURL), and you get a ChallengeShopper resultCode immediately after submitting the payment request, use the following card:

Card Type Card Number
Visa 4212 3456 7891 0006

To test the frictionless flow, specify in your payment request:

  • amount.value: 12002

App-based integration

To test different authentication scenarios for app-based integration, use the following test cards:

Card number Authentication scenario
5201 2855 6567 2311 Basic text authentication
5201 2874 9905 2008 Basic single select
5201 2815 9233 1633 Basic multi select
5201 2888 2269 6974 Basic out-of-band (OOB) authentication
5201 2895 0084 3268 HTML OOB authentication
5201 2861 5377 1465 App single select then text authentication

Other scenarios

Card number Scenario
4199 3500 0000 0002 The card is not enrolled for 3D Secure transactions,
5201 2829 9900 5515 There was a technical error.

See also