stripe-react-native icon indicating copy to clipboard operation
stripe-react-native copied to clipboard

Crash only in production when presenting the payment sheet

Open tux2nicolae opened this issue 2 years ago • 23 comments

Describe the bug

Screenshot 2023-08-30 at 21 51 21

Te crash seems to happen when calling STPAnalyticsClient.sharedClient.logPaymentSheetEvent(event: .paymentSheetLoadStarted) (on debug it seems that there are some guards guard type(of: self).shouldCollectAnalytics() which prevents the crash from happening)

tux2nicolae avatar Aug 30 '23 18:08 tux2nicolae

Hi! Can you share your version information as well as how to reproduce the crash?

charliecruzan-stripe avatar Aug 30 '23 18:08 charliecruzan-stripe

Sure,

"@stripe/stripe-react-native": "^0.30.0", "react-native": "0.72.4",

It seems to reproduce every time on my side on a real device, unfortunately I can't figure it out how to make it actually work

tux2nicolae avatar Aug 30 '23 19:08 tux2nicolae

Please share the code you're using to call initPaymentSheet and presentPaymentSheet

charliecruzan-stripe avatar Aug 30 '23 19:08 charliecruzan-stripe

  const initializePaymentSheet = useCallback(
    async (
      order: any,
      customer: string,
      ephemeralKey: string,
      paymentIntent: string,
    ) => {
      if (!isApp) {
        return;
      }

      const iosCartItems: Array<ApplePay.CartSummaryItem> = order.items
        .map((item: {name: string; price: number; quantity: number}) => ({
          paymentType: 'Immediate',
          label: `${item.quantity} x ${item.name}`,
          amount: item.price.toFixed(2),
        }))
        .concat(
          order.fees.map((fee: {type: ''; name: string; price: number}) => ({
            paymentType: 'Immediate',
            label: fee.name,
            amount: fee.price.toFixed(2),
          })),
        )
        // last item is total
        // https://developer.apple.com/documentation/passkit/pkpaymentrequest/1619231-paymentsummaryitems
        // 1. Set the grand total amount to the sum of all the other items in the array.
        // 2. Set the grand total label to the name of your company.
        .concat([
          {
            paymentType: 'Immediate',
            label: `Menoo`,
            amount: order.total.toFixed(2),
          },
        ]);

      const {error} = await initPaymentSheet({
        merchantDisplayName: 'Menoo',
        customerId: customer,
        customerEphemeralKeySecret: ephemeralKey,
        paymentIntentClientSecret: paymentIntent,
        allowsDelayedPaymentMethods: false,
        defaultBillingDetails: {
          address: {
            country: billingInfo?.country || 'RO',
          },
        },
        billingDetailsCollectionConfiguration: {
          address: PaymentSheet.AddressCollectionMode.NEVER,
        },
        applePay: {
          merchantCountryCode: 'RO',
          cartItems: iosCartItems,
        },
        googlePay: {
          merchantCountryCode: 'RO',
          testEnv: __DEV__,
        },
      });

      if (error) {
        throw error;
      }
    },
    [isApp, billingInfo, initPaymentSheet],
  );
  const pay = useCallback(
    async (orderId: string) => {
      if (!isApp) {
        return true;
      }

      const {order, customer, ephemeralKey, paymentIntent} =
        await ClientApi.delivery.fetchPaymentSheetParams(orderId);

      await initializePaymentSheet(
        order,
        customer,
        ephemeralKey,
        paymentIntent,
      );

      const {error} = await presentPaymentSheet();
      if (error) {
        throw error;
      }

      return true;
    },
    [isApp, navigation, presentPaymentSheet, showSnackbar, clearCart],
  );

Also I need to mention that it seems to happen only on IOS.

tux2nicolae avatar Aug 30 '23 19:08 tux2nicolae

In STPAnalyticsClient this seems to be the analytics event:

https://q.stripe.com/?analytics_ua=analytics.stripeios-1.0
                     &app_name=Menoo
                     &app_version=2.8.2
                     &apple_pay_enabled=true
                     &bindings_version=23.12.0
                     &device_type=iPhone14%2C4
                     &event=mc_complete_init_customer_applepay
                     &install=C
                     &is_decoupled=false
                     &locale=en_RO
                     &mpe_config%5Ballows_delayed_payment_methods%5D=false
                     &mpe_config%5Bappearance%5D%5Bborder_width%5D=false
                     &mpe_config%5Bappearance%5D%5Bcolors%5D=false
                     &mpe_config%5Bappearance%5D%5Bcorner_radius%5D=false
                     &mpe_config%5Bappearance%5D%5Bfont%5D=false
                     &mpe_config%5Bappearance%5D%5Bprimary_button%5D=false
                     &mpe_config%5Bappearance%5D%5Bshadow%5D=false
                     &mpe_config%5Bappearance%5D%5Busage%5D=false
                     &mpe_config%5Bapple_pay_config%5D=true
                     &mpe_config%5Bbilling_details_collection_configuration%5D%5Baddress%5D=never
                     &mpe_config%5Bbilling_details_collection_configuration%5D%5Battach_defaults%5D=false
                     &mpe_config%5Bbilling_details_collection_configuration%5D%5Bemail%5D=automatic
                     &mpe_config%5Bbilling_details_collection_configuration%5D%5Bname%5D=automatic
                     &mpe_config%5Bbilling_details_collection_configuration%5D%5Bphone%5D=automatic
                     &mpe_config%5Bcustomer%5D=true&mpe_config%5Bdefault_billing_details%5D=true
                     &mpe_config%5Breturn_url%5D=false
                     &mpe_config%5Bsave_payment_method_opt_in_behavior%5D=automatic
                     &mpe_config%5Bstyle%5D=0
                     &network_type=Wi-Fi
                     &ocr_type=none
                     &os_version=16.6
                     &pay_var=legacy
                     &plugin_type=react-native
                     &product_usage%5B0%5D=PaymentSheet
                     &publishable_key=*****************************
                     &session_id=*****************************

p.s I've censored publishable_key and session_id p.s.2 (edited) Actually, it seems to be one of the failing ones but I'm not sure if this is the only one or is really crashing by this event, I've just tried to collect as many data before the actual crash

tux2nicolae avatar Aug 30 '23 19:08 tux2nicolae

I see- can you try stepping through this method in the debugger to see where the bad access is happening? I'm not able to reproduce this on my end

charliecruzan-stripe avatar Aug 30 '23 19:08 charliecruzan-stripe

@charliecruzan-stripe I'll try to investigate more on this issue after 18:00 (GMT+3), including stepping through the method you provided and give you more information

tux2nicolae avatar Aug 31 '23 08:08 tux2nicolae

Hi, I'm back

The problem seems to be when exiting from the function you mention above and it's parent, apparently when some object is destructed, unfortunately my debugging skills with swift aren't helping me, but it seems the crash is happening here in line 77 (but I'm not really sure)

Screenshot 2023-08-31 at 19 56 17

Also noticed a few strange things in logs

2023-08-31 19:52:05.968656+0300 Menoo[8008:2146590] Task <F80296DA-89DB-4742-B56F-6A0C2CCB2425>.<18> finished with error [-997] Error Domain=NSURLErrorDomain Code=-997 "Lost connection to background transfer service" UserInfo={NSErrorFailingURLStringKey=https://q.stripe.com?analytics_ua=analytics.stripeios-1.0&app_name=Menoo&app_version=2.8.2&apple_pay_enabled=true&bindings_version=23.12.0&device_type=iPhone14%2C4&event=mc_load_started&install=C&is_decoupled=false&locale=en_RO&network_type=4G&ocr_type=none&os_version=16.6&pay_var=legacy&plugin_type=react-native&product_usage%5B0%5D=PaymentSheet&publishable_key=******************&session_id=******************, NSErrorFailingURLKey=https://q.stripe.com?analytics_ua=analytics.stripeios-1.0&app_name=Menoo&app_version=2.8.2&apple_pay_enabled=true&bindings_version=23.12.0&device_type=iPhone14%2C4&event=mc_load_started&install=C&is_decoupled=false&locale=en_RO&network_type=4G&ocr_type=none&os_version=16.6&pay_var=legacy&plugin_type=react-native&product_usage%5B0%5D=PaymentSheet&publishable_key=******************&session_id=******************, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "BackgroundDataTask <F80296DA-89DB-4742-B56F-6A0C2CCB2425>.<18>",
    "LocalDataTask <F80296DA-89DB-4742-B56F-6A0C2CCB2425>.<18>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundDataTask <F80296DA-89DB-4742-B56F-6A0C2CCB2425>.<18>, NSLocalizedDescription=Lost connection to background transfer service}
2023-08-31 19:53:09.149754+0300 Menoo[8008:2146573] [Client] Updating selectors after delegate addition failed with: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service with pid 97 named com.apple.commcenter.coretelephony.xpc was invalidated from this process." UserInfo={NSDebugDescription=The connection to service with pid 97 named com.apple.commcenter.coretelephony.xpc was invalidated from this process.}

p.s I've censored my publishable key and session id

tux2nicolae avatar Aug 31 '23 17:08 tux2nicolae

@charliecruzan-stripe if you can give me some directions I'm more than willing to help investigating this, even more this is a blocker for us

Are there any other steps I can do to get more informations? Until then I will try to change parameters I'm instantiating the payment sheet, maybe I'll get it working ...

tux2nicolae avatar Aug 31 '23 17:08 tux2nicolae

I would pare down your payment sheet calls to the minimum required params and go from there

I would also be curious if changing versions (say to maybe 0.25.0) would change anything. That might help us to find what changed

charliecruzan-stripe avatar Aug 31 '23 17:08 charliecruzan-stripe

I've tested with 0.25.0, indeed it seems not to crash anymore, and seems to have an error instead IMG_CDAF6AC1512F-1

I'll try without AddressCollectionMode and get back here with updates

tux2nicolae avatar Aug 31 '23 17:08 tux2nicolae

False alarm, I'm sorry, the parameter with AddressCollectionMode didn't even exist on 25.0, without it all seems to be working as expected

IMG_4BB88DFF1828-1

tux2nicolae avatar Aug 31 '23 17:08 tux2nicolae

Ok so the latest version with these parameters still crashes

      const {error} = await initPaymentSheet({
        merchantDisplayName: 'Menoo',
        customerId: customer,
        customerEphemeralKeySecret: ephemeralKey,
        paymentIntentClientSecret: paymentIntent,
        allowsDelayedPaymentMethods: false,
        // defaultBillingDetails: {
        //   address: {
        //     country: billingInfo?.country || 'RO',
        //   },
        // },
        // billingDetailsCollectionConfiguration: {
        //   address: PaymentSheet.AddressCollectionMode.NEVER,
        // },
        applePay: {
          merchantCountryCode: 'RO',
          //cartItems: iosCartItems,
        },
        // googlePay: {
        //   merchantCountryCode: 'RO',
        //   testEnv: __DEV__,
        // },
      });

and with these as well

      const {error} = await initPaymentSheet({
        merchantDisplayName: 'Menoo',
        //customerId: customer,
        //customerEphemeralKeySecret: ephemeralKey,
        paymentIntentClientSecret: paymentIntent,
        // allowsDelayedPaymentMethods: false,
        // defaultBillingDetails: {
        //   address: {
        //     country: billingInfo?.country || 'RO',
        //   },
        // },
        // billingDetailsCollectionConfiguration: {
        //   address: PaymentSheet.AddressCollectionMode.NEVER,
        // },
        // applePay: {
        //   merchantCountryCode: 'RO',
        //   //cartItems: iosCartItems,
        // },
        // googlePay: {
        //   merchantCountryCode: 'RO',
        //   testEnv: __DEV__,
        // },
      });

@charliecruzan-stripe I'm out of ideas, if I can test anything else just let me know, regarding binary search for versions to check which is the culprit which one is the next version do you want me to test?

tux2nicolae avatar Aug 31 '23 18:08 tux2nicolae

0.28.0, seems to work as expected

tux2nicolae avatar Aug 31 '23 19:08 tux2nicolae

In conclusion 0.29.0 seems to be the problem, I'll remain on 0.28.0 until this issue is fixed ...

Unfortunately I can't isolate this issue to provide you with more details about how this happens, but just let me know if there is anything else I can help you with.

tux2nicolae avatar Aug 31 '23 19:08 tux2nicolae

Stil happens with the latest version 0.31.1 :(

tux2nicolae avatar Sep 10 '23 09:09 tux2nicolae

After more investigations I think it may have to do with the fact that a background dataTask is used in conjunction with

// Set up a configuration with a background session ID
       let configuration = URLSessionConfiguration.background(withIdentifier: "com.stripe.analyticsclient")

Because it may appear to fail as mentioned before:

Also noticed a few strange things in logs

2023-08-31 19:52:05.968656+0300 Menoo[8008:2146590] Task <F80296DA-89DB-4742-B56F-6A0C2CCB2425>.<18> finished with error [-997] Error Domain=NSURLErrorDomain Code=-997 "Lost connection to background transfer service" UserInfo={NSErrorFailingURLStringKey=https://q.stripe.com?analytics_ua=analytics.stripeios-1.0&app_name=Menoo&app_version=2.8.2&apple_pay_enabled=true&bindings_version=23.12.0&device_type=iPhone14%2C4&event=mc_load_started&install=C&is_decoupled=false&locale=en_RO&network_type=4G&ocr_type=none&os_version=16.6&pay_var=legacy&plugin_type=react-native&product_usage%5B0%5D=PaymentSheet&publishable_key=******************&session_id=******************, NSErrorFailingURLKey=https://q.stripe.com?analytics_ua=analytics.stripeios-1.0&app_name=Menoo&app_version=2.8.2&apple_pay_enabled=true&bindings_version=23.12.0&device_type=iPhone14%2C4&event=mc_load_started&install=C&is_decoupled=false&locale=en_RO&network_type=4G&ocr_type=none&os_version=16.6&pay_var=legacy&plugin_type=react-native&product_usage%5B0%5D=PaymentSheet&publishable_key=******************&session_id=******************, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "BackgroundDataTask <F80296DA-89DB-4742-B56F-6A0C2CCB2425>.<18>",
    "LocalDataTask <F80296DA-89DB-4742-B56F-6A0C2CCB2425>.<18>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundDataTask <F80296DA-89DB-4742-B56F-6A0C2CCB2425>.<18>, NSLocalizedDescription=Lost connection to background transfer service}
2023-08-31 19:53:09.149754+0300 Menoo[8008:2146573] [Client] Updating selectors after delegate addition failed with: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service with pid 97 named com.apple.commcenter.coretelephony.xpc was invalidated from this process." UserInfo={NSDebugDescription=The connection to service with pid 97 named com.apple.commcenter.coretelephony.xpc was invalidated from this process.}

p.s I do have 'Background fetch' capability but not 'Background processing'

edited:

From a few unverified resources there seems to be a problem with dataTask for background tasks

I've tried to use

let task: URLSessionDownloadTask = urlSession.downloadTask(with: request as URLRequest)

instead of

let task: URLSessionDataTask = urlSession.dataTask(with: request as URLRequest)

based on a few unverified resources

NSURLSessionDataTask : Data tasks exchange data using NSData. NSURLSessionDataTask is not supported in Background Sessions because it does not write the content in a form of a local file(stored in memory). Therefore it can not be resumed later onwards .

Resources : https://stackoverflow.com/questions/61232998/should-i-choose-urlsessiondatatask-or-urlsessiondownloadtask-for-getting-image https://stackoverflow.com/questions/51684021/swift-background-data-task https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1407496-background https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background

but without any luck :(

tux2nicolae avatar Sep 10 '23 11:09 tux2nicolae

I've tried reverting this MR https://github.com/stripe/stripe-ios/pull/2698, by that I've got rid of the errors from the log but the crash is still present :(

tux2nicolae avatar Sep 11 '23 20:09 tux2nicolae

So, apparently I was wrong with investigating the analytics event, the problem seems to happen after executing the line before queue-ing some code to run in the UI thread using Task { @MainActor in ... }. I saw that stripe already uses some queue DispatchQueue.main.async {} to call the .present() function, maybe it has something to do with that. Also apparently it reproduces only on release build, I thought it could be a bug in the compiler and updated the Xcode from 14.3.1 to 15.0 beta 8 without luck, the problem still persists.

        let loadingStartDate = Date()
        let loadingStartDate2 = Date()
        let loadingStartDate3 = Date()
        let loadingStartDate4 = Date() <--------------------------- after executing this line
  
        Task { @MainActor in                <--------------------------- any line before this one will crash
            print("This is on the main actor.")
        }
        
        let loadingStartDate5 = Date()
        
        STPAnalyticsClient.sharedClient.logPaymentSheetEvent(event: .paymentSheetLoadStarted)

        Task { @MainActor in
            do {
                // Fetch PaymentIntent, SetupIntent, or ElementsSession
                async let _intent = fetchIntent(mode: mode, configuration: configuration)
                ...

@charliecruzan-stripe do you have any thoughts on this, or on what should I investigate further?

tux2nicolae avatar Sep 12 '23 20:09 tux2nicolae

I'm insisting in this issue to be investigated because I'm completely blocked in using Stripe, the problem is that I can't use the 0.28.0 version because of another bug here https://github.com/stripe/stripe-android/issues/7124 where the google pay button does not work (it's fixed in the current version)

So I'm forced to chose between current version without Apple Pay or the 0.28.0 version without Google Pay.

tux2nicolae avatar Sep 13 '23 09:09 tux2nicolae

You can use the current version without apple pay? So this works if you don't pass applePay to initPaymentSheet?

charliecruzan-stripe avatar Sep 13 '23 17:09 charliecruzan-stripe

Unfortunately no, it was a mistake in my previous message, I wanted to say that I'm blocked on IOS

Just tried again with just these parameters and the crash still occurs

        merchantDisplayName: 'Menoo',
        customerId: customer,
        customerEphemeralKeySecret: ephemeralKey,
        paymentIntentClientSecret: paymentIntent,

edited ---------------------

Note: In emulator release version apparently crashes too (tested on IOS 17), so it does not have to do with my phone OS

tux2nicolae avatar Sep 13 '23 17:09 tux2nicolae

I'll leave these here for reference (maybe there is indeed some issue in the swift language)

https://developer.apple.com/forums/thread/697070 https://forums.swift.org/t/async-await-crash-on-ios14-with-xcode-13-2-1/54541/94

I don't know how you can not reproduce it, on our side it reproduces every time (on release configuration)

tux2nicolae avatar Sep 13 '23 20:09 tux2nicolae

Hello, we have some updates on this.

At this point we where somehow forced to investigate this, because we upgraded react-native to 0.74.2 and were forced to use the latest stripe-react-native.

After a few days of struggle we came to the conclusion that the problem was with the libswift_Concurrency.dylib and the fact it is soft linked, we noticed that the crash wasn't reproducing in case we use IOS 15 as the deployment target version, in IOS 15 the libswift_Concurrency.dylib is already on the system, but in case you use a deployment target version lower than 15, the complier adds the lib in your app for backporting to versions lower than 15, and in our case apparently that lib was used on IOS 15 and up as well instead of the system one.

In our case changing this

LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";

To this

LD_RUNPATH_SEARCH_PATHS = "$(inherited) /usr/lib/swift @executable_path/Frameworks";

Solved our problem, on devices starting with IOS 15 the lib will be resolved from the user device, and the backported lib will be used only for lower versions (which is the intended behaviour).

So, I will close this issue as It is more a problem with configuration then a bug in stripe-react-native library, but decided to share our investigation in case someone else has the same issue.

Thank you

tux2nicolae avatar Jun 29 '24 16:06 tux2nicolae