3 Business::BatchPayment::Processor - Common interface for batch payment gateways
7 Business::BatchPayment::Processor is a Moose role. Modules implementing
8 the protocol to talk to specific payment processing services should compose
11 There are two general schemes for interacting with a batch payment gateway:
17 In this mode, you (the merchant) assemble a batch of payment requests,
18 including account numbers, and send it to the gateway. At some point in
19 the future, the gateway sends back one or more reply batches indicating the
20 results of processing the payments.
22 When submitting a request batch, the merchant software marks each payment
23 request with a unique transaction ID (the "tid" field). This should be
24 stored somewhere. When the reply batch is processed, each item will have
25 a tid matching its request.
27 Note that some gateways will provide results only for approved payments,
28 or even only for declined payments. It is then up to the merchant software
29 to follow a sensible policy for approving or declining payments whose
30 ultimate status is unconfirmed.
34 In this mode, the gateway transmits a batch file containing approved
35 payments, without those payments being requested. For example, most
36 commercial banks provide check lockbox services and periodically
37 send the merchant a statement of payments received to the lockbox.
38 This statement would be processed as a one-way batch.
44 Most attributes for Processor objects are defined by the module.
50 See L<Business::BatchPayment::Transport>. This must be set before calling
51 submit() or receive(). Some modules will set it themselves; others require a
52 transport to be supplied. Check for the existence of a 'default_transport'
57 Debug level. This may be interpreted in various ways by the module.
61 Communicate with a test server instead of the production gateway. Not all
62 processors support this. Test for the L<Business::BatchPayment::TestMode>
63 role to determine if it's supported.
67 Callback to handle errors when formatting items. Arguments are the Processor
68 object, the Item object, and the error thrown by C<format_item>. The callback
69 can die to stop submitting the batch.
73 Callback to handle errors when parsing items. Arguments are the Processor
74 object, the Item object, and the error thrown by C<parse_item>. The callback
75 can die to stop receiving the batch.
79 =head1 OTHER PARAMETERS
87 If either of these is passed when constructing a Processor object, the
88 transport will be replaced with a File transport with those parameters.
89 Specifying only 'input' will direct 'output' to /dev/null, and vice versa.
99 Send a batch of requests to the gateway. BATCH must be a
100 L<Business::BatchPayment::Batch>.
104 Download/otherwise acquire the available confirmed transactions from the
105 gateway, parse them, and return a list of L<Business::BatchPayment::Batch>
106 objects. The items in these batches will have, at minimum, the 'approved'
107 field and either the 'tid' or 'amount' field set.
109 =item format_request BATCH
111 Default method to serialize BATCH for submission. Returns the formatted
112 text as a string. By default, this calls C<format_header>, then
113 C<format_item> on each Item in the batch, then C<format_trailer>,
114 joining the output with no delimiters. Override this if your processor
115 needs something different.
117 =item format_header BATCH
119 =item format_trailer BATCH
121 Optional methods to produce the header and trailer sections of the
122 formatted batch. By default these are empty strings.
124 =item format_item ITEM, BATCH
126 Required method (if using the default C<format_request>) to produce
127 the per-item part of the formatted batch. By default this throws
130 =item parse_response DATA
132 Default method to deserialize a received batch. Takes the string received
133 from the gateway, returns a L<Business::BatchPayment::Batch>. By default,
134 calls C<parse_batch_id> on the entire batch, then splits DATA into lines
135 and calls C<parse_item> on each line.
137 =item parse_batch_id DATA
139 Optional method to obtain the batch identifier from the received file.
140 By default this returns nothing.
142 =item parse_item LINE
144 Required method to parse a line from the received file. Should
145 return zero or more L<Business::BatchPayment::Item>s.
153 package Business::BatchPayment::Processor;
158 with 'Business::BatchPayment::Debug';
162 does => 'Business::BatchPayment::Transport',
163 # possibly this part should be a separate role
165 builder => 'default_transport',
168 sub default_transport {
170 die blessed($self). " requires a transport or input/output files\n";
173 has 'on_format_error' => (
176 handles => { format_error => 'execute_method' },
177 default => sub { \&default_on_error },
180 has 'on_parse_error' => (
183 handles => { parse_error => 'execute_method' },
184 default => sub { \&default_on_error },
187 sub default_on_error { #re-throw it
188 my ($self, $item, $error) = @_;
189 $DB::single = 1 if defined($DB::single);
193 # No error callbacks for other parts of this. The per-item case
194 # is special in that it might make sense to continue with the
197 around BUILDARGS => sub {
198 my ($orig, $class, %args) = @_;
199 %args = %{ $class->$orig(%args) }; #process as usual
201 if ( $args{input} or $args{output} ) {
202 $args{transport} = Business::BatchPayment->create( 'Transport::File',
203 input => $args{input},
204 output => $args{output},
210 # override this if your processor produces one-way batches
218 my $request = $self->format_request($batch);
219 warn $request if $self->debug >= 2;
220 $self->transport->upload($request);
225 my @responses = $self->transport->download;
226 warn join("\n\n", @responses) if $self->debug >= 2 and scalar(@responses);
228 foreach my $response (@responses) {
229 push @batches, $self->parse_response($response);
239 my $output = $self->format_header($batch);
240 foreach my $item ($batch->elements) {
242 $output .= $self->format_item($item, $batch);
244 $self->format_error($item, $_);
247 $output .= $self->format_trailer($batch);
254 my $batch = Business::BatchPayment->create(Batch =>
255 incoming => $self->incoming,
256 batch_id => $self->parse_batch_id($input)
258 while ( $input =~ s/(.*)\n//m ) {
261 $batch->push( $self->parse_item($row) );
263 $self->parse_error($row, $_);
271 sub format_header { '' };
272 sub format_trailer { '' };
273 sub format_item { die "format_item unimplemented\n" }
275 sub parse_batch_id { '' };
276 sub parse_item { die "parse_item unimplemented\n" }