[Catalyst-commits] r7144 - in trunk/Catalyst-Model-PayPal-IPN: .
lib/Catalyst/Model/PayPal
ghenry at dev.catalyst.perl.org
ghenry at dev.catalyst.perl.org
Tue Nov 20 14:57:28 GMT 2007
Author: ghenry
Date: 2007-11-20 14:57:26 +0000 (Tue, 20 Nov 2007)
New Revision: 7144
Modified:
trunk/Catalyst-Model-PayPal-IPN/README
trunk/Catalyst-Model-PayPal-IPN/lib/Catalyst/Model/PayPal/IPN.pm
Log:
Docs complete (as well as they can be just now).
Modified: trunk/Catalyst-Model-PayPal-IPN/README
===================================================================
--- trunk/Catalyst-Model-PayPal-IPN/README 2007-11-19 21:22:05 UTC (rev 7143)
+++ trunk/Catalyst-Model-PayPal-IPN/README 2007-11-20 14:57:26 UTC (rev 7144)
@@ -1,34 +1,299 @@
NAME
- Catalyst::Model::PayPal::IPN - [One line description of module's purpose
- here]
+ Catalyst::Model::PayPal::IPN - Handle Instant Payment Notifications and
+ PayPal Button Generation
VERSION
This document describes Catalyst::Model::PayPal::IPN version 0.02
SYNOPSIS
- use Catalyst::Model::PayPal::IPN;
+ package MyApp::Model::Paypal::IPN;
+ use strict;
+ use warnings;
+ use base 'Catalyst::Model::PayPal::IPN';
+
+ =head1 NAME
+
+ MyApp::Model::Paypal::IPN - Catalyst Model
+
+ =head1 DESCRIPTION
+
+ Catalyst Model.
+
+ =head1 AUTHOR
+
+ Gavin Henry
+
+ =head1 LICENSE
+
+ This library is free software, you can redistribute it and/or modify
+ it under the same terms as Perl itself.
+
+ =cut
+
+ 1;
+
+ myapp.yml
+
+ paypal:
+ cert_id: 3TFC4UDJER95J
+ page_style: MyApp
+ no_note: 1
+ no_shipping: 1
+ lc: GB
+ bn: PP-BuyNowBF
+
+ Model::Paypal::IPN:
+ debug_mode: 1
+ encrypt_mode: 0
+ business_email: ghenry_1188297224_biz at suretecsystems.com
+ currency_code: GBP
+ cert: /home/ghenry/MyApp/root/auth/paypal_certs/www.myapp.net.crt
+ cert_key: /home/ghenry/MyApp/root/auth/paypal_certs/www.myapp.net.key
+ paypal_cert: /home/ghenry/MyApp/root/auth/paypal_certs/paypal_sandbox_cert.pem
+ completion_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - received
+ postback_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - ipn
+ cancellation_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - cancelled
+
+ MyApp::Controller::Subscribe
+
+ =head2 ipn
+
+ Handle PayPal IPN stuff
+
+ =cut
+
+ sub ipn : Path('payment/ipn') {
+ my ( $self, $c ) = @_;
+
+ my $ipn = $c->model('Paypal::IPN');
+
+ if ( $ipn->is_completed ) {
+ my %ipn_vars = $ipn->buyer_info();
+ $c->stash->{ipn_vars} = \%ipn_vars;
+
+ Do stuff here
+
+ # Just so we reply with something, which in turn sends a HTTP Status 200
+ # OK, which we need to stop PayPal.
+ # We don't get as we don't use a template and RenderView looks for a
+ # template, a body or status equal to 3XX
+ $c->res->body('ok');
+ }
+ else {
+
+ # Just so we reply with something, which in turn sends a HTTP Status 200
+ # OK, which we need to stop PayPal.
+ # We don't get as we don't use a template and RenderView looks for a
+ # template, a body or status equal to 3XX
+ $c->res->body('not_ok');
+ $c->log->debug( $record_payment_result->transmsgtext ) if $c->debug;
+ $c->log->debug( $ipn->error ) if $ipn->error && $c->debug;
+ }
+ }
+
+ =head2 cancelled
+
+ Cancelled Payment
+
+ =cut
+
+ sub cancelled : Path('payment/cancelled') {
+ my ( $self, $c ) = @_;
+
+ Do stuff on cancel
+
+ $c->stash->{template} = 'user/subscribe/cancelled.tt';
+ }
+
+ =head2 generate_paypal_buttons
+
+ =cut
+
+ sub generate_paypal_buttons : Private {
+ my ( $self, $c ) = @_;
+
+ if ( $c->stash->{all_buttons} ) {
+ $c->stash->{subtypes} = [
+ $c->model('FTMAdminDB::FTMTariffs')->search(
+ {
+ objectname => 'FTM_SUB_TARIFFS',
+ objectitem => 'TARIFFTYPENO',
+ lovlangid => $langid,
+ },
+ )
+ ];
+
+ for my $tariff ( @{ $c->stash->{subtypes} } ) {
+ next if $tariff->tariffid == 1;
+ my %data = (
+ #cert_id => $c->config->{paypal}->{cert_id},
+ cmd => '_xclick',
+ item_name => $tariff->itemdesc,
+ item_number => $tariff->tariffid,
+ amount => $tariff->peruser,
+ page_style => $c->config->{paypal}->{page_style},
+ no_shipping => $c->config->{paypal}->{no_shipping},
+ no_note => $c->config->{paypal}->{no_note},
+ 'lc' => $c->config->{paypal}->{lc},
+ bn => $c->config->{paypal}->{bn},
+ custom => $c->req->param('subid'),
+ );
+
+ if ( $c->debug ) {
+ for my $param ( keys %data ) {
+ $c->log->debug( $param . '=' . $data{$param} );
+ }
+ }
+ $c->stash->{unencrypted_form_data} =
+ $c->model('Paypal::IPN')->form_info( \%data );
+
+ my @button_info = (
+ $tariff->itemdesc, $tariff->peruser,
+ $c->stash->{unencrypted_form_data}
+ );
+ push @{ $c->stash->{unencrypted_buttons} }, \@button_info;
+
+ #$c->stash->{encrypted_form_data} =
+ # $c->model('Paypal::IPN')->encrypt_form( \%data );
+
+ #my @button_info = (
+ # $tariff->itemdesc, $tariff->peruser,
+ # $c->stash->{encrypted_form_data}
+ #);
+ #push @{ $c->stash->{encrypted_buttons} }, \@button_info;
+ }
+ }
+ }
+
+ buttons.tt
+
+ <table>
+ [% FOREACH button IN unencrypted_buttons %]
+ <tr>
+ <td><b>[% button.0 %]</b></td>
+ <td><b>Price:</b> £[% button.1 %]</td>
+ <td class="content">
+ <form method="post" action="[% c.model('Paypal::IPN').paypal_gateway %]">
+ <input type="hidden" name="cmd" value="_xclick">
+ <input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but23.gif" border="0"
+ name="submit" alt="Make payments with PayPal - it's fast, free and secure!">
+ <img alt="" border="0" src="https://www.paypal.com/en_GB/i/scr/pixel.gif" width="1" height="1">
+ [% FOREACH key IN button.2.keys %]
+ <input type="hidden" name="[% key %]" value="[% button.2.$key %]" />
+ [% END %]
+ </form>
+ </td>
+ </tr>
+ [% END %]
+ <tr>
+ <td class="content">
+ <input type="button" onclick="dojo.widget.byId('profdiag').hide();" value="Close" name="close"/>
+ </td>
+ <td class="content" colspan="2">
+ <a href="#" onclick="javascript:window.open('https://www.paypal.com/uk/cgi-bin/webscr?cmd=xpt/cps/popup/OLCWhatIsPayPal-outside','olcwhatispaypal','toolbar=no,location=no, directories=no, status=no, menubar=no, scrollbars=yes,resizable=yes, width=400, height=350');"><img src="https://www.paypal.com/en_GB/i/bnr/horizontal_solution_PP.gif" border="0"
+ alt="Solution Graphics"></a>
+ </td>
+ </tr>
+ </table>
+
DESCRIPTION
+ This model handles all the latest PayPal IPN vars, and provides an easy
+ method for checking that the transaction was successful.
+
+ There are also convience methods for generating encrypted and
+ non-encrypted PayPal forms and buttons.
+
+ See Business::PayPal::IPN for more info.
+
INTERFACE
-DIAGNOSTICS
- "Error message here, perhaps with %s placeholders"
- [Description of error here]
+ build_paypal_gateway
+ If debug_mode is on, returns sandbox url, otherwise normal PayPal
+ gateway
- "Another error message here"
- [Description of error here]
+ is_completed
+ Calls is_completed from Business::PayPal::IPN
- [Et cetera, et cetera]
+ error
+ Calls error from Business::PayPal::IPN
+ buyer_info
+ Returns IPN vars via Business::PayPal::IPN
+
+ See
+ <https://www.paypal.com/IntegrationCenter/ic_ipn-pdt-variable-reference.
+ html>
+
+ form_info
+ Takes a hashref and returns form data for looping through to create your
+ form.
+
+ See SYNOPSIS
+
+ encrypt_form
+ Encrypts form data.
+
+ $c->model('Paypal::IPN')->encrypt_form( \%data );
+
CONFIGURATION AND ENVIRONMENT
- Catalyst::Model::PayPal::IPN requires no configuration files or
- environment variables.
+ The usual techniques for suppling model configuration data in Catalyst
+ apply, but the follow should be present:
+ Model::Paypal::IPN:
+ debug_mode: 1
+ encrypt_mode: 0
+ business_email: ghenry_1188297224_biz at suretecsystems.com
+ currency_code: GBP
+ completion_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - received
+ postback_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - ipn
+ cancellation_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - cancelled
+
+ debug_mode switches form url to the PayPal sandbox url. If using
+ encrypted buttons, i.e.
+
+ encrypt_mode: 1
+
+ then the following will be needed:
+
+ cert: /home/ghenry/MyApp/root/auth/paypal_certs/www.myapp.net.crt
+ cert_key: /home/ghenry/MyApp/root/auth/paypal_certs/www.myapp.net.key
+ paypal_cert: /home/ghenry/MyApp/root/auth/paypal_certs/paypal_sandbox_cert.pem
+
+ Catalyst::Model::PayPal::IPN requires:
+
DEPENDENCIES
- None.
+ Moose
-INCOMPATIBILITIES
- None reported.
+ namespace::clean
+ Business::PayPal::IPN
+
+ Business::PayPal::EWP
+
BUGS AND LIMITATIONS
No bugs have been reported.
@@ -37,16 +302,16 @@
interface at <http://rt.cpan.org>.
AUTHOR
- Matt S Trout "<mst at shadowcatsystems.co.uk>"
+ Matt S Trout "mst at shadowcatsystems.co.uk"
- Gavin Henry "<ghenry at suretecsystems.com>"
+ Gavin Henry "ghenry at suretecsystems.com"
LICENCE AND COPYRIGHT
- Copyright (c) 2007, Matt S Trout, "<mst at shadowcatsystems.co.uk>". All
+ Copyright (c) 2007, Matt S Trout, "mst at shadowcatsystems.co.uk". All
rights reserved.
- Copyright (c) 2007, Gavin Henry "<ghenry at suretecsystems.com>". All
- rights reserved.
+ Copyright (c) 2007, Gavin Henry "ghenry at suretecsystems.com". All rights
+ reserved.
This module is free software; you can redistribute it and/or modify it
under the same terms as Perl itself. See perlartistic.
Modified: trunk/Catalyst-Model-PayPal-IPN/lib/Catalyst/Model/PayPal/IPN.pm
===================================================================
--- trunk/Catalyst-Model-PayPal-IPN/lib/Catalyst/Model/PayPal/IPN.pm 2007-11-19 21:22:05 UTC (rev 7143)
+++ trunk/Catalyst-Model-PayPal-IPN/lib/Catalyst/Model/PayPal/IPN.pm 2007-11-20 14:57:26 UTC (rev 7144)
@@ -239,111 +239,321 @@
1;
__END__
-=head1 NAME
+=head1 NAME
-Catalyst::Model::PayPal::IPN - [One line description of module's purpose here]
+Catalyst::Model::PayPal::IPN - Handle Instant Payment Notifications and PayPal Button Generation
-
=head1 VERSION
This document describes Catalyst::Model::PayPal::IPN version 0.02
-
=head1 SYNOPSIS
- use Catalyst::Model::PayPal::IPN;
+ package MyApp::Model::Paypal::IPN;
-=for author to fill in:
- Brief code example(s) here showing commonest usage(s).
- This section will be as far as many users bother reading
- so make it as educational and exeplary as possible.
+ use strict;
+ use warnings;
+ use base 'Catalyst::Model::PayPal::IPN';
+
+ =head1 NAME
+
+ MyApp::Model::Paypal::IPN - Catalyst Model
+
+ =head1 DESCRIPTION
+
+ Catalyst Model.
+
+ =head1 AUTHOR
+
+ Gavin Henry
+
+ =head1 LICENSE
+
+ This library is free software, you can redistribute it and/or modify
+ it under the same terms as Perl itself.
+
+ =cut
+
+ 1;
+
+
+ myapp.yml
+
+ paypal:
+ cert_id: 3TFC4UDJER95J
+ page_style: MyApp
+ no_note: 1
+ no_shipping: 1
+ lc: GB
+ bn: PP-BuyNowBF
+
+ Model::Paypal::IPN:
+ debug_mode: 1
+ encrypt_mode: 0
+ business_email: ghenry_1188297224_biz at suretecsystems.com
+ currency_code: GBP
+ cert: /home/ghenry/MyApp/root/auth/paypal_certs/www.myapp.net.crt
+ cert_key: /home/ghenry/MyApp/root/auth/paypal_certs/www.myapp.net.key
+ paypal_cert: /home/ghenry/MyApp/root/auth/paypal_certs/paypal_sandbox_cert.pem
+ completion_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - received
+ postback_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - ipn
+ cancellation_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - cancelled
+
+
+ MyApp::Controller::Subscribe
+
+ =head2 ipn
+
+ Handle PayPal IPN stuff
+
+ =cut
+
+ sub ipn : Path('payment/ipn') {
+ my ( $self, $c ) = @_;
+
+ my $ipn = $c->model('Paypal::IPN');
+
+ if ( $ipn->is_completed ) {
+ my %ipn_vars = $ipn->buyer_info();
+ $c->stash->{ipn_vars} = \%ipn_vars;
+
+ Do stuff here
+
+ # Just so we reply with something, which in turn sends a HTTP Status 200
+ # OK, which we need to stop PayPal.
+ # We don't get as we don't use a template and RenderView looks for a
+ # template, a body or status equal to 3XX
+ $c->res->body('ok');
+ }
+ else {
+
+ # Just so we reply with something, which in turn sends a HTTP Status 200
+ # OK, which we need to stop PayPal.
+ # We don't get as we don't use a template and RenderView looks for a
+ # template, a body or status equal to 3XX
+ $c->res->body('not_ok');
+ $c->log->debug( $record_payment_result->transmsgtext ) if $c->debug;
+ $c->log->debug( $ipn->error ) if $ipn->error && $c->debug;
+ }
+ }
+
+ =head2 cancelled
+
+ Cancelled Payment
+
+ =cut
+
+ sub cancelled : Path('payment/cancelled') {
+ my ( $self, $c ) = @_;
+
+ Do stuff on cancel
+
+ $c->stash->{template} = 'user/subscribe/cancelled.tt';
+ }
+
+ =head2 generate_paypal_buttons
+
+ =cut
+
+ sub generate_paypal_buttons : Private {
+ my ( $self, $c ) = @_;
+
+ if ( $c->stash->{all_buttons} ) {
+ $c->stash->{subtypes} = [
+ $c->model('FTMAdminDB::FTMTariffs')->search(
+ {
+ objectname => 'FTM_SUB_TARIFFS',
+ objectitem => 'TARIFFTYPENO',
+ lovlangid => $langid,
+ },
+ )
+ ];
+
+ for my $tariff ( @{ $c->stash->{subtypes} } ) {
+ next if $tariff->tariffid == 1;
+ my %data = (
+ #cert_id => $c->config->{paypal}->{cert_id},
+ cmd => '_xclick',
+ item_name => $tariff->itemdesc,
+ item_number => $tariff->tariffid,
+ amount => $tariff->peruser,
+ page_style => $c->config->{paypal}->{page_style},
+ no_shipping => $c->config->{paypal}->{no_shipping},
+ no_note => $c->config->{paypal}->{no_note},
+ 'lc' => $c->config->{paypal}->{lc},
+ bn => $c->config->{paypal}->{bn},
+ custom => $c->req->param('subid'),
+ );
+
+ if ( $c->debug ) {
+ for my $param ( keys %data ) {
+ $c->log->debug( $param . '=' . $data{$param} );
+ }
+ }
+ $c->stash->{unencrypted_form_data} =
+ $c->model('Paypal::IPN')->form_info( \%data );
+
+ my @button_info = (
+ $tariff->itemdesc, $tariff->peruser,
+ $c->stash->{unencrypted_form_data}
+ );
+ push @{ $c->stash->{unencrypted_buttons} }, \@button_info;
+
+ #$c->stash->{encrypted_form_data} =
+ # $c->model('Paypal::IPN')->encrypt_form( \%data );
+
+ #my @button_info = (
+ # $tariff->itemdesc, $tariff->peruser,
+ # $c->stash->{encrypted_form_data}
+ #);
+ #push @{ $c->stash->{encrypted_buttons} }, \@button_info;
+ }
+ }
+ }
+
+ buttons.tt
+
+ <table>
+ [% FOREACH button IN unencrypted_buttons %]
+ <tr>
+ <td><b>[% button.0 %]</b></td>
+ <td><b>Price:</b> £[% button.1 %]</td>
+ <td class="content">
+ <form method="post" action="[% c.model('Paypal::IPN').paypal_gateway %]">
+ <input type="hidden" name="cmd" value="_xclick">
+ <input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but23.gif" border="0"
+ name="submit" alt="Make payments with PayPal - it's fast, free and secure!">
+ <img alt="" border="0" src="https://www.paypal.com/en_GB/i/scr/pixel.gif" width="1" height="1">
+ [% FOREACH key IN button.2.keys %]
+ <input type="hidden" name="[% key %]" value="[% button.2.$key %]" />
+ [% END %]
+ </form>
+ </td>
+ </tr>
+ [% END %]
+ <tr>
+ <td class="content">
+ <input type="button" onclick="dojo.widget.byId('profdiag').hide();" value="Close" name="close"/>
+ </td>
+ <td class="content" colspan="2">
+ <a href="#" onclick="javascript:window.open('https://www.paypal.com/uk/cgi-bin/webscr?cmd=xpt/cps/popup/OLCWhatIsPayPal-outside','olcwhatispaypal','toolbar=no,location=no, directories=no, status=no, menubar=no, scrollbars=yes,resizable=yes, width=400, height=350');"><img src="https://www.paypal.com/en_GB/i/bnr/horizontal_solution_PP.gif" border="0"
+alt="Solution Graphics"></a>
+ </td>
+ </tr>
+ </table>
=head1 DESCRIPTION
-=for author to fill in:
- Write a full description of the module and its features here.
- Use subsections (=head2, =head3) as appropriate.
+This model handles all the latest PayPal IPN vars, and provides an
+easy method for checking that the transaction was successful.
+There are also convience methods for generating encrypted and non-encrypted
+PayPal forms and buttons.
+See L<Business::PayPal::IPN> for more info.
+
+
=head1 INTERFACE
-=for author to fill in:
- Write a separate section listing the public components of the modules
- interface. These normally consist of either subroutines that may be
- exported, or methods that may be called on objects belonging to the
- classes provided by the module.
+=head2 build_paypal_gateway
-=head1 DIAGNOSTICS
+If debug_mode is on, returns sandbox url, otherwise normal PayPal gateway
-=for author to fill in:
- List every single error and warning message that the module can
- generate (even the ones that will "never happen"), with a full
- explanation of each problem, one or more likely causes, and any
- suggested remedies.
+=head2 is_completed
-=over
+Calls is_completed from L<Business::PayPal::IPN>
-=item C<< Error message here, perhaps with %s placeholders >>
+=head2 error
-[Description of error here]
+Calls error from L<Business::PayPal::IPN>
-=item C<< Another error message here >>
+=head2 buyer_info
-[Description of error here]
+Returns IPN vars via L<Business::PayPal::IPN>
-[Et cetera, et cetera]
+See L<https://www.paypal.com/IntegrationCenter/ic_ipn-pdt-variable-reference.html>
-=back
+=head2 form_info
+Takes a hashref and returns form data for looping through to create your form.
+See L<SYNOPSIS>
+
+=head2 encrypt_form
+
+Encrypts form data.
+
+ $c->model('Paypal::IPN')->encrypt_form( \%data );
+
=head1 CONFIGURATION AND ENVIRONMENT
-=for author to fill in:
- A full explanation of any configuration system(s) used by the
- module, including the names and locations of any configuration
- files, and the meaning of any environment variables or properties
- that can be set. These descriptions must also include details of any
- configuration language used.
-
-Catalyst::Model::PayPal::IPN requires no configuration files or environment variables.
+The usual techniques for suppling model configuration data in Catalyst apply,
+but the follow should be present:
+ Model::Paypal::IPN:
+ debug_mode: 1
+ encrypt_mode: 0
+ business_email: ghenry_1188297224_biz at suretecsystems.com
+ currency_code: GBP
+ completion_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - received
+ postback_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - ipn
+ cancellation_action:
+ - Subscribe
+ - subscribe
+ - payment
+ - cancelled
-=head1 DEPENDENCIES
+debug_mode switches form url to the PayPal sandbox url. If using encrypted
+buttons, i.e.
-=for author to fill in:
- A list of all the other modules that this module relies upon,
- including any restrictions on versions, and an indication whether
- the module is part of the standard Perl distribution, part of the
- module's distribution, or must be installed separately. ]
+ encrypt_mode: 1
-None.
+then the following will be needed:
+ cert: /home/ghenry/MyApp/root/auth/paypal_certs/www.myapp.net.crt
+ cert_key: /home/ghenry/MyApp/root/auth/paypal_certs/www.myapp.net.key
+ paypal_cert: /home/ghenry/MyApp/root/auth/paypal_certs/paypal_sandbox_cert.pem
-=head1 INCOMPATIBILITIES
+
+Catalyst::Model::PayPal::IPN requires:
-=for author to fill in:
- A list of any modules that this module cannot be used in conjunction
- with. This may be due to name conflicts in the interface, or
- competition for system or program resources, or due to internal
- limitations of Perl (for example, many modules that use source code
- filters are mutually incompatible).
-None reported.
+=head1 DEPENDENCIES
+L<Moose>
+
+L<namespace::clean>
+
+L<Business::PayPal::IPN>
+
+L<Business::PayPal::EWP>
+
+
=head1 BUGS AND LIMITATIONS
-=for author to fill in:
- A list of known problems with the module, together with some
- indication Whether they are likely to be fixed in an upcoming
- release. Also a list of restrictions on the features the module
- does provide: data types that cannot be handled, performance issues
- and the circumstances in which they may arise, practical
- limitations on the size of data sets, special cases that are not
- (yet) handled, etc.
-
No bugs have been reported.
Please report any bugs or feature requests to
@@ -353,15 +563,15 @@
=head1 AUTHOR
-Matt S Trout C<< <mst at shadowcatsystems.co.uk> >>
+Matt S Trout C<mst at shadowcatsystems.co.uk>
-Gavin Henry C<< <ghenry at suretecsystems.com> >>
+Gavin Henry C<ghenry at suretecsystems.com>
=head1 LICENCE AND COPYRIGHT
-Copyright (c) 2007, Matt S Trout, C<< <mst at shadowcatsystems.co.uk> >>. All rights reserved.
+Copyright (c) 2007, Matt S Trout, C<mst at shadowcatsystems.co.uk>. All rights reserved.
-Copyright (c) 2007, Gavin Henry C<< <ghenry at suretecsystems.com> >>. All rights reserved.
+Copyright (c) 2007, Gavin Henry C<ghenry at suretecsystems.com>. All rights reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself. See L<perlartistic>.
More information about the Catalyst-commits
mailing list