3D Secure
Step by Step Integration
Table of Contents
3DS Client side flow
Generate a client token
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 BTThreeDSecureClient
, 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 3DS
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.
- 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 UI
The iOS v6 SDK is not currently supported via the Drop-in SDK. Please see the v5 implementation guide if using the Drop-in.
Custom UI
The following instructions assume your app has already integrated Credit Card payments.
CocoaPods
Include Braintree/ThreeDSecure
in your podfile:
- Ruby
pod 'Braintree/ThreeDSecure'
Carthage
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
- BraintreeThreeDSecure.framework
- BraintreeDataCollector.framework
Swift Package Manager
You must include the BraintreeThreeDSecure
and BraintreeDataCollector
frameworks.
Implementation
First, initialize a BTThreeDSecureClient
:
- Swift
class MyCheckoutViewController: UIViewController {
var threeDSecureClient: BTThreeDSecureClient!
func setupThreeDSecureClient() {
self.threeDSecureClient = BTThreeDSecureClient(apiClient: apiClient)
}
}
The BTThreeDSecureRequest object must contain the following fields:
amount
nonce
- Swift
let threeDSecureRequest = BTThreeDSecureRequest()
threeDSecureRequest.amount = 10.00
threeDSecureRequest.nonce = tokenizedCard.nonce
//Pass other 3DS parameters here
To verify vaulted cards, you would need to first generate the nonce on the server and then pass it in the threeDSecureRequest
.
Then, call startPaymentFlow
, passing in the BTThreeDSecureRequest
object.
- 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 else {
// Handle error
return
}
self.threeDSecureClient.startPaymentFlow(request) { (result, error) in
guard 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 via the BTThreeDSecureRequestDelegate
. When ready to continue with the 3DS flow, you must call next()
.
- 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 aBTThreeDSecureResult
with atokenizedCard
property. You should transmit thetokenizedCard.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
wherecode
isBTThreeDSecureError.canceled
.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 flow
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 lookup
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.
- 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 call
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
.
- Mutation
mutation PerformThreeDSecureLookup($input: PerformThreeDSecureLookupInput!) {
performThreeDSecureLookup(input: $input) {
clientMutationId
threeDSecureLookupData {
authenticationId
}
paymentMethod {
id
details {
... on CreditCardDetails {
bin
threeDSecure {
authentication {
cavv
eciFlag
liabilityShifted
liabilityShiftPossible
}
}
}
}
}
}
}
- 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 challenge
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.
- 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
}
}