default to a session cookie instead of setting an explicit timeout, weird timezone...
[freeside.git] / FS / bin / freeside-eftca-download
1 #!/usr/bin/perl
2
3 use strict;
4 use Getopt::Std;
5 use Date::Format qw(time2str);
6 use File::Temp qw(tempdir);
7 use Net::SFTP::Foreign;
8 use Expect;
9 use FS::UID qw(adminsuidsetup datasrc);
10 use FS::Record qw(qsearch qsearchs);
11 use FS::pay_batch;
12 use FS::cust_pay_batch;
13 use FS::Conf;
14 use FS::Log;
15
16 use vars qw( $opt_v $opt_a );
17 getopts('va:');
18
19 #$Net::SFTP::Foreign::debug = -1;
20 sub HELP_MESSAGE { "
21   Usage:
22       freeside-eftca-download [ -v ] [ -a archivedir ] user\n
23 " }
24
25 my @fields = (
26   'tid',          # transaction ID
27   'paybatchnum',  # reference field
28   'returncode',   # status code
29   'returndate',
30   'paid',         # dollars and cents, with decimal
31   'type',
32   'first',
33   'last',
34   'account',
35   'bank',
36   'transit',
37 );
38
39 my $user = shift or die &HELP_MESSAGE;
40 adminsuidsetup $user;
41
42 my $log = FS::Log->new('freeside-eftca-download');
43 log_info( "EFT Canada download started\n" );
44
45 if ( $opt_a ) {
46   log_error_and_die( "no such directory: $opt_a\n" )
47     unless -d $opt_a;
48   log_error_and_die(
49     "archive directory $opt_a is not writable by the freeside user\n"
50   ) unless -w $opt_a;
51 }
52
53 #my $tmpdir = File::Temp->newdir();
54 my $tmpdir = tempdir( CLEANUP => 1 ); #DIR=>somewhere?
55
56 my $conf = new FS::Conf;
57
58 my @agents;
59 if ( $conf->exists('batch-spoolagent') ) {
60   local $@;
61   eval { @agents = qsearch('agent', { 'disabled' => '' }); };
62   log_error_and_die("Fatal database error: $@")
63     if $@;
64 } else {
65   @agents = (1);
66 }
67
68 foreach my $agent (@agents) {
69
70   my @batchconf;
71   if ( $conf->exists('batch-spoolagent') ) {
72     @batchconf = $conf->config('batchconfig-eft_canada', $agent->agentnum, 1);
73     if ( !length($batchconf[0]) ) {
74       log_info(
75         "agent '".$agent->agent.
76         "' has no batchconfig-eft_canada setting; skipped.\n"
77       );
78       next;
79     }
80   } else {
81     @batchconf = $conf->config('batchconfig-eft_canada');
82   }
83   # user, password, transaction code, delay days
84   my $user = $batchconf[0]
85     or log_error_and_die( "no EFT Canada batch username configured\n" );
86   my $pass = $batchconf[1]
87     or log_error_and_die( "no EFT Canada batch password configured\n" );
88
89   my $host = 'ftp.eftcanada.com';
90   log_info( "Connecting to $user\@$host...\n" );
91
92   my $sftp = Net::SFTP::Foreign->new( host     => $host,
93                                       user     => $user,
94                                       password => $pass,
95                                       timeout  => 30,
96                                     );
97   log_error_and_die("failed to connect to '$user\@$host'\n(".$sftp->error.")\n")
98     if $sftp->error;
99
100   $sftp->setcwd('/Returns');
101
102   my $files = $sftp->ls('.', wanted => qr/\.txt$/, names_only => 1);
103   log_info_and_die( "Finished: No response files found\n" )
104     if !@$files;
105
106   FILE: foreach my $filename (@$files) {
107     log_info( "Retrieving $filename\n" );
108     $sftp->get("$filename", "$tmpdir/$filename");
109     if($sftp->error) {
110       log_info( "failed to download $filename\n" );
111       next FILE;
112     }
113
114     #move to server archive dir
115     $sftp->rename("$filename", "Archive/$filename");
116     if($sftp->error) {
117       log_info(  "failed to archive $filename on server\n" );
118     } # process it anyway though
119
120     #copy to local archive dir
121     if ( $opt_a ) {
122       log_info( "Copying $tmpdir/$filename to archive dir $opt_a\n" );
123       system 'cp', "$tmpdir/$filename", $opt_a;
124       log_info( "failed to copy $tmpdir/$filename to $opt_a: $@" )
125         if $@;
126     }
127
128     open my $fh, "<$tmpdir/$filename";
129     # Some duplication with FS::pay_batch::import_results, but we're really 
130     # doing something different here.
131     my $csv = new Text::CSV_XS ( { quote_char => undef, sep_char => '|' } );
132     my %hash;
133     while (my $line = <$fh>) {
134       next if $line =~ /^\s*$/;
135       $csv->parse($line) or do {
136         log_info( "can't parse $filename: ".$csv->error_input."\n" );
137         next FILE; #parsing errors = reading the wrong kind of file
138       };
139       @hash{@fields} = $csv->fields();
140       log_info( "voiding paybatchnum#$hash{paybatchnum}\n" );
141       my $cpb = qsearchs('cust_pay_batch', 
142                           { paybatchnum => $hash{'paybatchnum'} });
143       if ( !$cpb ) {
144         log_info(
145           "can't find paybatchnum #$hash{paybatchnum} ".
146           "($hash{first} $hash{last}, $hash{paid})\n"
147         );
148         next;
149       }
150       my $error = $cpb->decline("Returned payment ($hash{returncode})");
151       if ( $error ) {
152         log_info( "can't void paybatchnum #$hash{paybatchnum}: $error\n" );
153       }
154     }
155     close $fh;
156   }
157
158 }
159
160 log_info( "Finished!\n" );
161
162 sub log_info {
163   my $log_message = shift;
164   $log->info( $log_message );
165   print STDERR $log_message if $opt_v;
166 }
167
168 sub log_info_and_die {
169   my $log_message = shift;
170   $log->info( $log_message );
171   die $log_message;
172 }
173
174 sub log_error_and_die {
175   my $log_message = shift;
176   $log->error( $log_message );
177   die $log_message;
178 }
179
180 =head1 NAME
181
182 freeside-eftca-download - Retrieve payment batch responses from EFT Canada.
183
184 =head1 SYNOPSIS
185
186   freeside-eftca-download [ -v ] [ -a archivedir ] user
187
188 =head1 DESCRIPTION
189
190 Command line tool to download returned payment reports from the EFT Canada 
191 gateway and void the returned payments.  Uses the login and password from 
192 'batchconfig-eft_canada'.
193
194 -v: Be verbose.
195
196 -a directory: Archive response files in the provided directory.
197
198 user: freeside username
199
200 =head1 BUGS
201
202 You need to manually SFTP to ftp.eftcanada.com from the freeside account 
203 and accept their key before running this script.
204
205 =head1 SEE ALSO
206
207 L<FS::pay_batch>
208
209 =cut
210
211 1;
212