Skip to content

Commit abfca0a

Browse files
authored
Complete card payment only after alert presentation is done (#15967)
2 parents cc68679 + a3e400c commit abfca0a

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)