-=item payby - Payment Type (See L<FS::payinfo_Mixin> for valid payby values)
+=item payby
+
+Payment Type (See L<FS::payinfo_Mixin> for valid payby values)
+
+=item payinfo
+
+Payment Information (See L<FS::payinfo_Mixin> for data format)
+
+=item paymask
+
+Masked payinfo (See L<FS::payinfo_Mixin> for how this works)
+
+=item paydate
+
+Expiration date
+
+=item payunique
+
+Unique identifer to prevent duplicate transactions.
+
+=item status
+
+Pending transaction status, one of the following:
+
+=over 4
-=item status - new (acquires basic lock on payunique), pending (transaction is pending with the gateway), authorized (only used for two-stage transactions that require a separate capture step), captured/declined (transaction completed with payment gateway, not yet recorded in the database), done (transaction recorded in database)
+Only used for two-stage transactions that require a separate capture step
+
+=item captured
+
+Transaction completed with payment gateway (sucessfully), not yet recorded in
+the database
+
+=item declined
+
+Transaction completed with payment gateway (declined), not yet recorded in
+the database
+
+=item done
+
+Transaction recorded in database
+
+=back
+
+=item statustext
+
+Additional status information.
|| $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
|| $self->ut_money('paid')
|| $self->ut_numbern('_date')
|| $self->ut_textn('payunique')
|| $self->ut_text('status')
#|| $self->ut_textn('statustext')
|| $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
|| $self->ut_money('paid')
|| $self->ut_numbern('_date')
|| $self->ut_textn('payunique')
|| $self->ut_text('status')
#|| $self->ut_textn('statustext')
#|| $self->ut_money('cust_balance')
|| $self->ut_foreign_keyn('paynum', 'cust_pay', 'paynum' )
|| $self->payinfo_check() #payby/payinfo/paymask/paydate
#|| $self->ut_money('cust_balance')
|| $self->ut_foreign_keyn('paynum', 'cust_pay', 'paynum' )
|| $self->payinfo_check() #payby/payinfo/paymask/paydate
# UNIQUE index should catch this too, without race conditions, but this
# should give a better error message the other 99.9% of the time...
if ( length($self->payunique) ) {
# UNIQUE index should catch this too, without race conditions, but this
# should give a better error message the other 99.9% of the time...
if ( length($self->payunique) ) {
- my $cust_pay_pending =
- qsearchs('cust_pay_pending', { 'payunique' => $self->payunique } );
+ my $cust_pay_pending = qsearchs('cust_pay_pending', {
+ 'payunique' => $self->payunique,
+ 'paypendingnum' => { op=>'!=', value=>$self->paypendingnum },
+ });
if ( $cust_pay_pending ) {
#well, it *could* be a better error message
return "duplicate transaction - a payment with unique identifer ".
if ( $cust_pay_pending ) {
#well, it *could* be a better error message
return "duplicate transaction - a payment with unique identifer ".
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+
+sub _upgrade_data { #class method
+ my ($class, %opts) = @_;
+
+ my $sql =
+ "DELETE FROM cust_pay_pending WHERE status = 'new' AND _date < ".(time-600);
+
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+
+}
+