Migrating from iOS SDK v1.0.0 to v2.0.0

  Last updated: 


The following documentation explains what has changed between v1.0.0 and v2.0.0 of the iOS SDK and what to consider when migrating to the latest version.


Changes in v2.0.0

We have updated the parameters that are returned on payment completion. This now includes:

  • List of request responses.
  • The field threedresponse (if present).
  • Any errors that have occurred.


What can threedresponse be used for?

In v1.0.0, you could use the returned object array (responseObjects) in transactionSuccessClosure, and find the response object from the THREEDQUERY request.


let threeDQueryResponse = responseObjects.filter({ $0.requestTypeDescription(contains: TypeDescription.threeDQuery) }).first

 Then you could use the fields (threeDQueryResponse.threeDResponse or threeDQueryResponse.pares) stored in this object to generate the payload for the AUTH request.

In v2.0.0, the threedresponse object was created (containing the authorization parameters), which is returned directly in transactionResponseClosure.

You can use the fields (threedresponse.threeDResponse or threedresponse.pares) stored in this object to generate the payload for the AUTH request.



In v1.0.0, as part of the transactionSuccessClosure and transactionErrorClosure, we returned a parsed transaction response objects.

However, in v2.0.0, we only have one transactionResponseClosure, because we wanted to provide our merchants more control over how they interpret the response. As shown in the example below:

v2.0.0 v1.0.0
transactionResponseClosure: {
responseJwtList: [String],
threeDResponse: ThreeDResponse?,
error: APIClientError?

Example scenario

In v1.0.0, a request of “THREEDQUERY”,”AUTH”,”SUBSCRIPTION” where the THREEDQUERY and AUTH were successful, but the SUBSCRIPTION encountered an error, then the SDK would return a transactionErrorClosure, despite the THREEDQUERY and AUTH having been processed successfully. Displaying an error message to the customer in this particular scenario may be misleading, because funds had been reserved against their bank account.

To reduce the chance of the customer being misinformed about the result of the payment transaction, in v2.0.0 we no longer distinguish between success and failure in this way, and we will now only return the full list of results that you must first verify, parse and then analyse the errorcode returned in each of the responses.

In regards to the scenario above, you could configure your system to treat the THREEDQUERY and AUTH responses as a success and display the appropriate response message in the customer’s browser (to indicate funds were reserved for payment), but also display an error message to inform them that the automated subscription payments have yet to be scheduled due to an error, and that the customer is recommended to contact you for assistance.

Before information in the response object can be trusted, you must verify the signature of each JWT response.

We provide a parsing utility to extract the payload field data as part of reviewing the payment response fields:

transactionResponseClosure: { [unowned self] responseJwtList, threeDResponse, error in

// Every JWT returned from the SDK should be verified before further usage.
let isVerified = !responseJwtList.map { JWTHelper.verifyJwt(jwt: $0, secret: self.appFoundation.keys.jwtSecretKey) }.contains(false)

AppLog.log("JWT verification status: \(isVerified)")

if !isVerified {
// throw error and show the appropriate message

// error indicating that there was a general error in connecting to the server or in 3dsecure authentication (APIClientError enum)

guard let error = error else {

// a parsing utility
guard let tpResponses = try? TPHelper.getTPResponses(jwt: responseJwtList) else { return }

// an example of how to get a card reference object which in version 1.0.0 was part of the closure (TPCardReference object)
let cardReference = tpResponses.last?.cardReference

// an example of how to get the JWTResponseObject array which in version 1.0.0 was part of the closure
let responseObjects = tpResponses.flatMap { $0.responseObjects }

// an example of how to find a response object that interests us
let riskDecResponse = tpResponses.compactMap { $0.responseObjects.first(where: { $0.requestTypeDescription(contains: TypeDescription.riskDec) }) }.first

// the error object constructed from the errorcode property (in version 1.0.0 it was part of the APIClientError class, in version 2.0.0 the TPError class was created)
guard let firstTPError = tpResponses.compactMap({ $0.tpError }).first else {

self.showAlert(controller: self.navigationController, message: LocalizableKeys.DropInViewController.successfulPayment.localizedStringOrEmpty) { _ in
self.navigationController.popViewController(animated: true)

if case TPError.invalidField(let errorCode, let localizedError) = firstTPError {
AppLog.log("RESPONSEVALIDATIONERROR.INVALIDFIELD: code: \(errorCode.rawValue), message: \(localizedError ?? errorCode.message)")

if case TPError.gatewayError(let errorCode, let error) = firstTPError {
AppLog.log("GATEWAYERROR: responseErrorCode: \(errorCode.rawValue)), errorCode: \((error as NSError).code) message: \(error.localizedDescription)")

self.showAlert(controller: self.navigationController, message: firstTPError.humanReadableDescription) { _ in }

self.showAlert(controller: self.navigationController, message: error.humanReadableDescription) { _ in }
Was this article helpful?
0 out of 0 found this helpful