Skip to content

Commit

Permalink
update paypal to use async fetchConfiguration
Browse files Browse the repository at this point in the history
  • Loading branch information
KunJeongPark committed Aug 27, 2024
1 parent 1814e7c commit 7708861
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 65 deletions.
98 changes: 48 additions & 50 deletions Sources/BraintreePayPal/BTPayPalClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -311,67 +311,65 @@ import BraintreeDataCollector
linkType = (request as? BTPayPalVaultRequest)?.enablePayPalAppSwitch == true ? .universal : .deeplink

apiClient.sendAnalyticsEvent(BTPayPalAnalytics.tokenizeStarted, isVaultRequest: isVaultRequest, linkType: linkType)
apiClient.fetchOrReturnRemoteConfiguration { configuration, error in
if let error {
self.notifyFailure(with: error, completion: completion)
return
}

Task {
do {
let configuration = try await apiClient.fetchConfiguration()

guard let configuration, let json = configuration.json else {
self.notifyFailure(with: BTPayPalError.fetchConfigurationFailed, completion: completion)
return
}

self.isConfigFromCache = configuration.isFromCache

guard json["paypalEnabled"].isTrue else {
self.notifyFailure(with: BTPayPalError.disabled, completion: completion)
return
}
self.isConfigFromCache = configuration.isFromCache

self.payPalRequest = request
self.apiClient.post(
request.hermesPath,
parameters: request.parameters(
with: configuration,
universalLink: self.universalLink,
isPayPalAppInstalled: self.application.isPayPalAppInstalled()
)
) { body, response, error in
if let error = error as? NSError {
guard let jsonResponseBody = error.userInfo[BTCoreConstants.jsonResponseBodyKey] as? BTJSON else {
self.notifyFailure(with: error, completion: completion)
return
}

let errorDetailsIssue = jsonResponseBody["paymentResource"]["errorDetails"][0]["issue"]
var dictionary = error.userInfo
dictionary[NSLocalizedDescriptionKey] = errorDetailsIssue
self.notifyFailure(with: BTPayPalError.httpPostRequestError(dictionary), completion: completion)
guard configuration.isPayPalEnabled else {
self.notifyFailure(with: BTPayPalError.disabled, completion: completion)
return
}

guard let body, let approvalURL = BTPayPalApprovalURLParser(body: body) else {
self.notifyFailure(with: BTPayPalError.invalidURL("Missing approval URL in gateway response."), completion: completion)
return
}

self.payPalContextID = approvalURL.baToken ?? approvalURL.ecToken

let dataCollector = BTDataCollector(apiClient: self.apiClient)
self.clientMetadataID = self.payPalRequest?.riskCorrelationID ?? dataCollector.clientMetadataID(self.payPalContextID)
self.payPalRequest = request
self.apiClient.post(
request.hermesPath,
parameters: request.parameters(
with: configuration,
universalLink: self.universalLink,
isPayPalAppInstalled: self.application.isPayPalAppInstalled()
)
) { body, response, error in
if let error = error as? NSError {
guard let jsonResponseBody = error.userInfo[BTCoreConstants.jsonResponseBodyKey] as? BTJSON else {
self.notifyFailure(with: error, completion: completion)
return
}

let errorDetailsIssue = jsonResponseBody["paymentResource"]["errorDetails"][0]["issue"]
var dictionary = error.userInfo
dictionary[NSLocalizedDescriptionKey] = errorDetailsIssue
self.notifyFailure(with: BTPayPalError.httpPostRequestError(dictionary), completion: completion)
return
}

switch approvalURL.redirectType {
case .payPalApp(let url):
guard let baToken = approvalURL.baToken else {
self.notifyFailure(with: BTPayPalError.missingBAToken, completion: completion)
guard let body, let approvalURL = BTPayPalApprovalURLParser(body: body) else {
self.notifyFailure(with: BTPayPalError.invalidURL("Missing approval URL in gateway response."), completion: completion)
return
}

self.launchPayPalApp(with: url, baToken: baToken, completion: completion)
case .webBrowser(let url):
self.handlePayPalRequest(with: url, paymentType: request.paymentType, completion: completion)
self.payPalContextID = approvalURL.baToken ?? approvalURL.ecToken

let dataCollector = BTDataCollector(apiClient: self.apiClient)
self.clientMetadataID = self.payPalRequest?.riskCorrelationID ?? dataCollector.clientMetadataID(self.payPalContextID)

switch approvalURL.redirectType {
case .payPalApp(let url):
guard let baToken = approvalURL.baToken else {
self.notifyFailure(with: BTPayPalError.missingBAToken, completion: completion)
return
}

self.launchPayPalApp(with: url, baToken: baToken, completion: completion)
case .webBrowser(let url):
self.handlePayPalRequest(with: url, paymentType: request.paymentType, completion: completion)
}
}
} catch {
notifyFailure(with: error, completion: completion)
}
}
}
Expand Down
104 changes: 89 additions & 15 deletions UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class BTPayPalClient_Tests: XCTestCase {
expectation.fulfill()
}

self.waitForExpectations(timeout: 1)
waitForExpectations(timeout: 2, handler: nil)
}

func testTokenizePayPalAccount_whenPayPalNotEnabledInConfiguration_callsBackWithError() {
Expand Down Expand Up @@ -68,7 +68,12 @@ class BTPayPalClient_Tests: XCTestCase {
let checkoutRequest = BTPayPalCheckoutRequest(amount: "1")
checkoutRequest.intent = .sale

payPalClient.tokenize(checkoutRequest) { _, _ in }
let expectation = self.expectation(description: "Posts to ednpoint")
payPalClient.tokenize(checkoutRequest) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertEqual("v1/paypal_hermes/create_payment_resource", mockAPIClient.lastPOSTPath)
guard let lastPostParameters = mockAPIClient.lastPOSTParameters else { XCTFail(); return }
Expand All @@ -83,7 +88,13 @@ class BTPayPalClient_Tests: XCTestCase {
let vaultRequest = BTPayPalVaultRequest()
vaultRequest.billingAgreementDescription = "description"

payPalClient.tokenize(vaultRequest) { _, _ in }
let expectation = self.expectation(description: "Posts to correct endpoint")

payPalClient.tokenize(vaultRequest) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertEqual("v1/paypal_hermes/setup_billing_agreement", mockAPIClient.lastPOSTPath)
guard let lastPostParameters = mockAPIClient.lastPOSTParameters else { XCTFail(); return }
Expand Down Expand Up @@ -134,9 +145,15 @@ class BTPayPalClient_Tests: XCTestCase {
"redirectUrl": "https://www.paypal.com/checkout?EC-Token=EC-Random-Value"
]
])
let expectation = self.expectation(description: "Unsuccessful tokenization")

let request = BTPayPalCheckoutRequest(amount: "1")
payPalClient.tokenize(request) { _, _ in }

payPalClient.tokenize(request) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertEqual("https://www.paypal.com/checkout?EC-Token=EC-Random-Value", payPalClient.approvalURL?.absoluteString)
}
Expand All @@ -149,7 +166,13 @@ class BTPayPalClient_Tests: XCTestCase {
])

let request = BTPayPalVaultRequest()
payPalClient.tokenize(request) { _, _ in }
let expectation = self.expectation(description: "Url is not modified")

payPalClient.tokenize(request) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertEqual("https://checkout.paypal.com/one-touch-login-sandbox", payPalClient.approvalURL?.absoluteString)
}
Expand All @@ -164,7 +187,12 @@ class BTPayPalClient_Tests: XCTestCase {
let request = BTPayPalCheckoutRequest(amount: "1")
request.userAction = BTPayPalRequestUserAction.none

payPalClient.tokenize(request) { _, _ in }
let expectation = self.expectation(description: "Url is not modified")
payPalClient.tokenize(request) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertEqual("https://www.paypal.com/checkout?EC-Token=EC-Random-Value", payPalClient.approvalURL?.absoluteString)
}
Expand Down Expand Up @@ -222,8 +250,14 @@ class BTPayPalClient_Tests: XCTestCase {

mockWebAuthenticationSession.cannedResponseURL = URL(string: "https://www.paypal.com/checkout/success")

let expectation = self.expectation(description: "Successful tokenization")

let request = BTPayPalCheckoutRequest(amount: "1")
payPalClient.tokenize(request) { _, _ in }
payPalClient.tokenize(request) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertEqual(mockAPIClient.postedPayPalContextID, "EC-Random-Value")
XCTAssertTrue(mockAPIClient.postedAnalyticsEvents.contains("paypal:tokenize:handle-return:started"))
Expand Down Expand Up @@ -278,7 +312,12 @@ class BTPayPalClient_Tests: XCTestCase {
mockWebAuthenticationSession.cannedResponseURL = URL(string: "https://www.paypal.com/checkout/success")

let request = BTPayPalCheckoutRequest(amount: "1")
payPalClient.tokenize(request) { _, _ in }
let expectation = self.expectation(description: "Sends BA token to analytics")
payPalClient.tokenize(request) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertEqual(mockAPIClient.postedPayPalContextID, "BA-Random-Value")
XCTAssertEqual(mockAPIClient.postedLinkType, .deeplink)
Expand All @@ -297,7 +336,12 @@ class BTPayPalClient_Tests: XCTestCase {
payPalClient.webAuthenticationSession = mockWebAuthenticationSession

let request = BTPayPalVaultRequest()
payPalClient.tokenize(request) { _, _ in }
let expectation = self.expectation(description: "Sends BA Token as PayPalContextID in Analytics")
payPalClient.tokenize(request) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertEqual(mockAPIClient.postedPayPalContextID, "A_FAKE_BA_TOKEN")
XCTAssertEqual(mockAPIClient.postedLinkType, .deeplink)
Expand All @@ -311,7 +355,13 @@ class BTPayPalClient_Tests: XCTestCase {
request.currencyCode = "GBP"
request.offerPayLater = true

payPalClient.tokenize(request) { _, _ in }
let expectation = self.expectation(description: "Performs switch correctly")

payPalClient.tokenize(request) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertNotNil(payPalClient.webAuthenticationSession)

Expand All @@ -333,7 +383,13 @@ class BTPayPalClient_Tests: XCTestCase {
let request = BTPayPalVaultRequest()
request.offerCredit = true

payPalClient.tokenize(request) { _, _ in }
let expectation = self.expectation(description: "Performs switch correctly")

payPalClient.tokenize(request) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertNotNil(payPalClient.webAuthenticationSession)

Expand All @@ -352,9 +408,14 @@ class BTPayPalClient_Tests: XCTestCase {
}

func testTokenizePayPalAccount_whenPayPalPaymentCreationSuccessful_performsAppSwitch() {

let expectation = self.expectation(description: "Successful app switch")
let request = BTPayPalCheckoutRequest(amount: "1")
payPalClient.tokenize(request) { _, _ in }
payPalClient.tokenize(request) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)
XCTAssertNotNil(payPalClient.webAuthenticationSession)
XCTAssertNotNil(payPalClient.clientMetadataID)
}
Expand Down Expand Up @@ -762,7 +823,9 @@ class BTPayPalClient_Tests: XCTestCase {
enablePayPalAppSwitch: true
)

payPalClient.tokenize(vaultRequest) { _, _ in }
payPalClient.tokenize(vaultRequest) { _, _ in

}

XCTAssertTrue(fakeApplication.openURLWasCalled)

Expand Down Expand Up @@ -944,7 +1007,12 @@ class BTPayPalClient_Tests: XCTestCase {
]
])

payPalClient.tokenize(vaultRequest) { _, _ in }
let expectation = self.expectation(description: "tokenize succeded")
payPalClient.tokenize(vaultRequest) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertEqual("v1/paypal_hermes/setup_billing_agreement", mockAPIClient.lastPOSTPath)
guard let lastPostParameters = mockAPIClient.lastPOSTParameters else { XCTFail(); return }
Expand All @@ -965,13 +1033,19 @@ class BTPayPalClient_Tests: XCTestCase {
enablePayPalAppSwitch: true
)

let expectation = self.expectation(description: "Unsuccessful tokenization")

mockAPIClient.cannedResponseBody = BTJSON(value: [
"agreementSetup": [
"paypalAppApprovalUrl": "https://www.some-url.com/some-path?token=value1"
]
])

payPalClient.tokenize(vaultRequest) { _, _ in }
payPalClient.tokenize(vaultRequest) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)

XCTAssertEqual("v1/paypal_hermes/setup_billing_agreement", mockAPIClient.lastPOSTPath)
guard let lastPostParameters = mockAPIClient.lastPOSTParameters else { XCTFail(); return }
Expand Down

0 comments on commit 7708861

Please sign in to comment.