forked from hideokamoto/stripe-ios
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extract common payment method operations (stripe#3559)
## Summary - Extracts some common code between PaymentSheet and PaymentSheetFlow controller w.r.t to modify payment methods. - Improves overall testing for these code paths. ## Motivation - Reuse for vertical mode ## Testing - Existing tests - New tests ## Changelog N/A
- Loading branch information
1 parent
e139562
commit a45b584
Showing
5 changed files
with
251 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
StripePaymentSheet/StripePaymentSheet/Source/Helpers/SavedPaymentMethodManager.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// | ||
// SavedPaymentMethodManager.swift | ||
// StripePaymentSheet | ||
// | ||
// Created by Nick Porter on 5/2/24. | ||
// | ||
|
||
import Foundation | ||
@_spi(STP) import StripePayments | ||
|
||
/// Provides shared implementations of common operations for managing saved payment methods in PaymentSheet | ||
final class SavedPaymentMethodManager { | ||
|
||
let configuration: PaymentSheet.Configuration | ||
|
||
init(configuration: PaymentSheet.Configuration) { | ||
self.configuration = configuration | ||
} | ||
|
||
func update(paymentMethod: STPPaymentMethod, | ||
with updateParams: STPPaymentMethodUpdateParams, | ||
using ephemeralKey: String) async throws -> STPPaymentMethod { | ||
return try await configuration.apiClient.updatePaymentMethod(with: paymentMethod.stripeId, | ||
paymentMethodUpdateParams: updateParams, | ||
ephemeralKeySecret: ephemeralKey) | ||
} | ||
|
||
func detach(paymentMethod: STPPaymentMethod, using ephemeralKey: String) { | ||
if let customerAccessProvider = configuration.customer?.customerAccessProvider, | ||
case .customerSession = customerAccessProvider, | ||
paymentMethod.type == .card, | ||
let customerId = configuration.customer?.id { | ||
configuration.apiClient.detachPaymentMethodRemoveDuplicates( | ||
paymentMethod.stripeId, | ||
customerId: customerId, | ||
fromCustomerUsing: ephemeralKey | ||
) { (_) in | ||
// no-op | ||
} | ||
} else { | ||
configuration.apiClient.detachPaymentMethod( | ||
paymentMethod.stripeId, | ||
fromCustomerUsing: ephemeralKey | ||
) { (_) in | ||
// no-op | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
177 changes: 177 additions & 0 deletions
177
StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/SavedPaymentMethodManagerTest.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
// | ||
// SavedPaymentMethodManagerTest.swift | ||
// StripePaymentSheet | ||
// | ||
// Created by Nick Porter on 5/3/24. | ||
// | ||
|
||
import Foundation | ||
import OHHTTPStubs | ||
import OHHTTPStubsSwift | ||
import StripeCoreTestUtils | ||
@_spi(STP)@_spi(CustomerSessionBetaAccess)@testable import StripePaymentSheet | ||
import XCTest | ||
|
||
final class SavedPaymentMethodManagerTests: XCTestCase { | ||
|
||
let ephemeralKey = "test-eph-key" | ||
let paymentMethod = STPPaymentMethod.stubbedPaymentMethod() | ||
|
||
var configuration: PaymentSheet.Configuration { | ||
let apiClient = APIStubbedTestCase.stubbedAPIClient() | ||
var configuration = PaymentSheet.Configuration() | ||
configuration.apiClient = apiClient | ||
return configuration | ||
} | ||
|
||
// MARK: Update tests | ||
func testUpdatePaymentMethod() async throws { | ||
let paymentMethod = STPPaymentMethod.stubbedPaymentMethod() | ||
let expectation = stubUpdatePaymentMethod(paymentMethod: paymentMethod, | ||
ephemeralKey: ephemeralKey) | ||
|
||
let sut = SavedPaymentMethodManager(configuration: configuration) | ||
let updatedPaymentMethod = try await sut.update(paymentMethod: paymentMethod, | ||
with: STPPaymentMethodUpdateParams(), | ||
using: ephemeralKey) | ||
|
||
// Verify the response was valid | ||
XCTAssertEqual("pm_123card", updatedPaymentMethod.stripeId) | ||
await fulfillment(of: [expectation], timeout: 5.0) | ||
} | ||
|
||
// MARK: Detach tests | ||
func testDetachPaymentMethod_noCustomer() { | ||
let expectation = stubDetachPaymentMethod(paymentMethod: STPPaymentMethod.stubbedPaymentMethod(), | ||
ephemeralKey: ephemeralKey) | ||
|
||
let sut = SavedPaymentMethodManager(configuration: configuration) | ||
sut.detach(paymentMethod: paymentMethod, using: ephemeralKey) | ||
|
||
wait(for: [expectation], timeout: 5.0) | ||
} | ||
|
||
func testDetachPaymentMethod_withLegacyCustomer() { | ||
var configuration = configuration | ||
configuration.customer = .init(id: "cus_test123", ephemeralKeySecret: ephemeralKey) | ||
|
||
let expectation = stubDetachPaymentMethod(paymentMethod: STPPaymentMethod.stubbedPaymentMethod(), | ||
ephemeralKey: ephemeralKey) | ||
|
||
let sut = SavedPaymentMethodManager(configuration: configuration) | ||
sut.detach(paymentMethod: paymentMethod, using: ephemeralKey) | ||
|
||
wait(for: [expectation], timeout: 5.0) | ||
} | ||
|
||
func testDetachPaymentMethod_withCustomerSession() { | ||
var configuration = configuration | ||
configuration.customer = .init(id: "cus_test123", customerSessionClientSecret: "session_123") | ||
|
||
let listPaymentMethodsExpectation = stubListPaymentMethods(customerId: configuration.customer!.id, | ||
ephemeralKey: ephemeralKey) | ||
|
||
let detachExpectation = stubDetachPaymentMethod(paymentMethod: STPPaymentMethod.stubbedPaymentMethod(), | ||
ephemeralKey: ephemeralKey) | ||
|
||
let sut = SavedPaymentMethodManager(configuration: configuration) | ||
sut.detach(paymentMethod: paymentMethod, using: ephemeralKey) | ||
|
||
wait(for: [listPaymentMethodsExpectation, detachExpectation], timeout: 5.0) | ||
} | ||
} | ||
|
||
extension SavedPaymentMethodManagerTests { | ||
// MARK: HTTP Stubs | ||
|
||
func stubRequest(urlContains: String, | ||
ephemeralKey: String, | ||
httpMethod: String, | ||
responseObject: Any) -> XCTestExpectation { | ||
let exp = expectation(description: "Request \(httpMethod) \(urlContains)") | ||
|
||
stub { urlRequest in | ||
return urlRequest.url?.absoluteString.contains(urlContains) ?? false | ||
&& urlRequest.allHTTPHeaderFields?["Authorization"] == "Bearer \(ephemeralKey)" | ||
&& urlRequest.httpMethod == httpMethod | ||
} response: { _ in | ||
DispatchQueue.main.async { | ||
exp.fulfill() | ||
} | ||
return HTTPStubsResponse(jsonObject: responseObject, | ||
statusCode: 200, | ||
headers: nil) | ||
} | ||
|
||
return exp | ||
} | ||
|
||
func stubUpdatePaymentMethod(paymentMethod: STPPaymentMethod, ephemeralKey: String) -> XCTestExpectation { | ||
return stubRequest(urlContains: "/payment_methods/\(paymentMethod.stripeId)", | ||
ephemeralKey: ephemeralKey, | ||
httpMethod: "POST", | ||
responseObject: STPPaymentMethod.paymentMethodJson) | ||
} | ||
|
||
func stubDetachPaymentMethod(paymentMethod: STPPaymentMethod, ephemeralKey: String) -> XCTestExpectation { | ||
return stubRequest(urlContains: "/payment_methods/\(paymentMethod.stripeId)/detach", | ||
ephemeralKey: ephemeralKey, | ||
httpMethod: "POST", | ||
responseObject: [:]) | ||
} | ||
|
||
func stubListPaymentMethods(customerId: String, ephemeralKey: String) -> XCTestExpectation { | ||
return stubRequest(urlContains: "/payment_methods?customer=\(customerId)&type=card", | ||
ephemeralKey: ephemeralKey, | ||
httpMethod: "GET", | ||
responseObject: STPPaymentMethod.paymentMethodsJson) | ||
} | ||
} | ||
|
||
extension STPPaymentMethod { | ||
|
||
static var paymentMethodJson: [String: Any] { | ||
return [ | ||
"id": "pm_123card", | ||
"type": "card", | ||
"card": [ | ||
"last4": "4242", | ||
"brand": "visa", | ||
], | ||
] | ||
} | ||
|
||
static var paymentMethodsJson: [String: Any] = [ | ||
"data": [ | ||
[ | ||
"id": "pm_123card", | ||
"type": "card", | ||
"card": [ | ||
"last4": "4242", | ||
"brand": "visa", | ||
], | ||
], | ||
[ | ||
"id": "pm_123mastercard", | ||
"type": "card", | ||
"card": [ | ||
"last4": "5555", | ||
"brand": "mastercard", | ||
], | ||
], | ||
[ | ||
"id": "pm_123amex", | ||
"type": "card", | ||
"card": [ | ||
"last4": "6789", | ||
"brand": "amex", | ||
], | ||
], | ||
], | ||
] | ||
|
||
/// Creates a fake payment method for tests | ||
static func stubbedPaymentMethod() -> STPPaymentMethod { | ||
return STPPaymentMethod.decodedObject(fromAPIResponse: paymentMethodJson)! | ||
} | ||
} |