Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create encodable for credit cards post #1488

Draft
wants to merge 18 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Braintree.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@
A9E80A9C24FEF37C00196BD3 /* MockDelegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42D79395233A65EB001FDD89 /* MockDelegates.swift */; };
A9E80A9F24FEF40C00196BD3 /* BraintreeTestShared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A903E1A624F9D34000C314E1 /* BraintreeTestShared.framework */; };
A9E80AA324FEF4D800196BD3 /* MockLocalPaymentRequestDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E80AA224FEF4D800196BD3 /* MockLocalPaymentRequestDelegate.swift */; };
B8559B6C2CEBE9640014AE47 /* BTCreditCardGraphQLBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8559B6B2CEBE9640014AE47 /* BTCreditCardGraphQLBody.swift */; };
B86AF77B2CDBF3C3002CF3B4 /* BTCreditCardBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86AF77A2CDBF3AA002CF3B4 /* BTCreditCardBody.swift */; };
BC17F9B428D23C5C004B18CC /* BTGraphQLMultiErrorNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC17F9B328D23C5C004B18CC /* BTGraphQLMultiErrorNode.swift */; };
BC17F9BC28D24C9E004B18CC /* BTGraphQLErrorTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC17F9BB28D24C9E004B18CC /* BTGraphQLErrorTree.swift */; };
BC17F9BE28D25054004B18CC /* BTGraphQLErrorNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC17F9BD28D25054004B18CC /* BTGraphQLErrorNode.swift */; };
Expand Down Expand Up @@ -959,6 +961,8 @@
A9E5C22424FD6D0800EE691F /* BraintreeCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BraintreeCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A9E5C22824FD6D0800EE691F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A9E80AA224FEF4D800196BD3 /* MockLocalPaymentRequestDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLocalPaymentRequestDelegate.swift; sourceTree = "<group>"; };
B8559B6B2CEBE9640014AE47 /* BTCreditCardGraphQLBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTCreditCardGraphQLBody.swift; sourceTree = "<group>"; };
B86AF77A2CDBF3AA002CF3B4 /* BTCreditCardBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTCreditCardBody.swift; sourceTree = "<group>"; };
BC17F9B328D23C5C004B18CC /* BTGraphQLMultiErrorNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTGraphQLMultiErrorNode.swift; sourceTree = "<group>"; };
BC17F9BB28D24C9E004B18CC /* BTGraphQLErrorTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTGraphQLErrorTree.swift; sourceTree = "<group>"; };
BC17F9BD28D25054004B18CC /* BTGraphQLErrorNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTGraphQLErrorNode.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1821,6 +1825,8 @@
BEFE9A3C29C8ECAA00BF69AB /* BTCardError.swift */,
BE80C00A29C66F4800793A6C /* BTCardNonce.swift */,
BE80C00829C559B800793A6C /* BTCardRequest.swift */,
B86AF77A2CDBF3AA002CF3B4 /* BTCreditCardBody.swift */,
B8559B6B2CEBE9640014AE47 /* BTCreditCardGraphQLBody.swift */,
BE80C00629C5516E00793A6C /* BTThreeDSecureInfo.swift */,
806C85622B90EBED00A2754C /* PrivacyInfo.xcprivacy */,
);
Expand Down Expand Up @@ -3488,6 +3494,8 @@
BE80C00529C54F2000793A6C /* BTAuthenticationInsight.swift in Sources */,
BE80C00729C5516E00793A6C /* BTThreeDSecureInfo.swift in Sources */,
BEFE9A3D29C8ECAA00BF69AB /* BTCardError.swift in Sources */,
B86AF77B2CDBF3C3002CF3B4 /* BTCreditCardBody.swift in Sources */,
B8559B6C2CEBE9640014AE47 /* BTCreditCardGraphQLBody.swift in Sources */,
BEFE9A3B29C8EC6B00BF69AB /* BTCardClient.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
2 changes: 1 addition & 1 deletion IntegrationTests/BTCardClient_IntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class BTCardClient_IntegrationTests: XCTestCase {
expectation.fulfill()
}

waitForExpectations(timeout: 5)
waitForExpectations(timeout: 10)
}

func testTokenizeCard_whenUsingVersionThreeClientTokenAndCardHasValidationEnabledAndCardIsValid_tokenizesSuccessfully() {
Expand Down
122 changes: 95 additions & 27 deletions Sources/BraintreeCard/BTCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,44 +80,112 @@ import Foundation

// MARK: - Internal Methods

func parameters() -> [String: Any] {
var cardDictionary: [String: Any] = buildCardDictionary(isGraphQL: false)
let billingAddressDictionary: [String: String] = buildBillingAddressDictionary(isGraphQL: false)

if !billingAddressDictionary.isEmpty {
cardDictionary["billing_address"] = billingAddressDictionary
}
func parameters() -> BTCreditCardBody.CreditCard {
let cardBody = creditCardParams()

cardBody.billingAddress = billingAddress()
cardBody.options = BTCreditCardBody.CreditCard.Options(validate: shouldValidate)

return cardBody
}

let options: [String: Bool] = ["validate": shouldValidate]
cardDictionary["options"] = options
return cardDictionary
private func creditCardParams() -> BTCreditCardBody.CreditCard {
BTCreditCardBody.CreditCard(
number: number,
expirationMonth: expirationMonth,
cvv: cvv,
expirationYear: expirationYear,
cardHolderName: cardholderName
)
}

func graphQLParameters() -> [String: Any] {
var cardDictionary: [String: Any] = buildCardDictionary(isGraphQL: true)
let billingAddressDictionary: [String: String] = buildBillingAddressDictionary(isGraphQL: true)
private func billingAddress() -> BTCreditCardBody.CreditCard.BillingAddress {
BTCreditCardBody.CreditCard.BillingAddress(
firstName: firstName,
lastName: lastName,
company: company,
postalCode: postalCode,
streetAddress: streetAddress,
extendedAddress: extendedAddress,
locality: locality,
region: region,
countryName: countryName,
countryCodeAlpha2: countryCodeAlpha2,
countryCodeAlpha3: countryCodeAlpha3,
countryCodeNumeric: countryCodeNumeric
)
}

if !billingAddressDictionary.isEmpty {
cardDictionary["billingAddress"] = billingAddressDictionary
func graphQLParameters() -> BTCreditCardGraphQLBody {
let cardBody = BTCreditCardGraphQLBody.Variables.Input.CreditCard(
number: number,
expirationMonth: expirationMonth,
cvv: cvv,
expirationYear: expirationYear,
cardHolderName: cardholderName
)

if firstName == nil {
cardBody.billingAddress = BTCreditCardGraphQLBody.Variables.Input.CreditCard.BillingAddress(
firstName: firstName,
lastName: lastName,
company: company,
postalCode: postalCode,
streetAddress: streetAddress,
extendedAddress: extendedAddress,
locality: locality,
region: region,
countryName: countryName,
countryCodeAlpha2: countryCodeAlpha2,
countryCodeAlpha3: countryCodeAlpha3,
countryCodeNumeric: countryCodeNumeric
)
}

let options: [String: Bool] = ["validate": shouldValidate]
let inputDictionary: [String: Any] = ["creditCard": cardDictionary, "options": options]
var variables: [String: Any] = ["input": inputDictionary]


let options = BTCreditCardGraphQLBody.Variables.Input.Options(validate: shouldValidate)

let input = BTCreditCardGraphQLBody.Variables.Input(
creditCard: cardBody,
options: options
)

let variables = BTCreditCardGraphQLBody.Variables(input: input)

if authenticationInsightRequested {
if let merchantAccountID {
variables["authenticationInsightInput"] = ["merchantAccountId": merchantAccountID]
} else {
variables["authenticationInsightInput"] = [:]
let merchantAccountID = BTCreditCardGraphQLBody
.Variables
.Input
.AuthenticationInsightInput(
merchantAccountId: merchantAccountID
)

input.authenticationInsightInput = merchantAccountID
}
}

let body = BTCreditCardGraphQLBody(
variables: variables,
query: cardTokenizationGraphQLMutation(),
operationName: "TokenizeCreditCard"
)

return [
"operationName": "TokenizeCreditCard",
"query": cardTokenizationGraphQLMutation(),
"variables": variables
]
inspectEncodable(body)
return body
}

func inspectEncodable<T: Encodable>(_ object: T) {
do {
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys] // Optional formatting
let jsonData = try encoder.encode(object)
if let jsonString = String(data: jsonData, encoding: .utf8) {
print("Encoded Object:\n\(jsonString)")
}
} catch {
print("Failed to encode object: \(error)")
}
}

// MARK: - Private Methods
Expand Down
32 changes: 17 additions & 15 deletions Sources/BraintreeCard/BTCardClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,24 +136,26 @@ import BraintreeCore
return false
}

private func clientAPIParameters(for card: BTCard) -> [String: Any] {
var parameters: [String: Any] = [:]
parameters["credit_card"] = card.parameters()

let metadata: [String: String] = [
"source": apiClient.metadata.source.stringValue,
"integration": apiClient.metadata.integration.stringValue,
"sessionId": apiClient.metadata.sessionID
]

parameters["_meta"] = metadata

private func clientAPIParameters(for card: BTCard) -> BTCreditCardBody {

let creditCardBody = BTCreditCardBody()
let meta = BTCreditCardBody.Meta(
integration: apiClient.metadata.integration.stringValue,
source: apiClient.metadata.source.stringValue,
sessionId: apiClient.metadata.sessionID
)
creditCardBody.meta = meta
if card.authenticationInsightRequested {
parameters["authenticationInsight"] = true
parameters["merchantAccountId"] = card.merchantAccountID
creditCardBody.authenticationInsight = true
creditCardBody.merchantAccountId = card.merchantAccountID
}

creditCardBody.creditCard = card.parameters()

return parameters
return creditCardBody
}

// MARK: - Error Construction Methods
Expand Down
149 changes: 149 additions & 0 deletions Sources/BraintreeCard/BTCreditCardBody.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// swiftlint:disable all
import Foundation

class BTCreditCardBody: NSObject, Encodable {
var authenticationInsight: Bool?
var merchantAccountId: String?
var meta: Meta?
var creditCard: CreditCard?

private var usesGraphQL: Bool

enum CodingKeys: String, CodingKey {
case authenticationInsight
case meta = "_meta"
case merchantAccountId
case creditCard = "credit_card"
}

init(
authenticationInsight: Bool? = nil,
merchantAccountId: String? = nil,
meta: Meta? = nil,
creditCard: CreditCard? = nil,
usesGraphQL: Bool = false
) {
self.authenticationInsight = authenticationInsight
self.merchantAccountId = merchantAccountId
self.meta = meta
self.creditCard = creditCard
self.usesGraphQL = usesGraphQL

}

class Meta: Encodable {
var integration: String
var source: String
var sessionId: String

init(integration: String, source: String, sessionId: String) {
self.integration = integration
self.source = source
self.sessionId = sessionId
}
}

class CreditCard: Encodable {
var billingAddress: BillingAddress?
var number: String?
var expirationMonth: String?
var cvv: String?
var options: Options?
var expirationYear: String?
var cardHolderName: String?

init(
billingAddress: BillingAddress? = nil,
number: String?,
expirationMonth: String?,
cvv: String?,
options: Options? = nil,
expirationYear: String?,
cardHolderName: String?
) {
self.billingAddress = billingAddress
self.number = number
self.cvv = cvv
self.options = options
self.expirationMonth = expirationMonth
self.expirationYear = expirationYear
self.cardHolderName = cardHolderName
}

enum CodingKeys: String, CodingKey {
case billingAddress = "billing_address"
case number
case expirationMonth = "expiration_month"
case cvv
case options
case expirationYear = "expiration_year"
case cardHolderName = "cardholder_name"
}

class BillingAddress: Encodable {
var firstName: String?
var lastName: String?
var company: String?
var postalCode: String?
var streetAddress: String?
var extendedAddress: String?
var locality: String?
var region: String?
var countryName: String?
var countryCodeAlpha2: String?
var countryCodeAlpha3: String?
var countryCodeNumeric: String?

init(
firstName: String?,
lastName: String?,
company: String?,
postalCode: String?,
streetAddress: String?,
extendedAddress: String?,
locality: String?,
region: String?,
countryName: String?,
countryCodeAlpha2: String?,
countryCodeAlpha3: String?,
countryCodeNumeric: String?
) {
self.firstName = firstName
self.lastName = lastName
self.company = company
self.postalCode = postalCode
self.streetAddress = streetAddress
self.extendedAddress = extendedAddress
self.locality = locality
self.region = region
self.countryName = countryName
self.countryCodeAlpha2 = countryCodeAlpha2
self.countryCodeAlpha3 = countryCodeAlpha3
self.countryCodeNumeric = countryCodeNumeric
}

enum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName = "last_name"
case company
case postalCode = "postal_code"
case streetAddress = "street_address"
case extendedAddress = "extended_address"
case locality
case region
case countryName = "country_name"
case countryCodeAlpha2 = "country_code_alpha2"
case countryCodeAlpha3 = "country_code_alpha3"
case countryCodeNumeric = "country_code_numeric"
}
}

class Options: Encodable {
var validate: Bool

init(validate: Bool) {
self.validate = validate
}
}
}
}
Loading
Loading