diff --git a/Podfile.lock b/Podfile.lock index 2f24f42..3878aeb 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -25,4 +25,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 31ceadafb7a670350be1722f7ac2941417d82516 -COCOAPODS: 1.10.0 +COCOAPODS: 1.10.1 diff --git a/Xendit.podspec b/Xendit.podspec index 87ee9a1..d2b0582 100755 --- a/Xendit.podspec +++ b/Xendit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Xendit" - s.version = "3.2.0" + s.version = "3.3.0" s.summary = "Xendit is an API for accepting payments online" s.homepage = "https://www.xendit.co" s.license = "MIT" diff --git a/Xendit/Models/XenditAuthenticatedToken.swift b/Xendit/Models/XenditAuthenticatedToken.swift index 86e8c31..09616fe 100644 --- a/Xendit/Models/XenditAuthenticatedToken.swift +++ b/Xendit/Models/XenditAuthenticatedToken.swift @@ -37,6 +37,9 @@ import Foundation // Credit card metadata open var cardInfo : XenditCardMetadata? + // Failure reason + open var failureReason : String? + func getPayerAuthenticationUrl() -> String? { return authenticationURL; } @@ -51,6 +54,7 @@ import Foundation if environment != nil { json["environment"] = environment } if threedsVersion != nil { json["threeds_version"] = threedsVersion } if cardInfo != nil { json["card_info"] = cardInfo?.toJsonObject() } + if failureReason != nil { json["failure_reason"] = failureReason } return json } @@ -68,5 +72,6 @@ import Foundation self.environment = response["environment"] as? String self.threedsVersion = response["threeds_version"] as? String self.cardInfo = XenditCardMetadata(response: response["card_info"] as? [String : Any]) + self.failureReason = response["failure_reason"] as? String } } diff --git a/Xendit/Models/XenditAuthentication.swift b/Xendit/Models/XenditAuthentication.swift index 2491dbc..ef8742d 100644 --- a/Xendit/Models/XenditAuthentication.swift +++ b/Xendit/Models/XenditAuthentication.swift @@ -31,6 +31,11 @@ import Foundation open var cardInfo: XenditCardMetadata? + // 3DS version + open var threedsVersion : String? + + // Failure reason + open var failureReason : String? func getPayerAuthenticationUrl() -> String? { return authenticationURL @@ -45,6 +50,8 @@ import Foundation if requestPayload != nil { json["pa_res"] = requestPayload } if maskedCardNumber != nil { json["masked_card_number"] = maskedCardNumber } if cardInfo != nil { json["card_info"] = cardInfo?.toJsonObject() } + if threedsVersion != nil { json["threeds_version"] = threedsVersion } + if failureReason != nil { json["failure_reason"] = failureReason } return json } @@ -64,6 +71,8 @@ import Foundation self.maskedCardNumber = response["masked_card_number"] as? String self.tokenId = response["credit_card_token_id"] as? String self.cardInfo = XenditCardMetadata(response: response["card_info"] as? [String: Any]) + self.threedsVersion = response["threeds_version"] as? String + self.failureReason = response["failure_reason"] as? String } } diff --git a/Xendit/Models/XenditCCToken.swift b/Xendit/Models/XenditCCToken.swift index 7a3ba13..09e6b47 100755 --- a/Xendit/Models/XenditCCToken.swift +++ b/Xendit/Models/XenditCCToken.swift @@ -30,6 +30,9 @@ import Foundation open var cardInfo : XenditCardMetadata? + // Failure reason + open var failureReason : String? + func toJsonObject() -> [String : Any] { var json: [String: Any] = [:] if id != nil { json["id"] = id } @@ -39,6 +42,7 @@ import Foundation if maskedCardNumber != nil { json["masked_card_number"] = maskedCardNumber } if should3DS != nil { json["should_3ds"] = should3DS } if cardInfo != nil { json["card_info"] = cardInfo!.toJsonObject() } + if failureReason != nil { json["failure_reason"] = failureReason } return json } } @@ -60,6 +64,9 @@ import Foundation self.cardInfo = XenditCardMetadata(response: response["card_info"] as? [String : Any]) } + if (response["failure_reason"] != nil) { + self.failureReason = (response["failure_reason"] as? String) + } } } @@ -73,6 +80,7 @@ import Foundation self.authenticationURL = authenticatedToken.authenticationURL self.maskedCardNumber = authenticatedToken.maskedCardNumber self.cardInfo = authenticatedToken.cardInfo + self.failureReason = authenticatedToken.failureReason } } @@ -86,6 +94,8 @@ import Foundation self.authenticationURL = authentication.authenticationURL self.maskedCardNumber = authentication.maskedCardNumber self.cardInfo = authentication.cardInfo + self.failureReason = authentication.failureReason + } } diff --git a/Xendit/Utils/CreditCard.swift b/Xendit/Utils/CreditCard.swift index 9fbc74e..ee38c07 100644 --- a/Xendit/Utils/CreditCard.swift +++ b/Xendit/Utils/CreditCard.swift @@ -72,6 +72,16 @@ class CreditCard { return false } + // Check 3DS EMV version + public static func is3ds2Version(version: String?) -> Bool { + if version != nil { + let index = version!.index(version!.startIndex, offsetBy: 1) + let currentMajorVersion = Int(version![..= 2 + } + return false + } + // Get Card Type internal static func getCardType(cardNumber: String) -> CYBCardTypes { if cardNumber.hasPrefix("4") { diff --git a/Xendit/XDTCards.swift b/Xendit/XDTCards.swift index a06b901..21a2a1e 100644 --- a/Xendit/XDTCards.swift +++ b/Xendit/XDTCards.swift @@ -150,6 +150,13 @@ public class XDTCards: CanTokenize, CanAuthenticate { } XDTApiClient.createAuthenticationRequest(publishableKey: publishableKey!, tokenId: tokenId, bodyJson: requestBody, extraHeader: header) { (authentication, error) in + if error == nil && + (authentication?.status == "VERIFIED" || !CreditCard.is3ds2Version(version: authentication?.threedsVersion)) { + // Handle frictionless flow & 3ds version 1.0 + let token = XenditCCToken.init(tokenId: tokenId, authentication: authentication!) + return completion(token, nil) + } + if error != nil || authentication?.status == "FAILED" || authentication?.authenticationTransactionId == nil || @@ -166,12 +173,6 @@ public class XDTCards: CanTokenize, CanAuthenticate { } let authenticationTransactionId = authentication?.authenticationTransactionId let requestPayload = authentication?.requestPayload - let status = authentication?.status - if status == "VERIFIED" { - // Handle frictionless flow - let token = XenditCCToken.init(tokenId: tokenId, authentication: authentication!) - return completion(token, nil) - } let xdtDelegate = XDTValidationDelegate(completion: { (response, jwt, error) in verifyAuthentication(authentication: authentication!) { (authentication, error) in @@ -251,7 +252,7 @@ public class XDTCards: CanTokenize, CanAuthenticate { if status != nil { if status == "IN_REVIEW" { - if authenticatedToken?.threedsVersion == "2.0" && jwt != nil { + if CreditCard.is3ds2Version(version: authenticatedToken?.threedsVersion) && jwt != nil { handleEmv3DSFlow(fromViewController: fromViewController, tokenId: tokenId, environment: authenticatedToken!.environment!, amount: amount, jwt: jwt!, onBehalfOf: onBehalfOf, completion: completion) } else if let authenticationURL = authenticatedToken?.authenticationURL { cardAuthenticationProvider.authenticate(