Skip to content

Commit a3e400c

Browse files
committed
Complete card payment only after alert presentation is done
isEligibleForBackendReceipts is async resulting in onPaymentCompletion called before alert presentation is done. This causes CardPresentPaymentCollectOrderPaymentUseCaseAdaptor to deinitialize CollectOrderPaymentUseCase before payment success is shown resulting in card payment stuck at processing step.
1 parent cc68679 commit a3e400c

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentUseCase.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,9 @@ where TapToPayAlertProvider.AlertDetails == AlertPresenter.AlertDetails,
179179
alertProvider: paymentAlertProvider,
180180
onCompleted: onCompleted)
181181
}
182+
onPaymentCompletion()
182183
}
183184
}
184-
onPaymentCompletion()
185185
})
186186
case .canceled(let cancellationSource, _):
187187
self.handlePaymentCancellation(from: cancellationSource)

WooCommerce/WooCommerceTests/Mocks/MockReceiptEligibilityUseCase.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ final class MockReceiptEligibilityUseCase: ReceiptEligibilityUseCaseProtocol {
77
var isEligibleForFailedPaymentEmailReceipts: Bool = false
88
var isEligibleForReceipt: Bool = true
99

10+
var mockIsEligibleForBackendReceiptsHandler: ((@escaping (Bool) -> Void) -> Void)?
11+
1012
func isEligibleForBackendReceipts(onCompletion: @escaping (Bool) -> Void) {
11-
onCompletion(isEligibleForBackendReceipts)
13+
if let handler = mockIsEligibleForBackendReceiptsHandler {
14+
handler(onCompletion)
15+
} else {
16+
onCompletion(isEligibleForBackendReceipts)
17+
}
1218
}
1319

1420
func isEligibleForSuccessfulPaymentEmailReceipts(onCompletion: @escaping (Bool) -> Void) {

WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CollectOrderPaymentUseCaseTests.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,55 @@ final class CollectOrderPaymentUseCaseTests: XCTestCase {
361361
// Then
362362
XCTAssertEqual(mockPaymentOrchestrator.spyChannel, .pos)
363363
}
364+
365+
func test_completion_called_after_alert_presentation() throws {
366+
receiptEligibilityUseCase.isEligibleForBackendReceipts = true
367+
let paymentMethod = PaymentMethod.cardPresent(details: .fake())
368+
let intent = PaymentIntent.fake().copy(charges: [.fake().copy(paymentMethod: paymentMethod)])
369+
let capturedPaymentData = CardPresentCapturedPaymentData(paymentMethod: paymentMethod, receiptParameters: .fake())
370+
mockSuccessfulCardPresentPaymentActions(intent: intent, capturedPaymentData: capturedPaymentData)
371+
enum Event {
372+
case receiptEligibilityCheck
373+
case alertPresented
374+
case paymentCompletion
375+
}
376+
var eventOrder: [Event] = []
377+
378+
receiptEligibilityUseCase.mockIsEligibleForBackendReceiptsHandler = { completion in
379+
// Force receiptEligibilityCheck completion delay
380+
DispatchQueue.main.async {
381+
eventOrder.append(.receiptEligibilityCheck)
382+
completion(true)
383+
}
384+
}
385+
386+
// Track when receipt alert is presented
387+
alertsPresenter.onPresentCalled = { viewModel in
388+
if viewModel is CardPresentModalSuccessWithoutEmail ||
389+
viewModel is CardPresentModalSuccessEmailSent {
390+
eventOrder.append(.alertPresented)
391+
}
392+
}
393+
394+
// When payment succeeds
395+
waitFor { promise in
396+
self.useCase.collectPayment(
397+
using: .bluetoothScan,
398+
channel: .storeManagement,
399+
onFailure: { _ in },
400+
onCancel: {},
401+
onPaymentCompletion: {
402+
eventOrder.append(.paymentCompletion)
403+
promise(())
404+
},
405+
onCompleted: {}
406+
)
407+
self.mockPreflightController.completeConnection(reader: MockCardReader.wisePad3(), gatewayID: Mocks.paymentGatewayAccount)
408+
}
409+
410+
// Then ensure payment completion happens after alert presentation to avoid CollectOrderPaymentUseCase deinit before alert presentation
411+
XCTAssertEqual(eventOrder, [.receiptEligibilityCheck, .alertPresented, .paymentCompletion])
412+
}
364413
}
365414

366415
private extension CollectOrderPaymentUseCaseTests {

0 commit comments

Comments
 (0)