Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crashes handling Apple Pay payments #219

Closed
roryprior opened this issue Nov 22, 2023 · 8 comments · Fixed by #258
Closed

Crashes handling Apple Pay payments #219

roryprior opened this issue Nov 22, 2023 · 8 comments · Fixed by #258
Assignees
Labels
bug Something isn't working

Comments

@roryprior
Copy link

roryprior commented Nov 22, 2023

Hi, we're seeing a continual low level number of crashes processing ApplePay payments in production, but it's proving hard to replicate during testing. This is the stack trace we're seeing on Crashlytics: (I've censored our product name)

EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000000000001
Crashed: com.apple.main-thread
0  XXXXX                          0xfebe8 @objc completion handler block implementation for @escaping @callee_unowned @convention(block) (@unowned JPResponse?, @unowned JPError?) -> () with result type (JPResponse?, JPError?) + 28 (<compiler-generated>:28)
1  XXXXX                          0x339364 __67-[JudoKit applePayViewControllerWithMode:configuration:completion:]_block_invoke.13 + 225 (JudoKit.m:225)
2  XXXXX                          0x36d7a8 __68-[JPApplePayController paymentAuthorizationViewControllerDidFinish:]_block_invoke + 77 (JPApplePayController.m:77)
3  UIKitCore                      0x784060 -[UIViewController dismissViewControllerWithTransition:completion:] + 2132
4  UIKitCore                      0x129b78 -[_UIViewControllerTransitionCoordinator _applyBlocks:releaseBlocks:] + 128
5  UIKitCore                      0x129680 -[_UIViewControllerTransitionContext _runAlongsideCompletions] + 140
6  UIKitCore                      0x128d10 -[_UIViewControllerTransitionContext completeTransition:] + 128
7  UIKitCore                      0x29bf80 -[UITransitionView notifyDidCompleteTransition:] + 180
8  UIKitCore                      0x29bc38 -[UITransitionView _didCompleteTransition:] + 832
9  UIKitCore                      0x84a38 __UIVIEW_IS_EXECUTING_ANIMATION_COMPLETION_BLOCK__ + 36
10 UIKitCore                      0x841d0 -[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 624
11 UIKitCore                      0x83848 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 436
12 UIKitCore                      0x65aa4 -[UIViewAnimationState animationDidStop:finished:] + 196
13 UIKitCore                      0x65bb8 -[UIViewAnimationState animationDidStop:finished:] + 472
14 QuartzCore                     0x72098 run_animation_callbacks(void*) + 132
15 libdispatch.dylib              0x4300 _dispatch_client_callout + 20
16 libdispatch.dylib              0x12998 _dispatch_main_queue_drain + 984
17 libdispatch.dylib              0x125b0 _dispatch_main_queue_callback_4CF + 44
18 CoreFoundation                 0x3720c __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
19 CoreFoundation                 0x33f18 __CFRunLoopRun + 1996
20 CoreFoundation                 0x33668 CFRunLoopRunSpecific + 608
21 GraphicsServices               0x35ec GSEventRunModal + 164
22 UIKitCore                      0x22c2b4 -[UIApplication _run] + 888
23 UIKitCore                      0x22b8f0 UIApplicationMain + 340
24 XXXXX                          0x9dbc main + 20 (AppDelegate.swift:20)
25 ???                            0x1c88dadcc (Missing)

This is how we're invoking Apple Pay with JudoKit :

let preAuthItem = JPPaymentSummaryItem(label: "XXXXX", amount: NSDecimalNumber(string: itemAmount), type: showAmount ? JPPaymentSummaryItemType.final : JPPaymentSummaryItemType.pending)
// Create Config Object
let applePayConfig = JPApplePayConfiguration(merchantId: Hub.JudoPay.applePayMerchantID, currency: "GBP", countryCode: "GB", paymentSummaryItems: [preAuthItem])
applePayConfig.supportedCardNetworks = [.AMEX, .visa, .masterCard]
// API Call Config
let config = JPConfiguration(judoID: Hub.JudoPay.appID, amount: JPAmount(itemAmount, currency: "GBP"), reference: reference)
config.challengeRequestIndicator = RemoteConfigManager.shared.challengeRequestForPayment.judoKey
config.applePayConfiguration = applePayConfig
config.isDelayedAuthorisation = kDelayedAuthEnabled
        
// tuple with (JPResponse?, JPError?)
let result = await judo.invokeApplePay(with: JPTransactionMode.preAuth, configuration: config)

We also make the same call with JPTransactionMode.payment elsewhere in our code, it's not clear at the moment which of these is failing but I imagine they share a lot of the same code behind the scenes.

@stefan-tudor
Copy link
Contributor

Hi @roryprior,
Thank you for raising this,

We're on it!

@stefan-tudor stefan-tudor added the bug Something isn't working label Nov 22, 2023
@roryprior
Copy link
Author

Are you getting any closer to finding the cause of this issue? It's been over 8 months and this is the largest source of crashes on our app. We don't have this issue with card payments, it's exclusively Apple Pay. It seems like you're not holding onto the reference for the apple pay view controller strongly somewhere and it's potluck whether it's still in memory when it's accessed.

@stefan-tudor
Copy link
Contributor

We've been unable to reproduce it so far, and we have it in our backlog as it requires further investigation,
But yes, from the stack trace you attached - it's an attempt to access a deallocated object.
Can you attach a complete implementation example that reproduces it? The snippet from above is incomplete;

@roryprior
Copy link
Author

roryprior commented Aug 1, 2024

For further context we're seeing this impact about 0.8%-1% of Apple Pay transactions in our app but this still constitutes 700-800 failed transactions each week which isn't really acceptable. With the relatively low failure rate reproduction is difficult, I'd suggest trying to automate it by creating transactions sequentially until failure, but respectfully we shouldn't have to provide a sample project for this we're using your SDK in the way we've indicated and don't see this issue at all on non-Apple Pay transactions so the problem is clearly in the SDK as the code paths are otherwise identical.

@stefan-tudor
Copy link
Contributor

stefan-tudor commented Aug 1, 2024

Are you keeping a strong reference to judo object, the instance you're using to call judo.invokeApplePay?

the code paths are otherwise identical - this is not true, ApplePay implementation is not sharing any code with the card transactions, except the Transaction API Client.

@stefan-tudor
Copy link
Contributor

In addition, I see that you're using await in your code snippet attached, and this library is an ObjC completion handler-style API. From what I remember, it's a bit more code needed to correctly interface Swift's async tasks and completion handler style APIs (https://github.com/swiftlang/swift-evolution/blob/main/proposals/0300-continuation.md).

@roryprior
Copy link
Author

roryprior commented Aug 2, 2024

Yes we keep a strong reference to the judo object, invokeApplePay presents as an async method in swift so I guess Apple is doing some behind the scenes magic here, but again we don't have this problem anywhere else we're using async/await with your SDK. When I said the code paths are otherwise identical I meant on our side. A couple of releases ago we changed how we were calling the Apple Pay preauth to try and work around this bug from our end but we're still seeing the same crashes unfortunately keeping our own strong reference to the AP view controller. With this method we're seeing the crash within the continuation executing the completion handler, which is where the crash is occurring using the direct invokeApplePay method.

@MainActor
private func preauthApplePay(configuration: JPConfiguration, on viewController: UIViewController) async throws -> (JPResponse?, JPError?) {
        return try await withUnsafeThrowingContinuation({
            (continuation: UnsafeContinuation<(JPResponse?, JPError?), Error>) -> Void in
            
            judoApplePayViewController = judoKit.applePayViewController(
                with: JPTransactionMode.preAuth,
                configuration: configuration,
                completion: { response, error in
                    continuation.resume(returning: (response, error))
                }
            )
            
            if let judoApplePayViewController = self.judoApplePayViewController {
                viewController.present(judoApplePayViewController, animated: true)
            } else {
                continuation.resume(throwing: JPError(code: 0, message: "Unable to invoke Apple Pay UI"))
            }
        })
    }

Crash log:

Crashed: com.apple.main-thread
0  XXXXX                          0x113764 closure #1 in closure #1 in XXXXXAPI.preauthApplePay(configuration:on:) + 359 (XXXXXAPI+Card.swift:359)
1  XXXXX                          0x1137d4 thunk for @escaping @callee_guaranteed (@guaranteed JPResponse?, @guaranteed JPError?) -> () + 80 (<compiler-generated>:80)
2  XXXXX                          0x3d700c __67-[JudoKit applePayViewControllerWithMode:configuration:completion:]_block_invoke.10 + 225 (JudoKit.m:225)
3  XXXXX                          0x40cbb0 __68-[JPApplePayController paymentAuthorizationViewControllerDidFinish:]_block_invoke + 77 (JPApplePayController.m:77)
4  UIKitCore                      0x56a0a4 -[UIViewController dismissViewControllerWithTransition:completion:] + 2132
5  UIKitCore                      0x311e14 -[_UIViewControllerTransitionCoordinator _applyBlocks:releaseBlocks:] + 128
6  UIKitCore                      0x311ba4 -[_UIViewControllerTransitionContext _runAlongsideCompletions] + 140
7  UIKitCore                      0x3119b8 -[_UIViewControllerTransitionContext completeTransition:] + 128
8  UIKitCore                      0x3b5e30 -[UITransitionView notifyDidCompleteTransition:] + 180
9  UIKitCore                      0x3b5ab4 -[UITransitionView _didCompleteTransition:] + 832
10 UIKitCore                      0x9091c __UIVIEW_IS_EXECUTING_ANIMATION_COMPLETION_BLOCK__ + 36
11 UIKitCore                      0x90800 -[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 624
12 UIKitCore                      0x8f518 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 436
13 UIKitCore                      0x9cb14 -[UIViewAnimationState animationDidStop:finished:] + 192
14 UIKitCore                      0x9cb84 -[UIViewAnimationState animationDidStop:finished:] + 304
15 QuartzCore                     0x4dc50 run_animation_callbacks(void*) + 132
16 libdispatch.dylib              0x3dd4 _dispatch_client_callout + 20
17 libdispatch.dylib              0x125a4 _dispatch_main_queue_drain + 988
18 libdispatch.dylib              0x121b8 _dispatch_main_queue_callback_4CF + 44
19 CoreFoundation                 0x56710 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
20 CoreFoundation                 0x53914 __CFRunLoopRun + 1996
21 CoreFoundation                 0x52cd8 CFRunLoopRunSpecific + 608
22 GraphicsServices               0x11a8 GSEventRunModal + 164
23 UIKitCore                      0x40a90c -[UIApplication _run] + 888
24 UIKitCore                      0x4be9d0 UIApplicationMain + 340
25 XXXXX                          0x21a8c8 main + 20 (AppDelegate.swift:20)
26 ???                            0x1b137de4c (Missing)


@roryprior
Copy link
Author

Another solution we're open to if this bug proves intractable is directly invoking Apple Pay ourselves if you could advise on what we'd need to do to achieve that while still working with the rest of your SDK and keeping PCI compliance on your side if that's possible.

@stefan-tudor stefan-tudor linked a pull request Aug 8, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

Successfully merging a pull request may close this issue.

3 participants