diff options
author | Mark Wells <mark@freeside.biz> | 2012-07-11 16:03:18 -0700 |
---|---|---|
committer | Mark Wells <mark@freeside.biz> | 2012-07-11 16:03:18 -0700 |
commit | 622a7ed7d079b7ae183053d2f807c862cc015db7 (patch) | |
tree | c5d01459f76f61d93fabc5f527f55669393534ee /BatchPayment/Processor.pm | |
parent | 3c5cccf1bf74f2e60482fe62cfbcbe06722da1f5 (diff) |
error callbacks, more structure for parse/format methods
Diffstat (limited to 'BatchPayment/Processor.pm')
-rw-r--r-- | BatchPayment/Processor.pm | 161 |
1 files changed, 147 insertions, 14 deletions
diff --git a/BatchPayment/Processor.pm b/BatchPayment/Processor.pm index 26f5ba0..2093132 100644 --- a/BatchPayment/Processor.pm +++ b/BatchPayment/Processor.pm @@ -41,22 +41,38 @@ This statement would be processed as a one-way batch. =head1 ATTRIBUTES -Most attributes for B::BP::Processor objects are defined by the module. +Most attributes for Processor objects are defined by the module. =over 4 -=item transport - See L<Business::BatchPayment::Transport>. This must -be set before calling submit() or receive(). Some modules will set it -themselves; others require a transport to be supplied. Check for the -existence of a 'default_transport' method. +=item transport -=item debug - Debug level. This may be interpreted in various ways by -the module. +See L<Business::BatchPayment::Transport>. This must be set before calling +submit() or receive(). Some modules will set it themselves; others require a +transport to be supplied. Check for the existence of a 'default_transport' +method. -=item test_mode - Communicate with a test server instead of the production -gateway. Not all processors support this. -C<$processor->does('Business::BatchPayment::TestMode')> should tell whether -it's supported. +=item debug + +Debug level. This may be interpreted in various ways by the module. + +=item test_mode + +Communicate with a test server instead of the production gateway. Not all +processors support this. Test for the L<Business::BatchPayment::TestMode> +role to determine if it's supported. + +=item on_format_error + +Callback to handle errors when formatting items. Arguments are the Processor +object, the Item object, and the error thrown by C<format_item>. The callback +can die to stop submitting the batch. + +=item on_parse_error + +Callback to handle errors when parsing items. Arguments are the Processor +object, the Item object, and the error thrown by C<parse_item>. The callback +can die to stop receiving the batch. =back @@ -68,7 +84,7 @@ it's supported. =item output FILE -If either of these is passed when constructing a Processor object, the +If either of these is passed when constructing a Processor object, the transport will be replaced with a File transport with those parameters. Specifying only 'input' will direct 'output' to /dev/null, and vice versa. @@ -90,17 +106,57 @@ gateway, parse them, and return a list of L<Business::BatchPayment::Batch> objects. The items in these batches will have, at minimum, the 'approved' field and either the 'tid' or 'amount' field set. +=item format_request BATCH + +Default method to serialize BATCH for submission. Returns the formatted +text as a string. By default, this calls C<format_header>, then +C<format_item> on each Item in the batch, then C<format_trailer>, +joining the output with no delimiters. Override this if your processor +needs something different. + +=item format_header BATCH + +=item format_trailer BATCH + +Optional methods to produce the header and trailer sections of the +formatted batch. By default these are empty strings. + +=item format_item ITEM, BATCH + +Required method (if using the default C<format_request>) to produce +the per-item part of the formatted batch. By default this throws +a fatal error. + +=item parse_response DATA + +Default method to deserialize a received batch. Takes the string received +from the gateway, returns a L<Business::BatchPayment::Batch>. By default, +calls C<parse_batch_id> on the entire batch, then splits DATA into lines +and calls C<parse_item> on each line. + +=item parse_batch_id DATA + +Optional method to obtain the batch identifier from the received file. +By default this returns nothing. + +=item parse_item LINE + +Required method to parse a line from the received file. Should +return zero or more L<Business::BatchPayment::Item>s. + +=cut + =back =cut package Business::BatchPayment::Processor; +use strict; +use Try::Tiny; use Moose::Role; with 'Business::BatchPayment::Debug'; -requires 'format_request', 'parse_response'; - has 'transport' => ( is => 'rw', does => 'Business::BatchPayment::Transport', @@ -109,6 +165,11 @@ has 'transport' => ( builder => 'default_transport', ); +sub default_transport { + my $self = shift; + die blessed($self). " requires a transport or input/output files\n"; +} + around BUILDARGS => sub { my ($orig, $class, %args) = @_; %args = %{ $class->$orig(%args) }; #process as usual @@ -122,6 +183,11 @@ around BUILDARGS => sub { \%args; }; +# override this if your processor produces one-way batches +sub incoming { 0 }; + +#top-level interface + sub submit { my $self = shift; my $batch = shift; @@ -138,4 +204,71 @@ sub receive { map { $self->parse_response($_) } @responses; } +# next level down + +sub format_request { + my $self = shift; + my $batch = shift; + my $output = $self->format_header($batch); + foreach my $item ($batch->elements) { + try { + $output .= $self->format_item($item, $batch); + } catch { + $self->format_error($self, $item, $_); + } + } + $output .= $self->format_trailer($batch); + return $output; +} + +sub parse_response { + my $self = shift; + my $input = shift; + my $batch = Business::BatchPayment->create(Batch => + incoming => $self->incoming, + batch_id => $self->parse_batch_id($input) + ); + while ( $input =~ s/(.*)\n//m ) { + my $row = $1; + try { + $batch->push( $self->parse_item($row) ); + } catch { + $self->parse_error($row, $_); + }; + } + $batch; +} + +# nuts and bolts + +sub format_header { '' }; +sub format_trailer { '' }; +sub format_item { die "format_item unimplemented\n" } + +sub parse_batch_id { '' }; +sub parse_item { die "parse_item unimplemented\n" } + +sub default_on_error { #re-throw it + my ($self, $item, $error) = @_; + die $error; +}; + +has 'on_format_error' => ( + traits => ['Code'], + is => 'rw', + handles => { format_error => 'execute_method' }, + default => sub { \&default_on_error }, +); + +has 'on_parse_error' => ( + traits => ['Code'], + is => 'rw', + handles => { parse_error => 'execute_method' }, + default => sub { \&default_on_error }, +); + +# No error callbacks for other parts of this. The per-item case +# is special in that it might make sense to continue with the +# other items. + 1; |