1 package Business::BatchPayment::CardFortress;
10 Business::BatchPayment::CardFortress
14 Batch payment processing via the CardFortress secure payment proxy service.
18 See Business::BatchPayment for general usage information.
20 =head2 PROCESSOR ATTRIBUTES
26 Your login credentials for CardFortress.
30 The L<Business::BatchPayment> module to use for upstream processing. You
31 don't need to have this module installed, but the proxy server does.
35 Processor options to pass along to the gateway module. The 'gateway_' prefix
42 use Business::BatchPayment;
44 with 'Business::BatchPayment::Processor';
45 with 'Business::BatchPayment::TestMode';
46 with 'Business::BatchPayment::Debug';
50 has [ qw(login password private_key gateway) ] => (
56 has 'gateway_opts' => (
59 default => sub { {login => ''} },
65 handles => [qw(serialize deserialize)],
67 # doesn't have to be Storable, but it must support blessed objects
68 Data::Serializer->new(serializer => 'Storable', encoding => 'b64')
72 # pull any arg named "gateway_foo" into gateway_opts
73 around 'BUILDARGS' => sub {
74 my ($orig, $class, %args) = @_;
75 foreach (keys %args) {
76 if (/^gateway_(.*)/) {
77 $args{'gateway_opts'}->{$1} = delete $args{$_};
83 sub default_transport {
85 Business::BatchPayment->create('CardFortress::Transport',
86 login => $self->login,
87 password => $self->password,
88 debug => $self->debug,
89 test_mode => $self->test_mode,
90 private_key => $self->private_key,
91 serializer => $self->serializer,
98 bless $batch, 'Business::BatchPayment::CardFortress::Batch';
99 # things that get encoded in the batch:
100 $batch->gateway($self->gateway);
101 $batch->gateway_opts($self->gateway_opts);
110 package Business::BatchPayment::CardFortress::Batch;
113 extends 'Business::BatchPayment::Batch';
119 has 'gateway_opts' => (
124 package Business::BatchPayment::CardFortress::Transport;
127 use Moose::Util::TypeConstraints;
128 extends 'Business::BatchPayment::Transport::HTTPS';
129 with 'Business::BatchPayment::TestMode';
135 use Crypt::OpenSSL::RSA;
137 local $Data::Dumper::Useqq = 1; # because encryption keys will be unprintable
142 $self->test_mode ? 'test.cardfortress.com'
143 : 'gw.cardfortress.com'
148 has ['login', 'password', 'private_key'] => (isa => 'Str', is => 'rw');
150 has 'serializer' => ( handles => [qw(serialize deserialize)] );
153 # simplify this a little
154 my ($self, $path, $args) = @_;
155 warn "Sending to $path\n" if $self->debug;
157 warn Dumper($args)."\n\n" if $self->debug >= 2;
159 content => $self->serialize($args),
160 login => $self->login,
161 password => $self->password
163 my ($page, $response, %reply_headers) = Net::HTTPS::Any::https_post(
170 die "Bad response from server: $response\n" if $response !~ /^200/;
171 warn "$response\n" if $self->debug;
172 my ($result, $error);
174 $result = $self->deserialize($page);
176 # Storable error messages are useless.
177 $error = "Bad data from server:\n$page\n";
179 warn Dumper($result)."\n\n" if $self->debug >= 2;
180 $error ||= $result->{error} if ref $result eq 'HASH';
181 die "$error\n" if $error;
187 my ($self, $content) = @_;
188 warn "Sending batch...\n" if $self->debug;
189 my $result = $self->https_post('/batch/submit', $content);
190 die $result->{error} if $result->{error};
192 my $batch_id = $result->{batch_id};
193 my $private_key = read_file($self->private_key)
194 or die "No private key available";
195 my $rsa = Crypt::OpenSSL::RSA->new_private_key($private_key);
198 foreach my $item (@{ $result->{items} }) {
199 if ( $item->{error} ) {
200 # We have no reliable way to report an error in a specific transaction
201 # at this stage. The server will send the error in the reply batch.
202 # For now do nothing.
203 } elsif ( $item->{challenge} ) {
204 my $challenge = $item->{challenge};
205 $answers{ $item->{tid} } = $rsa->decrypt($challenge);
209 warn "Answering cryptographic challenge...\n" if $self->debug;
210 $self->https_post('/batch/run', { batch_id => $batch_id, answers => \%answers});
215 warn "Fetching batch index...\n" if $self->debug;
216 my $result = $self->https_post('/batch/status');
218 my @batches_in_transit;
219 foreach (@{ $result->{batches} }) {
220 if ( $_->{status} eq 'received' ) {
221 push @batches_in_transit, $_->{batch_id};
224 return if scalar(@batches_in_transit) == 0;
225 $result = $self->https_post('/batch/receive',
226 { batch_id => \@batches_in_transit });
227 # this shouldn't contain errors, since the server just told us
228 # that the batches exist...
231 die "Error receiving batch: ".$_->{error}."\n";
234 # $self->https_post('/batch/close', {
235 # login => $self->login,
236 # password => $self->password,
237 # batch_id => \@batches_in_transit,
243 Mark Wells, C<< <mark at freeside.biz> >>
247 You can find documentation for this module with the perldoc command.
249 perldoc Business::BatchPayment::CardFortress
251 For information about the CardFortress system, contact Freeside Internet
254 L<http://www.freeside.biz>
256 =head1 ACKNOWLEDGEMENTS
258 =head1 LICENSE AND COPYRIGHT
260 Copyright 2013 Mark Wells.
262 This program is free software; you can redistribute it and/or modify it
263 under the terms of either: the GNU General Public License as published
264 by the Free Software Foundation; or the Artistic License.
266 See http://dev.perl.org/licenses/ for more information.
271 1; # End of Business::BatchPayment::Paymentech