paypal / paypalcheckout-ios

Need to add Native Checkout to your iOS Application? We can help!
http://www.paypal.com
Other
69 stars 55 forks source link

[🐞] None of the Checkout callbacks are called when running the app on a real device #72

Closed santiagofm closed 1 year ago

santiagofm commented 2 years ago

Hey guys!

I'm seeing this really weird behavior on our own (React Native) app, but before creating this issue i noticed I can repro it on your sample app so i'll stick to it.

🐞 Describe the Bug

πŸ”¬ Minimal Reproduction

  1. clone https://github.com/paypal/paypalcheckout-samples-ios
  2. Set both clientId & returnUrl on the PayPal class in the Paypal.API file
  3. Modify the CheckoutViewController's startNativeCheckout to use a pre-created orderId:
    /// Request an ECToken/orderID/payToken with PayPal Orders API,
    /// then checkout with `ECToken`/`orderID`/`payToken`
    case 1:
      Checkout.start(createOrder: { action in
        action.set(orderId: "66S90911NR6185831")
      },
      onApprove: { approval in
        self.processOrderActions(with: approval)
      },
      onCancel: {
        print("Checkout cancelled")
      },
      onError: { errorInfo in
        print("Checkout failed with error info \(errorInfo.error.localizedDescription)")
      }
    )
  4. Run the app and checkout

πŸ˜• Actual Behavior

The SDK just doesn't seem to want to deal with the checkout and just opens a webview, ui gets a little broken too and I can see multiple "Unable to simultaneously satisfy constraints." warnings on the Xcode console.

See recording:

πŸ€” Expected Behavior

I would expect the checkout to succeed and the callbacks to fire, like they do when running on a simulator:

🌍 Environment

βž• Additional Context

Let me know if there's any other info i can provide and i'll do so, thanks!

jonathajones commented 2 years ago

Hi @santiagofm ! Thanks for opening the issue. We'll start investigating today to understand what's going on.

Thanks!

jonathajones commented 2 years ago

@santiagofm After you finish checkout on device - what are the values of State.correlationIDs? There will be a lot of identifiers in there, and knowing what they are will help us debug what went wrong.

Thanks!

santiagofm commented 2 years ago

Well getting those values might get tricky since neither onApprove nor onError are called! πŸ˜•

jonathajones commented 2 years ago

It's inelegant, but I'd suggest even putting a timer or something in place just so you can get the correlationID values. Since they're statically set, you can reference them from anywhere. Specifically, these two would be helpful to know:

 /// Our correlation id associated when checking funding eligibility for payment buttons
  public internal(set) var fundingEligibilityDebugID: String?

  /// Our correlation id associated when checking eligibility.
  public internal(set) var eligibilityDebugID: String?
santiagofm commented 2 years ago

Ok, so i put a breakpoint right before calling Checkout.start and tapped on the "Checkout with ECToken" twice without closing the app:

order was 702242514U747841S

jonathajones commented 2 years ago

Thank you! I think we can use this to better understand what's happening. I'll let you know if we need more information from you.

santiagofm commented 2 years ago

hello @jonathajones! is there any update regarding this issue ?

jonathajones commented 2 years ago

Hi @santiagofm - Yes, the issue has been identified. We're working on a fix now, and it will be included in the next release. Expect it next week.

santiagofm commented 2 years ago

Hi @santiagofm - Yes, the issue has been identified. We're working on a fix now, and it will be included in the next release. Expect it next week.

That's good to know, thank you! Looking forward for that release. In the meantime, is there any way we could workaround it?

minhthenguyen commented 2 years ago

@santiagofm Yes, for now the workaround is to create your order with an application_context.return_url.

If you create the order client-side, you can do it like so in the createOrder callback:

createOrder: { action in
   let order = OrderRequest(intent: <some_intent>, purchaseUnits: <some_purchase_units>, applicationContext: OrderApplicationContext(returnUrl: <this can be any string>))
   action.create(order: order)
}

If you create the order from your server using Orders v2 API, make sure to have application_context.return_url in the request body:

curl --location --request POST 'https://api.sandbox.paypal.com/v2/checkout/orders/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <access_token>' \
--data-raw '{
    "intent": "AUTHORIZE",
    "purchase_units": [
        {
            "amount": {
                "currency_code": "USD",
                "value": "10.00"
            }
        }
    ],
    "application_context": {
        "return_url": "any://string"
    }
}'

but actually we're gonna do a new release today with the fix for this issue :smile:

minhthenguyen commented 2 years ago

@santiagofm version 0.77.0 containing this fix has been released, can you help us verify the issue has been resolved for you in latest version? Thank you :smile:

santiagofm commented 2 years ago

@santiagofm version 0.77.0 containing this fix has been released, can you help us verify the issue has been resolved for you in latest version? Thank you πŸ˜„

wohoo! I'll upgrade asap and let you know, thanks!

santiagofm commented 2 years ago

@minhthenguyen Here's what I see:

Although this is great πŸ‘πŸ» , is there anyway we can prevent it from going to the web fallback? What could be the cause for the native sdk not handling the checkout flow?

We'd not only like for our members to have the best UX– which seems to be the one shown when I run the app on a simulator where everything is handled natively– but, most importantly, to not have to signin to paypal every time they want to checkout πŸ˜ƒ

Thank you!

jonathajones commented 2 years ago

Hi @santiagofm - Glad the no-callback issue was resolved. Regarding the double login - are you seeing that on sandbox, or can you reproduce that in prod?

Regarding the fallback flow - before launching, the SDK makes an eligibility request to see if the transaction can be handled natively. There are a number of reasons that a transaction might not be eligible for the native experience - mostly related to regional compliance or transaction type. Currently, we support one time payment transactions in NA and EU - other regions or transaction types (ie billing agreements) would default to a web fallback flow for now. Work is underway to expand our native experience availability.

santiagofm commented 2 years ago

Hi @santiagofm - Glad the no-callback issue was resolved. Regarding the double login - are you seeing that on sandbox, or can you reproduce that in prod?

Sandbox, yes. Unfortunately I'm not able to test this on production as I don't have credentials yet. By "double login" I mean the first time it showed me an alert telling me the app wants to use "paypal.com" to signin(like the following GIF), I did, then after the webview dismissed it displayed yet another webview where I needed to login again and was able to checkout successfully.

This was the first-time experience though, the next ones it will immediately present me with a webview for the web-based checkout experience.

Regarding the fallback flow - before launching, the SDK makes an eligibility request to see if the transaction can be handled natively. There are a number of reasons that a transaction might not be eligible for the native experience - mostly related to regional compliance or transaction type. Currently, we support one time payment transactions in NA and EU - other regions or transaction types (ie billing agreements) would default to a web fallback flow for now. Work is underway to expand our native experience availability.

Thank you, this is really helpful! Looking forward for that to happen πŸ˜„

santiagofm commented 2 years ago

Regarding the fallback flow - before launching, the SDK makes an eligibility request to see if the transaction can be handled natively. There are a number of reasons that a transaction might not be eligible for the native experience - mostly related to regional compliance or transaction type. Currently, we support one time payment transactions in NA and EU - other regions or transaction types (ie billing agreements) would default to a web fallback flow for now. Work is underway to expand our native experience availability.

And as a side note, we use the native android sdk too since our product is powered by React Native and I couldn't help but notice that transactions seem to be handled nicely by the native SDK 🀷🏻 . Not sure what you mentioned is an iOS-only restriction but putting it out there..

jonathajones commented 2 years ago

Ah interesting - for a successful transaction, could you give me the values you see in State.correlationIDs.fundingEligibilityDebugID and State.correlationIDs.eligibilityDebugID? We'll be able to use those to understand why you would see different experiences in Android and iOS.

jonathajones commented 2 years ago

Additionally - the manifest for the sandbox environment was recently updated. It should resolve the double login issue you've been experiencing - please let me know if you're still seeing it.

santiagofm commented 2 years ago

Ah interesting - for a successful transaction, could you give me the values you see in State.correlationIDs.fundingEligibilityDebugID and State.correlationIDs.eligibilityDebugID? We'll be able to use those to understand why you would see different experiences in Android and iOS.

Both of them are nil. And the approval object still shows some error with the returnURL, not sure if that matters:

(PayPalCheckout.Approval) $R10 = 0x0000000281549ec0 {
  ObjectiveC.NSObject = {
    isa = PPCApproval
  }
  data = 0x00000002825388c0 {
    ObjectiveC.NSObject = {
      isa = PPCApprovalData
    }
    cart = nil
    buyer = nil
    payerID = "ATBKQDWBULQB8"
    ecToken = "3J265108RX6675340"
    intent = "sale"
    returnURL = Failed to get the 'some' field from optional 'returnURL'
    paymentID = nil
  }
  actions = 0x000000028155cee0 {
    ObjectiveC.NSObject = {
      isa = PPCOrderActions
    }
  }
}

The transaction was successfull though; callbacks fired as expected.

santiagofm commented 2 years ago

@jonathajones Last night we tested on the Live environment–as part of the app review process– and we did see the web based experience(as expected) w/ the double login "issue"; the transaction was successful though.

jonathajones commented 2 years ago

Got it - I'll sync with the web auth team to investigate and let you know what we find. Are you able to provide the correlation IDs from your test in the live environment?

santiagofm commented 2 years ago

Got it - I'll sync with the web auth team to investigate and let you know what we find. Are you able to provide the correlation IDs from your test in the live environment?

Unfortunately no πŸ˜•

jonathajones commented 2 years ago

Hi @santiagofm - We looked into some of the logs, and it looks like you're receiving the web fallback experience because of an ineligible buyer country associated with the buyer account in sandbox. Are you using the same end user account for testing across android and iOS?

We are still looking into the double login on web fallback. Just wanted to give you an update!

quintonpryce commented 2 years ago

Hi @jonathajones I'm experiencing this same "double login issue" as @santiagofm. I wanted to add one thing here, whenever I start up the checkout I get the following errors. I did attempt to fix it by adding the query schemes but it didn't fix my issue so I removed them.

2021-11-03 06:37:10.150008-0700 1v1Me[6261:1905912] -canOpenURL: failed for URL: "cydia://" - error: "This app is not allowed to query for scheme cydia"
2021-11-03 06:37:10.150506-0700 1v1Me[6261:1905912] -canOpenURL: failed for URL: "sileo://" - error: "This app is not allowed to query for scheme sileo"
2021-11-03 06:37:10.151050-0700 1v1Me[6261:1905912] -canOpenURL: failed for URL: "undecimus://" - error: "This app is not allowed to query for scheme undecimus"
2021-11-03 06:37:10.151487-0700 1v1Me[6261:1905912] -canOpenURL: failed for URL: "zbra://" - error: "This app is not allowed to query for scheme zbra"
2021-11-03 06:37:10.151938-0700 1v1Me[6261:1905912] -canOpenURL: failed for URL: "tweakbox://" - error: "This app is not allowed to query for scheme tweakbox"
2021-11-03 06:37:10.152808-0700 1v1Me[6261:1905912] -canOpenURL: failed for URL: "emus4u://" - error: "This app is not allowed to query for scheme emus4u"
2021-11-03 06:37:10.197543-0700 1v1Me[6261:1906303] [assertion] Error acquiring assertion: <Error Domain=RBSServiceErrorDomain Code=1 "target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit" UserInfo={NSLocalizedFailureReason=target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit}>
2021-11-03 06:37:10.197592-0700 1v1Me[6261:1906303] [ProcessSuspension] 0x11bbfa040 - ProcessAssertion: Failed to acquire RBS assertion 'ConnectionTerminationWatchdog' for process with PID=6264, error: Error Domain=RBSServiceErrorDomain Code=1 "target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit" UserInfo={NSLocalizedFailureReason=target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit}

On Create:

(lldb) p State.correlationIDs.fundingEligibilityDebugID
(String?) $R3 = nil
(lldb) p State.correlationIDs.eligibilityDebugID
(String?) $R4 = nil

On Approve:

(lldb) p State.correlationIDs.fundingEligibilityDebugID
(String?) $R6 = nil
(lldb) p State.correlationIDs.eligibilityDebugID
(String?) $R7 = nil

(PayPalCheckout.Approval) $R9 = 0x0000000281087fc0 {
  ObjectiveC.NSObject = {
    isa = PPCApproval
  }
  data = 0x00000002820a5ea0 {
    ObjectiveC.NSObject = {
      isa = PPCApprovalData
    }
    cart = nil
    buyer = nil
    payerID = "LHDFWLMVSLHKU"
    ecToken = "1NC14493AR9257343"
    intent = "sale"
    returnURL = Failed to get the 'some' field from optional 'returnURL'
    paymentID = nil
  }
  actions = 0x0000000281084540 {
    ObjectiveC.NSObject = {
      isa = PPCOrderActions
    }
  }
}
quintonpryce commented 2 years ago

This is the approval object when I go through the simulator with the same order creation steps:

(PayPalCheckout.Approval) $R4 = 0x00006000036c5100 {
  ObjectiveC.NSObject = {
    isa = PPCApproval
  }
  data = 0x000060000064f2a0 {
    ObjectiveC.NSObject = {
      isa = PPCApprovalData
    }
    cart = some {
      cartID = "48N61707VR1242034"
      intent = "SALE"
      _items = 0 values {}
      shippingMethods = 0 values {}
      amounts = some {
        handlingFee = 0x00006000013dfc00 {
          ObjectiveC.NSObject = {
            isa = PPCUnitAmount
          }
          value = "0.00"
          currencyCodeString = "USD"
          currencyFormat = nil
        }
        insurance = 0x00006000013de160 {
          ObjectiveC.NSObject = {
            isa = PPCUnitAmount
          }
          value = "0.00"
          currencyCodeString = "USD"
          currencyFormat = nil
        }
        shippingAndHandling = 0x00006000013dfde0 {
          ObjectiveC.NSObject = {
            isa = PPCUnitAmount
          }
          value = "0.00"
          currencyCodeString = "USD"
          currencyFormat = nil
        }
        subtotal = 0x00006000013de700 {
          ObjectiveC.NSObject = {
            isa = PPCUnitAmount
          }
          value = "0.00"
          currencyCodeString = "USD"
          currencyFormat = nil
        }
        tax = 0x00006000013de220 {
          ObjectiveC.NSObject = {
            isa = PPCUnitAmount
          }
          value = "0.00"
          currencyCodeString = "USD"
          currencyFormat = nil
        }
        total = 0x00006000013dcb40 {
          ObjectiveC.NSObject = {
            isa = PPCUnitAmount
          }
          value = "10.00"
          currencyCodeString = "USD"
          currencyFormat = nil
        }
      }
      _cancelURL = nil
      _returnURL = nil
      _total = 0x00006000013de7c0 {
        ObjectiveC.NSObject = {
          isa = PPCUnitAmount
        }
        value = "10.00"
        currencyCodeString = "USD"
        currencyFormat = nil
      }
      paymentID = nil
      billingToken = nil
      billingType = nil
    }
    buyer = some {
      userId = "LHDFWLMVSLHKU"
      name = nil
      _locale = nil
      _email = some {
        stringValue = "quintonpryce@gmail.com"
      }
      profileImage = nil
      _addresses = nil
    }
    payerID = "LHDFWLMVSLHKU"
    ecToken = "48N61707VR1242034"
    intent = "SALE"
    returnURL = Failed to get the 'some' field from optional 'returnURL'
    paymentID = nil
  }
  actions = 0x00006000036c4340 {
    ObjectiveC.NSObject = {
      isa = PPCOrderActions
    }
  }
}
quintonpryce commented 2 years ago

@jonathajones if you need to live debug this I can either add you to a slack channel or we can do a google meet to try and iterate a bit quicker on this issue. Let me know whatever works for you, happy to continue to chat through this thread too. I will check back hourly on EST.

jonathajones commented 2 years ago

We're still engaging with the web team on double login scenario, I'll update here when we have more information to share. In our next release we will have a fix in place to properly set the State.correlationIDs.eligibilityDebugID value.

quintonpryce commented 2 years ago

If you want I can point my sdk to a different branch to get you the eligibility debug id quicker.

jonathajones commented 2 years ago

@quintonpryce Two questions:

  1. What's the merchant and user region?
  2. Are you using the billing agreement flow?
quintonpryce commented 2 years ago

@jonathajones

  1. Our merchant & user region is USA
  2. No we are not using the bulling agreement flow
jonathajones commented 2 years ago

@quintonpryce What version of the SDK are you on?

quintonpryce commented 2 years ago

@jonathajones 0.77

quintonpryce commented 2 years ago

@jonathajones Any updates on this issue?

jonathajones commented 2 years ago

Sorry for the radio silence - we're still actively in progress resolving the issue. Will update you with an ETA once the fix is identified and in place.

santiagofm commented 2 years ago

hey @jonathajones πŸ‘‹πŸ» ! Was this addressed in the 0.79 release ?

jonathajones commented 2 years ago

Hi @santiagofm ! Thanks for checking back in. The 0.79.0 release does contain a fix to the issue of having to log in twice. The issue was related to improper scoping on some access tokens created by the SDK.

Please do integrate with it and let me know if you're still experiencing the issue. Note - the 0.79.0 release was created on Xcode 13. We'll be cutting a new version of the SDK next week generated from Xcode 12.4 - the bump was unintentional.

alihanaktay commented 2 years ago

Same double login issue happens on both production & sandbox environment, any update on that?

jonathajones commented 2 years ago

@alihanaktay - What version of the checkout SDK are you on?

If you're still experiencing this after updating to version 0.79.0, please open a new issue with reproduction steps. There have been a couple issues triaged here and I'd like to keep the issues focused on a single problem, when possible.

alihanaktay commented 2 years ago

Hi @jonathajones, the version number is 0.79.0,

Since other users open an issue about that I'm not opening new issue.