1 # Copyright 2004-2007 Interchange Development Group and others
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version. See the LICENSE file for details.
8 # $Id: pay_cert.tag,v 1.4 2009-05-01 13:50:00 pajamian Exp $
10 UserTag pay-cert Order code
11 UserTag pay-cert addAttr
12 UserTag pay-cert Routine <<EOR
14 my ($code, $opt) = @_;
17 my $counter_file = $::Variable->{GIFT_CERT_COUNTER} || 'etc/pay_cert.number';
18 my $cert_table = $::Variable->{GIFT_CERT_TABLE} || 'pay_certs';
19 my $redeem_table = $::Variable->{GIFT_CERT_REDEEM_TABLE} || 'pay_cert_redeem';
20 my $lock_table = $::Variable->{GIFT_CERT_LOCK_TABLE} || 'pay_cert_lock';
22 my $ldb = dbref($lock_table)
23 or die errmsg("cannot open payment certs lock table '%s'", $lock_table);
25 my $ltab = $ldb->name();
26 my $ldbh = $ldb->dbh()
27 or die errmsg("cannot get handle for certs lock table '%s'", $lock_table);
28 my $q = "insert into $ltab (code, pid, ip_addr) values (?,?,?)";
32 my $sth_lock = $ldbh->prepare($q)
33 or die errmsg("cannot prepare lock query '%s'", $q);
35 $q = "delete from $ltab where code = ?";
36 my $sth_unlock = $ldbh->prepare($q)
37 or die errmsg("cannot prepare lock query '%s'", $q);
39 $opt->{code_scratch} = 'pay_cert_code' unless defined $opt->{code_scratch};
40 $opt->{check_scratch} = 'pay_cert_check' unless defined $opt->{check_scratch};
41 $opt->{order_number} ||= $::Values->{mv_order_number};
43 if($opt->{transaction}) {
44 $opt->{$opt->{transaction}} = 1;
52 $errname ||= 'pay_certificate';
54 $sth_unlock->execute($code) if $locked;
56 $Tag->error( { name => $errname, set => $msg } );
61 if(! $opt->{order_number}) {
62 return $die->("Must have order number to issue payment certificate. Not issued.");
64 if(! $opt->{amount}) {
65 return $die->("Must specify amount to issue payment certificate. Not issued.");
68 ## Time to issue a certificate
69 my $start = int(rand 300000);
70 $start .= '0' while length($start) < 6;
71 my $base = $Tag->counter({ file => $counter_file, start => $start });
72 $base .= int(rand(10));
75 last if Vend::Order::luhn($code, 8);
79 my @date_issued = localtime($now);
81 my $issue_date = POSIX::strftime('%Y%m%d%H%M%S', @date_issued);
83 $opt->{expires} ||= $opt->{expire} || $opt->{expiration};
84 if($opt->{expires} =~ /^\s*(\d+)\s*y/i) {
85 @date_expires = @date_issued;
86 $date_expires[5] += $1;
88 elsif($opt->{expires} =~ /^\s*(\d+)\s*mon/i) {
89 @date_expires = @date_issued;
90 $date_expires[4] += $1;
92 elsif($opt->{expires} =~ /^\s*(\d+)\s*[mhdwy]/) {
93 @date_expires = localtime(adjust_time($opt->{expires}, $now));
95 elsif($opt->{expires}) {
96 ::logError("Expiration date '%s' not understood, ignoring.", $opt->{expires});
100 $expire_date = POSIX::strftime('%Y%m%d%H%M%S', @date_expires);
103 #::logDebug("generated code=$code, expires=$opt->{expires} date_expires=$expire_date ");
104 my $check = int rand(10);
105 $check .= int(rand(10)) while length($check) < 4;
106 #::logDebug("generated check=$check");
108 amount => $opt->{amount},
109 ip_addr => $CGI::remote_addr,
110 order_number => $opt->{order_number},
111 date_issued => $issue_date,
112 date_expires => $expire_date,
113 check_value => $check,
114 orig_amount => $opt->{amount},
117 my $db = dbref($cert_table)
118 or die errmsg("cannot open pay_cert table '%s'", $cert_table);
119 $db->set_slice($code, \%record)
120 or die errmsg("cannot write cert number $code in pay_cert table '%s'", $cert_table);
122 ## Create expire date for cookie
124 $edate = POSIX::strftime("%a, %d-%b-%Y %H:%M:%S GMT ", @date_expires)
125 unless ! $expire_date or $opt->{no_cookie};
127 if($opt->{code_scratch}) {
128 $::Scratch->{$opt->{code_scratch}} = $code;
129 unless( ! $edate or $opt->{no_cookie}) {
130 #::logDebug("setting cookie");
131 my $prior_cookie = $Tag->read_cookie({name => 'MV_GIFT_CERT_CODE'});
134 $cvalue = join ",", $prior_cookie, $cvalue;
137 name => 'MV_GIFT_CERT_CODE',
144 if($opt->{check_scratch}) {
145 $::Scratch->{$opt->{check_scratch}} = $check;
146 my $prior_cookie = $Tag->read_cookie({name => 'MV_GIFT_CERT_CHECK'});
149 $cvalue = join ",", $prior_cookie, $cvalue;
151 unless( ! $edate or $opt->{no_cookie}) {
152 #::logDebug("setting cookie");
154 name => 'MV_GIFT_CERT_CHECK',
161 if(defined $opt->{item_pointer}) {
162 my $ptr = $opt->{item_pointer};
163 my $cart = $opt->{cart}
164 ? ($Vend::Session->{carts}{$opt->{cart}})
166 my $item = $cart->[$ptr];
167 $item->{pay_cert_code} = $code;
168 $item->{pay_cert_check} = $check;
173 my $cdb = dbref($cert_table)
174 or die errmsg("cannot open pay_certs table '%s'", $cert_table);
180 my $rdb = dbref($redeem_table)
181 or return $die->("Cannot open redemption table %s", $redeem_table);
182 my $rname = $rdb->name();
183 my $rdbh = $rdb->dbh()
184 or return $die->("Cannot get redemption table %s DBI handle", $redeem_table);
188 $sth_lock->execute($code, $$, $CGI::remote_addr)
192 not $locked and return $die->("Cannot lock pay cert %s", $code);
194 $code or return $die->("Must have payment certificate number.");
195 $record = $cdb->row_hash($code)
196 or return $die->("Gift certificate %s does not exist.", $code);
197 if($opt->{amount} > $record->{amount}) {
198 return $die->("Tried to redeem, limit (%s) exceeded.", $record->{amount} );
202 trans_date => POSIX::strftime('%Y%m%d%H%M%S', localtime()),
203 ip_addr => $CGI::remote_addr,
204 trans_type => 'auth',
207 username => $Vend::username,
208 amount => $opt->{amount},
209 items => $opt->{items},
212 $opt->{tid} = $status = $rdb->set_slice(undef, \%redeem)
213 or $die->("Auth redemption of %s failed: %s", $code, $rdb->errstr());
214 #::logDebug("Redemption auth tid=$status");
215 my $new_amount = $cdb->set_field(
218 $record->{amount} - $opt->{amount},
220 #::logDebug("Redemption amount=$record->{amount} redeeming=$opt->{amount} new_amount=$new_amount");
223 or $die->("Auth redemption of %s failed: %s", $code, $rdb->errstr());
226 elsif($opt->{capture}) {
227 $opt->{tid} or return $die->("Must have transaction ID to capture.");
228 my $red_record = $rdb->row_hash($opt->{tid})
229 or return $die->("Unknown transaction ID %s.", $opt->{tid});
230 if($red_record->{voided}) {
231 return $die->("Cannot capture voided auth %s.", $opt->{tid});
234 if($red_record->{captured}) {
235 return $die->("Auth %s already captured.", $opt->{tid});
238 $code = $red_record->{pay_id};
241 $sth_lock->execute($code, $$, $CGI::remote_addr)
245 not $locked and return $die->("Cannot lock payment cert %s", $code);
249 trans_date => POSIX::strftime('%Y%m%d%H%M%S', localtime()),
250 link_tid => $opt->{tid},
251 ip_addr => $CGI::remote_addr,
252 trans_type => 'capture',
255 username => $Vend::username,
256 amount => $red_record->{amount},
259 $opt->{new_tid} = $status = $rdb->set_slice(undef, \%redeem)
260 or $die->("Auth redemption of %s failed: %s", $code, $rdb->errstr());
261 #::logDebug("Redemption auth tid=$status");
263 $rdb->set_field($opt->{tid}, 'captured', 1);
264 #::logDebug("Capture amount=$red_record->{amount}");
267 elsif($opt->{void}) {
268 $opt->{tid} or return $die->("Must have transaction ID to void.");
270 my $red_record = $rdb->row_hash($opt->{tid})
271 or return $die->("Unknown transaction ID %s.", $opt->{tid});
273 if($red_record->{voided}) {
274 return $die->("Cannot void already voided auth %s.", $opt->{tid});
277 if($red_record->{captured}) {
278 return $die->("Cannot void captured auth %s.", $opt->{tid});
281 $code = $red_record->{pay_id};
283 $record = $cdb->row_hash($code)
284 or return $die->("Gift certificate %s does not exist.", $code);
287 $sth_lock->execute($code, $$, $CGI::remote_addr)
291 not $locked and return $die->("Cannot lock payment cert %s", $code);
293 if( ($red_record->{amount} + $record->{amount}) > $record->{orig_amount}) {
295 "Cannot void to equal more than original_amount %s.",
296 $record->{orig_amount},
302 trans_date => POSIX::strftime('%Y%m%d%H%M%S', localtime()),
303 link_tid => $opt->{tid},
304 ip_addr => $CGI::remote_addr,
305 trans_type => 'void',
308 username => $Vend::username,
309 amount => $red_record->{amount},
312 $opt->{new_tid} = $status = $rdb->set_slice(undef, \%redeem)
313 or $die->("Auth redemption of %s failed: %s", $code, $rdb->errstr());
314 #::logDebug("Redemption auth tid=$status");
316 $rdb->set_field($opt->{tid}, 'voided', 1);
317 #::logDebug("Capture amount=$red_record->{amount}");
319 my $new_amount = $cdb->set_field($code, 'amount', $record->{amount} + $red_record->{amount});
320 #::logDebug("void amount=$red_record->{amount} new_amount=$new_amount");
323 elsif ($opt->{return}) {
324 $code or return $die->("Must have payment certificate number for a return.");
326 $sth_lock->execute($code, $$, $CGI::remote_addr)
330 not $locked and return $die->("Cannot lock payment cert %s", $code);
332 $record = $cdb->row_hash($code)
333 or return $die->("Gift certificate %s does not exist.", $code);
334 if( ($opt->{amount} + $record->{amount}) > $record->{orig_amount}) {
336 "Cannot return more than original_amount %s.",
337 $record->{orig_amount},
342 trans_date => POSIX::strftime('%Y%m%d%H%M%S', localtime()),
343 ip_addr => $CGI::remote_addr,
344 trans_type => 'return',
347 username => $Vend::username,
348 amount => $opt->{amount},
349 items => $opt->{items},
352 $opt->{tid} = $status = $rdb->set_slice(undef, \%redeem)
353 or $die->("Auth redemption of %s failed: %s", $code, $rdb->errstr());
354 #::logDebug("Redemption auth tid=$status");
355 my $new_amount = $cdb->set_field(
358 $record->{amount} + $opt->{amount},
360 #::logDebug("return amount=$record->{amount} redeeming=$opt->{amount} new_amount=$new_amount");
363 or $die->("Return of %s failed: %s", $code, $rdb->errstr());
367 my $rc = $sth_unlock->execute($code) and $locked = 0;
368 #::logDebug("unlock rc=$rc");
371 return $die->("Gift certificate %s lock was not released.", $code);
375 #::logDebug("Not locked??!!?? THis should not happen.");