Initial import start
authorjeff <jeff>
Wed, 26 Sep 2007 21:53:00 +0000 (21:53 +0000)
committerjeff <jeff>
Wed, 26 Sep 2007 21:53:00 +0000 (21:53 +0000)
Changes [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
README [new file with mode: 0644]
t/00load.t [new file with mode: 0644]
t/bop.t [new file with mode: 0644]
t/credit_card.t [new file with mode: 0644]
t/live_card.t [new file with mode: 0644]
t/pod-coverage.t [new file with mode: 0644]
t/pod.t [new file with mode: 0644]
viaKLIX.pm [new file with mode: 0644]

diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..307aaa4
--- /dev/null
+++ b/Changes
@@ -0,0 +1,2 @@
+0.01  Tue Sep 26 13:24:25 PST 2007
+        - original version; created by jeff
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..9ae1cd7
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,11 @@
+Changes
+Makefile.PL
+MANIFEST
+README
+viaKLIX.pm
+t/00load.t
+t/bop.t
+t/credit_card.t
+t/live_card.t
+t/pod-coverage.t
+t/pod.t
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..f9eaa1a
--- /dev/null
@@ -0,0 +1,13 @@
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+    'NAME'         => 'Business::OnlinePayment::viaKLIX',
+    'VERSION_FROM' => 'viaKLIX.pm', # finds $VERSION
+    'AUTHOR'       => 'Jeff Finucane <jeff@cmh.net>',
+    'PREREQ_PM'    => { 
+                        'Business::OnlinePayment' => 3,
+                        'Business::OnlinePayment::HTTPS' => 0.04,
+                        'Business::CreditCard' => 0.27,
+                      },
+);
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..390082b
--- /dev/null
+++ b/README
@@ -0,0 +1,24 @@
+Copyright (c) 2007 Jeff Finucane
+Copyright (c) 2007 Freeside Internet Services, Inc.
+All rights reserved. This program is free software; you can
+redistribute it and/or modify it under the same terms as Perl itself.
+
+This is Business::OnlinePayment::viaKLIX, a Business::OnlinePayment
+back end module for viaKLIX. http://www.viaklix.com/
+
+Based on Business::OnlinePayment::AuthorizeNet written by Jason Kohles.
+Currently maintained by Jeff Finucane <viaklix@weasellips.com>.
+
+Business::OnlinePayment is a generic interface for processing payments
+through online credit card processors, online check acceptance houses,
+etc.  (If you like buzzwords, call it an "multiplatform
+ecommerce-enabling middleware solution").
+
+You should be aware that portions of "make test" which interact with
+viaKLIX will fail if you configure your virtual terminal to require
+fields the tester does not expect to be required.  The documentation
+specifies that ssl_merchant_id, ssl_pin, ssl_transaction_type, and
+ssl_amount are required.  All others are optional.  You will probably
+need to set ssl_user_id via the environment as well, since viaKLIX
+recommends not using the administrative login.
+
diff --git a/t/00load.t b/t/00load.t
new file mode 100644 (file)
index 0000000..db03d45
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More tests => 2;
+
+BEGIN {
+    use_ok("Business::OnlinePayment")
+      or BAIL_OUT("unable to load Business::OnlinePayment\n");
+
+    use_ok("Business::OnlinePayment::viaKLIX")
+      or BAIL_OUT("unable to load Business::OnlinePayment::viaKLIX\n");
+}
diff --git a/t/bop.t b/t/bop.t
new file mode 100644 (file)
index 0000000..89de36a
--- /dev/null
+++ b/t/bop.t
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More tests => 11;
+
+use Business::OnlinePayment;
+
+my $package = "Business::OnlinePayment";
+my $driver  = "viaKLIX";
+
+{    # new
+    my $obj;
+
+    $obj = $package->new($driver);
+    isa_ok( $obj, $package );
+
+    # convenience methods
+    can_ok( $obj, qw(order_number avs_code cvv2_response) );
+    can_ok( $obj, qw(debug expdate_mmyy) );
+
+    # internal methods
+    can_ok( $obj, qw(_map_fields _revmap_fields) );
+
+    # defaults
+    my $server = "www.viaKLIX.com";
+
+    is( $obj->server, $server, "server($server)" );
+    is( $obj->port, "443", "port(443)" );
+    is( $obj->path, "/process.asp", "process.asp" );
+}
+
+{    # expdate
+    my $obj = $package->new($driver);
+    my @exp = (
+
+        #OFF [qw(1999.8   0899)],
+        #OFF [qw(1984-11  1184)],
+        #OFF [qw(06/7     0706)],
+        #OFF [qw(06-12    1206)],
+        [qw(12/06    1206)],
+        [qw(6/2000   0600)],
+        [qw(10/2000  1000)],
+        [qw(1/99     0199)],
+    );
+    foreach my $aref (@exp) {
+        my ( $exp, $moyr ) = @$aref;
+        my ($mmyy) = $obj->expdate_mmyy($exp);
+        is( $mmyy, $moyr, "$exp: MMYY '$mmyy' eq '$moyr' from $exp" );
+    }
+}
diff --git a/t/credit_card.t b/t/credit_card.t
new file mode 100644 (file)
index 0000000..b13048b
--- /dev/null
@@ -0,0 +1,197 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use POSIX qw(strftime);
+use Test::More;
+
+use Business::OnlinePayment;
+
+my $runinfo =
+    "to test set environment variables:"
+  . " (required) VIAKLIX_ACCOUNT, VIAKLIX_USERID, and VIAKLIX_PASSWORD";
+
+plan(
+      (   $ENV{"VIAKLIX_ACCOUNT"}
+       && $ENV{"VIAKLIX_USERID"}
+       && $ENV{"VIAKLIX_PASSWORD"} )
+    ? ( tests => 42 )
+    : ( skip_all => $runinfo )
+);
+
+my %opts = (
+    "debug"                      => 0,
+    "default_ssl_user_id"        => $ENV{"VIAKLIX_USERID"},
+    "default_ssl_salestax"       => "0.00",
+    "default_ssl_customer_code"  => "TESTCUSTOMER",
+);
+
+my %content = (
+    login          => $ENV{"VIAKLIX_ACCOUNT"},
+    password       => $ENV{"VIAKLIX_PASSWORD"},
+    action         => "Normal Authorization",
+    type           => "VISA",
+    description    => "Business::OnlinePayment::viaKLIX test",
+    card_number    => "4111111111111111",
+    cvv2           => "123",
+    expiration     => "12/" . strftime( "%y", localtime ),
+    amount         => "0.01",
+    invoice_number => "Test1",
+    first_name     => "Tofu",
+    last_name      => "Beast",
+    email          => 'viaklix@weasellips.com',
+    address        => "123 Anystreet",
+    city           => "Anywhere",
+    state          => "GA",
+    zip            => "30004",
+    country        => "US",
+);
+
+{    # valid card number test
+    my $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+    $tx->content(%content);
+    tx_check(
+        $tx,
+        desc          => "valid card_number",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => "123456",
+        avs_code      => "X",
+        cvv2_response => "P",
+        order_number  => "00000000-0000-0000-0000-000000000000",
+    );
+}
+
+{    # invalid card number test
+
+    my $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+    $tx->content( %content, card_number => "4111111111111112" );
+    tx_check(
+        $tx,
+        desc          => "invalid card_number",
+        is_success    => 0,
+        result_code   => 4000,
+        authorization => undef,
+        avs_code      => undef,
+        cvv2_response => undef,
+        order_number  => undef,
+    );
+}
+
+
+SKIP: {    # avs_code() / AVSZIP and AVSADDR tests
+
+    skip "AVS tests broken", 18;
+
+    my $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+
+    $tx->content( %content, "address" => "500 Any street" );
+    tx_check(
+        $tx,
+        desc          => "AVSADDR=N,AVSZIP=Y",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => "123456",
+        avs_code      => "Z",
+        cvv2_response => "P",
+        order_number  => "00000000-0000-0000-0000-000000000000",
+    );
+
+    $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+    $tx->content( %content, "zip" => "99999" );
+    tx_check(
+        $tx,
+        desc          => "AVSADDR=Y,AVSZIP=N",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => "123456",
+        avs_code      => "A",
+        cvv2_response => "P",
+        order_number  => "00000000-0000-0000-0000-000000000000",
+    );
+
+    $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+    $tx->content( %content, "address" => "500 Any street", "zip" => "99999" );
+    tx_check(
+        $tx,
+        desc          => "AVSADDR=N,AVSZIP=N",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => "123456",
+        avs_code      => "N",
+        cvv2_response => "P",
+        order_number  => "00000000-0000-0000-0000-000000000000",
+    );
+}
+
+SKIP: {    # cvv2_response() / CVV2MATCH
+
+    skip "CVV2 tests broken", 6;
+
+    my $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+
+    $tx->content( %content, "cvv2" => "301" );
+    tx_check(
+        $tx,
+        desc          => "wrong cvv2",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => "123456",
+        avs_code      => "X",
+        cvv2_response => "N",
+        order_number  => "00000000-0000-0000-0000-000000000000",
+    );
+
+}
+
+SKIP: {    # refund test
+
+    skip "credit/refund tests broken", 6;
+
+    my $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+    $tx->content( %content, 'action' => "Credit",
+                            'card_number' => "4444333322221111",
+                );
+    tx_check(
+        $tx,
+        desc          => "refund/credit",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => undef,
+        avs_code      => undef,
+        cvv2_response => undef,
+        order_number  => "00000000-0000-0000-0000-000000000000",
+    );
+}
+
+sub tx_check {
+    my $tx = shift;
+    my %o  = @_;
+
+    $tx->test_transaction(1);
+    $tx->submit;
+
+    is( $tx->is_success,    $o{is_success},    "$o{desc}: " . tx_info($tx) );
+    is( $tx->result_code,   $o{result_code},   "result_code(): RESULT" );
+    is( $tx->authorization, $o{authorization}, "authorization() / AUTHCODE" );
+    is( $tx->avs_code,  $o{avs_code},  "avs_code() / AVSADDR and AVSZIP" );
+    is( $tx->cvv2_response, $o{cvv2_response}, "cvv2_response() / CVV2MATCH" );
+    is( $tx->order_number, $o{order_number}, "order_number() / PNREF" );
+}
+
+sub tx_info {
+    my $tx = shift;
+
+    no warnings 'uninitialized';
+
+    return (
+        join( "",
+            "is_success(",     $tx->is_success,    ")",
+            " order_number(",  $tx->order_number,  ")",
+            " result_code(",   $tx->result_code,   ")",
+            " auth_info(",     $tx->authorization, ")",
+            " avs_code(",      $tx->avs_code,      ")",
+            " cvv2_response(", $tx->cvv2_response, ")",
+        )
+    );
+}
diff --git a/t/live_card.t b/t/live_card.t
new file mode 100644 (file)
index 0000000..b239ffc
--- /dev/null
@@ -0,0 +1,202 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use POSIX qw(strftime);
+use Test::More;
+
+use Business::OnlinePayment;
+
+my $runinfo =
+    "to test set environment variables:"
+  . " (required) VIAKLIX_ACCOUNT, VIAKLIX_PASSWORD, VIAKLIX_CARD,"
+  . " VIAKLIX_CVV2, VIAKLIX_EXP, VIAKLIX_CARD_NAME, VIAKLIX_CARD_ADDRESS,"
+  . " VIAKLIX_CARD_CITY, VIAKLIX_CARD_STATE, VIAKLIX_CARD_ZIP, and VIAKLIX_DO_LIVE ";
+
+plan(
+      ( $ENV{"VIAKLIX_ACCOUNT"} && $ENV{"VIAKLIX_PASSWORD"} &&
+        $ENV{"VIAKLIX_CARD"} && $ENV{"VIAKLIX_CVV2"} &&
+        $ENV{"VIAKLIX_EXP"} && $ENV{"VIAKLIX_CARD_NAME"} &&
+        $ENV{"VIAKLIX_CARD_ADDRESS"} && $ENV{"VIAKLIX_CARD_CITY"} &&
+        $ENV{"VIAKLIX_CARD_STATE"} && $ENV{"VIAKLIX_CARD_ZIP"} &&
+        $ENV{"VIAKLIX_DO_LIVE"}
+      )
+    ? ( tests => 42 )
+    : ( skip_all => $runinfo )
+);
+
+my %opts = (
+    "debug"                      => 2,
+    "default_ssl_user_id"        => $ENV{"VIAKLIX_USERID"},
+    "default_ssl_salestax"       => "0.00",
+    "default_ssl_customer_code"  => "LIVETESTCUSTOMER",
+);
+
+my %content = (
+    login          => $ENV{"VIAKLIX_ACCOUNT"},
+    password       => $ENV{"VIAKLIX_PASSWORD"},
+    action         => "Normal Authorization",
+    type           => "CC",
+    description    => "Business::OnlinePayment::viaKLIX live test",
+    card_number    => $ENV{"VIAKLIX_CARD"},
+    cvv2           => $ENV{"VIAKLIX_CVV2"},
+    expiration     => $ENV{"VIAKLIX_EXP"},
+    amount         => "0.01",
+    invoice_number => "LiveTest",
+    name           => $ENV{"VIAKLIX_CARD_NAME"},
+    address        => $ENV{"VIAKLIX_CARD_ADDRESS"},
+    city           => $ENV{"VIAKLIX_CARD_CITY"},
+    state          => $ENV{"VIAKLIX_CARD_STATE"},
+    zip            => $ENV{"VIAKLIX_CARD_ZIP"},
+);
+
+my $credit_amount = 0;
+
+{    # valid card number test
+    my $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+    $tx->content(%content);
+    tx_check(
+        $tx,
+        desc          => "valid card_number",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => qr/^\w{6}$/,
+        avs_code      => "Y",
+        cvv2_response => "M",
+        order_number  => qr/^([0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12})$/,
+    );
+   $credit_amount += $content{amount} if $tx->is_success;
+}
+
+{    # invalid card number test
+
+    my $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+    $tx->content( %content, card_number => "4111111111111112" );
+    tx_check(
+        $tx,
+        desc          => "invalid card_number",
+        is_success    => 0,
+        result_code   => 4000,
+        authorization => qr/^$/,
+        avs_code      => undef,
+        cvv2_response => undef,
+        order_number  => qr/^$/,
+    );
+}
+
+
+{    # avs_code() / AVSZIP and AVSADDR tests
+
+    my $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+
+    $tx->content( %content, "address" => "500 Any street" );
+    tx_check(
+        $tx,
+        desc          => "AVSADDR=N,AVSZIP=Y",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => qr/^\w{6}$/,
+        avs_code      => "Z",
+        cvv2_response => "M",
+        order_number  => qr/^([0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12})$/,
+    );
+    $credit_amount += $content{amount} if $tx->is_success;
+
+    $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+    $tx->content( %content, "zip" => "99999" );
+    tx_check(
+        $tx,
+        desc          => "AVSADDR=Y,AVSZIP=N",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => qr/^\w{6}$/,
+        avs_code      => "A",
+        cvv2_response => "M",
+        order_number  => qr/^([0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12})$/,
+    );
+    $credit_amount += $content{amount} if $tx->is_success;
+
+    $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+    $tx->content( %content, "address" => "500 Any street", "zip" => "99999" );
+    tx_check(
+        $tx,
+        desc          => "AVSADDR=N,AVSZIP=N",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => qr/^\w{6}$/,
+        avs_code      => "N",
+        cvv2_response => "M",
+        order_number  => qr/^([0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12})$/,
+    );
+    $credit_amount += $content{amount} if $tx->is_success;
+}
+
+{    # cvv2_response() / CVV2MATCH
+
+    my $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+
+    $tx->content( %content, "cvv2" => $content{cvv2}+1 );
+    tx_check(
+        $tx,
+        desc          => "wrong cvv2",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => qr/^\w{6}$/,
+        avs_code      => "Y",
+        cvv2_response => "N",
+        order_number  => qr/^([0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12})$/,
+    );
+    $credit_amount += $content{amount} if $tx->is_success;
+
+}
+
+SKIP: {    # refund test
+
+    skip "Refund tests require account with refund capability", 6;
+
+    my $tx = new Business::OnlinePayment( "viaKLIX", %opts );
+    $tx->content( %content, 'action' => "Credit",
+                            'amount' => sprintf("%.2f", $credit_amount),
+                );
+    tx_check(
+        $tx,
+        desc          => "refund/credit",
+        is_success    => 1,
+        result_code   => "0",
+        authorization => qr/^$/,
+        avs_code      => undef,
+        cvv2_response => undef,
+        order_number  => qr/^$/,
+    );
+}
+
+sub tx_check {
+    my $tx = shift;
+    my %o  = @_;
+
+    $tx->submit;
+
+    is( $tx->is_success,    $o{is_success},    "$o{desc}: " . tx_info($tx) );
+    is( $tx->result_code,   $o{result_code},   "result_code(): RESULT" );
+    like( $tx->authorization, $o{authorization}, "authorization() / AUTHCODE" );
+    is( $tx->avs_code,  $o{avs_code},  "avs_code() / AVSADDR and AVSZIP" );
+    is( $tx->cvv2_response, $o{cvv2_response}, "cvv2_response() / CVV2MATCH" );
+    like( $tx->order_number, $o{order_number}, "order_number() / PNREF" );
+}
+
+sub tx_info {
+    my $tx = shift;
+
+    no warnings 'uninitialized';
+
+    return (
+        join( "",
+            "is_success(",     $tx->is_success,    ")",
+            " order_number(",  $tx->order_number,  ")",
+            " result_code(",   $tx->result_code,   ")",
+            " auth_info(",     $tx->authorization, ")",
+            " avs_code(",      $tx->avs_code,      ")",
+            " cvv2_response(", $tx->cvv2_response, ")",
+        )
+    );
+}
diff --git a/t/pod-coverage.t b/t/pod-coverage.t
new file mode 100644 (file)
index 0000000..640ed2c
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More;
+
+eval "use Test::Pod::Coverage 1.00";
+plan skip_all => "Test::Pod::Coverage 1.00 required for testing POD coverage"
+    if $@;
+all_pod_coverage_ok({ also_private => [ qw( required_fields ) ]});
diff --git a/t/pod.t b/t/pod.t
new file mode 100644 (file)
index 0000000..2c9935c
--- /dev/null
+++ b/t/pod.t
@@ -0,0 +1,9 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More;
+
+eval "use Test::Pod 1.00";
+plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
+all_pod_files_ok();
diff --git a/viaKLIX.pm b/viaKLIX.pm
new file mode 100644 (file)
index 0000000..15bba8d
--- /dev/null
@@ -0,0 +1,487 @@
+package Business::OnlinePayment::viaKLIX;
+
+use strict;
+use vars qw($VERSION $DEBUG);
+use Carp qw(carp croak);
+
+use base qw(Business::OnlinePayment::HTTPS);
+
+$VERSION = '0.01';
+$VERSION = eval $VERSION;
+$DEBUG   = 0;
+
+sub debug {
+    my $self = shift;
+
+    if (@_) {
+        my $level = shift || 0;
+        if ( ref($self) ) {
+            $self->{"__DEBUG"} = $level;
+        }
+        else {
+            $DEBUG = $level;
+        }
+        $Business::OnlinePayment::HTTPS::DEBUG = $level;
+    }
+    return ref($self) ? ( $self->{"__DEBUG"} || $DEBUG ) : $DEBUG;
+}
+
+sub set_defaults {
+    my $self = shift;
+    my %opts = @_;
+
+    # standard B::OP methods/data
+    $self->server("www.viaKLIX.com");
+    $self->port("443");
+    $self->path("/process.asp");
+
+    $self->build_subs(qw( 
+                          order_number avs_code cvv2_response
+                          response_page response_code response_headers
+                     ));
+
+    # module specific data
+    if ( $opts{debug} ) {
+        $self->debug( $opts{debug} );
+        delete $opts{debug};
+    }
+
+    my %_defaults = ();
+    foreach my $key (keys %opts) {
+      $key =~ /^default_(\w*)$/ or next;
+      $_defaults{$1} = $opts{$key};
+      delete $opts{$key};
+    }
+    $self->{_defaults} = \%_defaults;
+
+}
+
+sub _map_fields {
+    my ($self) = @_;
+
+    my %content = $self->content();
+
+    #ACTION MAP
+    my %actions = (
+        'normal authorization' => 'SALE',  # Authorization/Settle transaction
+        'credit'               => 'CREDIT', # Credit (refund)
+    );
+
+    $content{'ssl_transaction_type'} = $actions{ lc( $content{'action'} ) }
+      || $content{'action'};
+
+    # TYPE MAP
+    my %types = (
+        'visa'             => 'CC',
+        'mastercard'       => 'CC',
+        'american express' => 'CC',
+        'discover'         => 'CC',
+        'cc'               => 'CC',
+    );
+
+    $content{'type'} = $types{ lc( $content{'type'} ) } || $content{'type'};
+
+    $self->transaction_type( $content{'type'} );
+
+    # stuff it back into %content
+    $self->content(%content);
+}
+
+sub _revmap_fields {
+    my ( $self, %map ) = @_;
+    my %content = $self->content();
+    foreach ( keys %map ) {
+        $content{$_} =
+          ref( $map{$_} )
+          ? ${ $map{$_} }
+          : $content{ $map{$_} };
+    }
+    $self->content(%content);
+}
+
+sub expdate_mmyy {
+    my $self       = shift;
+    my $expiration = shift;
+    my $expdate_mmyy;
+    if ( defined($expiration) and $expiration =~ /^(\d+)\D+\d*(\d{2})$/ ) {
+        my ( $month, $year ) = ( $1, $2 );
+        $expdate_mmyy = sprintf( "%02d", $month ) . $year;
+    }
+    return defined($expdate_mmyy) ? $expdate_mmyy : $expiration;
+}
+
+sub required_fields {
+    my($self,@fields) = @_;
+
+    my @missing;
+    my %content = $self->content();
+    foreach(@fields) {
+      next
+        if (exists $content{$_} && defined $content{$_} && $content{$_}=~/\S+/);
+      push(@missing, $_);
+    }
+
+    Carp::croak("missing required field(s): " . join(", ", @missing) . "\n")
+      if(@missing);
+
+}
+
+sub submit {
+    my ($self) = @_;
+
+    $self->_map_fields();
+
+    my %content = $self->content;
+
+    my %required;
+    $required{CC_SALE} =  [ qw( ssl_transaction_type ssl_merchant_id ssl_pin
+                                ssl_amount ssl_card_number ssl_exp_date
+                              ) ];
+    $required{CC_CREDIT} = $required{CC_SALE};
+    my %optional;
+    $optional{CC_SALE} =  [ qw( ssl_user_id ssl_salestax ssl_cvv2 ssl_cvv2cvc2
+                                ssl_description ssl_invoice_number
+                                ssl_customer_code ssl_company ssl_first_name
+                                ssl_last_name ssl_avs_address ssl_address2
+                                ssl_city ssl_state ssl_avs_zip ssl_country
+                                ssl_phone ssl_email ssl_ship_to_company
+                                ssl_ship_to_first_name ssl_ship_to_last_name
+                                ssl_ship_to_address ssl_ship_to_city
+                                ssl_ship_to_state ssl_ship_to_zip
+                                ssl_ship_to_country
+                              ) ];
+    $optional{CC_CREDIT} = $optional{CC_SALE};
+
+    my $type_action = $self->transaction_type(). '_'. $content{ssl_transaction_type};
+    unless ( exists($required{$type_action}) ) {
+      $self->error_message("viaKLIX can't handle transaction type: ".
+        "$content{action} on " . $self->transaction_type() );
+      $self->is_success(0);
+      return;
+    }
+
+    my $expdate_mmyy = $self->expdate_mmyy( $content{"expiration"} );
+    my $zip          = $content{'zip'};
+    $zip =~ s/[^[:alnum:]]//g;
+
+    my $cvv2indicator = 'present' if ( $content{"cvv2"} ); # visa only
+
+    $self->_revmap_fields(
+
+        ssl_merchant_id     => 'login',
+        ssl_pin             => 'password',
+
+        ssl_amount          => 'amount',
+        ssl_card_number     => 'card_number',
+        ssl_exp_date        => \$expdate_mmyy,    # MMYY from 'expiration'
+        ssl_cvv2            => \$cvv2indicator,
+        ssl_cvv2cvc2        => 'cvv2',
+        ssl_description     => 'description',
+        ssl_invoice_number  => 'invoice_number',
+        ssl_customer_code   => 'customer_id',
+
+        ssl_first_name      => 'first_name',
+        ssl_last_name       => 'last_name',
+        ssl_avs_address     => 'address',
+        ssl_city            => 'city',
+        ssl_state           => 'state',
+        ssl_avs_zip         => \$zip,          # 'zip' with non-alnums removed
+        ssl_country         => 'country',
+        ssl_phone           => 'phone',
+        ssl_email           => 'email',
+
+    );
+
+    my %params = $self->get_fields( @{$required{$type_action}},
+                                    @{$optional{$type_action}},
+                                  );
+
+    foreach ( keys ( %{($self->{_defaults})} ) ) {
+      $params{$_} = $self->{_defaults}->{$_} unless exists($params{$_});
+    }
+
+    $params{ssl_test_mode}='true' if $self->test_transaction;
+    
+    $params{ssl_show_form}='false';
+    $params{ssl_result_format}='ASCII';
+
+    $self->required_fields(@{$required{$type_action}});
+    
+    warn join("\n", map{ "$_ => $params{$_}" } keys(%params)) if $DEBUG > 1;
+    my ( $page, $resp, %resp_headers ) = 
+      $self->https_post( %params );
+
+    $self->response_code( $resp );
+    $self->response_page( $page );
+    $self->response_headers( \%resp_headers );
+
+    warn "$page\n" if $DEBUG > 1;
+    # $page should contain key/value pairs
+
+    my $status ='';
+    my %results = map { s/\s*$//; split '=', $_, 2 } split '^', $page;
+
+    # AVS and CVS values may be set on success or failure
+    $self->avs_code( $results{ssl_avs_response} );
+    $self->cvv2_response( $results{ ssl_cvv2_response } );
+    $self->result_code( $status = $results{ ssl_result } );
+    $self->order_number( $results{ ssl_txn_id } );
+    $self->authorization( $results{ ssl_approval_code } );
+    $self->error_message( $results{ ssl_result_message } );
+
+
+    if ( $resp =~ /^(HTTP\S+ )?200/ && $status eq "0" ) {
+        $self->is_success(1);
+    } else {
+        $self->is_success(0);
+    }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Business::OnlinePayment::viaKLIX - viaKLIX backend for Business::OnlinePayment
+
+=head1 SYNOPSIS
+
+  use Business::OnlinePayment;
+  
+  my $tx = new Business::OnlinePayment(
+      'viaKLIX', 'default_ssl_user_id' => 'webuser',
+  );
+  
+  # See the module documentation for details of content()
+  $tx->content(
+      type           => 'CC',
+      action         => 'Normal Authorization',
+      description    => 'Business::OnlinePayment::viaKLIX test',
+      amount         => '49.95',
+      invoice_number => '100100',
+      customer_id    => 'jef',
+      name           => 'Jeff Finucane',
+      address        => '123 Anystreet',
+      city           => 'Anywhere',
+      state          => 'GA',
+      zip            => '30004',
+      email          => 'viaklix@weasellips.com',
+      card_number    => '4111111111111111',
+      expiration     => '12/09',
+      cvv2           => '123',
+      order_number   => 'string',
+  );
+  
+  $tx->submit();
+  
+  if ( $tx->is_success() ) {
+      print(
+          "Card processed successfully: ", $tx->authorization, "\n",
+          "order number: ",                $tx->order_number,  "\n",
+          "CVV2 response: ",               $tx->cvv2_response, "\n",
+          "AVS code: ",                    $tx->avs_code,      "\n",
+      );
+  }
+  else {
+      print(
+          "Card was rejected: ", $tx->error_message, "\n",
+          "order number: ",      $tx->order_number,  "\n",
+      );
+  }
+
+=head1 DESCRIPTION
+
+This module is a back end driver that implements the interface
+specified by L<Business::OnlinePayment> to support payment handling
+via viaKLIX's Internet payment solution.
+
+See L<Business::OnlinePayment> for details on the interface this
+modules supports.
+
+=head1 Standard methods
+
+=over 4
+
+=item set_defaults()
+
+This method sets the 'server' attribute to 'www.viaklix.com' and
+the port attribute to '443'.  This method also sets up the
+L</Module specific methods> described below.
+
+=item submit()
+
+=back
+
+=head1 Unofficial methods
+
+This module provides the following methods which are not officially part of the
+standard Business::OnlinePayment interface (as of 3.00_06) but are nevertheless
+supported by multiple gateways modules and expected to be standardized soon:
+
+=over 4
+
+=item L<order_number()|/order_number()>
+
+=item L<avs_code()|/avs_code()>
+
+=item L<cvv2_response()|/cvv2_response()>
+
+=back
+
+=head1 Module specific methods
+
+This module provides the following methods which are not currently
+part of the standard Business::OnlinePayment interface:
+
+=over 4
+
+=item L<expdate_mmyy()|/expdate_mmyy()>
+
+=item L<debug()|/debug()>
+
+=back
+
+=head1 Settings
+
+The following default settings exist:
+
+=over 4
+
+=item server
+
+www.viaklix.com
+
+=item port
+
+443
+
+=item path
+
+/process.asp
+
+=back
+
+=head1 Parameters passed to constructor
+
+If any of the key/value pairs passed to the constructor have a key
+beginning with "default_" then those values are passed to viaKLIX as
+a the corresponding form field (without the "default_") whenever
+content(%content) lacks that key.
+
+=head1 Handling of content(%content)
+
+The following rules apply to content(%content) data:
+
+=head2 type
+
+If 'type' matches one of the following keys it is replaced by the
+right hand side value:
+
+  'visa'               => 'CC',
+  'mastercard'         => 'CC',
+  'american express'   => 'CC',
+  'discover'           => 'CC',
+
+The value of 'type' is used to set transaction_type().  Currently this
+module only supports the above values.
+
+=head1 Setting viaKLIX parameters from content(%content)
+
+The following rules are applied to map data to viaKLIX parameters
+from content(%content):
+
+    # viaKLIX param     => $content{<key>}
+      ssl_merchant_id   => 'login',
+      ssl_pin           => 'password',
+
+      ssl_amount        => 'amount',
+      ssl_card_number   => 'card_number',
+      ssl_exp_date      => \( $month.$year ), # MM/YY from 'expiration'
+      ssl_cvv2          => 'present' whenever cvv2 data is provided
+      ssl_cvv2cvc2      => 'cvv2',
+      ssl_description   => 'description',
+      ssl_invoice_number=> 'invoice_number',
+      ssl_customer_code   => 'customer_id',
+
+      ssl_first_name    => 'first_name',
+      ssl_last_name     => 'last_name',
+      ssl_avs_address   => 'address',
+      ssl_city          => 'city',
+      ssl_state         => 'state',
+      ssl_zip           => \$zip,       # 'zip' with non-alphanumerics removed
+      ssl_country       => 'country',
+      ssl_phone         => 'phone',
+      ssl_email         => 'email',
+
+      CardHolderName    => 'name',
+      CustomerName      => 'account_name',
+
+
+=head1 Mapping viaKLIX transaction responses to object methods
+
+The following methods provides access to the transaction response data
+resulting from a viaKLIX request (after submit()) is called:
+
+=head2 order_number()
+
+This order_number() method returns the ssl_txn_id field for card transactions
+to uniquely identify the transaction.
+
+=head2 result_code()
+
+The result_code() method returns the ssl_result field for card transactions.
+It is the numeric return code indicating the outcome of the attempted
+transaction.
+
+=head2 error_message()
+
+The error_message() method returns the ssl_result_message field for
+transactions.  This provides more details about the transaction result.
+
+=head2 authorization()
+
+The authorization() method returns the ssl_approval_code field,
+which is the approval code obtained from the card processing network.
+
+=head2 avs_code()
+
+The avs_code() method returns the ssl_avs_response field from the
+transaction result.
+
+=head2 cvv2_response()
+
+The cvv2_response() method returns the ssl_cvvw_response field, which is a
+response message returned with the transaction result.
+
+=head2 expdate_mmyy()
+
+The expdate_mmyy() method takes a single scalar argument (typically
+the value in $content{expiration}) and attempts to parse and format
+and put the date in MMYY format as required by PayflowPro
+specification.  If unable to parse the expiration date simply leave it
+as is and let the PayflowPro system attempt to handle it as-is.
+
+=head2 debug()
+
+Enable or disble debugging.  The value specified here will also set
+$Business::OnlinePayment::HTTPS::DEBUG in submit() to aid in
+troubleshooting problems.
+
+=head1 COMPATIBILITY
+
+This module implements an interface to the viaKLIX API version 2.0
+
+=head1 AUTHORS
+
+Jeff Finucane <viaklix@weasellips.com>
+
+Based on Business::OnlinePayment::PayflowPro written by Ivan Kohler
+and Phil Lobbes.
+
+=head1 SEE ALSO
+
+perl(1), L<Business::OnlinePayment>, L<Carp>, and the Developer Guide to the
+viaKLIX Virtual Terminal.
+
+=cut