stripe-react-native
stripe-react-native copied to clipboard
Crash only in production when presenting the payment sheet
Describe the bug
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)
Hi! Can you share your version information as well as how to reproduce the crash?
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
Please share the code you're using to call initPaymentSheet and presentPaymentSheet
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.
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
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 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
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)
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
@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 ...
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
I've tested with 0.25.0, indeed it seems not to crash anymore, and seems to have an error instead
I'll try without AddressCollectionMode and get back here with updates
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
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?
0.28.0, seems to work as expected
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.
Stil happens with the latest version 0.31.1 :(
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 :(
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 :(
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?
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.
You can use the current version without apple pay? So this works if you don't pass applePay to initPaymentSheet?
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
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)
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