Skip to content

Commit

Permalink
Refactor and add tests for dashboard params (stripe#2984)
Browse files Browse the repository at this point in the history
## Summary
- Refactors the Dashboard hack so it is only applied to deferred
PaymentIntents
- Adds unit tests

## Motivation
https://jira.corp.stripe.com/browse/RUN_MOBILESDK-2650
https://jira.corp.stripe.com/browse/RUN_MOBILESDK-2653

## Testing
- New unit tests

## Changelog
N/A
  • Loading branch information
porter-stripe authored Oct 13, 2023
1 parent b917726 commit efeb1ac
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 53 deletions.
32 changes: 32 additions & 0 deletions Stripe/StripeiOSTests/IntentConfirmParamsTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// IntentConfirmParamsTest.swift
// StripeiOSTests
//
// Created by Nick Porter on 10/11/23.
//

import XCTest

@testable@_spi(STP) import Stripe
@testable@_spi(STP) import StripeCore
@testable@_spi(STP) import StripePayments
@testable@_spi(STP) import StripePaymentSheet
@testable@_spi(STP) import StripePaymentsUI

final class IntentConfirmParamsTest: XCTestCase {

func testMakeDashboardParams() {
let params = IntentConfirmParams.makeDashboardParams(paymentIntentClientSecret: "test_client_secret",
paymentMethodID: "test_payment_method_id",
shouldSave: true,
paymentMethodType: .card,
customer: .init(id: "test_id",
ephemeralKeySecret: "test_key"))

XCTAssertEqual(params.clientSecret, "test_client_secret")
XCTAssertEqual(params.paymentMethodId, "test_payment_method_id")
XCTAssertEqual(params.paymentMethodOptions?.cardOptions?.additionalAPIParameters["setup_future_usage"] as? String, "off_session")
XCTAssertTrue(params.paymentMethodOptions?.cardOptions?.additionalAPIParameters["moto"] as? Bool ?? false)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,22 @@ class IntentConfirmParams {
self.confirmPaymentMethodOptions = STPConfirmPaymentMethodOptions()
}

func makeDashboardParams(
static func makeDashboardParams(
paymentIntentClientSecret: String,
paymentMethodID: String,
configuration: PaymentSheet.Configuration
shouldSave: Bool,
paymentMethodType: STPPaymentMethodType,
customer: PaymentSheet.CustomerConfiguration?
) -> STPPaymentIntentParams {
let params = STPPaymentIntentParams(clientSecret: paymentIntentClientSecret)
params.paymentMethodId = paymentMethodID

// Dashboard only supports a specific payment flow today
let options = STPConfirmPaymentMethodOptions()
options.setSetupFutureUsageIfNecessary(
saveForFutureUseCheckboxState == .selected,
shouldSave,
paymentMethodType: paymentMethodType,
customer: configuration.customer
customer: customer
)
params.paymentMethodOptions = options

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,50 +107,22 @@ extension PaymentSheet {
switch intent {
// MARK: ↪ PaymentIntent
case .paymentIntent(let paymentIntent):
// The Dashboard app cannot pass `paymentMethodParams` ie payment_method_data
if configuration.apiClient.publishableKeyIsUserKey {
configuration.apiClient.createPaymentMethod(with: confirmParams.paymentMethodParams) {
paymentMethod,
error in
if let error = error {
completion(.failed(error: error), nil)
return
}
let paymentIntentParams = confirmParams.makeDashboardParams(
paymentIntentClientSecret: paymentIntent.clientSecret,
paymentMethodID: paymentMethod?.stripeId ?? "",
configuration: configuration
)
paymentIntentParams.shipping = makeShippingParams(
for: paymentIntent,
configuration: configuration
)
paymentHandler.confirmPayment(
paymentIntentParams,
with: authenticationContext,
completion: { actionStatus, _, error in
paymentHandlerCompletion(actionStatus, error)
}
)
let params = makePaymentIntentParams(
confirmPaymentMethodType: .new(
params: confirmParams.paymentMethodParams,
paymentOptions: confirmParams.confirmPaymentMethodOptions,
shouldSave: confirmParams.saveForFutureUseCheckboxState == .selected
),
paymentIntent: paymentIntent,
configuration: configuration
)
paymentHandler.confirmPayment(
params,
with: authenticationContext,
completion: { actionStatus, _, error in
paymentHandlerCompletion(actionStatus, error)
}
} else {
let params = makePaymentIntentParams(
confirmPaymentMethodType: .new(
params: confirmParams.paymentMethodParams,
paymentOptions: confirmParams.confirmPaymentMethodOptions,
shouldSave: confirmParams.saveForFutureUseCheckboxState == .selected
),
paymentIntent: paymentIntent,
configuration: configuration
)
paymentHandler.confirmPayment(
params,
with: authenticationContext,
completion: { actionStatus, _, error in
paymentHandlerCompletion(actionStatus, error)
}
)
}
)
// MARK: ↪ SetupIntent
case .setupIntent(let setupIntent):
let setupIntentParams = makeSetupIntentParams(
Expand Down Expand Up @@ -193,11 +165,6 @@ extension PaymentSheet {
case .paymentIntent(let paymentIntent):
let paymentIntentParams = makePaymentIntentParams(confirmPaymentMethodType: .saved(paymentMethod), paymentIntent: paymentIntent, configuration: configuration)

// The Dashboard app requires MOTO
if configuration.apiClient.publishableKeyIsUserKey {
paymentIntentParams.paymentMethodOptions?.setMoto()
}

paymentHandler.confirmPayment(
paymentIntentParams,
with: authenticationContext,
Expand Down Expand Up @@ -491,6 +458,7 @@ extension PaymentSheet {
params.paymentMethodOptions = paymentOptions
params.returnURL = configuration.returnURL
params.shipping = makeShippingParams(for: paymentIntent, configuration: configuration)

return params
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,21 @@ extension PaymentSheet {
if [STPPaymentIntentStatus.requiresPaymentMethod, STPPaymentIntentStatus.requiresConfirmation].contains(paymentIntent.status) {
// 4a. Client-side confirmation
try PaymentSheetDeferredValidator.validate(paymentIntent: paymentIntent, intentConfiguration: intentConfig, isFlowController: isFlowController)
let paymentIntentParams = makePaymentIntentParams(
var paymentIntentParams = makePaymentIntentParams(
confirmPaymentMethodType: confirmType,
paymentIntent: paymentIntent,
configuration: configuration,
mandateData: mandateData
)

// Dashboard specfic logic
if configuration.apiClient.publishableKeyIsUserKey {
paymentIntentParams = setParamsForDashboardApp(confirmType: confirmType,
paymentIntentParams: paymentIntentParams,
paymentIntent: paymentIntent,
configuration: configuration)
}

paymentHandler.confirmPayment(
paymentIntentParams,
with: authenticationContext
Expand Down Expand Up @@ -148,4 +157,32 @@ extension PaymentSheet {
}
return paymentUserAgentValues
}

static func setParamsForDashboardApp(confirmType: ConfirmPaymentMethodType,
paymentIntentParams: STPPaymentIntentParams,
paymentIntent: STPPaymentIntent,
configuration: PaymentSheet.Configuration) -> STPPaymentIntentParams {
var intentParamsCopy = paymentIntentParams
switch confirmType {
case .saved:
// The Dashboard app requires MOTO
intentParamsCopy.paymentMethodOptions = intentParamsCopy.paymentMethodOptions == nil ? .init() : intentParamsCopy.paymentMethodOptions
intentParamsCopy.paymentMethodOptions?.setMoto()
case .new(_, _, let paymentMethod, let shouldSave):
// The Dashboard app cannot pass `paymentMethodParams` ie payment_method_data
intentParamsCopy = IntentConfirmParams.makeDashboardParams(
paymentIntentClientSecret: paymentIntent.clientSecret,
paymentMethodID: paymentMethod?.stripeId ?? "",
shouldSave: shouldSave,
paymentMethodType: paymentMethod?.type ?? .unknown,
customer: configuration.customer
)
intentParamsCopy.shipping = makeShippingParams(
for: paymentIntent,
configuration: configuration
)
}

return intentParamsCopy
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// PaymentSheet+DeferredAPITest.swift
// StripePaymentSheetTests
//
// Created by Nick Porter on 10/11/23.
//

@testable@_spi(STP) import StripeCore
@testable@_spi(STP) import StripeCoreTestUtils
@testable@_spi(STP) import StripePayments
@testable@_spi(STP) import StripePaymentSheet
@testable@_spi(STP) import StripePaymentsTestUtils
@testable@_spi(STP) import StripeUICore
import XCTest

final class PaymentSheet_DeferredAPITest: XCTestCase {
let apiClient = STPAPIClient(publishableKey: STPTestingDefaultPublishableKey)

lazy var configuration: PaymentSheet.Configuration = {
var config = PaymentSheet.Configuration()
config.apiClient = apiClient
config.allowsDelayedPaymentMethods = true
config.shippingDetails = {
return .init(
address: .init(
country: "US",
line1: "Line 1"
),
name: "Jane Doe",
phone: "5551234567"
)
}
return config
}()

// MARK: setParamsForDashboardApp tests
func testSetParamsForDashboardApp_saved() {
let examplePaymentMethodParams = STPPaymentMethodParams(card: STPFixtures.paymentMethodCardParams(), billingDetails: nil, metadata: nil)
let paymentOptions = STPConfirmPaymentMethodOptions()
let examplePaymentMethod = STPFixtures.paymentMethod()
var configurationWithCustomer = configuration
configurationWithCustomer.customer = .init(id: "id", ephemeralKeySecret: "ek")
let params = PaymentSheet.setParamsForDashboardApp(confirmType: .new(params: examplePaymentMethodParams,
paymentOptions: paymentOptions,
paymentMethod: examplePaymentMethod,
shouldSave: true),
paymentIntentParams: .init(),
paymentIntent: STPFixtures.makePaymentIntent(),
configuration: configurationWithCustomer)

// moto should be set to true and sfu = off_session
XCTAssertTrue(params.paymentMethodOptions?.cardOptions?.additionalAPIParameters["moto"] as? Bool ?? false)
XCTAssertEqual(params.paymentMethodOptions?.cardOptions?.additionalAPIParameters["setup_future_usage"] as? String, "off_session")
}

func testSetParamsForDashboardApp_new() {
let params = PaymentSheet.setParamsForDashboardApp(confirmType: .saved(createValidSavedPaymentMethod()),
paymentIntentParams: .init(),
paymentIntent: STPFixtures.makePaymentIntent(),
configuration: configuration)

// moto should be set to true
XCTAssertTrue(params.paymentMethodOptions?.cardOptions?.additionalAPIParameters["moto"] as? Bool ?? false)
}

// MARK: Helpers
func createValidSavedPaymentMethod() -> STPPaymentMethod {
var validSavedPM: STPPaymentMethod?
let createPMExpectation = expectation(description: "Create PM")
apiClient.createPaymentMethod(with: ._testValidCardValue()) { paymentMethod, error in
guard let paymentMethod = paymentMethod else {
XCTFail(String(describing: error))
return
}
validSavedPM = paymentMethod
createPMExpectation.fulfill()
}
waitForExpectations(timeout: 10)
return validSavedPM!
}
}

0 comments on commit efeb1ac

Please sign in to comment.