use warnings;
use strict;
#use vars qw( $DEBUG $me );
+use File::Slurp;
+use MIME::Base64;
+use Crypt::OpenSSL::RSA;
-our $VERSION = 0.01;
+our $VERSION = 0.03;
sub _info {
{
- 'info_version' => '0.01',
+ 'info_compat' => '0.01',
'module_version' => $VERSION,
'supported_types' => [ 'CC' ],
'supported_actions' => { 'CC' => [
'Post Authorization',
'Void',
'Credit',
+ 'Tokenize',
],
},
- 'token_support' => 'challenge/response',
+ 'token_support' => 1,
#need to figure out how to pass through for gateways that do... an option?
#'CC_void_requires_card' => 1,
};
$self->build_subs(qw( order_number avs_code cvv2_response
response_page response_code response_headers
- card_token
+ card_token private_key
));
}
sub submit {
my $self = shift;
- $self->server('test.cardfortress.com');
+ $self->server('test.cardfortress.com') if $self->test_transaction;
- my ($page,$server_response,%headers) = $self->https_post($self->content);
+ my %content = $self->content;
+ $content{$_} = $self->$_() for qw( gateway gateway_login gateway_password );
- die $server_response unless $server_response =~ /^200/;
+ $content{$_} = $self->$_() for grep $self->can($_), qw( bop_options );
- my %response = {};
+ my ($page,$server_response,%headers) = $self->https_post(%content);
+
+ die "$server_response\n" unless $server_response =~ /^200/;
+
+ my %response = ();
#this encoding good enough? wfm... if something's easier for other
#languages they can always use a different URL
foreach my $line ( grep /^\w+=/, split(/\n/, $page) ) {
# response_headers()
# response_page()
+ #handle the challenge/response handshake
+ if ( $self->error_message eq '_challenge' ) { #XXX infinite loop protection?
+
+ my $private_key = $self->private_key
+ or die "no private key available";
+
+ $private_key = read_file($private_key)
+ if $private_key !~ /-----BEGIN/ && -r $private_key;
+
+ #decrypt the challenge with the private key
+ my $challenge = decode_base64($response{'card_challenge'});
+
+ #here is the hardest part to implement at each client side
+ my $rsa_priv = Crypt::OpenSSL::RSA->new_private_key($private_key);
+ my $response = $rsa_priv->decrypt($challenge);
+
+ #try the transaction again with the challenge response
+ # (B:OP could sure use a better way to alter one value)
+ my %content = $self->content;
+ $content{'card_response'} = encode_base64($response, '');
+ $self->content(%content);
+ $self->submit;
+ }
+
}
1;
use Business::OnlinePayment;
- my $tx = new Business::OnlinePayment( 'CardFortress',
- 'gateway' => 'ProcessingGateway',
- 'gateway_login' => 'gwlogin',
- 'gateway_password' => 'gwpass',
- #private_key not necessary
- );
+ my $tx = new Business::OnlinePayment(
+ 'CardFortress',
+ 'gateway' => 'ProcessingGateway',
+ 'gateway_login' => 'gwlogin',
+ 'gateway_password' => 'gwpass',
+ #private_key not necessary
+ );
$tx->content(
type => 'VISA',
# ... time slips by ...
- my $rx = new Business::OnlinePayment( 'CardFortress',
- 'gateway' => 'ProcessingGateway',
- 'gateway_login' => 'gwlogin',
- 'gateway_password' => 'gwpass',
- 'private_key' => '/path/to/keyfile',
- );
+ my $rx = new Business::OnlinePayment(
+ 'CardFortress',
+ 'gateway' => 'ProcessingGateway',
+ 'gateway_login' => 'gwlogin',
+ 'gateway_password' => 'gwpass',
+ 'private_key' => $private_key_string, #or filename
+ 'bop_options' => join('/', map "$_=".$options{$_}, keys %options),
+ );
$rx->content(
type => 'VISA',