Business::Stripe::Webhook - A Perl module for handling webhooks sent by Stripe
Version 1.12
use Stripe::Webhook; my $payload; read(STDIN, $payload, $ENV{'CONTENT_LENGTH'}); my $webhook = Stripe::Webhook->new( signing_secret => 'whsec_...', api_secret => 'sk_test_...', payload => $payload, invoice-paid => \&update_invoice, checkout-session-completed => \&update_session, ); die $webhook->error unless $webhook->success; my $result = $webhook->process(); if ($webhook->success()) { $webhook->reply(status => 'OK'); } else { $webhook->reply(error => $webhook->error()); } sub update_invoice { # Process paid invoice ... } sub update_session { # Process checkout ... }
Business::Stripe::Webhook is a Perl module that provides an interface for handling webhooks sent by Stripe. It provides a simple way to verify the signature of the webhook, and allows the user to define a number of methods for processing specific types of webhooks.
This module is designed to run on a webserver as that is where Stripe webhooks would typically be sent. It reads the payload sent from Stripe from STDIN because Stripe sends an HTTP POST request. Ensure that no other module is reading from STDIN or Business::Stripe::Webhook will not get the correct input.
STDIN
POST
The typical workflow for Business::Stripe::Webhook is to initally create an instance of the module and to define one or more Stripe events to listen for. This is done by providing references to your subroutines as part of the new method. Note that the webhook events you want to listen for need to be enabled in the Stripe Dashboard.
new
my $webhook = Stripe::Webhook->new( invoice-paid => \&sub_to_handle_paid_invoice, );
The Stripe event names have a fullstop replaced with a minus sign. So, invoice.paid becomes invoice-paid.
invoice.paid
invoice-paid
Next is to process the webhook from Stripe.
my $result = $webhook->process();
This will call the subroutines that were defined when the module was created and pass them the event object from Stripe.
Finally, a reply is sent back to Stripe.
print reply(status => 'OK');
This produces a fully formed HTTP Response complete with headers as required by Stripe.
Stripe requires a timely reply to webhook calls. Therefore, if you need to carry out any lengthy processing after the webhook has been sent, this should be done after calling the reply method and flushing STDOUT
reply
STDOUT
use Stripe::Webhook; my $webhook = Stripe::Webhook->new( signing_secret => 'whsec_...', payload => $payload, invoice-paid => \&update_invoice, ); $webhook->process(); # Send reply for unhandled webhooks $webhook->reply(); sub invoice-paid { # Send reply quickly and flush buffer print $webhook->reply(); select()->flush(); # Process paid invoice which will take time then do not return ... exit; }
By default, any errors or warnings are sent to STDERR. These can be altered to instead go to your own subroutine to handle errors and/or warnings by defining these when create the object.
STDERR
my $webhook = Stripe::Webhook->new( invoice-paid => \&sub_to_handle_paid_invoice, error => \&my_error_handler, warning => \&my_warning_handler, );
Additionally, warnings can be turned off by setting the warning parameter to nowarn. Errors cannot be turned off.
warning
nowarn
Creates a new Stripe::Webhook object.
my $webhook = Stripe::Webhook->new( signing_secret => 'whsec_...', payload => $payload, );
This method takes one or more parameters:
signing_secret: The webhook signing secret provided by Stripe. If omitted, the Stripe Signature will not be checked.
payload: A JSON string. Required (see below). The JSON object from Stripe.
api_secret: The Stripe secret API Key - see https://stripe.com/docs/keys. Optional but will be required if the get_subscription method is needed.
get_subscription
stripe-event: One or more callbacks to the subroutines to handle the webhooks events sent by Stripe. See https://stripe.com/docs/api/events/list.
To listen for an event, change the fullstop in the Stripe event name to a minus sign and use that as the parameter. The events you define should match the events you ask Stripe to send. Any events Stripe sends that do not have a callback defined will be ignored (unless all-webhooks is defined).
all-webhooks
Stripe event invoice.paid becomes invoice-paid Stripe event invoice.payment_failed becomes invoice-payment_failed
invoice.payment_failed
invoice-payment_failed
all-webhooks: A callback subroutine which will be called for every event received from Stripe even if a callback subroutine for that event has not been defined.
error: A callback subroutine to handle errors. If not defined, errors are sent to STDERR.
warning: A callback subroutine to handle warnings. If not defined, warnings are sent to STDERR. If set to nowarn, warnings are ignored.
Previous versions on Business::Stripe::Webhook allowed the payload parameter to be omitted. In this case, the module would read STDIN to obtain the JSON string. This continues to work for backward compatability only but will be removed from furture versions.
Returns true if the last operation was successful, or false otherwise.
if ($webhook->success()) { ... }
Returns the error message from the last operation, or an empty string if there was no error.
my $error = $webhook->error();
This method processes the webhook sent from Stripe. It checks the Stripe Signature if a signing_secret parameter has been included and calls the defined subroutine to handle the Stripe event. Each subroutine is passed a JSON decoded Event Object from Stripe.
signing_secret
This method takes no parameters.
Normally, the return value can be ignored. Returns undef if there was an error or warning.
undef
Checks the signature of the webhook to verify that it was sent by Stripe.
my $sig_ok = $webhook->check_signature();
Normally, this method does not need to be called. It is called by the process method if a signing_secret parameter was included when the object was created.
process
Sends a reply to Stripe.
It takes one or more optional parameters.
Parameters passed to this method are then passed through to Stripe. These are available in the Stripe Dashboard and are especially useful for troubleshooting during development.
The following parameters are always passed to Stripe:
status: noaction if the event did not have a handler, success if the event was handled or failed if it wasn't
noaction
success
failed
sent_to: An array containing the names of the callback subroutines that handled the event.
sent_to_all: true or false to indicate if the all-webhooks parameter was set
true
false
timestamp: The server time at which the webhook was handled
Retrieves a subscription object from Stripe. This is required to retrieve information such as the end of the current subscription period or whether the subscription is set to cancel after the current period.
my $response = $webhook->get_subscription($subscription_id, $secret_key);
This method takes two parameters:
$subscription_id: The ID of the subscription to retrieve. Required.
$secret_key: The secret API key to use to retrieve the subscription. Optional.
This is usually supplied when the object is created but can be supplied when calling this method. If the API Key has alreay been supplied, this paramter will override the previous key.
An HTTP::Tiny response is returned representing the Subscription Object from Stripe - see https://perldoc.perl.org/HTTP::Tiny#request
Note: times sent from Stripe are in seconds since the epoch. If adding them to a database which would be a typical scenario, use the SQL FROM_UNIXTIME function:
FROM_UNIXTIME
$dbh->do("UPDATE table SET currentPeriodEnd = FROM_UNIXTIME( ? ) WHERE idSubscription = ?", undef, $response->{'current_period_end'}, $response->{'subscription'});
Here's an example of how to use the module to handle a webhook:
use Business::Stripe::Webhook; my $payload; read(STDIN, $payload, $ENV{'CONTENT_LENGTH'}); my $webhook = Business::Stripe::Webhook->new( signing_secret => 'whsec_...', payload => $payload, invoice-paid => \&pay_invoice, ); $webhook->process(); print $webhook->reply; sub pay_invoice { my $event = $_[0]; my $subscription = $event->{'data'}->{'object'}->{'subscription'}; }
Here's an example of how to use the module to retrieve a subscription object:
use Business::Stripe::Webhook; use JSON::PP; my $webhook = Business::Stripe::Webhook->new( api_secret => 'sk_...', ); my $response = $webhook->get_subscription('sub_...'); if ($response->{'success'}) { my $subscription = decode_json($response->{'content'}); ... } else { my $error = $response->{'content'}; ... }
Stripe Subscriptions API
Business::Stripe::Subscription
Business::Stripe::WebCheckout
Ian Boddison <ian at boddison.com>
Please report any bugs or feature requests to bug-business-stripe-webhook at rt.cpan.org, or through the web interface at https://rt.cpan.org/NoAuth/ReportBug.html?Queue=bug-business-stripe-webhook. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
bug-business-stripe-webhook at rt.cpan.org
You can find documentation for this module with the perldoc command.
perldoc Business::Stripe::Webhook
You can also look for information at:
RT: CPAN's request tracker (report bugs here)
https://rt.cpan.org/NoAuth/Bugs.html?Dist=Business-Stripe-Webhook
Search CPAN
https://metacpan.org/release/Business::Stripe::Webhook
Thanks to the help and support provided by members of Perl Monks https://perlmonks.org/.
This software is copyright (c) 2023 by Ian Boddison.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
To install Business::Stripe::Webhook, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Business::Stripe::Webhook
CPAN shell
perl -MCPAN -e shell install Business::Stripe::Webhook
For more information on module installation, please visit the detailed CPAN module installation guide.