Skip to content

Commit

Permalink
Add Buyer Phone Number Sent By Merchant to PayPalRequest (#1304)
Browse files Browse the repository at this point in the history
* add phone number field to paypalrequest

* add small unit sanity tests

* update CHANGELOG.md

* code cleanup

* update param name

* resolve conflicts and update changelog.md

* cleanup

* cleanup and address pr feedback

* fix unit tests

* address pr feedback
  • Loading branch information
agedd authored Oct 17, 2024
1 parent 78c6c12 commit 16a1b2f
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 3 deletions.
4 changes: 4 additions & 0 deletions Braintree.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
6298A1992B91010600E46EDF /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 6298A1982B91010600E46EDF /* PrivacyInfo.xcprivacy */; };
62A659A42B98CB23008DFD67 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 62A659A32B98CB23008DFD67 /* PrivacyInfo.xcprivacy */; };
62A746412B9255AC003D32FF /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 62A746402B9255AC003D32FF /* PrivacyInfo.xcprivacy */; };
62B811882CC002470024A688 /* BTPayPalPhoneNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62B811872CC002470024A688 /* BTPayPalPhoneNumber.swift */; };
62D5EC502B9F6E9D00D09C5D /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 62D5EC4F2B9F6E9D00D09C5D /* PrivacyInfo.xcprivacy */; };
62DE8FBF2B9656BF00F08F53 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 62DE8FBE2B9656BF00F08F53 /* PrivacyInfo.xcprivacy */; };
62EA90492B63071800DD79BC /* BTEligiblePaymentMethods.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EA90482B63071800DD79BC /* BTEligiblePaymentMethods.swift */; };
Expand Down Expand Up @@ -829,6 +830,7 @@
6298A1982B91010600E46EDF /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
62A659A32B98CB23008DFD67 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
62A746402B9255AC003D32FF /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
62B811872CC002470024A688 /* BTPayPalPhoneNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPayPalPhoneNumber.swift; sourceTree = "<group>"; };
62D5EC4F2B9F6E9D00D09C5D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
62DE8FBE2B9656BF00F08F53 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
62EA90482B63071800DD79BC /* BTEligiblePaymentMethods.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTEligiblePaymentMethods.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1401,6 +1403,7 @@
BE349110294B77E100D2CF68 /* BTPayPalVaultRequest.swift */,
62A659A32B98CB23008DFD67 /* PrivacyInfo.xcprivacy */,
807D22F32C29ADA8009FFEA4 /* RecurringBillingMetadata */,
62B811872CC002470024A688 /* BTPayPalPhoneNumber.swift */,
);
path = BraintreePayPal;
sourceTree = "<group>";
Expand Down Expand Up @@ -3285,6 +3288,7 @@
57544F582952298900DEB7B0 /* BTPayPalAccountNonce.swift in Sources */,
8014221C2BAE935B009F9999 /* BTPayPalApprovalURLParser.swift in Sources */,
BE349111294B77E100D2CF68 /* BTPayPalVaultRequest.swift in Sources */,
62B811882CC002470024A688 /* BTPayPalPhoneNumber.swift in Sources */,
807D22F52C29ADE2009FFEA4 /* BTPayPalRecurringBillingPlanType.swift in Sources */,
57544820294A2EBE00DEB7B0 /* BTPayPalCreditFinancing.swift in Sources */,
807D22EE2C29A918009FFEA4 /* BTPayPalRecurringBillingDetails.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
* BraintreePayPal
* Add `BTPayPalRequest.userPhoneNumber` optional property

## 6.24.0 (2024-10-15)
* BraintreePayPal
* Add `BTPayPalRecurringBillingDetails` and `BTPayPalRecurringBillingPlanType` opt-in request objects. Including these details will provide transparency to users on their billing schedule, dates, and amounts, as well as launch a modernized checkout UI.
Expand Down
38 changes: 38 additions & 0 deletions Demo/Application/Features/PayPalWebCheckoutViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,34 @@ class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {
return textField
}()

lazy var countryCodeLabel: UILabel = {
let label = UILabel()
label.text = "Country Code:"
return label
}()

lazy var countryCodeTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "1"
textField.backgroundColor = .systemBackground
textField.keyboardType = .phonePad
return textField
}()

lazy var nationalNumberLabel: UILabel = {
let label = UILabel()
label.text = "National Number:"
return label
}()

lazy var nationalNumberTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "000-000-000"
textField.backgroundColor = .systemBackground
textField.keyboardType = .phonePad
return textField
}()

lazy var payLaterToggleLabel: UILabel = {
let label = UILabel()
label.text = "Offer Pay Later"
Expand Down Expand Up @@ -74,6 +102,8 @@ class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {

let stackView = UIStackView(arrangedSubviews: [
UIStackView(arrangedSubviews: [emailLabel, emailTextField]),
UIStackView(arrangedSubviews: [countryCodeLabel, countryCodeTextField]),
UIStackView(arrangedSubviews: [nationalNumberLabel, nationalNumberTextField]),
oneTimeCheckoutStackView,
vaultStackView
])
Expand Down Expand Up @@ -104,6 +134,10 @@ class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {

let request = BTPayPalCheckoutRequest(amount: "5.00")
request.userAuthenticationEmail = emailTextField.text
request.userPhoneNumber = BTPayPalPhoneNumber(
countryCode: countryCodeTextField.text ?? "",
nationalNumber: nationalNumberTextField.text ?? ""
)

let lineItem = BTPayPalLineItem(quantity: "1", unitAmount: "5.00", name: "item one 1234567", kind: .debit)
lineItem.upcCode = "123456789"
Expand Down Expand Up @@ -135,6 +169,10 @@ class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {

var request = BTPayPalVaultRequest()
request.userAuthenticationEmail = emailTextField.text
request.userPhoneNumber = BTPayPalPhoneNumber(
countryCode: countryCodeTextField.text ?? "",
nationalNumber: nationalNumberTextField.text ?? ""
)

if rbaDataToggle.isOn {
let billingPricing = BTPayPalBillingPricing(
Expand Down
2 changes: 1 addition & 1 deletion Sources/BraintreeCore/Encodable+Dictionary.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

// TODO: - To be removed once entire SDK is formatting POST bodies using Encodable
extension Encodable {
public extension Encodable {

/// Converts to dictionary `[String: Any]` type.
///
Expand Down
21 changes: 21 additions & 0 deletions Sources/BraintreePayPal/BTPayPalPhoneNumber.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Foundation

public struct BTPayPalPhoneNumber: Encodable {

private let countryCode: String
private let nationalNumber: String

private enum CodingKeys: String, CodingKey {
case countryCode = "country_code"
case nationalNumber = "national_number"
}

/// Intialize a `BTPayPalPhoneNumber`
/// - Parameters:
/// - countryCode: The international country code for the shopper's phone number i.e. "1" for US
/// - nationalNumber: The national segment of the shopper's phone number
public init(countryCode: String, nationalNumber: String) {
self.countryCode = countryCode
self.nationalNumber = nationalNumber
}
}
12 changes: 11 additions & 1 deletion Sources/BraintreePayPal/BTPayPalRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ import BraintreeCore
/// :nodoc: Exposed publicly for use by PayPal Native Checkout module. This property is not covered by semantic versioning.
@_documentation(visibility: private)
public var paymentType: BTPayPalPaymentType

/// Optional: A user's phone number to initiate a quicker authentication flow in the scenario where the user has a PayPal account
/// identified with the same phone number.
public var userPhoneNumber: BTPayPalPhoneNumber?

// MARK: - Static Properties

Expand All @@ -115,7 +119,8 @@ import BraintreeCore
merchantAccountID: String? = nil,
lineItems: [BTPayPalLineItem]? = nil,
billingAgreementDescription: String? = nil,
riskCorrelationId: String? = nil
riskCorrelationId: String? = nil,
userPhoneNumber: BTPayPalPhoneNumber? = nil
) {
self.hermesPath = hermesPath
self.paymentType = paymentType
Expand All @@ -129,6 +134,7 @@ import BraintreeCore
self.lineItems = lineItems
self.billingAgreementDescription = billingAgreementDescription
self.riskCorrelationID = riskCorrelationId
self.userPhoneNumber = userPhoneNumber
}

// MARK: Public Methods
Expand Down Expand Up @@ -169,6 +175,10 @@ import BraintreeCore
let lineItemsArray = lineItems.compactMap { $0.requestParameters() }
parameters["line_items"] = lineItemsArray
}

if let userPhoneNumberDict = try? userPhoneNumber?.toDictionary() {
parameters["phone_number"] = userPhoneNumberDict
}

parameters["return_url"] = BTCoreConstants.callbackURLScheme + "://\(BTPayPalRequest.callbackURLHostAndPath)success"
parameters["cancel_url"] = BTCoreConstants.callbackURLScheme + "://\(BTPayPalRequest.callbackURLHostAndPath)cancel"
Expand Down
2 changes: 1 addition & 1 deletion Sources/BraintreePayPal/BTPayPalVaultRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import BraintreeCore
if let userAuthenticationEmail {
baseParameters["payer_email"] = userAuthenticationEmail
}

if let universalLink, enablePayPalAppSwitch, isPayPalAppInstalled {
let appSwitchParameters: [String: Any] = [
"launch_paypal_app": enablePayPalAppSwitch,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class BTPayPalCheckoutRequest_Tests: XCTestCase {
request.billingAgreementDescription = "description"
request.userAction = .payNow
request.userAuthenticationEmail = "[email protected]"
request.userPhoneNumber = BTPayPalPhoneNumber(countryCode: "1", nationalNumber: "4087463271")

let shippingAddress = BTPostalAddress()
shippingAddress.streetAddress = "123 Main"
Expand Down Expand Up @@ -114,6 +115,13 @@ class BTPayPalCheckoutRequest_Tests: XCTestCase {
XCTAssertEqual(parameters["recipient_name"] as? String, "Recipient")
XCTAssertEqual(parameters["payer_email"] as? String, "[email protected]")
XCTAssertEqual(parameters["request_billing_agreement"] as? Bool, true)

guard let userPhoneNumberDetails = parameters["phone_number"] as? [String: String] else {
XCTFail()
return
}
XCTAssertEqual(userPhoneNumberDetails["country_code"], "1")
XCTAssertEqual(userPhoneNumberDetails["national_number"], "4087463271")

guard let billingAgreementDetails = parameters["billing_agreement_details"] as? [String : String] else {
XCTFail()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class BTPayPalVaultRequest_Tests: XCTestCase {
request.isShippingAddressEditable = true
request.offerCredit = true
request.userAuthenticationEmail = "[email protected]"
request.userPhoneNumber = BTPayPalPhoneNumber(countryCode: "1", nationalNumber: "4087463271")

let parameters = request.parameters(with: configuration)

Expand All @@ -60,6 +61,13 @@ class BTPayPalVaultRequest_Tests: XCTestCase {
XCTAssertNil(parameters["os_type"])
XCTAssertNil(parameters["merchant_app_return_url"])

guard let userPhoneNumberDetails = parameters["phone_number"] as? [String: String] else {
XCTFail()
return
}
XCTAssertEqual(userPhoneNumberDetails["country_code"], "1")
XCTAssertEqual(userPhoneNumberDetails["national_number"], "4087463271")

guard let shippingParams = parameters["shipping_address"] as? [String:String] else { XCTFail(); return }

XCTAssertEqual(shippingParams["line1"], "123 Main")
Expand Down

0 comments on commit 16a1b2f

Please sign in to comment.