# This is how you can use Signio PayPro with Interchange.
# 
Variable SIGNIO_HELP <<EOV

 1. Modify interchange.cfg to use this file.
 
 #include globalsub/signio

 2. Modify catalog.cfg to set the server and your Signio account info

 # Username and password
 Variable MV_PAYMENT_ID      YourSignioID
 Variable MV_PAYMENT_SECRET  YourSignioPassword
 # Use test.signio.com for testing
 Variable MV_PAYMENT_SERVER  connect.signio.com

 3. Set the payment mode in catalog.cfg:

 Variable MV_PAYMENT_MODE    custom signio

 4. Make sure CreditCardAuto is off (default)

 5. Restart Interchange.

EOV

GlobalSub <<EOS
sub signio {
#::logDebug("signio called");
    my ($user, $secret, $amount) = @_;
    my $exe = "$Global::VendRoot/lib/pfpro";

    my (%actual) = Vend::Order::map_actual();

    if(! $user  ) {
        $user    =  $::Variable->{MV_PAYMENT_ID} ||
                    $::Variable->{CYBER_ID}
                    or return undef;
    }
#::logDebug("signio user $user");
    if(! $secret) {
        $secret  =  $::Variable->{MV_PAYMENT_SECRET} ||
                    $::Variable->{CYBER_SECRET}
                    or return undef;
    }
#::logDebug("signio secret $secret");

    my $server  =   $::Variable->{MV_PAYMENT_SERVER} ||
                    $::Variable->{CYBER_SERVER} ||
                    'connect.signio.com';

    my $port    =   $::Variable->{MV_PAYMENT_PORT} ||
                    $::Variable->{CYBER_PORT} ||
                    443;

    my $precision = $::Variable->{MV_PAYMENT_PRECISION} ||
                    $::Variable->{CYBER_PRECISION} ||
                    2;

    $actual{mv_credit_card_exp_month} =~ s/\D//g;
    $actual{mv_credit_card_exp_month} =~ s/^0+//;
    $actual{mv_credit_card_exp_year} =~ s/\D//g;
    $actual{mv_credit_card_exp_year} =~ s/\d\d(\d\d)/$1/;

    $actual{mv_credit_card_number} =~ s/\D//g;

    my $exp = sprintf '%02d%02d',
                        $actual{mv_credit_card_exp_month},
                        $actual{mv_credit_card_exp_year};

    $actual{cyber_mode} = 'S'
        unless $actual{cyber_mode};

    my %type_map = (
        qw/
                        mauthcapture  S
                        mauthonly     C
                        mauthdelay    D
                        mauthreturn   V
                        S             S
                        C             C
                        D             D
                        V             V
        /
    );

    if (defined $type_map{$actual{cyber_mode}}) {
        $actual{cyber_mode} = $type_map{$actual{cyber_mode}};
    }
    else {
        $actual{cyber_mode} = 'S';
    }

    if(! $amount) {
        $amount = Vend::Interpolate::total_cost();
        $amount = sprintf("%.${precision}f", $amount);
    }

    my($orderID);
    my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time());

    # We'll make an order ID based on date, time, and Interchange session

    # $mon is the month index where Jan=0 and Dec=11, so we use
    # $mon+1 to get the more familiar Jan=1 and Dec=12
    $orderID = sprintf("%02d%02d%02d%02d%02d%05d%s",
            $year + 1900,$mon + 1,$mday,$hour,$min,$Vend::SessionName);

    my %varmap = ( qw/
                            ACCT     mv_credit_card_number
                            ZIP      zip
                            STREET   address
                            TRXTYPE  cyber_mode
        /
    );

    my %query = (
                    AMT         => $amount,
                    EXPDATE     => $exp,
                    TENDER      => 'C',
                    ORIGID      => $orderID,
                    PWD         => $secret,
                    USER        => $user,
    );

    for (keys %varmap) {
        $query{$_} = $actual{$varmap{$_}};
    }

    my @query;

    for (keys %query) {
        my $key = $_;
        my $val = $query{$key};
        $val =~ s/["\$\n\r]//g;
        $val =~ s/\$//g;
        my $len = length($val);
        if($val =~ /[&=]/) {
            $key .= "[$len]";
        }
        push @query, "$key=$val";
    }
    my $string = join '&', @query;
#::logDebug(qq{signio call: $exe $server $port "$string" |});
    open(CONNECT, qq{$exe $server $port "$string" |})
        or die "can't fork: $!\n";
    local($/);
    my $result = <CONNECT>;
    close CONNECT;

    my $decline = $?;

#::logDebug(qq{signio decline=$decline result: $result});

    my %result_map = ( qw/

            MStatus               RESULT
            pop.status            RESULT
            MErrMsg               RESPMSG
            pop.error-message     RESPMSG
            order-id              PNREF
            pop.order-id          PNREF
            pop.auth-code         AUTHCODE
            pop.avs_code          AVSZIP
            pop.avs_zip           AVSZIP
            pop.avs_addr          AVSADDR
    /
    );

    my %result = split /[&=]/, $result;

    for (keys %result_map) {
        $result{$_} = $result{$result_map{$_}}
            if defined $result{$result_map{$_}};
    }

    $Vend::Session->{cybercash_result} =
    $Vend::Session->{payment_result} = \%result;

    delete $Vend::Session->{cybercash_error};
    delete $Vend::Session->{payment_error};

    if ($decline) {
        $decline = $decline >> 8;
        $result{'pop.status'} =
        $result{'MStatus'} = 'failed';

        $Vend::Session->{cybercash_error}   =
            $Vend::Session->{payment_error} = $result{RESPMSG};

		my $msg = errmsg("Signio error: %s %s. Please call in your order or try again.",
			$result{RESULT} ,
			$result{RESPMSG},
		);

		$Vend::Session->{errors}{mv_credit_card_valid} = $msg;
    }
    else {
        $result{'pop.status'} =
        $result{'MStatus'} = 'success';

        $Vend::Session->{payment_id} = $result{'pop.order-id'};

        $Vend::Session->{cybercash_error}   =
            $Vend::Session->{payment_error} = $result{RESPMSG};
    }

#::logDebug(qq{signio decline=$decline result: } . ::uneval( \%result));

    return %result;
}
EOS
