NAME
Net::API::Stripe::WebHook::Apache - An Apache handler for Stripe Web Hook
SYNOPSIS
package My::Module::WebHook;
BEGIN
{
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 );
}
VERSION
v0.100.2
DESCRIPTION
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.
CONFIGURATION
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
</Directory>
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
</Directory>
<IfModule mod_perl.c>
PerlOptions +GlobalRequest
PerlPassEnv MOD_PERL
PerlPassEnv PATH_INFO
PerlModule Apache2::Request
<Perl>
unshift( @INC, "/home/john/lib" );
</Perl>
<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
</Location>
</IfModule>
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
</Virtualhost>
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.
CONSTRUCTOR
new
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:
- debug
-
Integer. When set to a true value, this will produce debugging output on STDERR or http server log.
- event_handlers
-
An hash reference of event type to subroutine reference. See example above
- ignore_ip
-
When set to true, "webhook_validate_caller_ip" in Net::API::Stripe will not check fo the validity of the webhook caller's ip.
- signing_secret
-
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
- stripe
-
The Net::API::Stripe object instantiated. This is used in "handler" and "validate_webhook" methods
handler
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
METHODS
handler
This is called by Apache with an Apache2::Request object and returns an Apache2::Constant code such as 200
event_handler
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.
event_handlers
Set/get an hash reference of Stripe event type to handling methods.
Returns an hash reference.
stripe
Set/get a Net::API::Stripe object. It returns the current value.
validate_webhook
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 );
}
HISTORY
v0.1
Initial version
AUTHOR
Jacques Deguest <jack@deguest.jp>
SEE ALSO
Stripe API documentation: https://stripe.com/docs/api/events/types
ModPerl::Registry, ModPerl::PerlRun, http://perl.apache.org/
COPYRIGHT & LICENSE
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.