summaryrefslogtreecommitdiff
path: root/BatchPayment/Processor.pm
blob: 26f5ba08f303f43ce7ef6d7c7ea7eaf08356404d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
=head1 NAME

Business::BatchPayment::Processor - Common interface for batch payment gateways

=head1 DESCRIPTION

Business::BatchPayment::Processor is a Moose role.  Modules implementing  
the protocol to talk to specific payment processing services should compose 
it.

There are two general schemes for interacting with a batch payment gateway:

=over 4

=item Request/Reply

In this mode, you (the merchant) assemble a batch of payment requests, 
including account numbers, and send it to the gateway.  At some point in
the future, the gateway sends back one or more reply batches indicating the
results of processing the payments.

When submitting a request batch, the merchant software marks each payment
request with a unique transaction ID (the "tid" field).  This should be 
stored somewhere.  When the reply batch is processed, each item will have 
a tid matching its request.

Note that some gateways will provide results only for approved payments,
or even only for declined payments.  It is then up to the merchant software
to follow a sensible policy for approving or declining payments whose 
ultimate status is unconfirmed.

=item One-Way

In this mode, the gateway transmits a batch file containing approved 
payments, without those payments being requested.  For example, most
commercial banks provide check lockbox services and periodically 
send the merchant a statement of payments received to the lockbox.  
This statement would be processed as a one-way batch.

=back

=head1 ATTRIBUTES

Most attributes for B::BP::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 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.
C<$processor->does('Business::BatchPayment::TestMode')> should tell whether
it's supported.

=back

=head1 OTHER PARAMETERS

=over 4

=item input FILE

=item output FILE

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.

=back

=head1 METHODS

=over 4

=item submit BATCH

Send a batch of requests to the gateway.  BATCH must be a 
L<Business::BatchPayment::Batch>.

=item receive

Download/otherwise acquire the available confirmed transactions from the 
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.

=back

=cut

package Business::BatchPayment::Processor;

use Moose::Role;
with 'Business::BatchPayment::Debug';

requires 'format_request', 'parse_response';

has 'transport' => (
  is      => 'rw',
  does    => 'Business::BatchPayment::Transport',
  # possibly this part should be a separate role
  lazy    => 1,
  builder => 'default_transport',
);

around BUILDARGS => sub {
  my ($orig, $class, %args) = @_;
  %args = %{ $class->$orig(%args) }; #process as usual
  # then:
  if ( $args{input} or $args{output} ) {
    $args{transport} = Business::BatchPayment->create( 'Transport::File',
      input => $args{input},
      output => $args{output},
    );
  }
  \%args;
};

sub submit {
  my $self = shift;
  my $batch = shift;
  my @items = @_;
  my $request = $self->format_request($batch);
  warn $request if $self->debug >= 2;
  $self->transport->upload($request);
}

sub receive {
  my $self = shift;
  my @responses = $self->transport->download;
  warn join("\n\n",@responses) if $self->debug >= 2 and scalar(@responses);
  map { $self->parse_response($_) } @responses;
}

1;