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.
- When creating the SCA Provider, use the type forter.
- 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.
- 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
- In Xcode, navigate to File > Swift Packages > Add Package Dependency....
- Enter the Forter3DS SDK repository URL:
Swift Package Manager: https://bitbucket.org/forter-mobile/forter-ios.git/src
- Set the Dependency Rule to be Up to Next Major Version then press Add Package
- On the "Chose Package" screen, verify that Forter3DS is selected and press Add Package
CocoaPods
Ensure that your Podfile includes the use_frameworks! flag
- 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
- 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]
Updated about 19 hours ago