Name

pay-cert —

ATTRIBUTES

Attribute Pos. Req. Default Description
code Yes Yes if auth=true
code_scratch
check_scratch
order_number Yes if issue=true
transaction
issue 0 Issue (create) the gift certificate?
amount Yes if issue=true Gift certificate amount.
expires | expire | expiration Validity period, specified as one of X y(ears), mon(ths), m(inutes), h(ours), d(ays) or w(eeks).
no_cookie Do not issue a MV_GIFT_CERT_CODE cookie to the client's browser?
item_pointer
cart
auth
items
tid
capture
new_tid
void
return
interpolate     0 interpolate output?
hide     0 Hide the tag return value?

DESCRIPTION

BEHAVIOR

This tag appears to be affected by, or affects, the following:
Catalog Variables: GIFT_CERT_COUNTER, GIFT_CERT_TABLE, GIFT_CERT_REDEEM_TABLE, GIFT_CERT_LOCK_TABLE

EXAMPLES

No examples are available at this time. We do consider this a problem and will try to supply some.

NOTES

AVAILABILITY

pay-cert is available in Interchange versions:

4.6.0-5.9.0 (git-head)

SOURCE

Interchange 5.9.0:

Source: dist/strap/config/pay_cert.tag
Lines: 370


UserTag pay-cert Order code
UserTag pay-cert addAttr
UserTag pay-cert Routine <<EOR
sub {
my ($code, $opt) = @_;

use vars qw/$Tag/;

my ($log, $die2, $warn) = $Tag->logger('pay_cert', 'logs/pay_cert.log');

my $counter_file = $::Variable->{GIFT_CERT_COUNTER} || 'etc/pay_cert.number';
my $cert_table   = $::Variable->{GIFT_CERT_TABLE}     || 'pay_certs';
my $redeem_table = $::Variable->{GIFT_CERT_REDEEM_TABLE} || 'pay_cert_redeem';
my $lock_table   = $::Variable->{GIFT_CERT_LOCK_TABLE}   || 'pay_cert_lock';

my $ldb = dbref($lock_table) 
or return $die2->("cannot open payment certs lock table '%s'", $lock_table);

my $ltab = $ldb->name();
my $ldbh = $ldb->dbh()
  or return $die2->("cannot get handle for certs lock table '%s'", $lock_table);
my $q = "insert into $ltab (code, pid, ip_addr) values (?,?,?)";

my $locked;

my $sth_lock = $ldbh->prepare($q)
  or return $die2->("cannot prepare lock query '%s'", $q);

$q = "delete from $ltab where code = ?";
my $sth_unlock = $ldbh->prepare($q)
  or return $die2->("cannot prepare lock query '%s'", $q);

my $die = sub {
  my $msg = errmsg(@_);
  Log( "died: $msg", { file => 'logs/pay_cert.log' });
  eval {
    $sth_unlock->execute($code) if $locked;
  };
  $Tag->error( { name => 'pay_cert', set => $msg } );
  return undef;
};

$opt->{code_scratch} = 'pay_cert_code'    unless defined $opt->{code_scratch};
$opt->{check_scratch} = 'pay_cert_check'  unless defined $opt->{check_scratch};
$opt->{order_number} ||= $::Values->{mv_order_number};

if($opt->{transaction}) {
  $opt->{$opt->{transaction}} = 1;
}

if($opt->{issue}) {
  if(! $opt->{order_number}) {
    return $die->("Must have order number to issue payment certificate. Not issued.");
  }
  if(! $opt->{amount}) {
    return $die->("Must specify amount to issue payment certificate. Not issued.");
  }
  
  ## Time to issue a certificate
  my $start = int(rand 300000);
  $start .= '0' while length($start) < 6;
  my $base = $Tag->counter({ file => $counter_file, start => $start });
  $base .= int(rand(10));
  for(0 .. 9) {
    $code = $base . $_;
    last if Vend::Order::luhn($code, 8);
  }

  my $now = time;
  my @date_issued = localtime($now);
  my @date_expires;
  my $issue_date = POSIX::strftime('%Y%m%d%H%M%S', @date_issued);
  my $expire_date = '';
  $opt->{expires} ||= $opt->{expire} || $opt->{expiration};
  if($opt->{expires} =~ /^\s*(\d+)\s*y/i) {
    @date_expires = @date_issued;
    $date_expires[5] += $1;
  }
  elsif($opt->{expires} =~ /^\s*(\d+)\s*mon/i) {
    @date_expires = @date_issued;
    $date_expires[4] += $1;
  }
  elsif($opt->{expires} =~ /^\s*(\d+)\s*[mhdwy]/) {
    @date_expires = localtime(adjust_time($opt->{expires}, $now));
  }
  elsif($opt->{expires}) {
    $log->("Expiration date '%s' not understood, ignoring.", $opt->{expires});
  }

  if(@date_expires) {
    $expire_date = POSIX::strftime('%Y%m%d%H%M%S', @date_expires);
  }

$log->("generated code=$code, expires=$opt->{expires} date_expires=$expire_date ");
  my $check = int rand(10);
  $check .= int(rand(10)) while length($check) < 4;
#$log->("generated check=$check");
  my %record = (
    amount => $opt->{amount},
    ip_addr => $CGI::remote_addr,
    order_number => $opt->{order_number},
    date_issued => $issue_date,
    date_expires => $expire_date,
    check_value => $check,
    orig_amount => $opt->{amount},
    process_flag => 0,
  );
  my $db = dbref($cert_table)
    or return $die->("cannot open pay_cert table '%s'", $cert_table);
  $db->set_slice($code, \%record)
    or return $die->("cannot write cert number $code in pay_cert table '%s'", $cert_table);

  ## Create expire date for cookie
  my $edate;
  $edate = POSIX::strftime("%a, %d-%b-%Y %H:%M:%S GMT ", @date_expires)
    unless ! $expire_date or $opt->{no_cookie};

  if($opt->{code_scratch}) {
    $::Scratch->{$opt->{code_scratch}} = $code unless $opt->{no_cookie};
    unless( ! $edate or $opt->{no_cookie}) {
#$log->("setting code cookie");
      my $prior_cookie = $Tag->read_cookie({name => 'MV_GIFT_CERT_CODE'});
      my $cvalue = $code;
      if($prior_cookie) {
        $cvalue = join ",", $prior_cookie, $cvalue;
      }
      $Tag->set_cookie({
              name => 'MV_GIFT_CERT_CODE',
              expire => $edate,
              value => $cvalue,
            });
    }
  }

  if($opt->{check_scratch}) {
    $::Scratch->{$opt->{check_scratch}} = $check unless $opt->{no_cookie};
    my $prior_cookie = $Tag->read_cookie({name => 'MV_GIFT_CERT_CHECK'});
    my $cvalue = $check;
    if($prior_cookie) {
      $cvalue = join ",", $prior_cookie, $cvalue;
    }
    unless( ! $edate or $opt->{no_cookie}) {
#$log->("setting cookie");
      $Tag->set_cookie({
                name => 'MV_GIFT_CERT_CHECK',
                expire => $edate,
                value => $cvalue,
            });
    }
  }

  if(defined $opt->{item_pointer}) {
    my $ptr =  $opt->{item_pointer};
    my $cart  = $opt->{cart}
          ? ($Vend::Session->{carts}{$opt->{cart}})
          : $Vend::Items;
    my $item = $cart->[$ptr];
    $item->{pay_cert_code} = $code;
    $item->{pay_cert_check} = $check;
  }
  return $opt->{admin} ? "$code/$check" : $code;
}

my $cdb = dbref($cert_table)
  or return $die->("cannot open pay_certs table '%s'", $cert_table);

my $status;

my $record;

my $rdb = dbref($redeem_table)
  or return $die->("Cannot open redemption table %s", $redeem_table);
my $rname = $rdb->name();
my $rdbh  = $rdb->dbh()
  or return $die->("Cannot get redemption table %s DBI handle", $redeem_table);

if($opt->{auth}) {
  eval {
    $sth_lock->execute($code, $$, $CGI::remote_addr)
      and $locked = 1;
  };

  not $locked and return $die->("Cannot lock pay cert %s", $code);

  $code or return $die->("Must have payment certificate number.");
  $record = $cdb->row_hash($code)
    or return $die->("Gift certificate %s does not exist.", $code);
  if($opt->{amount} > $record->{amount}) {
    return $die->("Tried to redeem, limit (%s) exceeded.", $record->{amount} );
  }
  my %redeem = (
    pay_id => $code,
    trans_date => POSIX::strftime('%Y%m%d%H%M%S', localtime()),
    ip_addr => $CGI::remote_addr,
    trans_type => 'auth',
    voided => 0,
    captured => 0,
    username => $Vend::username,
    amount => $opt->{amount},
    items => $opt->{items},
    );

  $opt->{tid} = $status = $rdb->set_slice(undef, \%redeem)
    or $die->("Auth redemption of %s failed: %s", $code, $rdb->errstr());
#$log->("Redemption auth tid=$status");
  my $new_amount = $cdb->set_field(
              $code,
              'amount',
              $record->{amount} - $opt->{amount},
            );
#$log->("Redemption amount=$record->{amount} redeeming=$opt->{amount} new_amount=$new_amount");

  defined $new_amount
    or $die->("Auth redemption of %s failed: %s", $code, $rdb->errstr());

}
elsif($opt->{capture}) {
  $opt->{tid}  or return $die->("Must have transaction ID to capture.");
  my $red_record = $rdb->row_hash($opt->{tid}) 
    or return $die->("Unknown transaction ID %s.", $opt->{tid});
  if($red_record->{voided}) {
    return $die->("Cannot capture voided auth %s.", $opt->{tid});
  }

  if($red_record->{captured}) {
    return $die->("Auth %s already captured.", $opt->{tid});
  }

  $code = $red_record->{pay_id};

  eval {
    $sth_lock->execute($code, $$, $CGI::remote_addr)
      and $locked = 1;
  };

  not $locked and return $die->("Cannot lock payment cert %s", $code);

  my %redeem = (
    pay_id => $code,
    trans_date => POSIX::strftime('%Y%m%d%H%M%S', localtime()),
    link_tid => $opt->{tid},
    ip_addr => $CGI::remote_addr,
    trans_type => 'capture',
    voided => 0,
    captured => 0,
    username => $Vend::username,
    amount => $red_record->{amount},
    );

  $opt->{new_tid} = $status = $rdb->set_slice(undef, \%redeem)
    or $die->("Auth redemption of %s failed: %s", $code, $rdb->errstr());
#$log->("Redemption auth tid=$status");

  $rdb->set_field($opt->{tid}, 'captured', 1);
#$log->("Capture amount=$red_record->{amount}");

}
elsif($opt->{void}) {
  $opt->{tid}  or return $die->("Must have transaction ID to void.");

  my $red_record = $rdb->row_hash($opt->{tid}) 
    or return $die->("Unknown transaction ID %s.", $opt->{tid});

  if($red_record->{voided}) {
    return $die->("Cannot void already voided auth %s.", $opt->{tid});
  }

  if($red_record->{captured}) {
    return $die->("Cannot void captured auth %s.", $opt->{tid});
  }

  $code = $red_record->{pay_id};

  $record = $cdb->row_hash($code)
    or return $die->("Gift certificate %s does not exist.", $code);

  eval {
    $sth_lock->execute($code, $$, $CGI::remote_addr)
      and $locked = 1;
  };

  not $locked and return $die->("Cannot lock payment cert %s", $code);

  if( ($red_record->{amount} + $record->{amount}) > $record->{orig_amount}) {
    return $die->(
          "Cannot void to equal more than original_amount %s.",
          $record->{orig_amount},
        );
  }

  my %redeem = (
    pay_id => $code,
    trans_date => POSIX::strftime('%Y%m%d%H%M%S', localtime()),
    link_tid => $opt->{tid},
    ip_addr => $CGI::remote_addr,
    trans_type => 'void',
    voided => 0,
    captured => 1,
    username => $Vend::username,
    amount => $red_record->{amount},
    );

  $opt->{new_tid} = $status = $rdb->set_slice(undef, \%redeem)
    or $die->("Auth redemption of %s failed: %s", $code, $rdb->errstr());
#$log->("Redemption auth tid=$status");

  $rdb->set_field($opt->{tid}, 'voided', 1);
#$log->("Capture amount=$red_record->{amount}");

  my $new_amount = $cdb->set_field($code, 'amount', $record->{amount} + $red_record->{amount});
#$log->("void amount=$red_record->{amount} new_amount=$new_amount");

}
elsif ($opt->{return}) {
  $code or return $die->("Must have payment certificate number for a return.");
  eval {
    $sth_lock->execute($code, $$, $CGI::remote_addr)
      and $locked = 1;
  };

  not $locked and return $die->("Cannot lock payment cert %s", $code);

  $record = $cdb->row_hash($code)
    or return $die->("Gift certificate %s does not exist.", $code);
  if( ($opt->{amount} + $record->{amount}) > $record->{orig_amount}) {
    return $die->(
          "Cannot return more than original_amount %s.",
          $record->{orig_amount},
        );
  }
  my %redeem = (
    pay_id => $code,
    trans_date => POSIX::strftime('%Y%m%d%H%M%S', localtime()),
    ip_addr => $CGI::remote_addr,
    trans_type => 'return',
    voided => 0,
    captured => 1,
    username => $Vend::username,
    amount => $opt->{amount},
    items => $opt->{items},
    );

  $opt->{tid} = $status = $rdb->set_slice(undef, \%redeem)
    or $die->("Auth redemption of %s failed: %s", $code, $rdb->errstr());
#$log->("Redemption auth tid=$status");
  my $new_amount = $cdb->set_field(
              $code,
              'amount',
              $record->{amount} + $opt->{amount},
            );
#$log->("return amount=$record->{amount} redeeming=$opt->{amount} new_amount=$new_amount");

  defined $new_amount
    or $die->("Return of %s failed: %s", $code, $rdb->errstr());
}

if($locked) {
  my $rc = $sth_unlock->execute($code) and $locked = 0;
#$log->("unlock rc=$rc");
  if($locked) {
    undef $locked;
    return $die->("Gift certificate %s lock was not released.", $code);
  }
}
else {
#$log->("Not locked??!!?? THis should not happen.");
}
return $status;
}
EOR

AUTHORS

Interchange Development Group

SEE ALSO

pay-cert-redeem(7ic)

DocBook! Interchange!