diff options
Diffstat (limited to 'CardFortress.pm')
-rw-r--r-- | CardFortress.pm | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/CardFortress.pm b/CardFortress.pm new file mode 100644 index 0000000..3690f1c --- /dev/null +++ b/CardFortress.pm @@ -0,0 +1,223 @@ +package Business::BatchPayment::CardFortress; + +use 5.006; +use strict; +use warnings; +our $VERSION = '0.01'; + +=head1 NAME + +Business::BatchPayment::CardFortress + +=head1 DESCRIPTION + +Batch payment processing via the CardFortress secure payment proxy service. + +=head1 USAGE + +See Business::BatchPayment for general usage information. + +=head2 PROCESSOR ATTRIBUTES + +=over 4 + +=item login, password + +Your login credentials for CardFortress. + +=item gateway + +The L<Business::BatchPayment> module to use for upstream processing. You +don't need to have this module installed, but the proxy server does. + +=item gateway_* + +Processor options to pass along to the gateway module. The 'gateway_' prefix +will be stripped. + +=back + +=cut + +use Business::BatchPayment; +use Moose; +with 'Business::BatchPayment::Processor'; +with 'Business::BatchPayment::TestMode'; +with 'Business::BatchPayment::Debug'; + +use Data::Serializer; + +has [ qw(login password private_key gateway) ] => ( + is => 'ro', + isa => 'Str', + required => 1, +); + +has 'gateway_opts' => ( + is => 'ro', + isa => 'HashRef', + default => sub { {login => ''} }, +); + +has 'serializer' => ( + is => 'ro', + lazy => 1, + handles => [qw(serialize deserialize)], + default => sub { +# doesn't have to be Storable, but it must support blessed objects + Data::Serializer->new(serializer => 'Storable', encoding => 'b64') + }, +); + +# pull any arg named "gateway_foo" into gateway_opts +around 'BUILDARGS' => sub { + my ($orig, $class, %args) = @_; + foreach (keys %args) { + if (/^gateway_(.*)/) { + $args{'gateway_opts'}->{$1} = delete $args{$_}; + } + } + $class->$orig(%args); +}; + +sub default_transport { + my $self = shift; + Business::BatchPayment->create('CardFortress::Transport', + login => $self->login, + password => $self->password, + debug => $self->debug, + test_mode => $self->test_mode, + private_key => $self->private_key, + serializer => $self->serializer, + ); +} + +sub format_request { + my $self = shift; + my $batch = shift; + bless $batch, 'Business::BatchPayment::CardFortress::Batch'; + # things that get encoded in the batch: + $batch->gateway($self->gateway); + $batch->gateway_opts($self->gateway_opts); + $self->serialize($batch); # that's all folks +} + +sub parse_response { + my $self = shift; + my $input = shift; + my $batch = $self->deserialize($input); +} + +package Business::BatchPayment::CardFortress::Batch; + +use Moose; +extends 'Business::BatchPayment::Batch'; + +has 'gateway' => ( + is => 'rw', + isa => 'Str', +); +has 'gateway_opts' => ( + is => 'rw', + isa => 'HashRef', +); + +package Business::BatchPayment::CardFortress::Transport; + +use Moose; +use Moose::Util::TypeConstraints; +extends 'Business::BatchPayment::Transport::HTTPS'; +with 'Business::BatchPayment::TestMode'; + +use File::Slurp; +use MIME::Base64; +use Crypt::OpenSSL::RSA; + +has '+host' => ( + default => sub { + my $self = shift; + $self->test_mode ? 'gw.cardfortress.com' + : 'test.cardfortress.com' + }, + lazy => 1, +); + +has ['login', 'password', 'private_key'] => (isa => 'Str', is => 'rw'); + +has 'serializer' => ( handles => [qw(serialize deserialize)] ); + +sub upload { + my ($self, $content) = @_; + warn "Sending batch...\n" if $self->debug; + my ($page, $response, %reply_headers) = + $self->https_post('/batch/submit', { + login => $self->login, + password => $self->password, + content => $content, + }); + $page = $self->deserialize($page); + die $page->{error} if $page->{error}; + my $batchid = $page->{batchid}; + + my $private_key = read_file($self->private_key) + or die "No private key available"; + my $rsa = Crypt::OpenSSL::RSA->new_private_key($private_key); + + my %answers; + foreach my $item (@{ $page->{items} }) { + if ( $item->{error} ) { + # We have no reliable way to report an error in a specific transaction + # at this stage. The server will send the error in the reply batch. + # For now do nothing. + } elsif ( $item->{challenge} ) { + my $challenge = $item->{challenge}; + $answers{ $item->{tid} } = $rsa->decrypt( decode_base64($challenge) ); + } else { + # newly created card--doesn't have a challenge, so do nothing + } + } + # post the response + warn "Answering cryptographic challenge...\n" if $self->debug; + my $answer_content = + $self->serialize({ batchid => $batchid, answers => \%answers }); + ($page, $response, %reply_headers) = + $self->https_post('/batch/run', { + login => $self->login, + password => $self->password, + content => $answer_content, + }); + $page = $self->deserialize($page); + die $page->{error} if $page->{error}; + return; +} + +=head1 AUTHOR + +Mark Wells, C<< <mark at freeside.biz> >> + +=head1 SUPPORT + +You can find documentation for this module with the perldoc command. + + perldoc Business::BatchPayment::CardFortress + +Commercial support is available from Freeside Internet Services, Inc. + +L<http://www.freeside.biz> + +=head1 ACKNOWLEDGEMENTS + +=head1 LICENSE AND COPYRIGHT + +Copyright 2012 Mark Wells. + +This program is free software; you can redistribute it and/or modify it +under the terms of either: the GNU General Public License as published +by the Free Software Foundation; or the Artistic License. + +See http://dev.perl.org/licenses/ for more information. + + +=cut + +1; # End of Business::BatchPayment::Paymentech |