Skip to content

Commit

Permalink
Fix Bug where fetchPaymentMethodNonces Return Incorrect Type (#1100)
Browse files Browse the repository at this point in the history
* We were never parsing out the type from the nonce as expected
* In Objective-C we did this in the load method, we can instead just parse it out from the JSON and return the type as we were in v5
  • Loading branch information
jaxdesmarais authored Sep 29, 2023
1 parent eadb707 commit 2559fa8
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 37 deletions.
10 changes: 5 additions & 5 deletions Braintree.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@
A95C411724FAEF5100045045 /* BTCardNonce_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7A094F51B8276E500D732CC /* BTCardNonce_Tests.swift */; };
A95C411824FAEF5100045045 /* BTThreeDSecureInfo_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0301A42E22E2286C008A26BD /* BTThreeDSecureInfo_Tests.swift */; };
A95C411C24FAF21B00045045 /* BraintreeTestShared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A903E1A624F9D34000C314E1 /* BraintreeTestShared.framework */; };
A977A04124FEC36F006049AB /* BTPaymentMethodNonceParser_PayPal_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A977A04024FEC36F006049AB /* BTPaymentMethodNonceParser_PayPal_Tests.swift */; platformFilter = ios; };
A977A06B24FECFC3006049AB /* NonceValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A977A06A24FECFC3006049AB /* NonceValidator.swift */; };
A9A76CA724F9E92E0044EAEE /* TestClientTokenFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A76CA624F9E92E0044EAEE /* TestClientTokenFactory.swift */; };
A9E5C1A324FD5A7C00EE691F /* BraintreeDataCollector.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A76D7C001BB1CAB00000FA6A /* BraintreeDataCollector.framework */; platformFilter = ios; };
Expand Down Expand Up @@ -270,6 +269,7 @@
BEDB820629B13F9500075AF3 /* BTPayPalNativeCheckoutAccountNonce_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEDB820529B13F9500075AF3 /* BTPayPalNativeCheckoutAccountNonce_Tests.swift */; };
BEDB820829B675DC00075AF3 /* BTVenmoAccountNonce.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEDB820729B675DC00075AF3 /* BTVenmoAccountNonce.swift */; };
BEDB820A29B7A9E600075AF3 /* BTVenmoClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEDB820929B7A9E600075AF3 /* BTVenmoClient.swift */; };
BEDEAF112AC1D049004EA970 /* BTPayPalAccountNonce_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEDEAF102AC1D049004EA970 /* BTPayPalAccountNonce_Tests.swift */; };
BEE2E4A728FDB64400C03FDD /* BTAnalyticsService_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEE2E4A628FDB64400C03FDD /* BTAnalyticsService_Tests.swift */; };
BEE2E4B9290043A200C03FDD /* PayPalCheckout in Frameworks */ = {isa = PBXBuildFile; productRef = BEE2E4B8290043A200C03FDD /* PayPalCheckout */; };
BEE2E4E6290080BD00C03FDD /* BTAnalyticsServiceError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEE2E4E5290080BD00C03FDD /* BTAnalyticsServiceError.swift */; };
Expand Down Expand Up @@ -778,7 +778,6 @@
A9589DD924FEA45C00AF4FF7 /* BTConfiguration+Venmo_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BTConfiguration+Venmo_Tests.swift"; sourceTree = "<group>"; };
A95C410824FAEF2100045045 /* BraintreeCardTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BraintreeCardTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A95C410C24FAEF2100045045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A977A04024FEC36F006049AB /* BTPaymentMethodNonceParser_PayPal_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPaymentMethodNonceParser_PayPal_Tests.swift; sourceTree = "<group>"; };
A977A06924FECBAF006049AB /* BraintreeThreeDSecureTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BraintreeThreeDSecureTests-Bridging-Header.h"; sourceTree = "<group>"; };
A977A06A24FECFC3006049AB /* NonceValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonceValidator.swift; sourceTree = "<group>"; };
A9A76CA624F9E92E0044EAEE /* TestClientTokenFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestClientTokenFactory.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -883,6 +882,7 @@
BEDB820729B675DC00075AF3 /* BTVenmoAccountNonce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTVenmoAccountNonce.swift; sourceTree = "<group>"; };
BEDB820929B7A9E600075AF3 /* BTVenmoClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTVenmoClient.swift; sourceTree = "<group>"; };
BEDE06942FEDCD2F91884DA9 /* Pods-Tests-IntegrationTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests-IntegrationTests.release.xcconfig"; path = "Target Support Files/Pods-Tests-IntegrationTests/Pods-Tests-IntegrationTests.release.xcconfig"; sourceTree = "<group>"; };
BEDEAF102AC1D049004EA970 /* BTPayPalAccountNonce_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPayPalAccountNonce_Tests.swift; sourceTree = "<group>"; };
BEE2E4A628FDB64400C03FDD /* BTAnalyticsService_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTAnalyticsService_Tests.swift; sourceTree = "<group>"; };
BEE2E4E329007FF100C03FDD /* BTAnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTAnalyticsService.swift; sourceTree = "<group>"; };
BEE2E4E5290080BD00C03FDD /* BTAnalyticsServiceError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTAnalyticsServiceError.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1605,8 +1605,8 @@
428F48E32624C9B700EC8DB4 /* BTVenmoAppSwitchRedirectURL_Tests.swift */,
4245D668256C255F00F1A413 /* BTVenmoAppSwitchReturnURL_Tests.swift */,
A71F7DE61B6180A3005DA1B0 /* BTVenmoClient_Tests.swift */,
4228D8682624E5C3001D2564 /* BTVenmoRequest_Tests.swift */,
09357DCA2A2FBEC10096D449 /* BTVenmoLineItem_Tests.swift */,
4228D8682624E5C3001D2564 /* BTVenmoRequest_Tests.swift */,
A948D6F424FAC8F900F4F178 /* Info.plist */,
);
path = BraintreeVenmoTests;
Expand Down Expand Up @@ -1641,7 +1641,7 @@
children = (
A9E5C1F724FD672A00EE691F /* BraintreePayPalTests-Bridging-Header.h */,
A95229C624FD949D006F7D25 /* BTConfiguration+PayPal_Tests.swift */,
A977A04024FEC36F006049AB /* BTPaymentMethodNonceParser_PayPal_Tests.swift */,
BEDEAF102AC1D049004EA970 /* BTPayPalAccountNonce_Tests.swift */,
3B7A261229C35B670087059D /* BTPayPalAnalytics_Tests.swift */,
42FC237025CE0E110047C49A /* BTPayPalCheckoutRequest_Tests.swift */,
427F32DF25D1D62D00435294 /* BTPayPalClient_Tests.swift */,
Expand Down Expand Up @@ -3014,7 +3014,7 @@
427F32E025D1D62D00435294 /* BTPayPalClient_Tests.swift in Sources */,
42FC218B25CDE0290047C49A /* BTPayPalRequest_Tests.swift in Sources */,
42FC237125CE0E110047C49A /* BTPayPalCheckoutRequest_Tests.swift in Sources */,
A977A04124FEC36F006049AB /* BTPaymentMethodNonceParser_PayPal_Tests.swift in Sources */,
BEDEAF112AC1D049004EA970 /* BTPayPalAccountNonce_Tests.swift in Sources */,
427F329025D1A7B900435294 /* BTPayPalVaultRequest_Tests.swift in Sources */,
3B7A261429C35BD00087059D /* BTPayPalAnalytics_Tests.swift in Sources */,
A95229C724FD949D006F7D25 /* BTConfiguration+PayPal_Tests.swift in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Braintree iOS SDK Release Notes

## unreleased
* BraintreeCore
* Fix bug where `type` was always returned as `Unknown` in `fetchPaymentMethodNonces` (fixes #1099)

## 6.6.0 (2023-08-22)
* BraintreePayPalNativeCheckout
* Update PayPalCheckout from 1.0.0 to 1.1.0.
Expand Down
5 changes: 5 additions & 0 deletions Sources/BraintreeCore/BTAPIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,20 @@ import Foundation
/// Fetches a customer's vaulted payment method nonces.
/// Must be using client token with a customer ID specified.
/// - Parameter completion: Callback that returns either an array of payment method nonces or an error
/// - Note: Only the top level `BTPaymentMethodNonce` type is returned, fetching any additional details will need to be done on the server
public func fetchPaymentMethodNonces(_ completion: @escaping ([BTPaymentMethodNonce]?, Error?) -> Void) {
fetchPaymentMethodNonces(false, completion: completion)
}

// NEXT_MAJOR_VERSION: this should move into the Drop-in for parity with Android
// This will also allow us to return the types directly which we were doing in the +load method
// previously in Obj-C - this is not available in Swift
/// Fetches a customer's vaulted payment method nonces.
/// Must be using client token with a customer ID specified.
/// - Parameters:
/// - defaultFirst: Specifies whether to sort the fetched payment method nonces with the default payment method or the most recently used payment method first
/// - completion: Callback that returns either an array of payment method nonces or an error
/// - Note: Only the top level `BTPaymentMethodNonce` type is returned, fetching any additional details will need to be done on the server
public func fetchPaymentMethodNonces(_ defaultFirst: Bool, completion: @escaping ([BTPaymentMethodNonce]?, Error?) -> Void) {
if clientToken == nil {
completion(nil, BTAPIClientError.notAuthorized)
Expand Down
68 changes: 65 additions & 3 deletions Sources/BraintreeCore/BTPaymentMethodNonceParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,80 @@ import Foundation
return nil
}

if let completionHandler = completionHandler {
if let completionHandler {
return completionHandler(json)
}

if json?["nonce"].isString != false {
if json?["nonce"].isString == false {
return nil
}

let type = json?["type"].asString()

if type == "CreditCard", let cardType = json?["details"]["cardType"].asString() {
return BTPaymentMethodNonce(
nonce: json?["nonce"].asString() ?? "",
type: self.cardType(from: cardType),
isDefault: json?["default"].isTrue ?? false
)
} else if type == "ApplePayCard" {
return BTPaymentMethodNonce(
nonce: json?["nonce"].asString() ?? "",
type: json?["details"]["cardType"].asString() ?? "ApplePayCard",
isDefault: json?["default"].isTrue ?? false
)
} else if type == "PayPalAccount" {
return BTPaymentMethodNonce(
nonce: json?["nonce"].asString() ?? "",
type: "PayPal",
isDefault: json?["default"].isTrue ?? false
)
} else if type == "VenmoAccount" {
return BTPaymentMethodNonce(
nonce: json?["nonce"].asString() ?? "",
type: "Venmo",
isDefault: json?["default"].isTrue ?? false
)
} else {
return BTPaymentMethodNonce(
nonce: json?["nonce"].asString() ?? "",
type: "Unknown",
isDefault: json?["default"].isTrue ?? false
)
}
}

private func cardType(from cardType: String) -> String {
let cardType = cardType.lowercased()

if cardType == "american express" {
return "AMEX"
} else if cardType == "diners club" {
return "DinersClub"
} else if cardType == "unionpay" {
return "UnionPay"
} else if cardType == "discover" {
return "Discover"
} else if cardType == "mastercard" {
return "MasterCard"
} else if cardType == "jcb" {
return "JCB"
} else if cardType == "hiper" {
return "Hiper"
} else if cardType == "hipercard" {
return "Hipercard"
} else if cardType == "laser" {
return "Laser"
} else if cardType == "solo" {
return "Solo"
} else if cardType == "switch" {
return "Switch"
} else if cardType == "uk maestro" {
return "UKMaestro"
} else if cardType == "visa" {
return "Visa"
}

return nil
return "Unknown"
}
}
2 changes: 2 additions & 0 deletions UnitTests/BraintreeCoreTests/BTAPIClient_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,11 @@ class BTAPIClient_Tests: XCTestCase {

let firstNonce = paymentMethodNonces[0];
XCTAssertEqual(firstNonce.nonce, "fake-nonce1")
XCTAssertEqual(firstNonce.type, "AMEX")

let secondNonce = paymentMethodNonces[1]
XCTAssertEqual(secondNonce.nonce, "fake-nonce2")
XCTAssertEqual(secondNonce.type, "PayPal")

expectation.fulfill()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import XCTest
@testable import BraintreeCore

class BTPaymentMethodNonceParser_Tests: XCTestCase {
var parser : BTPaymentMethodNonceParser = BTPaymentMethodNonceParser()
var parser: BTPaymentMethodNonceParser = BTPaymentMethodNonceParser()

func testRegisterType_addsTypeToTypes() {
parser.registerType("MyType") { _ -> BTPaymentMethodNonce? in return nil}
Expand Down Expand Up @@ -67,4 +67,86 @@ class BTPaymentMethodNonceParser_Tests: XCTestCase {
XCTAssertEqual(unknownNonce.type, "Unknown")
XCTAssertTrue(unknownNonce.isDefault)
}

func testSharedParser_whenTypeIsApplePayCard_returnsApplePayType() {
let sharedParser = BTPaymentMethodNonceParser.shared
let applePayCard = BTJSON(value: [
"consumed": false,
"details": ["cardType": "American Express"],
"isLocked": false,
"nonce": "a-nonce",
"securityQuestions": [] as [Any],
"type": "ApplePayCard",
] as [String: Any])

let applePayCardNonce = sharedParser.parseJSON(applePayCard, withParsingBlockForType: "ApplePayCard")

XCTAssertEqual(applePayCardNonce?.nonce, "a-nonce")
XCTAssertEqual(applePayCardNonce?.type, "American Express")
}

func testSharedParser_whenTypeIsCreditCard_returnsCardType() {
let sharedParser = BTPaymentMethodNonceParser.shared

let creditCardJSON = BTJSON(value: [
"consumed": false,
"description": "ending in 31",
"details": [
"cardType": "American Express",
"lastTwo": "31",
],
"isLocked": false,
"nonce": "0099b1d0-7a1c-44c3-b1e4-297082290bb9",
"securityQuestions": ["cvv"],
"threeDSecureInfo": NSNull(),
"type": "CreditCard",
"default": true
] as [String: Any])

let cardNonce = sharedParser.parseJSON(creditCardJSON, withParsingBlockForType:"CreditCard")!

XCTAssertEqual(cardNonce.nonce, "0099b1d0-7a1c-44c3-b1e4-297082290bb9")
XCTAssertEqual(cardNonce.type, "AMEX")
XCTAssertTrue(cardNonce.isDefault)
}

func testSharedParser_whenTypeIsVenmo_returnsVenmoType() {
let sharedParser = BTPaymentMethodNonceParser.shared

let venmoAccountJSON = BTJSON(value: [
"consumed": false,
"description": "VenmoAccount",
"details": ["username": "[email protected]"],
"isLocked": false,
"nonce": "a-nonce",
"securityQuestions": [] as [Any],
"type": "VenmoAccount",
"default": true
] as [String: Any])

let venmoAccountNonce = sharedParser.parseJSON(venmoAccountJSON, withParsingBlockForType: "VenmoAccount")

XCTAssertEqual(venmoAccountNonce?.nonce, "a-nonce")
XCTAssertEqual(venmoAccountNonce?.type, "Venmo")
}

func testSharedParser_whenTypeIsPayPal_returnsPayPalType() {
let sharedParser = BTPaymentMethodNonceParser.shared

let payPalAccountJSON = BTJSON(value: [
"consumed": false,
"description": "[email protected]",
"details": ["email": "[email protected]"],
"isLocked": false,
"nonce": "a-nonce",
"securityQuestions": [] as [Any],
"type": "PayPalAccount",
"default": true
] as [String: Any])

let payPalAccountNonce = sharedParser.parseJSON(payPalAccountJSON, withParsingBlockForType: "PayPalAccount")

XCTAssertEqual(payPalAccountNonce?.nonce, "a-nonce")
XCTAssertEqual(payPalAccountNonce?.type, "PayPal")
}
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,9 @@
import XCTest
@testable import BraintreePayPal

class BTPaymentMethodNonceParser_PayPal_Tests: XCTestCase {
func testSharedParser_whenTypeIsPayPal_returnsPayPalAccountNonce() {
let payPalAccountNonce = BTPayPalAccountNonce(
json: BTJSON(
value: [
"consumed": false,
"description": "[email protected]",
"details": [
"email": "[email protected]",
],
"isLocked": false,
"nonce": "a-nonce",
"securityQuestions": [] as [Any?],
"type": "PayPalAccount",
"default": true
] as [String: Any]
)
)

XCTAssertEqual(payPalAccountNonce?.nonce, "a-nonce")
XCTAssertEqual(payPalAccountNonce?.type, "PayPal")
XCTAssertEqual(payPalAccountNonce?.email, "[email protected]")
XCTAssertTrue(payPalAccountNonce!.isDefault)
XCTAssertNil(payPalAccountNonce?.creditFinancing)
}
final class BTPayPalAccountNonce_Tests: XCTestCase {

func testParsePayPalCreditFinancingAmount() {
func testPayPalAccountNonce_returnsPayPalCreditFinancingAmount() {
let payPalCreditFinancingAmount = BTJSON(value: [
"currency": "USD",
"value": "123.45",
Expand All @@ -41,7 +17,7 @@ class BTPaymentMethodNonceParser_PayPal_Tests: XCTestCase {
XCTAssertEqual(amount.value, "123.45")
}

func testParsePayPalCreditFinancing() {
func testPayPalAccountNonce_returnsPayPalCreditFinancing() {
let payPalCreditFinancing = BTJSON(value: [
"cardAmountImmutable": false,
"monthlyPayment": [
Expand Down Expand Up @@ -93,7 +69,7 @@ class BTPaymentMethodNonceParser_PayPal_Tests: XCTestCase {
XCTAssertEqual(totalInterest.value, "456.78")
}

func testSharedParser_whenTypeIsPayPal_returnsPayPalAccountNonceWithCreditFinancingOffered() {
func testPayPalAccountNonce_returnsPayPalAccountNonceWithCreditFinancingOffered() {
let payPalAccountNonce = BTPayPalAccountNonce(
json: BTJSON(
value: [
Expand Down

0 comments on commit 2559fa8

Please sign in to comment.