4 use vars qw( $user $cust_main @customers );
6 use FS::UID qw(adminsuidsetup);
7 use FS::Record qw(qsearchs);
11 use Business::OnlinePayment; # For retrieving the void list only.
13 use Date::Parse 'str2time';
14 use Date::Format 'time2str';
17 getopts("r:f:ca:g:s:e:", \%opt);
19 $user = shift or die &usage;
20 &adminsuidsetup( $user );
22 # The -g and -a options need to override this.
23 my $method = $opt{'c'} ? 'ECHECK' : 'CARD';
26 $gateway = FS::payment_gateway->by_key($opt{'g'})
27 or die "Payment gateway not found: '".$opt{'g'}."'.";
30 my $agent = FS::agent->by_key($opt{'a'})
31 or die "Agent not found: '".$opt{'a'}."'.";
32 $gateway = $agent->payment_gateway(method => $method)
33 or die "Agent has no payment gateway for method '$method'.";
36 my ($processor, $login, $password, $action, @bop_options) =
37 FS::cust_main->default_payment_gateway($method);
41 # override the default gateway
42 $gatewaynum = $gateway->gatewaynum . '-' if $gateway->gatewaynum;
43 $processor = $gateway->gateway_module;
44 $login = $gateway->gateway_username;
45 $password = $gateway->gateway_password;
46 $action = $gateway->gateway_action;
47 @bop_options = $gateway->options;
52 # Read the list of authorization numbers from a file.
54 open($in, '< '. $opt{'f'}) or die "Unable to open file: '".$opt{'f'}."'.";
55 @auths = grep /^\d+$/, <$in>;
59 # Get the list from the processor. This requires the processor module to
60 # support get_returns.
61 my $transaction = new Business::OnlinePayment ( $processor, @bop_options );
62 if(! $transaction->can('get_returns')) {
63 die "'$processor' does not provide an automated void list.";
65 my @local = localtime;
66 # Start and end dates for this can be set via -s and -e. If they're not,
67 # end defaults to midnight today and start defaults to one day before end.
68 my $end = defined($opt{'e'}) ?
69 str2time($opt{'e'}) : timelocal(0, 0, 0, @local[3,4,5]);
70 my $start = defined($opt{'s'}) ?
71 str2time($opt{'s'}) : $end - 86400;
72 die "Invalid date range: '$start'-'$end'" if not ($start and $end);
73 $transaction->content (
75 password => $password,
76 start => time2str("%Y-%m-%d",$start),
77 end => time2str("%Y-%m-%d",$end),
79 @auths = $transaction->get_returns;
82 $opt{'r'} ||= 'freeside-void-payments';
84 foreach my $authnum (@auths) {
85 my $paybatch = $gatewaynum . $processor . ':' . $authnum;
86 my $cust_pay = qsearchs('cust_pay', { paybatch => $paybatch } );
88 $cust_pay->void($opt{'r'});
91 warn "cust_pay record not found: '$paybatch'";
96 die "Usage:\n\n freeside-void-payments [ -f file | [ -s start-date ] [ -e end-date ] ] [ -r 'reason' ] [ -g gatewaynum | -a agentnum ] [ -c ] user\n";
105 freeside-void-payments - Automatically void a list of returned payments.
109 freeside-void-payments [ -f file | [ -s start-date ] [ -e end-date ] ] [ -r 'reason' ] [ -g gatewaynum | -a agentnum ] [ -c ] user
113 Voids payments that were returned by the payment processor. Can be
114 run periodically from crontab or manually after receiving a list of
115 returned payments. Normally this is a meaningful operation only for
118 This script voids payments based on the combination of gateway (see
119 L<FS::payment_gateway>) and authorization number, since this is
120 generally how the processor will identify them later.
122 -f: Read the list of authorization numbers from the specified file.
123 If they are not from the default payment gateway, -g or -a
124 must be given to identify the gateway.
126 If -f is not given, the script will attempt to contact the gateway
127 and download a list of returned transactions. To support this,
128 the Business::OnlinePayment module for the processor must implement
129 the I<get_returns()> method. For an example, see
130 L<Business::OnlinePayment::WesternACH>.
132 -s, -e: Specify the starting and ending dates for the void list.
133 This has no effect if -f is given. The end date defaults to
134 today, and the start date defaults to one day before the end date.
136 -r: The reason for voiding the payments, to be stored in the database.
138 -g: The L<FS::payment_gateway> number for the gateway that handled
139 these payments. If -f is not given, this determines which
140 gateway will be contacted. This overrides -a.
142 -a: The agentnum whose default gateway will be used. If neither -a
143 nor -g is given, the system default gateway will be used.
145 -c: Use the default gateway for check transactions rather than
148 A warning will be emitted for each transaction that can't be found.
149 This may happen if it's already been voided, or if the gateway
154 Given 'returns.txt', which contains one authorization number on each
155 line, provided by your default e-check processor:
157 freeside-void-payments -f returns.txt -c -r 'Returned check'
159 If your default processor is Western ACH, which supports automated
160 returns processing, this voids all returned payments since 2009-06-01:
162 freeside-void-payments -r 'Returned check' -s 2009-06-01
164 This, in your crontab, will void returned payments for the last
165 day at 8:30 every morning:
167 30 8 * * * /usr/local/bin/freeside-void-payments -r 'Returned check'
171 Most payment gateways don't support it, making the script largely useless.
175 L<Business::OnlinePayment>, L<FS::cust_pay>