++ed by:
1 non-PAUSE user
Author image Jacques Deguest
and 1 contributors


Net::API::Stripe::WebHook::Apache - An Apache handler for Stripe Web Hook


    package My::Module::WebHook;
        use strict;
        use curry;
        use parent qw( Net::API::Stripe::WebHook::Apache );
    sub init
        my $self = shift( @_ );
        $self->{event_handlers} =
        account => { updated => $self->curry::account_updated,
        # A fallback method for all other event types not purposely defined here
        fallback => $self->curry::fallback,
        # etc..
        # See here for a list of Stripe events:
        # https://stripe.com/docs/api/events/types
        # See https://stripe.com/docs/webhooks#signatures
        # For example:
        $self->{signing_secret} = 'whsec_fake1234567890mnbvcxz';
        # Set up Net::API::Stripe object
        my $stripe = Net::API::Stripe->new( $hash_ref_of_params ) || 
            return( $self->error( "Unable to create a Net::API::Stripe object: ", Net::API::Stripe->error ) );
        # Set the Stripe object that is needed later in the handler and validate_webhook methods
        $self->stripe( $stripe );
        # Set this to true if you want to test your application and not check the webhook caller's IP, 
        # which should normally be Stripe's ip
        $self->{ignore_ip} = 0;
        return( $self );




This is the module to handle Stripe Web Hooks using Apache/mod_perl configuration

The way this works is you create your own module which inherits from this one. You override the init method in which you create the object property event_handler with an hash value with keys corresponding to the types of Stripe events. A dot in the Stripe event type corresponds to a sub hash in our event_handler definition.

You can set up your endpoint on Stripe dashboard at: https://dashboard.stripe.com/webhooks or do it via the api with "webhook" in Net::API::Stripe.

See also the list of all possible Stripe endpoints

For example:

    sub init
        my $self = shift( @_ );
        $self->SUPER::init( @_ );
        $self->{event_handler} = 
        account =>
                updated => $self->curry::account_updated,
                application => 
                    authorized => $self->curry:account_application_authorised,
        charge => 
                captured => $self->curry::charge_captured,
                dispute =>
                    created => $self->curry::charge_dispute_created,
        customer =>
                created => $self->curry::customer_created,
        # A fallback method for all other event types not purposely defined here
        fallback => $self->curry::fallback,
        ## And so on....

Nota bene: here in this example above, I use curry which is a very handy module.

In a nutshell: when an http query is made by Stripe on your webhook, Apache will trigger the method handler, which will check and create the object environment, and call the method event_handler provided by this package to find out the sub in charge of this Stripe event type, as defined in your map event_handlers. Your own method is then called and you can do whatever you want with Stripe data.

It is also worth mentioning that Stripe requires ssl to be enabled to perform webhook queries.


Your Apache VirtualHost configuration would look something like this, assuming your module package is My::Module::WebHook

    <VirtualHost *:443>
        ServerName example.com:443
        ServerAdmin www@example.com
        DocumentRoot /home/john/example.com
        DirectoryIndex "index.html" "index.php"
        CustomLog "${APACHE_LOG_DIR}/example.com-access.log" combined
        ErrorLog "${APACHE_LOG_DIR}/example.com-error.log"
        LogLevel warn
        <Directory "/home/john/example.com">
            Options All +MultiViews -ExecCGI -Indexes +Includes +FollowSymLinks
            AllowOverride All
        ScriptAlias "/cgi-bin/"     "/home/john/example.com/cgi-bin/"
        <Directory "/home/john/example.com/cgi-bin/">
            Options All +Includes +ExecCGI -Indexes -MultiViews
            AllowOverride All
            SetHandler cgi-script
            AcceptPathInfo On
            Require all granted
        <IfModule mod_perl.c>
            PerlOptions     +GlobalRequest
            PerlPassEnv     MOD_PERL
            PerlPassEnv     PATH_INFO
            PerlModule      Apache2::Request
            unshift( @INC, "/home/john/lib" );
            <Location /hook>
                SetHandler      perl-script
                ## Switch it back to modperl once the soft is stable
                # SetHandler        modperl
                PerlSendHeader      On
                PerlSetupEnv        On
                PerlOptions         +GlobalRequest
                # PerlResponseHandler   Net::API::Stripe::WebHook::Apache
                PerlResponseHandler My::Module::WebHook
                Options             +ExecCGI
                Order allow,deny
                Allow from all

        SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
        Include /etc/letsencrypt/options-ssl-apache.conf
        SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem

The key part is the line with PerlResponseHandler and value Net::API::Stripe::WebHook::Apache. This will tell Apache/mod_perl that our module will handle all http request for this particular location.

So, if we get an incoming event from Stripe at https://example.com/hook/d18bbab7-e537-4dba-9a1f-dd6cc70ea6c1, we receive d18bbab7-e537-4dba-9a1f-dd6cc70ea6c1 as part of the path info, and we call validate_webhook() to validate it before processing the event incoming packet.

Apache will call our special method handler(), which will invoque validate_webhook() that should be implemented in your module if you want to do further checks, and which must return either a hash reference containing the payload or false. For example:

    sub validate_webhook
        my $self = shift( @_ );
        # Receive the hash reference containing the properties: payload (hash ref), headers (hash ref), remote_addr and remote_host
        my $hash = $self->SUPER::validate_webhook || return;
        # Do further check and make sure to return an Exception object that has the code and message method
        # Example:
        my $payload = $hash->{payload};
        return( $self->error({ code => Apache2::Const::HTTP_BAD_REQUEST, "Something is off with the payload received" }) ) if( !$payload->{object} );
        # Make sure to return the hash reference
        return( $hash );

Upon successful return from validate_webhook(), handler will create a new object from your class such as $class->new()

It will then call methods request providing it with the Net::API::REST::Request object and call the method response providing it with the Net::API::REST::Response object.

It will then collect the Stripe event data and create a Net::API::Stripe::Event object with it.

It will then call "event_handler" with the Stripe event type as the sole argument (See https://stripe.com/docs/api/events/types for a list of all possible Stripe events), and will get in return either a code reference to the handler for this event type, or an empty string if no event handler was set for this event type or undef() in scalar context or an empty list in list context if there was an error.

Finally it will call the referenced subroutine returned by "event_handler" passing it the Net::API::Stripe::Event object.

If your event handler returns undef, Net::API::Stripe::WebHook::Apache will return a server error. If your event handler returns either 1 or Apache2::Const::HTTP_OK, Net::API::Stripe::WebHook::Apache will return an OK code, and for anything else, Net::API::Stripe::WebHook::Apache will return the code as returned by your handler.

This means you can use Apache2::Const values as return code of your event handler.



Takes an hash or hash reference.

Creates a new Net::API::Stripe::WebHook::Apache object. This should be overriden by your own package.

Here are the object properties recognised and used in this module:


Integer. When set to a true value, this will produce debugging output on STDERR or http server log.


An hash reference of event type to subroutine reference. See example above


When set to true, "webhook_validate_caller_ip" in Net::API::Stripe will not check fo the validity of the webhook caller's ip.


String. This is the secret key used by Stripe to sign the webhook payload and used by us to check the payload received is authentic. See your Stripe dashboard


The Net::API::Stripe object instantiated. This is used in "handler" and "validate_webhook" methods


This is called by Apache/mod_perl upon incoming http request.

It takes your module class and the Apache2::Request object as arguments

Your module class is the one defined in the Apache Virtual Hsot configuration with PerlResponseHandler



This is called by Apache with an Apache2::Request object and returns an Apache2::Constant code such as 200


Provided with a Stripe event type such as customer.subscription.updated, this checks for a suitable handler (set up in your init method), then return the handler code reference.


Set/get an hash reference of Stripe event type to handling methods.

Returns an hash reference.


Set/get a Net::API::Stripe object. It returns the current value.


This checks the webhook call is valid and returns true upon success or false upon failure.

You want to override this like this:

    sub validate_webhook
        my $self = shift( @_ );
        ## Get the basic checks done by our default validate_webhook method
        $self->SUPER::validate_webhook || return;
        # Add checks of your own
        # And if all is ok, return true, or false otherwise
        return( 1 );



Initial version


Jacques Deguest <jack@deguest.jp>


Stripe API documentation: https://stripe.com/docs/api/events/types

Net::API::REST, Apache2

ModPerl::Registry, ModPerl::PerlRun, http://perl.apache.org/


Copyright (c) 2019-2020 DEGUEST Pte. Ltd.

You can use, copy, modify and redistribute this package and associated files under the same terms as Perl itself.