Most Charges in Auth.net Must Be Captured Manually/Error on Some Orders If Credit Card Expires B4

Hi there-

We submitted a ticket a week ago, heard back same day and never got another reply...trying again:

We've used this plugin before and don't believe we've run into this issue: We've been using the Membership plugin on a client's site for a couple months now. After launching and a number of orders were placed on the site, our client said the money for orders wasn't passing to their bank account.

We found most of the orders in Authorize.net showed that the transactions were authorized but needed to be captured manually. We talked to Authorize.net, everything looked good on their end.

We then got the following email from our client:

I think I discovered what may be a small bug in our membership sign-up process. It also may explain why so many transactions are having to be captured manually.

I just joined the club tonight. After entering all of the information, when I clicked the "process payment" button I received an error message that said "Credit Card expires before the start of the subscription." This actually isn't true since my card doesn't expire until Sept. 2015, but I bet others are receiving that message and it's why we've been having a few folks get confused or join twice. Since I get the Authorize.Net e-mails, I noticed that the payment did indeed process, and a "welcome to the club" confirmation also arrived from Andy.

I logged on to Authorize.Net and discovered that my transaction is one of those that needed to be captured manually. There may be something in the system that matches expiration dates and the subscription period that needs tweaking.

We believe that if someone creates a subscription for one year and their card expires within that year, then they see the notice "Credit Card expires before the start of the subscription."

We found this post: https://premium.wpmudev.org/forums/topic/authorizenet-membership-plugin-finite-recurring-charge-issue

But it doesn't appear as though the issue was resolved by the final post in September.

Help please...

THE ONLY REPLY:
This was problem in older versions of Membership.
It shouldn't be presented in newer versions after 3.5.2.3

Which version do you have running on the site with this problem?

OUR REPLY:
We are running Running Version 3.5.2.9

We DO have ARB enabled and show 125 Subscriptions active.

and

and for what it's worth...dev mentioned the site is also using BuddyPress - purchased to work together.

    Jude

    Hi Ann

    I would love to but the Grant Access button is grayed out.

    This is strange, but for purposes of solving the main issue quickly lets look into that later and in another thread

    Here is a safe way to send details over to me, remember to give me ftp access to view the folder where I can look at the logs, some hosts have this outside the public_html/www folder etc for security reasons

    Please send in

    Subject: "Attn: Jude Rosario"
    - WordPress super admin/ admin username + password
    - FTP credentials (host/username/password)
    - cPanel details
    - phpMyAdmin details ( To check database )
    - MySQL details ( IF you dont have phpmyadmin)

    -link back to this thread for reference

    Select "I have a different question" for your topic - this and the subject line ensure that it gets assigned to me

    https://premium.wpmudev.org/contact/

    Cheers

    Jude

    Hi there Ann,

    I just looked into the code to see how we can fix this. I have a hotfix here for you, take a look and tell me if it works

    Steps:

    1) Take a backup of the Authorize.php file found in this folder.

    membership/classes/Membership/Gateway

    2) Replace this function with what I have given you in this thread.

    public function process_purchase() {
                 // Code
            }

    Put this there

    public function process_purchase() {
    		global $M_options;
    		if ( empty( $M_options['paymentcurrency'] ) ) {
    			$M_options['paymentcurrency'] = 'USD';
    		}
    
    		if ( ! is_ssl() ) {
    			wp_die( __( 'You must use HTTPS in order to do this', 'membership' ) );
    			exit;
    		}
    
    		// fetch subscription and pricing
    		$sub_id              = filter_input( INPUT_POST, 'subscription_id', FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) ) );
    		$this->_subscription = Membership_Plugin::factory()->get_subscription( $sub_id );
    		$pricing             = $this->_subscription->get_pricingarray();
    		if ( ! $pricing ) {
    			status_header( 404 );
    			exit;
    		}
    
    		// apply a coupon
    		$coupon = membership_get_current_coupon();
    		if ( $coupon && $coupon->valid_for_subscription( $this->_subscription->id ) ) {
    			$pricing = $coupon->apply_coupon_pricing( $pricing );
    		}
    
    		// fetch member
    		$user_id       = is_user_logged_in() ? get_current_user_id() : $_POST['user_id'];
    		$this->_member = Membership_Plugin::factory()->get_member( $user_id );
    
    		// fetch CIM user and payment profiles info
    		// pay attention that CIM can't handle recurring transaction, so we need
    		// to use standard ARB aproach and full cards details
    		$has_serial = in_array( 'serial', wp_list_pluck( $pricing, 'type' ) );
    		if ( ! $has_serial ) {
    			$this->_cim_payment_profile_id = trim( filter_input( INPUT_POST, 'profile' ) );
    			if ( ! empty( $this->_cim_payment_profile_id ) ) {
    				$this->_cim_profile_id = get_user_meta( $this->_member->ID, 'authorize_cim_id', true );
    				if ( $this->_cim_profile_id ) {
    					$response = $this->_get_cim()->getCustomerPaymentProfile( $this->_cim_profile_id, $this->_cim_payment_profile_id );
    					if ( $response->isError() ) {
    						$this->_cim_payment_profile_id = false;
    					}
    				}
    			}
    		}
    
    		// process payments
    		$first_payment         = false;
    		$started               = new DateTime();
    		$this->_payment_result = array( 'status' => '', 'errors' => array() );
    		$this->_transactions   = array();
    
    		for ( $i = 0, $count = count( $pricing ); $i < $count; $i ++ ) {
    			if ( $first_payment === false && $pricing[$i]['amount'] > 0 ) {
    				$first_payment = $pricing[$i]['amount'];
    			}
    
    			switch ( $pricing[$i]['type'] ) {
    				case 'finite':
    					//Using AIM for onetime payment
    					$this->_transactions[] = $this->_process_nonserial_purchase( $pricing[$i], $started );
    					/*//Call ARB with only one recurrency for each subscription level.
    					$this->_transactions[] = $this->_process_serial_purchase( $pricing[$i], $started, 1, $unit = 'months', 12 );
    					$interval              = self::_get_period_interval_in_date_format( $pricing[$i]['unit'] );
    					$started->modify( sprintf( '+%d %s', $pricing[$i]['period'], $interval ) );*/
    					break;
    				case 'indefinite':
    					$this->_transactions[] = $this->_process_nonserial_purchase( $pricing[$i], $started );
    					break 2;
    				case 'serial':
    					//Call ARB with no end date (an ongoing subscription).
    					$this->_transactions[] = $this->_process_serial_purchase( $pricing[$i], $started, 12 );
    					break 2;
    			}
    
    			if ( $this->_payment_result['status'] == 'error' ) {
    				$this->_rollback_transactions();
    				break;
    			}
    		}
    
    		if ( $this->_payment_result['status'] == 'success' ) {
    			// create member subscription
    			if ( $this->_member->has_subscription() ) {
    				$from_sub_id = filter_input( INPUT_POST, 'from_subscription', FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) ) );
    				if ( $this->_member->on_sub( $from_sub_id ) ) {
    					$this->_member->drop_subscription( $from_sub_id );
    				}
    
    				if ( $this->_member->on_sub( $sub_id ) ) {
    					$this->_member->drop_subscription( $sub_id );
    				}
    			}
    			$this->_member->create_subscription( $sub_id, $this->gateway );
    
    			// create CIM profile it is not exists, otherwise update it if new card was added
    			$this->_cim_profile_id = get_user_meta( $this->_member->ID, 'authorize_cim_id', true );
    			if ( ! $this->_cim_profile_id ) {
    				$this->_create_cim_profile();
    			} elseif ( ! $has_serial && empty( $this->_cim_payment_profile_id ) ) {
    				$this->_update_cim_profile();
    			}
    
    			// process transactions
    			$this->_commit_transactions();
    
    			if ( $first_payment ) {
    				do_action( 'membership_authorizenet_payment_processed', $this->_member->ID, $sub_id );
    				do_action( 'membership_payment_processed', $this->_member->ID, $sub_id, $first_payment, $M_options['paymentcurrency'], $this->_transactions[0]['transaction'] );
    			}
    
    			// process response message and redirect
    			if ( self::is_popup() && ! empty( $M_options['registrationcompleted_message'] ) ) {
    				$html = '<div class="header" style="width: 750px"><h1>';
    				$html .= sprintf( __( 'Sign up for %s completed', 'membership' ), $this->_subscription->sub_name() );
    				$html .= '</h1></div><div class="fullwidth">';
    				$html .= stripslashes( wpautop( $M_options['registrationcompleted_message'] ) );
    				$html .= '</div>';
    
    				$this->_payment_result['redirect'] = 'no';
    				$this->_payment_result['message']  = $html;
    			} else {
    				$this->_payment_result['message']  = '';
    				$this->_payment_result['redirect'] = strpos( home_url(), 'https://' ) === 0
    					? str_replace( 'https:', 'http:', M_get_registrationcompleted_permalink() )
    					: M_get_registrationcompleted_permalink();
    			}
    		}
    
    		echo json_encode( $this->_payment_result );
    		exit;
    	}

    Let me know if this works for you.

    Also find the code here

    https://gist.github.com/JudeRosario/d0b9072e8479fb04b905

    Cheers,
    Jude

    Ann

    Apologies for the delay, had to wait for some other work to be finished on the site. Finally tested today.
    I replaced the code and we ran a test Membership order. We used a card that DOES expire before the end of the subscription cycle.

    ORIGINAL CODE: when clicked the "process payment" button, received an error message that said "Credit Card expires before the start of the subscription." The payment info was sent to Authorize.net and a "welcome to the club" confirmation was received.

    NEW CODE: when clicked the "process payment" button, received an error message that said "This transaction cannot be accepted" .The payment info was NOT sent to Authorize.net but a "welcome to the club" confirmation was received.

    We renamed the file back for now...
    Authorize.php* - live file
    Authorize.php.bak* AuthorizeORIGINAL.php* - two backups of live original file
    AuthorizeNEW.php* - file with your code

    Thank you!

    Ann

    Hi there-

    Thanks! Card expiration: 06/15
    We found other orders with same issue expired in different months.

    Duration is 1 year

    Membership > Subscription Plans:
    Settings for Each Plan are the same in the Members Section:
    Mode: Serial
    Period: 1 year(s)

    Membership > Options > Configuration tab:
    Membership Renewal > Renewal Period Limit: 1 day(s)
    Membership Upgrades > Upgrades Period Limit: 1 day(s)

    Ann

    I'm so sorry. When we tested we only checked that day. Once confirmed working we moved on.

    We were just notified by client and confirmed that now - anyone who has a card that expires within the year DOES get captured without having to log into authorize.net and do it manually, but they also get CHARGED A SECOND TIME THE NEXT DAY.

    They had to refund a bunch of folks... so, we've rolled back the plugin. Do hope that didn't make it to final release yet! Sorry we didn't catch! Still open to any fixes that may work but know we were at last ditch attempt to get it working with the last file change provided.