in reply to EyeBall stumps BodBall (Error Handling)

I similarly urge you to get into the habit of thinking long and hard about your module's error handling well before you release it, and for the same reasons.

You'll be pleased to hear that I thought at length about error handling before I wrote Business::Stripe::WebCheckout. A consistent approach has been carried over into my other Stripe modules:
Business::Stripe::Subscription
Business::Stripe::Webhook

AFAICT, your basic error handling strategy is for your methods to set the error property

Almost...

The modules set error in any error. The modules provide the success method which returns true on success or false if there was an error. The user of the module is expected to check success. The error method returns the error property so that the user can understand why success is false.

These modules are designed to be used on a webserver. As such, calling die is not helpful to anyone. The end user, the person browsing the website, will probably get an HTTP 500 error and the user of the module is unlikely to detect that quickly.

That's what I meant by: "However, I will not call die. I find it frustrating when modules die"

Instead, I opted for the default error handing approach of DBI. I use this module in default mode (i.e. with RaiseError false) and only turn on RaiseError for debugging, not for live code.

I'm not sure my Stripe modules warrant the equivalent of RaiseError in them. I may be wrong here.

Specifically with Business::Stripe::Webhook, this is designed for building scripts to listen for calls from Stripe to a webhook endpoint. Stripe specifies that the endpoint MUST give a JSON encoded response. The module provides a method for doing this. If the payload from Stripe is invalid or has been changed by the user of the module, then Stripe still needs this reply. If we were to call die then Stripe will not get the reply and may invalidate the endpoint. So not calling die is a very deliberate design decision.

I think a clear statement of your overall error-handling strategy, combined with a couple of real-world examples of handling common errors you've experienced when using your module, would be invaluable to your users

Thank you for this, I shall include it in the next release of each of the Stripe modules.

I work on my own and build modules that are for my use. They only get released if I think they will also be is use for other people. But, like everyone, I suffer from what I call "the curse of knowledge". It is obvious to me what it is supposed to do and how to use it. So it is very difficult to know which bit to explain in full detail and which only need a little explanation.

Replies are listed 'Best First'.
Re^2: EyeBall stumps BodBall
by eyepopslikeamosquito (Archbishop) on Jul 17, 2023 at 09:07 UTC

    The modules set error in any error. The modules provide the success method which returns true on success or false if there was an error. The user of the module is expected to check success. The error method returns the error value so that the user can understand why success is false.

    I see most of your Business::Stripe::Webhook module's methods begin by clearing the module error property via:

    $self->{'error'} = '';
    presumably to avoid confusion from a spurious error property set by an earlier method call. Is that right?

    I also noticed your get_subscription method does not do this. Is this a bug?

    In case it helps, another minor issue I noticed is the return value of your check_signature method is not documented. Examining the code indicates it can return 1 or 0 or undef. It would be good to document this method's return value and the difference between returning 0 and undef.

      presumably to avoid confusion from a spurious error property set by an earlier method call. Is that right?

      Spot on...

      I also noticed your get_subscription method does not do this. Is this a bug?

      Well spotted, yes it is a bug. It has been corrected in my code locally. Although I suppose I should have done that on GitHub!

      In case it helps, another minor issue I noticed is the return value of your check_signature method is not documented

      It helps a lot thank you.
      As it says in the documentation, there shouldn't be any need to call this method. Originally I was going to have it as a private method but decided that there may be a use case for checking Stripe signatures outside of the workflow that's documented. So, in the end it became a public method.

      The return values have now been added to POD in my local copy.

        > It helps a lot thank you

        You're welcome Bod. Always looking out for you.

        In fact, I've just received an urgent alert that a small red car has been swallowed up by a huge sinkhole in Coventry UK -- Bod, I urge you to avoid the Sewall Highway if you possibly can (hope it wasn't your car in the sinkhole :)

Re^2: EyeBall stumps BodBall
by kcott (Archbishop) on Jul 09, 2023 at 11:50 UTC

    G'day Bod,

    Here's a few things that I do which you may find useful (possibly in code outside the Business::Stripe:: namespace). In general, these are intended to promote consistency and reduce coding effort.

    Have a small number of routines to output messages (e.g. print_error(), print_warning() and so on). These accept a format for the message along with a variable number of arguments. Here's a very rough, off-the-top-of-my-head example:

    sub print_error { my ($fmt, @args) = @_; die 'ERROR: ', sprintf $fmt, @args; }

    Define each format once only. This could be globally as a constant, specific to a subroutine as a state variable, or using some other method; you may even use a combination of these. Here's some arbitrary examples of formats:

    'Start date (%s) cannot be after end date (%s)' 'Postcode (%s) contains invalid characters' 'ID is a required field'

    You can paste these formats directly into the DIAGNOSTICS (or equivalent) section of your POD. See perldiag for a multitude of examples of this type of documentation.

    Where possible, I try to collect as many problem reports as possible into a single message. So, if the above three example formats were needed, the user would know to make changes to date, postcode and ID fields from a single piece of feedback, rather than being drip-fed one problem at a time.

    How messages are presented will very much depend on the interface. With GUIs (web or other) I often find a small [i] button can provide information about what exactly is required in a field. A popup panel can list the problems and also contain the same [i] buttons for ease of reference.

    — Ken

      Very helpful, thanks Ken

      I have been trying (not always successfully) to have a small number (preferably one) to output everything...this includes a single method as a wrapper around the process method of Template.

      It's reassuring to read your advice which is generally aligned with what I'm striving to do.

Re^2: EyeBall stumps BodBall
by etj (Priest) on May 19, 2024 at 13:23 UTC