automate RBC payment batch transfer, #35228
[freeside.git] / FS / bin / freeside-rbc-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); #0.19 for ->newdir() interface, not in 5.10.0
7 use Net::FTPSSL;
8 use FS::UID qw(adminsuidsetup dbh);
9 use FS::Record qw(qsearch qsearchs);
10 use FS::pay_batch;
11 use FS::Conf;
12
13 use vars qw( $opt_v $opt_a $opt_f );
14 getopts('va:f:');
15
16 #$Net::SFTP::Foreign::debug = -1;
17 sub usage { "
18   Usage:
19       freeside-rbc-download [ -v ] [ -a archivedir ] [ -f filename ] user\n
20 " }
21
22 sub debug {
23   print STDERR $_[0] if $opt_v;
24 }
25
26 my $user = shift or die &usage;
27 adminsuidsetup $user;
28
29 $FS::UID::AutoCommit = 0;
30 my $dbh = dbh;
31
32 if ( $opt_a ) {
33   die "no such directory: $opt_a\n"
34     unless -d $opt_a;
35   die "archive directory $opt_a is not writable by the freeside user\n"
36     unless -w $opt_a;
37 }
38
39 my $tmpdir = tempdir( CLEANUP => 1 ); #DIR=>somewhere?
40
41 my $conf = new FS::Conf;
42 my ($username, $password) = $conf->config('batchconfig-RBC-login');
43 $username and $password
44   or die "RBC FTP login not configured. Enter your username and password in 'batchconfig-rbc-login'.\n";
45
46 my $host = 'ftpssl.rbc.com';
47 debug "Connecting to $username\@$host...\n";
48
49 my $ftp = Net::FTPSSL->new($host,
50                            Timeout => 30,
51                            Debug => ($opt_v ? 1 : 0),
52                            Croak => 1, # rely on auto-rollback when dbh closes
53                           );
54 $ftp->login($username, $password);
55
56 # directory layout:
57 # ~/                          # upload to here
58 # ~/inbound
59 # ~/inbound/valid             # batches move here while being processed
60 # ~/outbound
61 # ~/outbound/XXXX             # string of four characters; results arrive here
62
63 $ftp->cwd('outbound');
64 for my $dir ( $ftp->nlst ) {
65   debug "Entering outbound/$dir\n";
66   $ftp->cwd($dir);
67   FILE: for my $filename ( $ftp->nlst ) {
68     debug "$filename...";
69     # filenames look like "RPT9999X.111".
70     # 9999 is the four-digit report type
71     # X is "P" for production or "T" for test
72     # 111 is the sequential file number
73     if ( $opt_f ) {
74       if ( $filename ne $opt_f ) {
75         debug "is not the requested file.\n";
76         next FILE;
77       }
78       # -f can be used to download/process any file, even one that doesn't fit
79       # the naming rule (e.g. those that are already downloaded).
80     } elsif ( $filename =~ /^RPT(\d{4})[PT]\.\d{3}$/ ) {
81       # fallthrough; don't currently reject files based on RPT type, because
82       # our parser should be able to figure it out
83     } else {
84       debug "skipped.\n";
85       next FILE;
86     }
87
88     debug "downloading.\n";
89     $ftp->get($filename, "$tmpdir/$filename");
90
91     #copy to archive dir
92     if ( $opt_a ) {
93       debug "Copying to archive dir $opt_a\n";
94       system 'cp', "$tmpdir/$filename", $opt_a;
95       warn "failed to copy $tmpdir/$filename to $opt_a: $!\n" if $!;
96     }
97
98     debug "Processing batch...";
99     open(my $fh, '<', "$tmpdir/$filename")
100       or die "couldn't read temp file: $!\n";
101
102     my $error = FS::pay_batch->import_results(
103       filehandle  => $fh,
104       format      => 'RBC',
105     );
106
107     if ( $error ) {
108       die "Processing $filename failed:\n$error\n\n";
109     }
110
111     debug "done.\n";
112   } # FILE
113   $ftp->cdup();
114 } # $dir
115
116 debug "Finished.\n";
117 dbh->commit;
118 exit(0);
119
120 =head1 NAME
121
122 freeside-rbc-download - Retrieve payment batch responses from RBC.
123
124 =head1 SYNOPSIS
125
126   freeside-rbc-download [ -v ] [ -f filename ] [ -a archivedir ] user
127
128 =head1 DESCRIPTION
129
130 Command line tool to download payment batch responses from the Royal Bank of 
131 Canada ACH service. These files are fixed-width data files containing some
132 combination of valid, returned, or reversed payment records.
133
134 By default, the script will download any files with names like "RPT9999X.111"
135 where 9999 is a four-digit document type code (like "0900", all records), X is
136 the letter "P" for production or "T" for test mode, and 111 is a counter
137 incremented with each new response file. After the files are downloaded, RBC's
138 server will automatically rename them with the suffix '.downloaded%FTPS' to 
139 avoid double-processing them.
140
141
142 -v: Be verbose.
143
144 -f filename: Download a file with a specific name, instead of all files 
145 matching the pattern. This can be used to reprocess a specific file.
146
147 -a directory: Archive the files in the specified directory.
148
149 user: freeside username
150
151 =head1 BUGS
152
153 =head1 SEE ALSO
154
155 L<FS::pay_batch>
156
157 =cut
158
159 1;
160