# ==================================================================
# Gossamer Links - enhanced directory management system
#
#   Website  : http://gossamer-threads.com/
#   Support  : http://gossamer-threads.com/scripts/support/
#   CVS Info : 087,071,086,086,085      
#   Revision : $Id: AuthorizeDotNet.pm,v 1.3 2005/03/05 01:29:09 brewt Exp $
# 
# Copyright (c) 2003 Gossamer Threads Inc.  All Rights Reserved.
# Redistribution in part or in whole strictly prohibited.  Please
# see LICENSE file for full details.
# ==================================================================
#
# Glue between Gossamer Links and Authorize.Net payment interface

package Links::Payment::Direct::AuthorizeDotNet;
use strict;

# Make sure the payment module is available
use GT::Payment::Direct::AuthorizeDotNet;
use Links qw/$IN $CFG $DB/;
use vars qw/%INVALID %EMPTY/;

sub required {
# -----------------------------------------------------------------------------
# Returns a list of required field names.  Each field name will be looked for
# in the language file, prefixed with 'PAYMENT_DIRECT_AuthorizeDotNet_', for
# the title of the field, and 'PAYMENT_DIRECT_DESC_AuthorizeDotNet_' for a
# description of the field's contents.
# Note that these are just required SETUP fields, so things like credit card
# number, billing name, etc. are NOT included.
    return
        account_username => { type => 'TEXT', valid => '^\w+$' }, # FIXME - I have no idea what this can be
        account_key => { type => 'TEXT', valid => '^\w+$' };

}

sub optional {
# -----------------------------------------------------------------------------
    my @currencies;
    for (sort {
        $a eq 'USD' ? -1 : $b eq 'USD' ? 1 : $a eq 'CAD' ? -1 : $b eq 'CAD' ? 1 :
        $GT::Payment::Direct::AuthorizeDotNet::CURRENCY{$a} cmp
        $GT::Payment::Direct::AuthorizeDotNet::CURRENCY{$b}
    } keys %GT::Payment::Direct::AuthorizeDotNet::CURRENCY) {
        push @currencies, $_ => $GT::Payment::Direct::AuthorizeDotNet::CURRENCY{$_};
    }
    return
        currency => {
            type => 'SELECT',
            options => \@currencies
        },
        account_password => { type => 'TEXT', size => 40, valid => '.' }, # An optionally-required account password
        confirmation_merchant => { type => 'TEXT', size => 40, valid => '.@.' }, # A merchant confirmation e-mail address
        confirmation_confirm => { type => 'YESNO' }, # Whether or not to send a customer confirmation e-mail.
        test_mode => { type => 'YESNO' }
}

sub payment_info {
# -----------------------------------------------------------------------------
# Returns a hash of various parameters used to figure out how to display the
# payment form for this payment method.
    return {
        no_cc_brand => 1,
        fields => [
            grep ! /^(?:account|capture|currency|test)/, keys %GT::Payment::Direct::AuthorizeDotNet::VALID
        ],
        billing_phone_required => 1
    }
}

sub verify {
# -----------------------------------------------------------------------------
# Checks that $IN, combined with the saved admin settings, makes up all of the
# required information.  Returns 1 on success, or an array ref of invalid keys
# on failure.
    _collect_data();
    if (keys %INVALID or keys %EMPTY) {
        my ($i, %order);
        for (@{$GT::Payment::Direct::AuthorizeDotNet::REQUIRED{AUTHORIZE}}) { $order{$_} = $i++ }
        return [ # Error
            [sort { ($order{$a} || 0x7fff_ffff) <=> ($order{$b} || 0x7fff_ffff) } keys %INVALID],
            [sort { ($order{$a} || 0x7fff_ffff) <=> ($order{$b} || 0x7fff_ffff) } keys %EMPTY]
        ];
    }
    else {
        return 1; # Success
    }
}

sub complete {
# -----------------------------------------------------------------------------
# Checks that $IN, combined with the saved admin settings, makes up all of the
# required information.  Returns (1, $message) on success, (0, $reason) on
# declined, or (-1, $errormsg) on error.

    my $pay = _collect_data() or return;

# Set the admin-specified fields
    while (my ($k, $v) = each %{$CFG->{payment}->{direct}->{used}->{AuthorizeDotNet}}) {
        $pay->$k($v) or return (-1, "Payment configuration error (Invalid $k)");
    }

    $pay->check('sale') or return (-1, $pay->error);
    my $ret = $pay->sale;
    if (not defined $ret) { # An error occured in the module
        return (-1, $pay->error);
    }
    else { # The request at least got through to Authorize.Net
        my $response = $pay->response;
        if ($ret == 1) { # Approved!
            my @receipt = @{$response->{receipt}};

            my $receipt = "Transaction approved\n\n";
            while (@receipt) {
                my ($k, $v) = splice @receipt, 0, 2;
                $receipt .= "$k: $v\n";
            }

            return (1, $response->{reason_text}, $receipt);
        }
        elsif ($ret == 0) { # Declined
            return (0, $response->{reason_text});
        }
        else { # An error was generated by Authorize.Net
            return (-1, $response->{reason_text});
        }
    }
}

sub _collect_data {
# -----------------------------------------------------------------------------
# Collect data from the payment data saved in the admin, and any valid columns
# in $IN.  Anything from $IN is checked for validity, and $INVALID{column} is
# set if invalid.
    %INVALID = %EMPTY = ();
    return unless $CFG->{payment}->{direct}->{used}->{AuthorizeDotNet};
    my %data = %{$CFG->{payment}->{direct}->{used}->{AuthorizeDotNet}};
    my $pay = GT::Payment::Direct::AuthorizeDotNet->new();
    my %required = map { $_ => 1 } @{$GT::Payment::Direct::AuthorizeDotNet::REQUIRED{AUTHORIZE}};
    for my $field (keys %GT::Payment::Direct::AuthorizeDotNet::VALID) {
        # The account_*, capture_*, currency_*, etc. fields should not be user-settable.
        next if exists $data{$field} or $field =~ /^(?:account|capture|currency|test)/;
        if (my $value = $IN->param($field)) {
            if ($pay->$field($value)) {
                $data{$field} = $value;
            }
            else {
                $INVALID{$field}++;
                $data{$field} = undef;
            }
        }
        elsif ($required{$field}) {
            $EMPTY{$field}++;
            $data{$field} = undef;
        }
    }

    $pay->billing_ip($ENV{REMOTE_ADDR}) if $ENV{REMOTE_ADDR} and $ENV{REMOTE_ADDR} ne '127.0.0.1';

    return if keys %INVALID or keys %EMPTY;
    return $pay;
}

1;
