Spreedly 3DS2 Global Mobile

Spreedly mobile SDK support is powered by Forter mobile SDK's.

To prepare your mobile payment application for the Payment Services Directive and 3DS2 Requirements with Spreedly, you will need to implement and test each of the flows outlined below. If you have questions not addressed in the integration guide, please contact Spreedly support for assistance.

For general PSD2 Compliance information, please visit our PSD2 Compliance Guide.

If you have already implemented 3DS2 with Spreedly using the Spreedly 3DS2 Global Guide you will likely recognize many of these steps. However, it is important to note that this is a separate integration requiring the code blocks below to be integrated into your native mobile application and rendered in a webview and as such, all events from the lifecycle object should post messages to your native application.

What you’ll need:

  • A strong working knowledge of mobile development
  • A native mobile application for testing

Prerequisites

Before starting, make sure you have completed your Merchant Profile and SCA Provider set up using our Spreedly 3DS2 Global guide.

  1. When creating the SCA Provider, use the type forter.
  2. You will need a Spreedly Test gateway token to interact with the simulated 3DS2 flows. If you already have a Spreedly Test gateway setup you are likely good to go. If you don’t have a Spreedly Test gateway setup or want to spin up a new one for 3DS2 testing, follow the testing guide.
    Note: Alternatively, you may test against a specific gateway by creating one in sandbox mode.
  1. Verify your test gateway can successfully make a purchase without 3DS. This will help limit future troubleshooting to 3DS specific changes.

Integrate Applicable Forter Mobile SDK

IOS

Installation

The Forter3DS SDK offers flexible integration options, supporting both Swift Package Manager and CocoaPods. Choose the integration method that aligns with your project preferences and workflows add follow the steps below:

🚧 Ensure to select only one integration method and refrain from using both, as combining them may result in build conflicts

Swift Package Manager

  1. In Xcode, navigate to File > Swift Packages > Add Package Dependency....
  2. Enter the Forter3DS SDK repository URL:

Swift Package Manager: https://bitbucket.org/forter-mobile/forter-ios.git/src

  1. Set the Dependency Rule to be Up to Next Major Version then press Add Package
  2. On the "Chose Package" screen, verify that Forter3DS is selected and press Add Package

CocoaPods

Ensure that your Podfile includes the use_frameworks! flag

  1. In your Podfile add Forter3DS dependency to your target

Podfile

platform :ios, '11.0'
use_frameworks!

target 'YourProjectName' do
pod 'Forter3DS', :git => 'https://bitbucket.org/forter-mobile/forter-ios.git'
end

  1. Run pod install

Dependencies

Forter3DS SDK uses external libraries that are already embedded in the SDK

  • ASN1Decoder - Certificate parsing in ASN1 structure. MIT License
  • SwCrypt - Crypto library for JWS validation (used only in iOS 10 devices) MIT license
  • GMEllipticCurveCrypto - Security framework used for Elliptic-Curve keys Crypto library. License

Initialization

The Forter3DS SDK should be initialized from the Application Delegate during application launch. This ensures that it is loaded correctly as soon as the app becomes active.

Step 1: Import the SDK

import Forter3DS

Step 2: Setup

To initialize the Forter3DS SDK, you need to add the following line to your application delegate's didFinishLaunchingWithOptions method. This is a critical step to ensure the proper functioning of the Forter 3DS SDK within your iOS application. Replace with the site-id provided by Spreedly.

func application(\_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {  
    Forter3DS.setup(siteID: "<your-site-id>")  
}

Implementation

Do Challenge if Needed

This method should be called after the managedOrderToken is obtained from Spreedly. It will display an authorization challenge if required, otherwise it will continue with the transaction.

Method

doChallengeIfNeeded(token:vc:delegate:): Perform a managed order challenge if needed.

Parameters

token (String) : The managed order token obtained from the merchant’s backend via Spreedly.

vc (UIViewController) : The view controller responsible for presenting the challenge UI.

delegate (FTR3DSManagedOrderDelegate) : The delegate that will receive the challenge result.

Example

Forter3DS.doChallengeIfNeeded(  
    token: "your_managed_order_token",  
    vc: YourPresentingViewController,  
    delegate: self)  
Callback

The FTR3DSManagedOrderDelegate protocol provides a structured way to handle the completion of managed order flows. By adopting this protocol, developers can respond to the completion of managed order challenges and handle any potential errors that may arise during the process.

Method

challengeCompleted(error:): This method is called when the managed order flow is completed, either successfully or with an error. It provides information about the status of the managed order challenge.

Parameter

error: An optional parameter of type Error that indicates whether an error occurred during the managed order flow. If no error occurred, this parameter will be nil.

Example

Swift

class PaymentViewController: UIViewController, FTR3DSManagedOrderDelegate {  
    func performManagedOrderFlow() {  
        // Initiating the managed order flow  
    }  
    // FTR3DSManagedOrderDelegate method  
    func challengeCompleted(error: Error?) {  
        if let error = error {  
            // Handle error scenario  
            print("Managed order flow completed with error: \(error.localizedDescription)")  
            // also send request to your backend to hit the gratis-complete endpoint  
        } else {  
            // request to your backend to hit the gratis-complete endpoint on spredly  
        }  
    }  
}

Android

Installation

Forter3DS SDK supports installation via Maven. Minimum version supported is Android 5.1 (API level 22). Repository credentials can be obtained from Spreedly.

Step 1: Add to Maven

If the Gradle version is 7.0 and above:
Inside your app's settings.gradle, append the following repository, provided below, to your dependencyResolutionManagement block.

dependencyResolutionManagement {  
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)  
    repositories {  
        google()  
        mavenCentral()  
        ...  
        maven {  
            url "<https://mobile-sdks.forter.com/android">  
            credentials {  
                username "<username-provided-by-forter>"  
                password "<password-provided-by-forter>"  
            }  
        }  
    }  
}

If the Gradle version is earlier than 7.0:
Inside your app's build.gradle, append the following repository, provided below, to your repositories block.

repositories {  
    ...  
    maven { url '<https://maven.google.com'> }  
    maven {  
        url "<https://mobile-sdks.forter.com/android">  
        credentials {  
            username "<username-provided-by-forter>"  
            password "<password-provided-by-forter>"  
        }  
    }  
}

NOTE: Specify and commit the credentials to your Git. Note that these credentials are not sensitive, but we keep them private to prevent bots and search engines from accessing the repository.

Step 2: Add the Forter3DS SDK dependency

Add the Forter3DS SDK as a dependency to your app's build.gradle file under the dependencies block. Use one of the dependencies provided below depending on your setup

implementation 'com.forter.mobile:forter3ds:2.0.4@aar'
// or
implementation("com.forter.mobile:forter3ds:2.0.4")
Step 3: Manifest Permissions

Additionally, the Forter3DS SDK requires the common permission: internet. This permission is typically required by many applications and components. If you have not already included them in your permissions list, please add them to your manifest file.

Here are the pieces of the SDK that you will see used in the code examples that you will need to import where you want to use them.

import com.forter.forter3ds.Forter3DS  
import com.forter.forter3ds.Forter3DSConfig  
import com.forter.forter3ds.IForter3DSChallengeCallback  
import com.forter.forter3ds.IForter3DSInitCallback  
import com.forter.forter3ds.models.FTR3DSChallengeParams

Initialization

The Forter3DS SDK must be initialized from the main Application Context. If your app is not currently using an Application class, please add one and register it in the Manifest.

Application Class

Instantiation - You can obtain an instance of the Forter3DS object by calling Forter3DS.getInstance. This call generates a singleton object for the Forter3DS object.

Initialization - To initialize the Forter3DS SDK, add the code provided Kotlin to the onCreate method of your application class. This is where we recommend that you run the initialization but of course you could initialize it elsewhere in your app as long as it is initialized successfully before you try to use it.

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {  
     super.onCreate(savedInstanceState)  
     setContentView(R.layout.checkout_activity)  
     val config = Forter3DSConfig.Builder()  
            .setMerchantId(BuildConfig.SITE_ID) //(sandbox) SITE_ID = "1229af3c2d8a"  
            .setSiteId(BuildConfig.SITE_ID) //(sandbox)  SITE_ID = "1229af3c2d8a"  
     // initialize Forter3DS  
     Forter3DS.getInstance().init(this.applicationContext, config, object: IForter3DSInitCallback {  
        override fun onInitializationSucceeded() {  
        // whatever you may want to do when the sdk is initialized  
          Log.d(TAG, "3DS SDK initialized successfully")  
        }  
        override fun onInitializationFailed() {  
        // whatever you may want to do if the initialization fails  
          Log.d(TAG, "3DS SDK initialized failed")  
        }  
     })  
}

Implementation

Do Challenge if Needed

Using the managedOrderToken that was received from the backend, the doChallengeIfNeeded(activity:launcher:token:callback)should be called. This method will display the challenge if required, otherwise, it will continue with the transaction.

Method

doChallengeIfNeeded(activity:launcher:token:callback): Perform a managed order challenge if needed.

Parameters

activity: The activity responsible for presenting the challenge UI.

launcher: The launcher that will receive the WebView challenge result.

token: The managed order token obtained from the merchant's backend.

callback: The callback for the native challenge result.

Example

Kotlin

Forter3DS.getInstance().doChallengeIfNeeded(  
    activity: Activity?,  
    launcher: ActivityResultLauncher<FTR3DSChallengeParams>,  
    token: 'your_managed_order_token',  
    callback: IForter3DSChallengeCallback {  
        override fun onChallengeFinished(challengeResponse: JSONObject?) {  
          // call your backend to call spreedly's gratis_complete endpoint.  
        }  
        override fun onChallengeSkipped() {  
          // call your backend to call spreedly's gratis_complete endpoint.  
        }  
        override fun onChallengeFail() {  
          // call your backend to call spreedly's gratis_complete endpoint.  
        }  
    })  
IForter3DSChallengeCallback interface


The IForter3DSChallengeCallback interface provides callback methods for handling challenge results during the 3DS authentication process.

Callback Methods

onChallengeFinished: Invoked when a challenge is successfully completed. Receives a JSONObject with an encoded challenge result.

onChallengeFail(): Invoked when a challenge fails.

onChallengeSkipped(): Invoked when a challenge is skipped.

Generate or use an existing payment method token

This can be done using our API calls, or alternatively our iFrame or Express options through a webview. More details available at Spreedly API Payment Method.

Transaction Flow


Start the Transaction

Gathering Device Information

In addition to the standard information required for a non-3DS2 transaction, additional device information is required to help assess the risk of a given transaction. Forter requires customerIP and userAgent for 3DS2 transactions. These fields should be sent as a JSON object and then base64 encoded and sent to Spreedly in the device_info field.

{  
  "customerIP": "10.0.0.127", // Customer IP address in IPv4 or IPv6 format. If missing should be populated with 127.0.0.1  
  "userAgent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36"  
}

Initiating the Spreedly Transaction

When a user tries to make a payment, the first step should be calling your backend in some way with the required params to initiate a request to Spreedly.

An example request to your backend API may look like:

fetch('<https://your-backend.test/do-purchase.json'>, {  
    method: 'POST',  
    body: JSON.stringify({  
      your_param1: 'your_value1',  
      your_param2: 'your_value2',  
      // ... more params  
      your_paramN: 'your_value1'  
    })  
  });

In the example, your_param[1..N] will be the parameters to the backend, that you control and define, e.g.:

order_id, or something that helps you determine what amount to pass.

token, may represent the value for our payment_method_token below

client_device, the necessary device_info explained above

In your backend, create an authorize, purchase or sca_provider#authenticate request to the Spreedly API.

POST /v1/gateways/\<gateway_token>/purchase.json HTTPS/1.1  
  Host: core.spreedly.com  
  Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==  
  Content-Type: application/<format>  
  {  
    "transaction": {  
      "sca_provider_key": "\<sca_provider_key>",  
      "payment_method_token": "\<payment_method_token>",  
      "amount": 10000,  
      "currency_code": "EUR",  
      "callback_url": "\<callback_url (optional)>",  
      "device_info": "\<device_info>"  
    }  
  }

The communication between your frontend and backend is entirely up to you. We recommend you keep track of thepayment_method_token value and the token value returned from the Spreedly API call to make subsequent communication between your frontend and backend easier. For details see our Spreedly API purchase docs. Or our Spreedly API sca_authentication docs.

Spreedly will return the managedOrderToken received from Forter as part of the sca_authentication object on the transaction response.

Note: if you are doing a stand alone sca authentication the managedOrderToken is in the main transaction object, not nested like it is in a purchase.

{  
  "transaction": {  
    "on_test_gateway": false,  
    "created_at": "2020-11-12T15:28:15Z",  
    "updated_at": "2020-11-12T15:28:16Z",  
    "succeeded": false,  
    "state": "pending",  
    "token": "VvplgnvtAWAoa38801a7e24qAHI",  
    "transaction_type": "Purchase",  
    "order_id": null,  
    "ip": "127.0.0.1",  
    "description": null,  
    "email": null,  
    "merchant_name_descriptor": null,  
    "merchant_location_descriptor": null,  
    "gateway_specific_fields": null,  
    "gateway_specific_response_fields": {  
    },  
    "gateway_transaction_id": null,  
    "gateway_latency_ms": null,  
    "stored_credential_initiator": null,  
    "stored_credential_reason_type": null,  
    "warning": null,  
    "amount": 100,  
    "currency_code": "USD",  
    "retain_on_success": false,  
    "payment_method_added": false,  
    "smart_routed": false,  
    "message_key": "messages.transaction_pending",  
    "message": "Pending",  
    "gateway_token": "T11bJAANtTWnxl36GYjKWvbNK0g",  
    "gateway_type": "test",  
    "shipping_address": {  
      "name": "Newfirst Newlast",  
      "address1": null,  
      "address2": null,  
      "city": null,  
      "state": null,  
      "zip": null,  
      "country": null,  
      "phone_number": null  
    },  
    "api_urls": \[  
      {  
        "referencing_transaction": [  
        ]  
      },  
      {  
        "failover_transaction": [  
        ]  
      }  
    ],  
    "attempt_3dsecure": false,  
    "payment_method": {  
      "token": "1rpKvP8zOUhj4Y9EDrIoIYQzzD5",  
      "created_at": "2017-06-26T17:04:38Z",  
      "updated_at": "2020-11-10T19:38:14Z",  
      "email": "[[email protected]](mailto:[email protected])",  
      "data": {  
        "my_payment_method_identifier": "448",  
        "extra_stuff": {  
          "some_other_things": "Can be anything really"  
        }  
      },  
      "storage_state": "retained",  
      "test": true,  
      "metadata": {  
        "key": "string value"  
      },  
      "callback_url": null,  
      "last_four_digits": "1111",  
      "first_six_digits": "411111",  
      "card_type": "visa",  
      "first_name": "Newfirst",  
      "last_name": "Newlast",  
      "month": 3,  
      "year": 2032,  
      "address1": null,  
      "address2": null,  
      "city": null,  
      "state": null,  
      "zip": null,  
      "country": null,  
      "phone_number": null,  
      "company": null,  
      "full_name": "Newfirst Newlast",  
      "eligible_for_card_updater": true,  
      "shipping_address1": null,  
      "shipping_address2": null,  
      "shipping_city": null,  
      "shipping_state": null,  
      "shipping_zip": null,  
      "shipping_country": null,  
      "shipping_phone_number": null,  
      "payment_method_type": "credit_card",  
      "errors": [  
      ],  
      "fingerprint": "e3cef43464fc832f6e04f187df25af497994",  
      "verification_value": "",  
      "number": "XXXX-XXXX-XXXX-1111"  
    },  
    "sca_authentication": {  
      "created_at": "2020-11-12T15:28:15Z",  
      "updated_at": "2020-11-12T15:28:16Z",  
      "succeeded": false,  
      "state": "pending",  
      "token": "INP4rU2jLObOPm1tnzXfGo2tuH1",  
      "flow_performed": "challenge",  
      "message": null,  
      "sca_provider_key": "6JXUReoA9KgnYoSchcJU0OAA6N0",  
      "three_ds_version": "2.2.0",  
      "ecommerce_indicator": null,  
      "authentication_value": null,  
      "directory_server_transaction_id": "5c06e665-3675-494e-9ccf-cc078ebd8dc9",  
      "authentication_value_algorithm": null,  
      "directory_response_status": "C",  
      "authentication_response_status": "C",  
      "required_action": "challenge",  
      "acs_reference_number": null,  
      "acs_rendering_type": null,  
      "acs_signed_content": null,  
      "acs_transaction_id": null,  
      "sdk_transaction_id": null,  
      "challenge_form": "\<form action=\"<https://testds3.seglan.com/server/authentication/load\"> method=\"POST\">\\n  \<input name=\"browserChallengeToken\" value=\"b3055a53-7c4d-4f71-bb33-742af0daa96f\" type=\"hidden\"/>\\n</form>",  
      "challenge_form_embed_url": null,  
      "three_ds_server_trans_id": "b3055a53-7c4d-4f71-bb33-742af0daa96f",  
      "xid": null,  
      "enrolled": Y,  
      "transaction_type": "Sca::Authentication",  
      "managed_order_token": \<managed_order_token_from_Forter>  
    }  
  }  
}

After the purchase or authorize call, there are two possible scenarios you will need to handle:

If response.state == "failed", handle the error response and display feedback to your user.

If response.state == "pending", then a challenge is required to finish the authentication. Please see the following section on how to finish a pending transaction.

Handle Authentication

If a transaction’s state is pending , you will have to handle the challenge in your app using the Forter SDK.

SDK’s doChallengeIfNeeded

To present the challenge to the customer, call the doChallengeIfNeeded method as documented above in the Implementation sections of each SDK. When the challenge is completed (either successfully or with an error), the challengeCompleted callback method you have implemented will be called by the SDK. This method make a call to your backend to send an authenticated request to the complete_gratis endpoint at Spreedly in both success and error scenarios.

Spreedly’s complete_gratis

The complete_gratis endpoint will check the managed order status at Forter and handle the Spreedly transaction accordingly by either marking the transaction as failed if the authentication was unsuccessful or proceeding to synchronously send the transaction to the gateway. The transaction_token is the Spreedly transaction token returned by the original authorize or purchase call. The format can be either json or xml.

POST /v1/transactions/\<transaction_token>/complete_gratis.<format> HTTPS/1.1  
Host: core.spreedly.com  
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==  
Content-Type: application/<format>

The complete_gratis call will return the final transaction status.

{  
  "transaction": {  
    "on_test_gateway": false,  
    "created_at": "2020-11-12T15:28:15Z",  
    "updated_at": "2020-11-12T15:28:16Z",  
    "succeeded": true,  
    "state": "succeeded",  
    "token": "VvplgnvtAWAoa38801a7e24qAHI",  
    "transaction_type": "Purchase",  
    "order_id": null,  
    "ip": "127.0.0.1",  
    "description": null,  
    "email": null,  
    "merchant_name_descriptor": null,  
    "merchant_location_descriptor": null,  
    "gateway_specific_fields": null,  
    "gateway_specific_response_fields": {  
    },  
    "gateway_transaction_id": null,  
    "gateway_latency_ms": null,  
    "stored_credential_initiator": null,  
    "stored_credential_reason_type": null,  
    "warning": null,  
    "amount": 100,  
    "currency_code": "USD",  
    "retain_on_success": false,  
    "payment_method_added": false,  
    "smart_routed": false,  
    "message_key": "messages.transaction_succeeded",  
    "message": "Succeeded!",  
    "gateway_token": "T11bJAANtTWnxl36GYjKWvbNK0g",  
    "gateway_type": "test",  
    "shipping_address": {  
      "name": "Newfirst Newlast",  
      "address1": null,  
      "address2": null,  
      "city": null,  
      "state": null,  
      "zip": null,  
      "country": null,  
      "phone_number": null  
    },  
    "api_urls": \[  
      {  
        "referencing_transaction": [  
        ]  
      },  
      {  
        "failover_transaction": [  
        ]  
      }  
    ],  
    "attempt_3dsecure": false,  
    "payment_method": {  
      "token": "1rpKvP8zOUhj4Y9EDrIoIYQzzD5",  
      "created_at": "2017-06-26T17:04:38Z",  
      "updated_at": "2020-11-10T19:38:14Z",  
      "email": "[[email protected]](mailto:[email protected])",  
      "data": {  
        "my_payment_method_identifier": "448",  
        "extra_stuff": {  
          "some_other_things": "Can be anything really"  
        }  
      },  
      "storage_state": "retained",  
      "test": true,  
      "metadata": {  
        "key": "string value"  
      },  
      "callback_url": null,  
      "last_four_digits": "1111",  
      "first_six_digits": "411111",  
      "card_type": "visa",  
      "first_name": "Newfirst",  
      "last_name": "Newlast",  
      "month": 3,  
      "year": 2032,  
      "address1": null,  
      "address2": null,  
      "city": null,  
      "state": null,  
      "zip": null,  
      "country": null,  
      "phone_number": null,  
      "company": null,  
      "full_name": "Newfirst Newlast",  
      "eligible_for_card_updater": true,  
      "shipping_address1": null,  
      "shipping_address2": null,  
      "shipping_city": null,  
      "shipping_state": null,  
      "shipping_zip": null,  
      "shipping_country": null,  
      "shipping_phone_number": null,  
      "payment_method_type": "credit_card",  
      "errors": [  
      ],  
      "fingerprint": "e3cef43464fc832f6e04f187df25af497994",  
      "verification_value": "",  
      "number": "XXXX-XXXX-XXXX-1111"  
    },  
    "sca_authentication": {  
      "created_at": "2020-11-12T15:28:15Z",  
      "updated_at": "2020-11-12T15:28:16Z",  
      "succeeded": true,  
      "state": "succeeded",  
      "token": "INP4rU2jLObOPm1tnzXfGo2tuH1",  
      "flow_performed": "challenge",  
      "message": null,  
      "sca_provider_key": "6JXUReoA9KgnYoSchcJU0OAA6N0",  
      "three_ds_version": "2.2.0",  
      "ecommerce_indicator": null,  
      "authentication_value": null,  
      "directory_server_transaction_id": "5c06e665-3675-494e-9ccf-cc078ebd8dc9",  
      "authentication_value_algorithm": null,  
      "directory_response_status": "Y",  
      "authentication_response_status": "Y",  
      "required_action": "challenge",  
      "acs_reference_number": null,  
      "acs_rendering_type": null,  
      "acs_signed_content": null,  
      "acs_transaction_id": null,  
      "sdk_transaction_id": null,  
      "challenge_form": "\<form action=\"<https://testds3.seglan.com/server/authentication/load\"> method=\"POST\">\\n  \<input name=\"browserChallengeToken\" value=\"b3055a53-7c4d-4f71-bb33-742af0daa96f\" type=\"hidden\"/>\\n</form>",  
      "challenge_form_embed_url": null,  
      "three_ds_server_trans_id": "b3055a53-7c4d-4f71-bb33-742af0daa96f",  
      "xid": null,  
      "enrolled": Y,  
      "transaction_type": "Sca::Authentication",  
      "managed_order_token": \<managed_order_token_from_Forter>  
    }  
  }  
}

Requesting an Exemption

Spreedly 3DS2 Global supports the ability for merchants to request low value and Transaction Risk Analysis (TRA) exemptions.

The exemption_type field can be included at the transaction level for standalone SCA authentications, but for Purchases and Authorizations, it should be included as a field in the sca_authentication_parameters hash.

Valid exemption_type values

low_value_exemption

transaction_risk_analysis_exemption

Authorize or Purchase

{  
  "transaction": {  
    "payment_method_token": "56wyNnSmuA6CWYP7w0MiYCVIbW6",  
    "sca_provider_key": "ZI0HsrLtnvzvNry7f3HARhnOXbA",  
    "sca_authentication_parameters": {  
      "test_scenario": {  
        "scenario": "authenticated"  
      },  
      "exemption_type": "low_value_exemption"  
    },  
    "amount": 100,  
    "currency_code": "EUR",  
    "ip": "127.0.0.1",  
    "browser_info": "eyJ3aWR0aCI6MjEzMywiaGVpZ2h0IjoxMjAwLCJkZXB0aCI6MjQsInRpbWV6b25lIjozMDAsInVzZXJfYWdlbnQiOiJTcHJlZWRseSBBZ2VudCIsImphdmEiOmZhbHNlLCJsYW5ndWFnZSI6ImVuLVVTIiwiYnJvd3Nlcl9zaXplIjoiMDQiLCJhY2NlcHRfaGVhZGVyIjoidGV4dC9odG1sLGFwcGxpY2F0aW9uL3hodG1sK3htbCxhcHBsaWNhdGlvbi94bWw7cT0wLjksaW1hZ2Uvd2VicCwqLyo7cT0wLjgifQ=="  
  }  
}
<transaction>
  <payment_method_token>56wyNnSmuA6CWYP7w0MiYCVIbW6</payment_method_token>
  <amount>100</amount>
  <currency_code>EUR</currency_code>
  <sca_provider_key>ZI0HsrLtnvzvNry7f3HARhnOXbA</sca_provider_key>
  <sca_authentication_parameters>
    <exemption_type>low_value_exemption</exemption_type>
    <test_scenario><scenario>authenticated</scenario></test_scenario>
  </sca_authentication_parameters>
  <ip>127.0.0.1</ip>
  <browser_info>
    eyJ3aWR0aCI6MjEzMywiaGVpZ2h0IjoxMjAwLCJkZXB0aCI6MjQsInRpbWV6b25lIjozMDAsInVzZXJfYWdlbnQiOiJTcHJlZWRseSBBZ2VudCIsImphdmEiOmZhbHNlLCJsYW5ndWFnZSI6ImVuLVVTIiwiYnJvd3Nlcl9zaXplIjoiMDQiLCJhY2NlcHRfaGVhZGVyIjoidGV4dC9odG1sLGFwcGxpY2F0aW9uL3hodG1sK3htbCxhcHBsaWNhdGlvbi94bWw7cT0wLjksaW1hZ2Uvd2VicCwqLyo7cT0wLjgifQ==
  </browser_info>
</transaction>

Standalone SCA Authentication

{  
  "transaction": {  
    "payment_method_token": "56wyNnSmuA6CWYP7w0MiYCVIbW6",  
    "currency_code": "EUR",  
    "amount": 100,  
    "exemption_type": "low_value_exemption",  
    "test_scenario": {  
      "scenario": "authenticated"  
    },  
    "browser_info": "eyJ3aWR0aCI6MjEzMywiaGVpZ2h0IjoxMjAwLCJkZXB0aCI6MjQsInRpbWV6b25lIjozMDAsInVzZXJfYWdlbnQiOiJTcHJlZWRseSBBZ2VudCIsImphdmEiOmZhbHNlLCJsYW5ndWFnZSI6ImVuLVVTIiwiYnJvd3Nlcl9zaXplIjoiMDQiLCJhY2NlcHRfaGVhZGVyIjoidGV4dC9odG1sLGFwcGxpY2F0aW9uL3hodG1sK3htbCxhcHBsaWNhdGlvbi94bWw7cT0wLjksaW1hZ2Uvd2VicCwqLyo7cT0wLjgifQ=="  
  }  
}

<transaction>
  <payment_method_token>56wyNnSmuA6CWYP7w0MiYCVIbW6</payment_method_token>
  <currency_code>EUR</currency_code>
  <amount>100</amount>
  <exemption_type>low_value_exemption</exemption_type>
  <test_scenario><scenario>authenticated</scenario></test_scenario>
  <browser_info>
    eyJ3aWR0aCI6MjEzMywiaGVpZ2h0IjoxMjAwLCJkZXB0aCI6MjQsInRpbWV6b25lIjozMDAsInVzZXJfYWdlbnQiOiJTcHJlZWRseSBBZ2VudCIsImphdmEiOmZhbHNlLCJsYW5ndWFnZSI6ImVuLVVTIiwiYnJvd3Nlcl9zaXplIjoiMDQiLCJhY2NlcHRfaGVhZGVyIjoidGV4dC9odG1sLGFwcGxpY2F0aW9uL3hodG1sK3htbCxhcHBsaWNhdGlvbi94bWw7cT0wLjksaW1hZ2Uvd2VicCwqLyo7cT0wLjgifQ==
  </browser_info>
</transaction>

Testing values

3DS2 challenge flow

card number: 5267648608924299

email: [email protected]

3DS2 frictionless flow

card number: 5222220000000005

email: [email protected]