initial commit
[Business-BatchPayment.git] / BatchPayment / Processor.pm
1 =head1 NAME
2
3 Business::BatchPayment::Processor - Common interface for batch payment gateways
4
5 =head1 DESCRIPTION
6
7 Business::BatchPayment::Processor is a Moose role.  Modules implementing  
8 the protocol to talk to specific payment processing services should compose 
9 it.
10
11 There are two general schemes for interacting with a batch payment gateway:
12
13 =over 4
14
15 =item Request/Reply
16
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.
21
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.
26
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.
31
32 =item One-Way
33
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.
39
40 =back
41
42 =head1 ATTRIBUTES
43
44 Most attributes for B::BP::Processor objects are defined by the module.
45
46 =over 4
47
48 =item transport - See L<Business::BatchPayment::Transport>.  This must 
49 be set before calling submit() or receive().  Some modules will set it 
50 themselves; others require a transport to be supplied.  Check for the 
51 existence of a 'default_transport' method.
52
53 =item debug - Debug level.  This may be interpreted in various ways by
54 the module.
55
56 =item test_mode - Communicate with a test server instead of the production
57 gateway.  Not all processors support this.
58 C<$processor->does('Business::BatchPayment::TestMode')> should tell whether
59 it's supported.
60
61 =back
62
63 =head1 OTHER PARAMETERS
64
65 =over 4
66
67 =item input FILE
68
69 =item output FILE
70
71 If either of these is passed when constructing a Processor object, the 
72 transport will be replaced with a File transport with those parameters.
73 Specifying only 'input' will direct 'output' to /dev/null, and vice versa.
74
75 =back
76
77 =head1 METHODS
78
79 =over 4
80
81 =item submit BATCH
82
83 Send a batch of requests to the gateway.  BATCH must be a 
84 L<Business::BatchPayment::Batch>.
85
86 =item receive
87
88 Download/otherwise acquire the available confirmed transactions from the 
89 gateway, parse them, and return a list of L<Business::BatchPayment::Batch>
90 objects.  The items in these batches will have, at minimum, the 'approved' 
91 field and either the 'tid' or 'amount' field set.
92
93 =back
94
95 =cut
96
97 package Business::BatchPayment::Processor;
98
99 use Moose::Role;
100 with 'Business::BatchPayment::Debug';
101
102 requires 'format_request', 'parse_response';
103
104 has 'transport' => (
105   is      => 'rw',
106   does    => 'Business::BatchPayment::Transport',
107   # possibly this part should be a separate role
108   lazy    => 1,
109   builder => 'default_transport',
110 );
111
112 around BUILDARGS => sub {
113   my ($orig, $class, %args) = @_;
114   %args = %{ $class->$orig(%args) }; #process as usual
115   # then:
116   if ( $args{input} or $args{output} ) {
117     $args{transport} = Business::BatchPayment->create( 'Transport::File',
118       input => $args{input},
119       output => $args{output},
120     );
121   }
122   \%args;
123 };
124
125 sub submit {
126   my $self = shift;
127   my $batch = shift;
128   my @items = @_;
129   my $request = $self->format_request($batch);
130   warn $request if $self->debug >= 2;
131   $self->transport->upload($request);
132 }
133
134 sub receive {
135   my $self = shift;
136   my @responses = $self->transport->download;
137   warn join("\n\n",@responses) if $self->debug >= 2 and scalar(@responses);
138   map { $self->parse_response($_) } @responses;
139 }
140
141 1;