Skip to content

Commit 2357fb8

Browse files
committed
6643 Allow retries of failed interac refunds
When an interac refund fails, we show an error with a “Try again” button. When the user taps that, we attempt to cancel the Interac refund “intent” using the `refundCancelable` that Stripe Terminal gives us in response to `SCPTerminal.collectRefundPaymentMethod()` When there’s been a failure or error in the refund, the `refundCancelable` that Stripe gave us ends up in an unexpected situation: it’s not `completed`, but it also doesn’t call back the completion handler when we call `cancel`. This meant that our previous “Try again” handler did not start the new refund attempt. Cancelling a refund after Stripe have called the completion handler from `processRefund`, or given us an error from `collectRefundPaymentMethod`, shouldn’t be required: those calls from Stripe have already told us that the original refund attempt is complete (even if failed), so we can’t cancel it anyway. This change sets the `refundCancelable` to nil when Stripe have told us that the refund is complete, resulting in a `noRefundInProgress` error being returned to the `handleRefundFailureAndRetryRefund` handler. We don’t need to worry about this error or whether the cancellation was successful: in either case, we should try the refund again. This change does not address that some refund errors should not be retriable: that will follow in future work.
1 parent b882be2 commit 2357fb8

File tree

2 files changed

+13
-19
lines changed

2 files changed

+13
-19
lines changed

Hardware/Hardware/CardReader/StripeCardReader/StripeCardReaderService.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,10 +518,12 @@ extension StripeCardReaderService {
518518
return Future() { [weak self] promise in
519519
self?.refundCancellable = Terminal.shared.collectRefundPaymentMethod(parameters) { collectError in
520520
if let error = collectError {
521+
self?.refundCancellable = nil
521522
promise(.failure(CardReaderServiceError.refundPayment(underlyingError: UnderlyingError(with: error))))
522523
} else {
523524
// Process refund
524525
Terminal.shared.processRefund { processedRefund, processError in
526+
self?.refundCancellable = nil
525527
if let error = processError {
526528
promise(.failure(CardReaderServiceError.refundPayment(underlyingError: UnderlyingError(with: error))))
527529
} else if let refund = processedRefund {

WooCommerce/Classes/ViewRelated/Orders/Refund/RefundSubmissionUseCase.swift

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ final class RefundSubmissionUseCase: NSObject, RefundSubmissionProtocol {
107107
self.stores = stores
108108
self.storageManager = storageManager
109109
self.analytics = analytics
110+
111+
// Instantiates the alerts coordinator.
112+
let alerts = OrderDetailsPaymentAlerts(transactionType: .refund,
113+
presentingController: rootViewController)
114+
self.alerts = alerts
110115
}
111116

112117
/// Starts the refund submission flow.
@@ -232,13 +237,8 @@ private extension RefundSubmissionUseCase {
232237
return
233238
}
234239

235-
// Instantiates the alerts coordinator.
236-
let alerts = OrderDetailsPaymentAlerts(transactionType: .refund,
237-
presentingController: rootViewController)
238-
self.alerts = alerts
239-
240240
// Shows reader ready alert.
241-
alerts.readerIsReady(title: Localization.refundPaymentTitle(username: order.billingAddress?.firstName),
241+
self.alerts?.readerIsReady(title: Localization.refundPaymentTitle(username: order.billingAddress?.firstName),
242242
amount: formattedAmount,
243243
onCancel: { [weak self] in
244244
self?.cancelRefund()
@@ -275,20 +275,12 @@ private extension RefundSubmissionUseCase {
275275
// TODO: 5984 - tracks in-person refund error
276276
DDLogError("Failed to refund: \(error.localizedDescription)")
277277
// Informs about the error.
278+
//TODO: Check which refund errors can't be retried, and use that to call nonRetryableError(from: self.rootViewController, error: cancelError)
278279
alerts?.error(error: error) { [weak self] in
279-
// Cancels current payment.
280-
self?.cardPresentRefundOrchestrator.cancelRefund { [weak self] result in
281-
guard let self = self else { return }
282-
283-
switch result {
284-
case .success:
285-
// Retries refund.
286-
self.attemptCardPresentRefund(refundAmount: refundAmount, charge: charge, onCompletion: onCompletion)
287-
case .failure(let cancelError):
288-
// Informs that payment can't be retried.
289-
self.alerts?.nonRetryableError(from: self.rootViewController, error: cancelError)
290-
onCompletion(.failure(error))
291-
}
280+
// Cancels current refund, if possible.
281+
self?.cardPresentRefundOrchestrator.cancelRefund { [weak self] _ in
282+
// Regardless of whether the refund could be cancelled (e.g. it completed but failed), retry the refund.
283+
self?.attemptCardPresentRefund(refundAmount: refundAmount, charge: charge, onCompletion: onCompletion)
292284
}
293285
}
294286
}

0 commit comments

Comments
 (0)