RT#30600: Auto Apply for CC payments
[freeside.git] / FS / FS / cust_main / Billing_Realtime.pm
index c700cf7..e2a0267 100644 (file)
@@ -56,12 +56,7 @@ sub realtime_cust_payby {
 
   $options{amount} = $self->balance unless exists( $options{amount} );
 
-  my @cust_payby = qsearch({
-    'table'     => 'cust_payby',
-    'hashref'   => { 'custnum' => $self->custnum, },
-    'extra_sql' => " AND payby IN ( 'CARD', 'CHEK' ) ",
-    'order_by'  => 'ORDER BY weight ASC',
-  });
+  my @cust_payby = $self->cust_payby('CARD','CHEK');
                                                    
   my $error;
   foreach my $cust_payby (@cust_payby) {
@@ -159,7 +154,9 @@ specified invoice.  If the customer has exactly one open invoice, that
 invoice number will be assumed.  If you don't specify an I<invnum> you might 
 want to call the B<apply_payments> method or set the I<apply> option.
 
-I<apply> can be set to true to apply a resulting payment.
+I<apply> can be set to true to run B<apply_payments_and_credits> on success.
+
+I<no_auto_apply> can be set to true to prevent resulting payment from being automatically applied.
 
 I<quiet> can be set true to surpress email decline notices.
 
@@ -190,6 +187,15 @@ A third-party transaction will return a hashref containing:
 =cut
 
 # some helper routines
+#
+# _bop_recurring_billing: Checks whether this payment should have the 
+# recurring_billing flag used by some B:OP interfaces (IPPay, PlugnPay,
+# vSecure, etc.). This works in two different modes:
+# - actual_oncard (default): treat the payment as recurring if the customer
+#   has made a payment using this card before.
+# - transaction_is_recur: treat the payment as recurring if the invoice
+#   being paid has any recurring package charges.
+
 sub _bop_recurring_billing {
   my( $self, %opt ) = @_;
 
@@ -752,8 +758,7 @@ sub realtime_bop {
   # remove paycvv after initial transaction
   ###
 
-  #false laziness w/misc/process/payment.cgi - check both to make sure working
-  # correctly
+  # compare to FS::cust_main::save_cust_payby - check both to make sure working correctly
   if ( length($self->paycvv)
        && ! grep { $_ eq cardtype($options{payinfo}) } $conf->config('cvv-save')
   ) {
@@ -900,7 +905,7 @@ sub _realtime_bop_result {
        'processor'      => $payment_gateway->gateway_module,
        'auth'           => $transaction->authorization,
        'order_number'   => $order_number || '',
-
+       'no_auto_apply'  => $options{'no_auto_apply'} ? 'Y' : '',
     } );
     #doesn't hurt to know, even though the dup check is in cust_pay_pending now
     $cust_pay->payunique( $options{payunique} )
@@ -1320,14 +1325,14 @@ L<http://420.am/business-onlinepayment> for supported gateways.
 
 Available methods are: I<CC>, I<ECHECK> and I<LEC>
 
-Available options are: I<amount>, I<reason>, I<paynum>, I<paydate>
+Available options are: I<amount>, I<reasonnum>, I<paynum>, I<paydate>
 
 Most gateways require a reference to an original payment transaction to refund,
 so you probably need to specify a I<paynum>.
 
 I<amount> defaults to the original amount of the payment if not specified.
 
-I<reason> specifies a reason for the refund.
+I<reasonnum> specified an existing refund reason for the refund
 
 I<paydate> specifies the expiration date for a credit card overriding the
 value from the customer record or the payment record. Specified as yyyy-mm-dd
@@ -1370,6 +1375,8 @@ sub realtime_refund_bop {
     warn "  $_ => $options{$_}\n" foreach keys %options;
   }
 
+  return "No reason specified" unless $options{'reasonnum'} =~ /^\d+$/;
+
   my %content = ();
 
   ###
@@ -1528,7 +1535,12 @@ sub realtime_refund_bop {
       if $conf->exists('business-onlinepayment-test_transaction');
     $void->submit();
     if ( $void->is_success ) {
-      my $error = $cust_pay->void($options{'reason'});
+      # specified as a refund reason, but now we want a payment void reason
+      # extract just the reason text, let cust_pay::void handle new_or_existing
+      my $reason = qsearchs('reason',{ 'reasonnum' => $options{'reasonnum'} });
+      my $error;
+      $error = 'Reason could not be loaded' unless $reason;      
+      $error = $cust_pay->void($reason->reason) unless $error;
       if ( $error ) {
         # gah, even with transactions.
         my $e = 'WARNING: Card/ACH voided but database not updated - '.
@@ -1649,11 +1661,12 @@ sub realtime_refund_bop {
   my $cust_refund = new FS::cust_refund ( {
     'custnum'  => $self->custnum,
     'paynum'   => $options{'paynum'},
+    'source_paynum' => $options{'paynum'},
     'refund'   => $amount,
     '_date'    => '',
     'payby'    => $bop_method2payby{$options{method}},
     'payinfo'  => $payinfo,
-    'reason'   => $options{'reason'} || 'card or ACH refund',
+    'reasonnum'     => $options{'reasonnum'},
     'gatewaynum'    => $gatewaynum, # may be null
     'processor'     => $processor,
     'auth'          => $refund->authorization,
@@ -1662,6 +1675,7 @@ sub realtime_refund_bop {
   my $error = $cust_refund->insert;
   if ( $error ) {
     $cust_refund->paynum(''); #try again with no specific paynum
+    $cust_refund->source_paynum('');
     my $error2 = $cust_refund->insert;
     if ( $error2 ) {
       # gah, even with transactions.