3D Secure

Step by Step Integration

Table of ContentsAnchorIcon

3DS Client side flowAnchorIcon

Generate a client tokenAnchorIcon

Important
The SSL certificates for all Braintree SDKs are set to expire by June 30, 2025. This will impact existing versions of the SDK in published versions of your app. To reduce the impact, upgrade the iOS SDK to version 6.17.0+Android SDK to version 4.45.0+ or version 5.0.0+ for the new SSL certifications.

If you do not decommission your app versions that include the older SDK versions or force upgrade your app with the updated certificates by the expiration date, 100% of your customer traffic will fail.

Before you can initialize BTPaymentFlowDriver, you will need to set up the SDK and initialize it with a client token generated on your server.

If you would like to use a merchant account ID other than your default, specify the merchant_account_id when generating the client token. The merchant account ID used to create the client token must match the merchant account ID used to create the subsequent transaction or verification.

Verify a card using 3DSAnchorIcon

A prerequisite to enabling 3D Secure is to set up a URL Scheme in your project. This allows your app to open a SFSafariViewController to complete the 3D Secure verification workflow and return.

To use 3DS, you will need to create a BTThreeDSecureRequest object with relevant customer and transaction data in order to minimize the need for issuing banks to present authentication challenges to customers.

The object should contain as many of the following fields as possible. The full list of fields is described in the SDK documentation.
  1. Swift
let threeDSecureRequest = BTThreeDSecureRequest()
threeDSecureRequest.amount = 10.00
threeDSecureRequest.email = "test@email.com"
threeDSecureRequest.versionRequested = .version2

let address = BTThreeDSecurePostalAddress()
address.givenName = "Jill" // ASCII-printable characters required, else will throw a validation error
address.surname = "Doe" // ASCII-printable characters required, else will throw a validation error
address.phoneNumber = "5551234567"
address.streetAddress = "555 Smith St"
address.extendedAddress = "#2"
address.locality = "Chicago"
address.region = "IL" // ISO-3166-2 code
address.postalCode = "12345"
address.countryCodeAlpha2 = "US"
threeDSecureRequest.billingAddress = address

// Optional additional information.
// For best results, provide as many of these elements as possible.
let info = BTThreeDSecureAdditionalInformation()
info.shippingAddress = address
threeDSecureRequest.additionalInformation = info

Drop-in UIAnchorIcon

Important

3DS support requires Drop-in version 7.3.0 or higher.

Note

If you are using the Drop-In UI to tokenize cards, you have CVV rules enabled and you run 3D Secure verifications, the default setting to vault cards on your client will result in a processor error. Instead, you should disable vaulting via the Drop-In UI and vault the card on your server. You can do so by setting vaultCard to false when initializing the Drop-In UI and include the vault_on_success=true parameter when creating a transaction using transaction:sale or a GraphQL mutation from your server.

If you haven't already, include the Drop-in in your project.

The BTThreeDSecureRequest object must contain the following fields:

  • amount
  • versionRequested (set as .version2)
  1. Swift
let threeDSecureRequest = BTThreeDSecureRequest()
threeDSecureRequest.amount = 10.00
threeDSecureRequest.versionRequested = .version2

//Pass other 3DS parameters here

Then, add the BTThreeDSecureRequest object to your BTDropInRequest.

  1. Swift
let dropInRequest = BTDropInRequest()
dropInRequest.threeDSecureRequest = threeDSecureRequest

let dropIn = BTDropInController(authorization: self.authorizationString, request: dropInRequest) { (controller, result, error) in
    if (error != nil) {
        // Handle error
    } else {
        // Use the nonce returned in 'result.paymentMethod'
    }
    controller.dismiss(animated: true, completion: nil)
}

Custom UIAnchorIcon

The following instructions assume your app has already integrated Credit Card payments.

CocoaPodsAnchorIcon

Include Braintree/ThreeDSecure in your podfile:

  1. Ruby
pod 'Braintree/ThreeDSecure'
CarthageAnchorIcon

Add github "braintree/braintree_ios" to your Cartfile, and add the frameworks to your project.

You must include all of the following frameworks via Carthage:

  • CardinalMobile.framework
  • BraintreeCore.framework
  • BraintreeCard.framework
  • BraintreePaymentFlow.framework
  • BraintreeThreeDSecure.framework
  • PayPalDataCollector.framework
Swift Package ManagerAnchorIcon

You must include the BraintreeThreeDSecure and PayPalDataCollector frameworks.

ImplementationAnchorIcon

First, initialize the BTPaymentFlowDriver and set the viewControllerPresentingDelegate:

  1. Swift
class MyCheckoutViewController: UIViewController, BTViewControllerPresentingDelegate {

    var paymentFlowDriver: BTPaymentFlowDriver!

    func setupPaymentFlowDriver() {
        self.paymentFlowDriver = BTPaymentFlowDriver(apiClient: self.apiClient)
        self.paymentFlowDriver.viewControllerPresentingDelegate = self
    }
}

Next, conform your view controller to BTViewControllerPresentingDelegate in order to handle view controller presentation.

  1. Swift
func paymentDriver(_ driver: Any, requestsPresentationOf viewController: UIViewController) {
    present(viewController, animated: true, completion: nil)
}

func paymentDriver(_ driver: Any, requestsDismissalOf viewController: UIViewController) {
    dismiss(animated: true, completion: nil)
}

The BTThreeDSecureRequest object must contain the following fields:

  • amount
  • nonce
  • versionRequested (set as .version2)
  1. Swift
let threeDSecureRequest = BTThreeDSecureRequest()
threeDSecureRequest.amount = 10.00
threeDSecureRequest.nonce = tokenizedCard.nonce
threeDSecureRequest.versionRequested = .version2

//Pass other 3DS parameters here
Note

To verify vaulted cards, you would need to first generate the nonce on the server and then pass it in the BTThreeDSecureRequest.

Then, call startPaymentFlow, passing in the BTThreeDSecureRequest object.

  1. Swift
func tappedCheckout() {
    // Example: Create a BTCard based on the UI state
    let details = BTCard()
    details.number = cardNumberField.text
    details.expirationMonth = expirationMonthField.text
    details.expirationYear = expirationYearField.text

    // Tokenize the card
    cardClient.tokenizeCard(details) { (tokenizedCard, error) in
        guard let tokenizedCard = tokenizedCard else {
            // Handle error
            return
        }

        // Make sure that self conforms to the BTThreeDSecureRequestDelegate protocol
        threeDSecureRequest.threeDSecureRequestDelegate = self

        self.paymentFlowDriver.startPaymentFlow(request) { (result, error) in
            guard let result = result as? BTThreeDSecureResult, let tokenizedCard = result.tokenizedCard else {
                // Handle error
                return
            }

            if (tokenizedCard.threeDSecureInfo.liabilityShiftPossible) {
                if (tokenizedCard.threeDSecureInfo.liabilityShifted) {
                    // 3D Secure authentication success
                } else {
                    // 3D Secure authentication failed
                }
            } else {
                // 3D Secure authentication was not possible
            }

            // Use the 'tokenizedCard.nonce'
        }
    }
}

You are required to implement the onLookupComplete method where you can optionally inspect the lookup result. When ready to continue with the 3DS flow, you must call next().

  1. Swift
func onLookupComplete(_ request: BTThreeDSecureRequest, result: BTThreeDSecureResult, next: @escaping () -> Void) {
    // Optionally inspect 'result.lookup' and prepare UI if a challenge is required
    next()
}
  • If the user successfully completes the 3D Secure process, the startPaymentFlow callback block will receive a BTThreeDSecureResult with a tokenizedCard property. You should transmit the tokenizedCard.nonce to your server and create a transaction.

  • If the user cancels the 3D Secure authentication, the completion block will be called with an error where code is BTPaymentFlowDriverErrorTypeCanceled.

  • If the 3D Secure process fails, the completion block will be called with an error.

In order to perform 3D Secure authentication again, a new nonce needs to be supplied.

For each request to verify a card, the completion block will be called exactly once.

A validation error from Braintree will be returned if a field does not follow Cardinal's documentation.

3DS Server side flowAnchorIcon

An alternative way of performing 3DS is to make the 3DS call from the server instead of from the client machine.

To meet the device data collection requirements for 3DS, merchants must either make a prerequisite prepareLookup call in the client SDK or pass in the browser fields directly as input parameters.

Prepare the 3DS lookupAnchorIcon

The prepareLookup call will return the data needed to perform a 3D Secure lookup call. This call is triggered from the client's device. The payload returned by this call should be passed on to the server so a 3DS lookup can be done from there.

  1. Swift
let threeDSecureClient = BTThreeDSecureClientBTThreeDSecureClient(apiClient: apiClient)

threeDSecureClient.prepareLookup(BTThreeDSecureRequest()) { payload, error in
  guard let payload else {
    // handle error
    return
  }

  // send payload to server to perform server side lookup
}

Make the 3DS lookup callAnchorIcon

Use the GraphQL mutation performThreeDSecureLookup to attempt to perform 3D Secure Authentication on credit card payment method. This may consume the payment method and return a new single-use payment method.

Including device data is mandatory. This can be done either by passing along the dfReferenceId or browserInformation.

  1. Mutation
mutation PerformThreeDSecureLookup($input: PerformThreeDSecureLookupInput!) {
    performThreeDSecureLookup(input: $input) {
      clientMutationId
      threeDSecureLookupData {
        authenticationId
      }
      paymentMethod {
        id
        details {
          ... on CreditCardDetails {
            bin
            threeDSecure {
              authentication {
                cavv
                eciFlag
                liabilityShifted
                liabilityShiftPossible
              }
            }
          }
        }
      }
    }
  }
  1. Variables
{
    input: {
      paymentMethodId: single_use_payment_method_id,
      amount: "10.00",
      transactionInformation: {
        browserInformation: 
          {
            javaEnabled: false,
            acceptHeader: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
            language: "en-GB",
            colorDepth: 24,
            screenHeight: 720,
            screenWidth: 1280,
            timeZone: "0",
            userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safar  i/537.36",
            javascriptEnabled: true,
          }
        ipAddress: "82.34.105.112",
        deviceChannel: "BROWSER",
      },
    }
  }

Trigger 3DS challengeAnchorIcon

This call will launch the iframe challenge using a 3D Secure lookup response from a server side lookup. This call is only necessary if a challenge is required.

  1. Swift
let threeDSecureClient = BTThreeDSecureClientBTThreeDSecureClient(apiClient: apiClient)

threeDSecureClient.initializeChallenge(
  lookupResponse: "LOOKUP_RESPONSE_FROM_SERVER",
  request: BTThreeDSecureRequest()
) { threeDSecureResult, error in
      guard let threeDSecureResult else {
        // handle error
        return
      }

    let threeDSecureInfo = threeDSecureResult.tokenizedCard?.threeDSecureInfo
    if threeDSecureInfo?.liabilityShifted == true {
      // liability has shifted
      // send nonce to server
    } else if threeDSecureInfo?.liabilityShiftPossible == true {
      // liability may still be shifted
      // decide if you want to submit the nonce
    } else {
      // liability has not shifted and will not shift
      // decide if you want to submit the nonce
    }
}

If you accept cookies, we’ll use them to improve and customize your experience and enable our partners to show you personalized PayPal ads when you visit other sites. Manage cookies and learn more