Skip to content

Commit

Permalink
Merge branch 'v7' into update-localPayment
Browse files Browse the repository at this point in the history
  • Loading branch information
richherrera committed Oct 23, 2024
2 parents 219dca9 + f4906be commit b9d16f1
Show file tree
Hide file tree
Showing 18 changed files with 224 additions and 147 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
* Bump minimum supported deployment target to iOS 16+
* BraintreeVenmo
* Update `BTVenmoRequest` to make all properties accessible on the initializer only vs via the dot syntax.
* BraintreeSEPADirectDebit
* Update `BTSEPADirectDebitRequest` to make all properties accessible on the initializer only vs via the dot syntax.
* BraintreeLocalPayment
* Update `BTLocalPaymentRequest` to make all properties accessible on the initializer only vs via the dot syntax.

## unreleased
* BraintreePayPal
* Add `BTPayPalRequest.userPhoneNumber` optional property
* BraintreeVenmo
* Send `url` in `event_params` for App Switch events to PayPal's analytics service (FPTI)

## 6.24.0 (2024-10-15)
* BraintreePayPal
Expand Down
42 changes: 42 additions & 0 deletions Demo/Application/Features/Helpers/Toggle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import UIKit

class Toggle: UIView {

private let toggle = UISwitch()
private let title: String

var isOn: Bool {
toggle.isOn
}

init(title: String) {
self.title = title
super.init(frame: .zero)
layoutUI()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private func layoutUI() {
let label = UILabel()
label.text = title
label.translatesAutoresizingMaskIntoConstraints = false

toggle.translatesAutoresizingMaskIntoConstraints = false

let stackView = UIStackView(arrangedSubviews: [label, toggle])
stackView.alignment = .fill
stackView.distribution = .fill
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)

NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
stackView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor),
stackView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor)
])
}
}
61 changes: 32 additions & 29 deletions Demo/Application/Features/PayPalWebCheckoutViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,18 @@ class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {
lazy var emailTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "[email protected]"
textField.textAlignment = .right
textField.backgroundColor = .systemBackground
textField.keyboardType = .emailAddress
return textField
}()

lazy var emailStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [emailLabel, emailTextField])
stackView.distribution = .fillProportionally
return stackView
}()

lazy var countryCodeLabel: UILabel = {
let label = UILabel()
label.text = "Country Code:"
Expand All @@ -33,11 +41,18 @@ class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {
lazy var countryCodeTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "1"
textField.textAlignment = .right
textField.backgroundColor = .systemBackground
textField.keyboardType = .phonePad
return textField
}()

lazy var countryCodeStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [countryCodeLabel, countryCodeTextField])
stackView.distribution = .fillEqually
return stackView
}()

lazy var nationalNumberLabel: UILabel = {
let label = UILabel()
label.text = "National Number:"
Expand All @@ -47,40 +62,26 @@ class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {
lazy var nationalNumberTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "000-000-000"
textField.textAlignment = .right
textField.backgroundColor = .systemBackground
textField.keyboardType = .phonePad
return textField
}()

lazy var payLaterToggleLabel: UILabel = {
let label = UILabel()
label.text = "Offer Pay Later"
label.font = .preferredFont(forTextStyle: .footnote)
return label
}()

let payLaterToggle = UISwitch()

lazy var newPayPalCheckoutToggleLabel: UILabel = {
let label = UILabel()
label.text = "New PayPal Checkout Experience"
label.font = .preferredFont(forTextStyle: .footnote)
return label
lazy var nationalNumberStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [nationalNumberLabel, nationalNumberTextField])
stackView.distribution = .fillEqually
return stackView
}()

let newPayPalCheckoutToggle = UISwitch()
let payLaterToggle = Toggle(title: "Offer Pay Later")

lazy var rbaDataToggleLabel: UILabel = {
let label = UILabel()
label.text = "Recurring Billing (RBA) Data"
label.font = .preferredFont(forTextStyle: .footnote)
return label
}()
let newPayPalCheckoutToggle = Toggle(title: "New PayPal Checkout Experience")

let rbaDataToggle = UISwitch()
let rbaDataToggle = Toggle(title: "Recurring Billing (RBA) Data")

override func viewDidLoad() {
super.heightConstraint = 350
super.heightConstraint = 400
super.viewDidLoad()
}

Expand All @@ -90,20 +91,22 @@ class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {
let payPalAppSwitchButton = createButton(title: "PayPal App Switch", action: #selector(tappedPayPalAppSwitch))

let oneTimeCheckoutStackView = buttonsStackView(label: "1-Time Checkout", views: [
UIStackView(arrangedSubviews: [payLaterToggleLabel, payLaterToggle]),
UIStackView(arrangedSubviews: [newPayPalCheckoutToggleLabel, newPayPalCheckoutToggle]),
payLaterToggle,
newPayPalCheckoutToggle,
payPalCheckoutButton
])
oneTimeCheckoutStackView.spacing = 12
let vaultStackView = buttonsStackView(label: "Vault", views: [
UIStackView(arrangedSubviews: [rbaDataToggleLabel, rbaDataToggle]),
rbaDataToggle,
payPalVaultButton,
payPalAppSwitchButton
])
vaultStackView.spacing = 12

let stackView = UIStackView(arrangedSubviews: [
UIStackView(arrangedSubviews: [emailLabel, emailTextField]),
UIStackView(arrangedSubviews: [countryCodeLabel, countryCodeTextField]),
UIStackView(arrangedSubviews: [nationalNumberLabel, nationalNumberTextField]),
emailStackView,
countryCodeStackView,
nationalNumberStackView,
oneTimeCheckoutStackView,
vaultStackView
])
Expand Down
15 changes: 8 additions & 7 deletions Demo/Application/Features/SEPADirectDebitViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@ class SEPADirectDebitViewController: PaymentButtonBaseViewController {
billingAddress.postalCode = "09456"
billingAddress.countryCodeAlpha2 = "FR"

let sepaDirectDebitRequest = BTSEPADirectDebitRequest()
sepaDirectDebitRequest.accountHolderName = "John Doe"
sepaDirectDebitRequest.iban = BTSEPADirectDebitTestHelper.generateValidSandboxIBAN()
sepaDirectDebitRequest.customerID = generateRandomCustomerID()
sepaDirectDebitRequest.mandateType = .oneOff
sepaDirectDebitRequest.billingAddress = billingAddress
sepaDirectDebitRequest.merchantAccountID = "EUR-sepa-direct-debit"
let sepaDirectDebitRequest = BTSEPADirectDebitRequest(
accountHolderName: "John Doe",
iban: BTSEPADirectDebitTestHelper.generateValidSandboxIBAN(),
customerID: generateRandomCustomerID(),
billingAddress: billingAddress,
mandateType: .oneOff,
merchantAccountID: "EUR-sepa-direct-debit"
)

sepaDirectDebitClient.tokenize(sepaDirectDebitRequest) { sepaDirectDebitNonce, error in
if let sepaDirectDebitNonce {
Expand Down
52 changes: 11 additions & 41 deletions Demo/Application/Features/VenmoViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ class VenmoViewController: PaymentButtonBaseViewController {

// swiftlint:disable:next implicitly_unwrapped_optional
var venmoClient: BTVenmoClient!

let webFallbackToggle = Toggle(title: "Enable Web Fallback")
let vaultToggle = Toggle(title: "Vault")

override func viewDidLoad() {
super.viewDidLoad()
Expand All @@ -14,13 +17,11 @@ class VenmoViewController: PaymentButtonBaseViewController {

override func createPaymentButton() -> UIView {
let venmoButton = createButton(title: "Venmo", action: #selector(tappedVenmo))
let venmoECDButton = createButton(title: "Venmo (with ECD options)", action: #selector(tappedVenmoWithECD))
let venmoUniversalLinkButton = createButton(title: "Venmo Universal Links", action: #selector(tappedVenmoWithUniversalLinks))

let stackView = UIStackView(arrangedSubviews: [venmoButton, venmoECDButton, venmoUniversalLinkButton])
let stackView = UIStackView(arrangedSubviews: [webFallbackToggle, vaultToggle, venmoButton])
stackView.axis = .vertical
stackView.spacing = 5
stackView.alignment = .center
stackView.spacing = 15
stackView.alignment = .fill
stackView.distribution = .fillEqually
stackView.translatesAutoresizingMaskIntoConstraints = false

Expand All @@ -30,48 +31,17 @@ class VenmoViewController: PaymentButtonBaseViewController {
@objc func tappedVenmo() {
self.progressBlock("Tapped Venmo - initiating Venmo auth")

let venmoRequest = BTVenmoRequest(paymentMethodUsage: .multiUse, vault: true)

checkout(request: venmoRequest)
}

@objc func tappedVenmoWithECD() {
self.progressBlock("Tapped Venmo ECD - initiating Venmo auth")

let lineItem = BTVenmoLineItem(quantity: 1, unitAmount: "30.00", name: "item-1", kind: .debit)
lineItem.unitTaxAmount = "1.00"

let isWebFallbackEnabled = webFallbackToggle.isOn
let isVaultingEnabled = vaultToggle.isOn
let venmoRequest = BTVenmoRequest(
paymentMethodUsage: .multiUse,
vault: true,
collectCustomerBillingAddress: true,
collectCustomerShippingAddress: true,
discountAmount: "1.10",
taxAmount: "1.10",
shippingAmount: "0.00",
totalAmount: "30.00",
lineItems: [lineItem]
vault: isVaultingEnabled,
fallbackToWeb: isWebFallbackEnabled
)

checkout(request: venmoRequest)
}

@objc func tappedVenmoWithUniversalLinks() {
self.progressBlock("Tapped Venmo Universal Links - initiating Venmo auth")

let venmoRequest = BTVenmoRequest(
paymentMethodUsage: .multiUse,
vault: true,
fallbackToWeb: true
)

checkout(request: venmoRequest)
}

func checkout(request: BTVenmoRequest) {
Task {
do {
let venmoAccount = try await venmoClient.tokenize(request)
let venmoAccount = try await venmoClient.tokenize(venmoRequest)
progressBlock("Got a nonce 💎!")
completionBlock(venmoAccount)
} catch {
Expand Down
4 changes: 4 additions & 0 deletions Demo/Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
57108A172832EA04004EB870 /* BraintreePayPalNativeCheckout.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57108A162832EA04004EB870 /* BraintreePayPalNativeCheckout.framework */; };
57108A182832EA04004EB870 /* BraintreePayPalNativeCheckout.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 57108A162832EA04004EB870 /* BraintreePayPalNativeCheckout.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
62240F5A2B67FE1100ECE5C9 /* TextFieldWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62240F592B67FE1100ECE5C9 /* TextFieldWithLabel.swift */; };
8025988D2CC1A95400E7D898 /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8025988C2CC1A95400E7D898 /* Toggle.swift */; };
8028B9762B28C9E100C88CE8 /* ShopperInsightsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8028B9752B28C9E100C88CE8 /* ShopperInsightsViewController.swift */; };
8028B9782B28D42400C88CE8 /* BraintreeShopperInsights.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8028B9772B28D42400C88CE8 /* BraintreeShopperInsights.framework */; };
8028B9792B28D42400C88CE8 /* BraintreeShopperInsights.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8028B9772B28D42400C88CE8 /* BraintreeShopperInsights.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -148,6 +149,7 @@
62240F592B67FE1100ECE5C9 /* TextFieldWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldWithLabel.swift; sourceTree = "<group>"; };
6D23244B5E9EE59BAB3F3003 /* Pods_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
73498B4265CA7D315E2FBF38 /* Pods-Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.debug.xcconfig"; path = "Target Support Files/Pods-Demo/Pods-Demo.debug.xcconfig"; sourceTree = "<group>"; };
8025988C2CC1A95400E7D898 /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = "<group>"; };
8028B9752B28C9E100C88CE8 /* ShopperInsightsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShopperInsightsViewController.swift; sourceTree = "<group>"; };
8028B9772B28D42400C88CE8 /* BraintreeShopperInsights.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BraintreeShopperInsights.framework; sourceTree = BUILT_PRODUCTS_DIR; };
803D64EA256DAF9A00ACE692 /* BraintreeAmericanExpress.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BraintreeAmericanExpress.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -293,6 +295,7 @@
BEAAAD042970A70D000BD296 /* BTSEPADirectDebitTestHelper.swift */,
BED461822AD072D9001B0DDF /* CardHelpers.swift */,
62240F592B67FE1100ECE5C9 /* TextFieldWithLabel.swift */,
8025988C2CC1A95400E7D898 /* Toggle.swift */,
);
path = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -709,6 +712,7 @@
BEBD52832AAB62FB005D6687 /* ApplePayViewController.swift in Sources */,
BEAAAD052970A70D000BD296 /* BTSEPADirectDebitTestHelper.swift in Sources */,
809E86A62AD00AF4004998B0 /* AppDelegate.swift in Sources */,
8025988D2CC1A95400E7D898 /* Toggle.swift in Sources */,
A0988F9224DB44B20095EEEE /* BraintreeDemoSettings.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
10 changes: 0 additions & 10 deletions Demo/UI Tests/Venmo UI Tests/Venmo_UITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,6 @@ class Venmo_UITests: XCTestCase {
XCTAssertTrue(demoApp.buttons["Got a nonce. Tap to make a transaction."].waitForExistence(timeout: 30))
}

func testTokenizeVenmo_withECDOptions_whenSignInSuccessfulWithPaymentContext_returnsNonce() {
waitForElementToBeHittable(demoApp.buttons["Venmo (with ECD options)"])
demoApp.buttons["Venmo (with ECD options)"].tap()

waitForElementToBeHittable(mockVenmo.buttons["SUCCESS WITH PAYMENT CONTEXT"])
mockVenmo.buttons["SUCCESS WITH PAYMENT CONTEXT"].tap()

XCTAssertTrue(demoApp.buttons["Got a nonce. Tap to make a transaction."].waitForExistence(timeout: 30))
}

func testTokenizeVenmo_whenSignInSuccessfulWithoutPaymentContext_returnsNonce() {
waitForElementToBeHittable(demoApp.buttons["Venmo"])
demoApp.buttons["Venmo"].tap()
Expand Down
4 changes: 4 additions & 0 deletions Sources/BraintreeCore/Analytics/FPTIBatchData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct FPTIBatchData: Codable {
/// Encapsulates a single event by it's name and timestamp.
struct Event: Codable {

let appSwitchURL: String?
/// UTC millisecond timestamp when a networking task started establishing a TCP connection. See [Apple's docs](https://developer.apple.com/documentation/foundation/urlsessiontasktransactionmetrics#3162615).
/// `nil` if a persistent connection is used.
let connectionStartTime: Int?
Expand Down Expand Up @@ -61,6 +62,7 @@ struct FPTIBatchData: Codable {
let tenantName: String = "Braintree"

init(
appSwitchURL: URL? = nil,
connectionStartTime: Int? = nil,
correlationID: String? = nil,
endpoint: String? = nil,
Expand All @@ -76,6 +78,7 @@ struct FPTIBatchData: Codable {
requestStartTime: Int? = nil,
startTime: Int? = nil
) {
self.appSwitchURL = appSwitchURL?.absoluteString
self.connectionStartTime = connectionStartTime
self.correlationID = correlationID
self.endpoint = endpoint
Expand All @@ -93,6 +96,7 @@ struct FPTIBatchData: Codable {
}

enum CodingKeys: String, CodingKey {
case appSwitchURL = "url"
case connectionStartTime = "connect_start_time"
case correlationID = "correlation_id"
case errorDescription = "error_desc"
Expand Down
4 changes: 3 additions & 1 deletion Sources/BraintreeCore/BTAPIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -310,10 +310,12 @@ import Foundation
isVaultRequest: Bool? = nil,
linkType: LinkType? = nil,
paymentMethodsDisplayed: String? = nil,
payPalContextID: String? = nil
payPalContextID: String? = nil,
appSwitchURL: URL? = nil
) {
analyticsService.sendAnalyticsEvent(
FPTIBatchData.Event(
appSwitchURL: appSwitchURL,
correlationID: correlationID,
errorDescription: errorDescription,
eventName: eventName,
Expand Down
Loading

0 comments on commit b9d16f1

Please sign in to comment.