Skip to content

Commit

Permalink
wip on allowing confirm from presenting view controller before proces…
Browse files Browse the repository at this point in the history
…sing payment
  • Loading branch information
virginiacook committed Dec 6, 2023
1 parent d9f385f commit 4b7d744
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ public class PaymentSheet {
savedPaymentMethods: savedPaymentMethods,
configuration: self.configuration,
isApplePayEnabled: isApplePayEnabled,
isLinkEnabled: isLinkEnabled,
isLinkEnabled: isLinkEnabled,
isConfirmed: self.configuration.delegate == nil,
delegate: self
)

Expand Down Expand Up @@ -201,6 +202,15 @@ public class PaymentSheet {
}
}
}

public func confirmPayment() {
let psvc = self.findPaymentSheetViewController()
psvc?.confirmPayment()
}

public func presentError(_ error: Error) {
// VBC TODO
}

/// Deletes all persisted authentication state associated with a customer.
///
Expand Down Expand Up @@ -324,6 +334,11 @@ extension PaymentSheet: PaymentSheetViewControllerDelegate {
intent: paymentSheetViewController.intent
)
}

func paymentSheetViewControllerDidTapBuy(_ paymentSheetViewController: PaymentSheetViewController) {
self.configuration.delegate?.paymentSheetDidTapBuy(self)
}

}

extension PaymentSheet: LoadingViewControllerDelegate {
Expand Down Expand Up @@ -387,3 +402,9 @@ private extension PaymentSheet {
}

}

// MARK: - PaymentSheetDelegate

public protocol PaymentSheetDelegate: AnyObject {
func paymentSheetDidTapBuy(_ paymentSheet: PaymentSheet)
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ extension PaymentSheet {
/// - Note: If you omit payment methods from this list, they’ll be automatically ordered by Stripe after the ones you provide. Invalid payment methods are ignored.
@_spi(ExternalPaymentMethodsPrivateBeta)
public var paymentMethodOrder: [String]?

public weak var delegate: PaymentSheetDelegate?
}

/// Configuration related to the Stripe Customer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ protocol PaymentSheetViewControllerDelegate: AnyObject {
func paymentSheetViewControllerDidSelectPayWithLink(
_ paymentSheetViewController: PaymentSheetViewController
)
func paymentSheetViewControllerDidTapBuy(
_ paymentSheetViewController: PaymentSheetViewController
)
}

/// For internal SDK use only
Expand All @@ -44,6 +47,10 @@ class PaymentSheetViewController: UIViewController {
let isWalletEnabled = true

let shouldShowWalletHeader = true

var isConfirmed: Bool

var paymentOption: PaymentOption?

// MARK: - Writable Properties
weak var delegate: PaymentSheetViewControllerDelegate?
Expand Down Expand Up @@ -168,13 +175,15 @@ class PaymentSheetViewController: UIViewController {
configuration: PaymentSheet.Configuration,
isApplePayEnabled: Bool,
isLinkEnabled: Bool,
isConfirmed: Bool,
delegate: PaymentSheetViewControllerDelegate
) {
self.intent = intent
self.savedPaymentMethods = savedPaymentMethods
self.configuration = configuration
self.isApplePayEnabled = isApplePayEnabled
self.isLinkEnabled = isLinkEnabled
self.isConfirmed = isConfirmed
self.delegate = delegate

if savedPaymentMethods.isEmpty {
Expand Down Expand Up @@ -430,68 +439,84 @@ class PaymentSheetViewController: UIViewController {
pay(with: selectedPaymentOption)
}
}

func confirmPayment() {
guard let paymentOption = paymentOption else {
// VBC TODO: error handling if somehow we got to this point and there's no payment option
return
}
isConfirmed = true
pay(with: paymentOption)
}

func presentError() {
// VBC TODO: error handling from external sources
}

func pay(with paymentOption: PaymentOption) {
view.endEditing(true)
isPaymentInFlight = true
// Clear any errors
error = nil
updateUI()

// Confirm the payment with the payment option
let startTime = NSDate.timeIntervalSinceReferenceDate
self.delegate?.paymentSheetViewControllerShouldConfirm(self, with: paymentOption) { result, deferredIntentConfirmationType in
let elapsedTime = NSDate.timeIntervalSinceReferenceDate - startTime
DispatchQueue.main.asyncAfter(
deadline: .now() + max(PaymentSheetUI.minimumFlightTime - elapsedTime, 0)
) {
STPAnalyticsClient.sharedClient.logPaymentSheetPayment(
isCustom: false,
paymentMethod: paymentOption.analyticsValue,
result: result,
linkEnabled: self.intent.supportsLink,
activeLinkSession: LinkAccountContext.shared.account?.sessionState == .verified,
linkSessionType: self.intent.linkPopupWebviewOption,
currency: self.intent.currency,
intentConfig: self.intent.intentConfig,
deferredIntentConfirmationType: deferredIntentConfirmationType,
paymentMethodTypeAnalyticsValue: paymentOption.paymentMethodTypeAnalyticsValue,
error: result.error
)

self.isPaymentInFlight = false
switch result {
case .canceled:
// Do nothing, keep customer on payment sheet
self.updateUI()
case .failed(let error):
#if !STP_BUILD_FOR_VISION
UINotificationFeedbackGenerator().notificationOccurred(.error)
#endif
// Update state
self.error = error
// Handle error
if PaymentSheetError.isUnrecoverable(error: error) {
self.delegate?.paymentSheetViewControllerDidFinish(self, result: result)
}
self.updateUI()
UIAccessibility.post(notification: .layoutChanged, argument: self.errorLabel)
case .completed:
// We're done!
let delay: TimeInterval =
if isConfirmed {
// Confirm the payment with the payment option
let startTime = NSDate.timeIntervalSinceReferenceDate
self.delegate?.paymentSheetViewControllerShouldConfirm(self, with: paymentOption) { result, deferredIntentConfirmationType in
let elapsedTime = NSDate.timeIntervalSinceReferenceDate - startTime
DispatchQueue.main.asyncAfter(
deadline: .now() + max(PaymentSheetUI.minimumFlightTime - elapsedTime, 0)
) {
STPAnalyticsClient.sharedClient.logPaymentSheetPayment(
isCustom: false,
paymentMethod: paymentOption.analyticsValue,
result: result,
linkEnabled: self.intent.supportsLink,
activeLinkSession: LinkAccountContext.shared.account?.sessionState == .verified,
linkSessionType: self.intent.linkPopupWebviewOption,
currency: self.intent.currency,
intentConfig: self.intent.intentConfig,
deferredIntentConfirmationType: deferredIntentConfirmationType,
paymentMethodTypeAnalyticsValue: paymentOption.paymentMethodTypeAnalyticsValue,
error: result.error
)
self.isPaymentInFlight = false
switch result {
case .canceled:
// Do nothing, keep customer on payment sheet
self.updateUI()
case .failed(let error):
#if !STP_BUILD_FOR_VISION
UINotificationFeedbackGenerator().notificationOccurred(.error)
#endif
// Update state
self.error = error
// Handle error
if PaymentSheetError.isUnrecoverable(error: error) {
self.delegate?.paymentSheetViewControllerDidFinish(self, result: result)
}
self.updateUI()
UIAccessibility.post(notification: .layoutChanged, argument: self.errorLabel)
case .completed:
// We're done!
let delay: TimeInterval =
self.presentedViewController?.isBeingDismissed == true ? 1 : 0
// Hack: PaymentHandler calls the completion block while SafariVC is still being dismissed - "wait" until it's finished before updating UI
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
#if !STP_BUILD_FOR_VISION
UINotificationFeedbackGenerator().notificationOccurred(.success)
#endif
self.buyButton.update(state: .succeeded, animated: true) {
// Wait a bit before closing the sheet
self.delegate?.paymentSheetViewControllerDidFinish(self, result: .completed)
// Hack: PaymentHandler calls the completion block while SafariVC is still being dismissed - "wait" until it's finished before updating UI
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
#if !STP_BUILD_FOR_VISION
UINotificationFeedbackGenerator().notificationOccurred(.success)
#endif
self.buyButton.update(state: .succeeded, animated: true) {
// Wait a bit before closing the sheet
self.delegate?.paymentSheetViewControllerDidFinish(self, result: .completed)
}
}
}
}
}
} else {
// VBC TODO: call delegate
}
}
}
Expand Down

0 comments on commit 4b7d744

Please sign in to comment.