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

[V7] Update BTCard #1443

Merged
merged 31 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
096741e
Include all properties in public init
richherrera Oct 22, 2024
d4db0c9
Make all properties internal
richherrera Oct 22, 2024
56f49c8
Update BTCard tests
richherrera Oct 22, 2024
862763a
Update BTVardClient tests
richherrera Oct 22, 2024
2aa76c8
Update Integration tests
richherrera Oct 22, 2024
bab98ba
Update CardHelper newCard method
richherrera Oct 22, 2024
54a08af
Update demo app Amex View Controller
richherrera Oct 22, 2024
5cd4420
Update CHANGELOG
richherrera Oct 22, 2024
36dd53f
Update V7 readme
richherrera Oct 22, 2024
0e4df32
Move property docstrings to the init method
richherrera Oct 22, 2024
8724b64
Add optional on postalCode docstring
richherrera Oct 23, 2024
f0351a3
Merge branch 'v7' into update-btcard-parameters
richherrera Oct 23, 2024
6b1c65d
Fix code snippet
richherrera Oct 23, 2024
7f6c766
Merge branch 'v7' into update-btcard-parameters
richherrera Oct 23, 2024
bc42546
Add the missing commas in the documentation example
richherrera Oct 23, 2024
618dc37
Remove optional for required properties, change boolean to let
richherrera Oct 29, 2024
d62f43d
Make return optional for static method card helper
richherrera Oct 29, 2024
727ce54
Add message when user uses optional card helper method
richherrera Oct 29, 2024
c96278d
Fix BTCardTests
richherrera Oct 29, 2024
f4628db
Fix BTCardClient tests
richherrera Oct 29, 2024
a578f4a
Fix BTCardClient integration tests
richherrera Oct 29, 2024
c127063
Remove unnecessary code sample
richherrera Oct 29, 2024
dfb1c5e
Address PR feedback
richherrera Oct 30, 2024
8d74df5
Merge branch 'v7' into update-btcard-parameters
richherrera Nov 6, 2024
759bfc6
Remove cvv note
richherrera Nov 6, 2024
ecd23fc
Merge branch 'v7' into update-btcard-parameters
richherrera Nov 8, 2024
bb9cccb
Merge branch 'v7' into update-btcard-parameters
richherrera Nov 14, 2024
b9744d3
Add init with cvv
richherrera Nov 14, 2024
8e192df
Update amex integration test
richherrera Nov 14, 2024
6ae1adb
Update docstring and empty validation
richherrera Nov 19, 2024
65cadd5
Update UTs
richherrera Nov 19, 2024
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## unreleased
* BraintreePayPal
* Add `BTPayPalRequest.userPhoneNumber` optional property
* BraintreeCard
* Update `BTCard` to make all properties accessible on the initializer only vs via the dot syntax.

## 6.24.0 (2024-10-15)
* BraintreePayPal
Expand Down
11 changes: 6 additions & 5 deletions Demo/Application/Features/AmexViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ class AmexViewController: PaymentButtonBaseViewController {
}

private func getRewards(for cardNumber: String) {
let card = BTCard()
card.number = cardNumber
card.expirationMonth = "12"
card.expirationYear = CardHelpers.generateFuture(.year)
card.cvv = "1234"
let card = BTCard(
number: cardNumber,
expirationMonth: "12",
expirationYear: CardHelpers.generateFuture(.year),
cvv: "1234"
)

progressBlock("Tokenizing Card")

Expand Down
25 changes: 6 additions & 19 deletions Demo/Application/Features/Helpers/CardHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,12 @@ enum Format {
enum CardHelpers {

static func newCard(from cardFormView: BTCardFormView) -> BTCard {
let card = BTCard()

if let cardNumber = cardFormView.cardNumber {
card.number = cardNumber
}

if let expirationYear = cardFormView.expirationYear {
card.expirationYear = expirationYear
}

if let expirationMonth = cardFormView.expirationMonth {
card.expirationMonth = expirationMonth
}

if let cvv = cardFormView.cvv {
card.cvv = cvv
}

return card
BTCard(
number: cardFormView.cardNumber,
expirationMonth: cardFormView.expirationMonth,
expirationYear: cardFormView.expirationYear,
cvv: cardFormView.cvv
)
}

static func generateFuture(_ format: Format) -> String {
Expand Down
34 changes: 18 additions & 16 deletions IntegrationTests/BTCardClient_IntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ class BTCardClient_IntegrationTests: XCTestCase {
func testTokenizeCard_whenCardIsInvalidAndValidationIsEnabled_failsWithExpectedValidationError() {
let apiClient = BTAPIClient(authorization: BTIntegrationTestsConstants.sandboxClientToken)!
let cardClient = BTCardClient(apiClient: apiClient)
let card = BTCard()
card.number = "123"
card.expirationMonth = "12"
card.expirationYear = Helpers.shared.futureYear()
card.shouldValidate = true
let card = BTCard(
number: "123",
expirationMonth: "12",
expirationYear: Helpers.shared.futureYear(),
shouldValidate: true
)

let expectation = expectation(description: "Tokenize card")

Expand Down Expand Up @@ -160,8 +161,7 @@ class BTCardClient_IntegrationTests: XCTestCase {
func testTokenizeCard_withCVVOnly_tokenizesSuccessfully() {
let apiClient = BTAPIClient(authorization: BTIntegrationTestsConstants.sandboxClientTokenVersion3)!
let cardClient = BTCardClient(apiClient: apiClient)
let card = BTCard()
card.cvv = "123"
let card = BTCard(cvv: "123")

let expectation = expectation(description: "Tokenize card")

Expand All @@ -182,19 +182,21 @@ class BTCardClient_IntegrationTests: XCTestCase {
// MARK: - Private Helper Methods

func invalidCard() -> BTCard {
let card = BTCard()
card.number = "123123"
card.expirationMonth = "XX"
card.expirationYear = "XXXX"
let card = BTCard(
number: "123123",
expirationMonth: "XX",
expirationYear: "XXXX"
)
return card
}

func validCard() -> BTCard {
let card = BTCard()
card.number = "4111111111111111"
card.expirationMonth = "12"
card.expirationYear = Helpers.shared.futureYear()
card.cardholderName = "Cookie Monster"
let card = BTCard(
number: "4111111111111111",
expirationMonth: "12",
expirationYear: Helpers.shared.futureYear(),
cardholderName: "Cookie Monster"
)
return card
}
}
175 changes: 101 additions & 74 deletions Sources/BraintreeCard/BTCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,107 @@ import Foundation
/// Its main purpose is to serve as the input for tokenization.
@objcMembers public class BTCard: NSObject {

// MARK: - Public Properties

/// The card number
public var number: String?

/// The expiration month as a one or two-digit number on the Gregorian calendar
public var expirationMonth: String?

/// The expiration year as a two or four-digit number on the Gregorian calendar
public var expirationYear: String?

/// The card verification code (like CVV or CID).
/// - Note: If you wish to create a CVV-only payment method nonce to verify a card already stored in your Vault,
/// omit all other properties to only collect CVV.
public var cvv: String?

/// The postal code associated with the card's billing address
public var postalCode: String?

/// Optional: the cardholder's name.
public var cardholderName: String?

/// Optional: first name on the card.
public var firstName: String?

/// Optional: last name on the card.
public var lastName: String?

/// Optional: company name associated with the card.
public var company: String?

/// Optional: the street address associated with the card's billing address
public var streetAddress: String?

/// Optional: the extended address associated with the card's billing address
public var extendedAddress: String?

/// Optional: the city associated with the card's billing address
public var locality: String?

/// Optional: either a two-letter state code (for the US), or an ISO-3166-2 country subdivision code of up to three letters.
public var region: String?

/// Optional: the country name associated with the card's billing address.
/// - Note: Braintree only accepts specific country names.
/// - SeeAlso: https://developer.paypal.com/braintree/docs/reference/general/countries#list-of-countries
public var countryName: String?

/// Optional: the ISO 3166-1 alpha-2 country code specified in the card's billing address.
/// - Note: Braintree only accepts specific alpha-2 values.
/// - SeeAlso: https://developer.paypal.com/braintree/docs/reference/general/countries#list-of-countries
public var countryCodeAlpha2: String?

/// Optional: the ISO 3166-1 alpha-3 country code specified in the card's billing address.
/// - Note: Braintree only accepts specific alpha-3 values.
/// - SeeAlso: https://developer.paypal.com/braintree/docs/reference/general/countries#list-of-countries
public var countryCodeAlpha3: String?

/// Optional: The ISO 3166-1 numeric country code specified in the card's billing address.
/// - Note: Braintree only accepts specific numeric values.
/// - SeeAlso: https://developer.paypal.com/braintree/docs/reference/general/countries#list-of-countries
public var countryCodeNumeric: String?

/// Controls whether or not to return validations and/or verification results. By default, this is not enabled.
/// - Note: Use this flag with caution. By enabling client-side validation, certain tokenize card requests may result in adding the card to the vault.
/// These semantics are not currently documented.
public var shouldValidate: Bool = false

/// Optional: If authentication insight is requested. If this property is set to true, a `merchantAccountID` must be provided. Defaults to false.
public var authenticationInsightRequested: Bool = false

/// Optional: The merchant account ID.
public var merchantAccountID: String?

// MARK: - Internal Properties

let number: String?
let expirationMonth: String?
let expirationYear: String?
let cvv: String?
let postalCode: String?
let cardholderName: String?
let firstName: String?
let lastName: String?
let company: String?
let streetAddress: String?
let extendedAddress: String?
let locality: String?
let region: String?
let countryName: String?
let countryCodeAlpha2: String?
let countryCodeAlpha3: String?
let countryCodeNumeric: String?
var shouldValidate: Bool = false
var authenticationInsightRequested: Bool = false
let merchantAccountID: String?

// MARK: - Initializer

/// Creates a Card
/// - Parameters:
/// - number: The card number.
/// - expirationMonth: The expiration month as a one or two-digit number on the Gregorian calendar.
/// - expirationYear:The expiration year as a two or four-digit number on the Gregorian calendar.
/// - cvv: The card verification code (like CVV or CID).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on this doc string I wonder if we should have 2 inits:

  1. CVV only init
  2. Init with number, expirationMonth, expirationYear, CVV, and postal code required

What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though I don't think postal code is actually required. We can confirm with the gateway and if not we should update the docstring to denote optional.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After confirming with the Cards team, the only required parameters are number, expirationMonth, expirationYear, and cvv, the others are optional. Regarding overloading initializers, it’s not necessary, after the latest changes I made

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, does this mean that we no longer support CVV only nonces for validation? Based on this docs string If you wish to create a CVV-only payment method nonce to verify a card already stored in your Vault, omit all other properties to only collect CVV. was the intent for an overload. If this is no longer supported we should remove that note since currently this would not be possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Jax, for the heads up! I’ve added a new initializer with CVV as the only parameter. As you mentioned, this is only to verify cards that are already stored, let me know what do you think.

/// - Note: If you wish to create a CVV-only payment method nonce to verify a card already stored in your Vault, omit all other properties to only collect CVV.
/// - postalCode: The postal code associated with the card's billing address.
/// - cardholderName: Optional: the cardholder's name.
/// - firstName: Optional: first name on the card.
/// - lastName: Optional: last name on the card.
/// - company: Optional: company name associated with the card.
/// - streetAddress: Optional: the street address associated with the card's billing address.
/// - extendedAddress: Optional: the extended address associated with the card's billing address.
/// - locality: Optional: the city associated with the card's billing address.
/// - region: Optional: either a two-letter state code (for the US), or an ISO-3166-2 country subdivision code of up to three letters.
/// - countryName: Optional: the country name associated with the card's billing address.
/// - Note: Braintree only accepts specific country names.
/// - SeeAlso: https://developer.paypal.com/braintree/docs/reference/general/countries#list-of-countries
/// - countryCodeAlpha2: Optional: the ISO 3166-1 alpha-2 country code specified in the card's billing address.
/// - Note: Braintree only accepts specific alpha-2 values.
/// - SeeAlso: https://developer.paypal.com/braintree/docs/reference/general/countries#list-of-countries
/// - countryCodeAlpha3: Optional: the ISO 3166-1 alpha-3 country code specified in the card's billing address.
/// - Note: Braintree only accepts specific alpha-3 values.
/// - SeeAlso: https://developer.paypal.com/braintree/docs/reference/general/countries#list-of-countries
/// - countryCodeNumeric: Optional: The ISO 3166-1 numeric country code specified in the card's billing address.
/// - Note: Braintree only accepts specific numeric values.
/// - SeeAlso: https://developer.paypal.com/braintree/docs/reference/general/countries#list-of-countries
/// - shouldValidate: Controls whether or not to return validations and/or verification results. By default, this is not enabled.
/// - Note: Use this flag with caution. By enabling client-side validation, certain tokenize card requests may result in adding the card to the vault. These semantics are not currently documented.
/// - authenticationInsightRequested: Optional: If authentication insight is requested. If this property is set to `true`, a `merchantAccountID` must be provided. Defaults to `false`.
/// - merchantAccountID: Optional: The merchant account ID.
public init(
number: String? = nil,
expirationMonth: String? = nil,
expirationYear: String? = nil,
cvv: String? = nil,
postalCode: String? = nil,
cardholderName: String? = nil,
firstName: String? = nil,
lastName: String? = nil,
company: String? = nil,
streetAddress: String? = nil,
extendedAddress: String? = nil,
locality: String? = nil,
region: String? = nil,
countryName: String? = nil,
countryCodeAlpha2: String? = nil,
countryCodeAlpha3: String? = nil,
countryCodeNumeric: String? = nil,
shouldValidate: Bool = false,
authenticationInsightRequested: Bool = false,
merchantAccountID: String? = nil
) {
self.number = number
self.expirationMonth = expirationMonth
self.expirationYear = expirationYear
self.cvv = cvv
self.postalCode = postalCode
self.cardholderName = cardholderName
self.firstName = firstName
self.lastName = lastName
self.company = company
self.streetAddress = streetAddress
self.extendedAddress = extendedAddress
self.locality = locality
self.region = region
self.countryName = countryName
self.countryCodeAlpha2 = countryCodeAlpha2
self.countryCodeAlpha3 = countryCodeAlpha3
self.countryCodeNumeric = countryCodeNumeric
self.shouldValidate = shouldValidate
self.authenticationInsightRequested = authenticationInsightRequested
self.merchantAccountID = merchantAccountID
}

// MARK: - Internal Methods

func parameters() -> [String: Any] {
Expand Down
Loading