Error when user closes Stripe popup via (x)

This is most puzzling. The end-user should be able to reject a charge, at the last minute, by clicking the (x) in the upper right corner of Stripe’s pop-up. However, when they do that, Anvil then reports the following error, and I have no clue as to what I need to code in order to gracefully handle it.

Anvil’s error message:
AttributeError: ‘module’ object has no attribute ‘error’

Occurs during the following code:

        try:
            token, user_info = stripe.checkout.get_token(
                amount=self.descriptor['price'],
                currency="USD",
                title='DARMA(tm) by InjAnnuity, Inc.',
                description=self.descriptor['name_short']
                ) 

but only if they click the (x). If they click the Pay button, then everything’s fine.

Obviously, we really should handle the (x) without crashing, but at the moment, I have little idea how.

If the Form module needs to have a global variable named error, I’ll be happy to provide one. If it should have some initial value, I’ll need to know what that is. If it is supposed to be a function, then I need to know how it is supposed to behave.

Hi there,

That’s interesting! When I run this same piece of code, I get:

Exception: Stripe checkout cancelled
at Form1, line 14

as it should!

Can you share a Clone link that exhibits this behaviour?

Much appreciated, @meredydd.

Could be that last update took care of it. I’ll try it first thing in the morning. If the problem’s still there, then I’ll try to isolate for you.

UPDATE 1: It’s still there. I’ll see what I can do to isolate it, as a separate, minimal app.

UPDATE 2: Replicated in a 1-page app. I’ll send you a Clone link privately.

My problem was in the except clauses that I had after my try clause.

Per Anvil’s instructions

There are many ways a charge could abort - if the user closes the dialog box, for example. You should make sure to wrap all calls to stripe.checkout.charge in a try...except block.

So, in order to catch any Stripe errors, and distinguish them from any Anvil-raised errors, I looked for Anvil-based examples that made such distinctions. Finding none, I therefore followed Stripe’s example:

try:
  # Use Stripe's library to make requests...
  pass
except stripe.error.CardError as e:
.
.
.
except stripe.error.StripeError as e:
  # Display a very generic error to the user, and maybe send
  # yourself an email
  pass
except Exception as e:
  # Something else happened, completely unrelated to Stripe
  pass

using its except statements verbatim (with my own code in between).

How did this work in practice?

  • If the end-user exited the Stripe popup by clicking Pay (and Stripe found no problem), then no exception was raised, and my code continued normally.
  • If they exited by clicking (x), then Anvil’s Stripe wrapper raised an Exception, not a member of stripe.error.

In fact, stripe.error is not even defined at this point in the Form, so the very first except clause triggers a nested Exception: an AttributeError.

I’m not sure why clicking (x) rates an Exception. It seems that this would be one of the “normal” things for a perceptive (and frugal) user to do. But it does raise some kind of Exception.

I’m still not sure what subclass of Exception the wrapper is raising, but it can be caught (and distinguished from other cases) as follows:

        except Exception as e:
            # Catch-all (since we can't catch any documented stripe.error.xxx)
            # To distinguish expected from unexpected errors, we must check
            # the actual message!
            if e.args == ('Stripe checkout cancelled',):
                return 'You cancelled this transaction.', None
            pass # handle other error conditions.

Note that since we are checking the actual text of the message, the above may not work in languages other than American English, or if Stripe/Anvil subsequently changes the wording of the message.

Therefore, if there is a more robust way of detecting and distinguishing among these possible exception outcomes, I’m very interested in hearing about it.