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