Building my own gateway, question about actions.

I'm building a new payment gateway for the membership plugin and I'm modeling it after the paypal express gateway. I'm not an expert on the WP architecture yet, but I'm picking my way through. The question I have is regarding the actions that are called in the handle_paypal_return hook. For instance, membership_payment_denied is called when the gateway responds with a payment denial code. What I'd like to do is return my users to the subscription form with an error message. I'm assuming that I would need to write a hook for this action, but where is the proper place to do this? Based on what I see, these action calls are all placeholders with no implementation, so I'm kind of stuck with where to go next.

Another question I have is where is the user taken/what do they see once that hook completes execution? Is the do_subscription_shortcode method of the membershippublic class executed?

Thanks!

  • chimpsy

    Thank you,

    I've done some more work on this and I should add that the major difference between the gateway I'm building for and the PayPal gateway is that after submitting to this gateway, it returns immediately (redirects) back to the /paymentreturn/ URL. It appears that the PayPal gateway does the IPN call to the /paymentreturn/ URL yet redirects the user to a different URL. I'm able to successfully post payment to the gateway and then I'm redirected to the /paymentreturn/ URL, but I'm stuck at this point as I basically just end up at a blank page and I'm not sure how to redirect the user back to the sign up form for either confirmation or the subscription form again to retry payment (along with the error message returned from the gateway).

    I'm thinking I could emit a form and submit back to my page containing the membership shortcode with javascript, but there has to be a more elegant solution that that right?

  • chimpsy

    Sure, and as you'll see, I fully understand how my specific "membership_handle_payment_return_".$gateweyname method is invoked. In fact I'm able to successfully post payment to the gateway and handle the response. The problem is what to do after I get the response. If successful, I'd like to display the user some sort of confirmation, if not, I'd like to return them to the subscription form. I'm just not sure about the best way to do that from the payment return "page".

  • chimpsy

    hmm... can't attach the code, so here it is in-line:

    <?php
    
    class btree extends M_Gateway {
    
    	var $gateway = 'btree';
    	var $title = 'Braintree';
    
    	function btree() {
    		parent::M_Gateway();
    
    		require_once( ABSPATH . 'wp-includes/braintree-php-2.6.0/lib/Braintree.php' );
    
    		add_action('M_gateways_settings_' . $this->gateway, array(&$this, 'mysettings'));
    
    		// If I want to override the transactions output - then I can use this action
    		//add_action('M_gateways_transactions_' . $this->gateway, array(&$this, 'mytransactions'));
    
    		if($this->is_active()) {
    			// Subscription form gateway
    			add_action('membership_purchase_button', array(&$this, 'display_subscribe_button'), 1, 3);
    
    			// Payment return
    			add_action('membership_handle_payment_return_' . $this->gateway, array(&$this, 'handle_braintree_return'));
    		}
    
    	}
    
    	function mysettings() {
    
    		global $M_options;
    
    		?>
    		<table class="form-table">
    		<tbody>
    		  <tr valign="top">
    		  <th scope="row" style="width:225px;"><?php _e('Braintree Merchant ID', 'membership') ?></th>
    		  <td><input type="text" name="braintree_merchant_id" value="<?php esc_attr_e(get_option( $this->gateway . "_braintree_merchant_id" )); ?>" />
    		  <br />
    		  </td>
    		  </tr>
    		  <tr valign="top">
    		  <th scope="row"><?php _e('Braintree Public Key', 'membership') ?></th>
    		  <td><input type="text" name="braintree_public_key" value="<?php esc_attr_e(get_option( $this->gateway . "_braintree_public_key" )); ?>" />
    		  <br />
    		  </td>
    		  </tr>
    		  <tr valign="top">
    		  <th scope="row"><?php _e('Braintree Private Key', 'membership') ?></th>
    		  <td><input type="text" name="braintree_private_key" value="<?php esc_attr_e(get_option( $this->gateway . "_braintree_private_key" )); ?>" />
    		  <br />
    		  </td>
    		  </tr>
    		  <tr valign="top">
    		  <th scope="row"><?php _e('Braintree Environment', 'membership') ?></th>
    		  <td><select name="braintree_environment">
    		  <option value="sandbox" <?php if (get_option( $this->gateway . "_braintree_environment" ) == 'sandbox') echo 'selected="selected"'; ?>><?php _e('Sandbox', 'membership') ?></option>
    		  <option value="production" <?php if (get_option( $this->gateway . "_braintree_environment" ) == 'production') echo 'selected="selected"'; ?>><?php _e('Production', 'membership') ?></option>
    		  </select>
    		  <br />
    		  </td>
    		</tbody>
    		</table>
    		<?php
    	}
    
    	function single_button($pricing, $subscription, $user_id) {
    
    		/*global $M_options;
    
    		if(empty($M_options['paymentcurrency'])) {
    			$M_options['paymentcurrency'] = 'USD';
    		}
    
    		$form = '';
    
    		if (get_option( $this->gateway . "_braintree_environment" ) == 'sandbox') {
    			$form .= '<form action="https://www.paypal.com/cgi-bin/webscr" method="post">';
    		} else {
    			$form .= '<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">';
    		}
    		//$form .= '<input type="hidden" name="business" value="' . esc_attr(get_option( $this->gateway . "_paypal_email" )) . '">';
    		//$form .= '<input type="hidden" name="cmd" value="_xclick">';
    		//$form .= '<input type="hidden" name="item_number" value="' . $subscription->sub_id() . '">';
    		//$form .= '<input type="hidden" name="item_name" value="' . $subscription->sub_name() . '">';
    		//$form .= '<input type="hidden" name="amount" value="' . $pricing[0]['amount'] . '.00">';
    		//$form .= '<input type="hidden" name="currency_code" value="' . $M_options['paymentcurrency'] .'">';
    
    		//$form .= '<input type="hidden" name="custom" value="' . $this->build_custom($user_id, $subscription->id, $pricing[0]['amount'] . '.00') .'">';
    
    		//$form .= '<input type="hidden" name="lc" value="' . esc_attr(get_option( $this->gateway . "_paypal_site" )) . '">';
    		//$form .= '<input type="hidden" name="notify_url" value="' . trailingslashit(get_option('home')) . 'paymentreturn/' . esc_attr($this->gateway) . '">';
    
    		//$button = get_option( $this->gateway . "_paypal_button", 'https://www.paypal.com/en_US/i/btn/btn_subscribe_LG.gif' );
    
    		//$form .= '<input type="image" name="submit" border="0" src="' . $button . '" alt="PayPal - The safer, easier way to pay online">';
    		//$form .= '<img alt="" border="0" width="1" height="1" src="https://www.paypal.com/en_US/i/scr/pixel.gif" >';
    		$form .= '</form>';
    
    		return $form;
    		*/
    	}
    
    	function single_sub_button($pricing, $subscription, $user_id, $norepeat = false) {
    
    		global $M_options;
    
    		$form = '';
    
    		//Set up Braintree
    		Braintree_Configuration::environment(get_option( $this->gateway . "_braintree_environment" ));
    		Braintree_Configuration::merchantId(get_option( $this->gateway . "_braintree_merchant_id" ));
    		Braintree_Configuration::publicKey(get_option( $this->gateway . "_braintree_public_key" ));
    		Braintree_Configuration::privateKey(get_option( $this->gateway . "_braintree_private_key" ));
    
    		$member = new M_Membership( $user_id );
    
    		$trData = Braintree_TransparentRedirect::createCustomerData(
    		  array(
    		    'redirectUrl' => trailingslashit(get_option('home')) . 'paymentreturn/' . esc_attr($this->gateway),
    		    'customer' => array(
    		      'id' => $user_id
    		    )
    		  )
    		);
    
    		$form .= '<form action="' . Braintree_TransparentRedirect::url() . '" method="post" autocomplete="off">';
    		$form .= '<div class="formleft">';
    		$form .= '<p><label>Cardholder Name<span>*</span></label>';
    		$form .= '<input type="text" style="width:300px;" class="text_input" name="customer[credit_card][cardholder_name]" />';
    		$form .= '</p>';
    		$form .= '<p><label>Billing Address Street<span>*</span></label>';
    		$form .= '<input type="text" class="text_input" style="width:400px;" name="customer[credit_card][billing_address][street_address]" />';
    		$form .= '</p>';
    		$form .= '<p><label>Billing Address Postal Code<span>*</span></label>';
    		$form .= '<input type="text" class="text_input" style="width:100px;" name="customer[credit_card][billing_address][postal_code]" />';
    		$form .= '</p>';
    		$form .= '<div class="alignleft">';
    		$form .= '<p><label>Credit Card Number (No Spaces)<span>*</span></label>';
    		$form .= '<input type="text" class="text_input" name="customer[credit_card][number]" />';
    		$form .= '</p>';
    		$form .= '</div>';
    		$form .= '<div class="alignleft">';
    		$form .= '<p><label>Security Code<span>*</span></label>';
    		$form .= '<input type="text" class="text_input" style="width:50px;" name="customer[credit_card][cvv]" />';
    		$form .= '</p>';
    		$form .= '</div>';
    		$form .= '<p class="pass_hint">Hint: The security code for Visa & Mastercard is a 3 digit code on the back of the card. For American Express, the code is a 4 digit code on the front of the card.</p>';
    		$form .= '<div class="alignleft">';
    		$form .= '<p><label>Credit Card Expiration Month<span>*</span></label>';
    		$form .= '<select class="select" style="width:150px;" name="customer[credit_card][expiration_month]"><option value="01">January</option><option value="02">February</option><option value="03">March</option><option value="04">April</option><option value="05">May</option><option value="06">June</option><option value="07">July</option><option value="08">August</option><option value="09">September</option><option value="10">October</option><option value="11">November</option><option value="12">December</option></select>';
    		$form .= '</p>';
    		$form .= '</div>';
    		$form .= '<div class="alignleft">';
    		$form .= '<p><label>Credit Card Expiration Year<span>*</span></label>';
    		$form .= '<select class="select" style="width:150px;" name="customer[credit_card][expiration_year]">';
    		for ($y = 0; $y <= 10; $y++){
    			$form .= '<option value="' . (date('Y') + $y) . '">' . (date('Y') + $y) . '</option>';
    		}
    		$form .= '</select>';
    		$form .= '</p>';
    		$form .= '</div>';
    		$form .= '<input type="hidden" name="customer[email]" value="' . $member->data->user_email . '" />';
    		$form .= '<input type="hidden" name="customer[first_name]" value="' . $member->data->first_name . '" />';
    		$form .= '<input type="hidden" name="customer[last_name]" value="' . $member->data->last_name . '" />';
    		$form .= '<input type="hidden" name="customer[custom_fields][sub_code]" value="' . $subscription->sub_code() . '" />';
    		$form .= '<input type="hidden" name="customer[custom_fields][sub_id]" value="' . $subscription->sub_id() . '" />';
    		$form .= '<input type="hidden" name="tr_data" value="' . htmlentities($trData) . '" />';
    		$form .= '<p><input type="submit" value="SUBMIT PAYMENT" class="button large blue" name="submit"></p>';
    		$form .= '<p class="pass_hint">* By submitting payment you are granting permission to bill your credit card on a monthly basis at the rate of $' . number_format($pricing[0][amount],2,'.',',') . '/month.';
    		$form .= '</div>';
    		$form .= '</form>';
    
    		return $form;
    
    	}
    
    	function complex_sub_button($pricing, $subscription, $user_id) {
    
    		/*global $M_options;
    
    		if(empty($M_options['paymentcurrency'])) {
    			$M_options['paymentcurrency'] = 'USD';
    		}
    
    		$form = '';
    
    		if (get_option( $this->gateway . "_braintree_environment" ) == 'sandbox') {
    			$form .= '<form action="https://www.paypal.com/cgi-bin/webscr" method="post">';
    		} else {
    			$form .= '<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">';
    		}
    		//$form .= '<input type="hidden" name="business" value="' . esc_attr(get_option( $this->gateway . "_paypal_email" )) . '">';
    		//$form .= '<input type="hidden" name="cmd" value="_xclick-subscriptions">';
    		//$form .= '<input type="hidden" name="item_name" value="' . $subscription->sub_name() . '">';
    		//$form .= '<input type="hidden" name="item_number" value="' . $subscription->sub_id() . '">';
    		//$form .= '<input type="hidden" name="currency_code" value="' . $M_options['paymentcurrency'] .'">';
    
    		// complex bits here
    		$count = 1;
    		$ff = array();
    		foreach((array) $pricing as $key => $price) {
    
    			switch($price['type']) {
    
    				case 'finite':	if(empty($price['amount'])) $price['amount'] = '0';
    								if($count < 3) {
    									$ff['a' . $count] = $price['amount'] . '.00';
    									$ff['p' . $count] = $price['period'];
    									$ff['t' . $count] = strtoupper($price['unit']);
    								} else {
    									// Or last finite is going to be the end of the subscription payments
    									$ff['a3'] = $price['amount'] . '.00';
    									$ff['p3'] = $price['period'];
    									$ff['t3'] = strtoupper($price['unit']);
    									$ff['src'] = '0';
    								}
    								$count++;
    								break;
    
    				case 'indefinite':
    								if(empty($price['amount'])) $price['amount'] = '0';
    
    								if($price['amount'] == '0') {
    									// The indefinite rule is free, we need to move any previous
    									// steps up to this one as we can't have a free a3
    									if( isset($ff['a2']) && $ff['a2'] != '0.00' ) {
    										// we have some other earlier rule so move it up
    										$ff['a3'] = $ff['a2'];
    										$ff['p3'] = $ff['p2'];
    										$ff['t3'] = $ff['t2'];
    										unset($ff['a2']);
    										unset($ff['p2']);
    										unset($ff['t2']);
    										$ff['src'] = '0';
    									} elseif( isset($ff['a1']) && $ff['a1'] != '0.00' ) {
    										$ff['a3'] = $ff['a1'];
    										$ff['p3'] = $ff['p1'];
    										$ff['t3'] = $ff['t1'];
    										unset($ff['a1']);
    										unset($ff['p1']);
    										unset($ff['t1']);
    										$ff['src'] = '0';
    									}
    								} else {
    									$ff['a3'] = $price['amount'] . '.00';
    									$ff['p3'] = 1;
    									$ff['t3'] = 'Y';
    									$ff['src'] = '0';
    								}
    								break;
    				case 'serial':
    								if(empty($price['amount'])) $price['amount'] = '0';
    
    								if($price['amount'] == '0') {
    									// The serial rule is free, we need to move any previous
    									// steps up to this one as we can't have a free a3
    									if( isset($ff['a2']) && $ff['a2'] != '0.00' ) {
    										// we have some other earlier rule so move it up
    										$ff['a3'] = $ff['a2'];
    										$ff['p3'] = $ff['p2'];
    										$ff['t3'] = $ff['t2'];
    										unset($ff['a2']);
    										unset($ff['p2']);
    										unset($ff['t2']);
    										$ff['src'] = '1';
    									} elseif( isset($ff['a1']) && $ff['a1'] != '0.00' ) {
    										$ff['a3'] = $ff['a1'];
    										$ff['p3'] = $ff['p1'];
    										$ff['t3'] = $ff['t1'];
    										unset($ff['a1']);
    										unset($ff['p1']);
    										unset($ff['t1']);
    										$ff['src'] = '1';
    									}
    								} else {
    									$ff['a3'] = $price['amount'] . '.00';
    									$ff['p3'] = $price['period'];
    									$ff['t3'] = strtoupper($price['unit']);
    									$ff['src'] = '1';
    								}
    
    								break;
    			}
    		}
    
    		if(!empty($ff)) {
    			foreach($ff as $key => $value) {
    			//	$form .= '<input type="hidden" name="' . $key . '" value="' . $value . '">';
    			}
    		}
    
    		//$form .= '<input type="hidden" name="custom" value="' . $this->build_custom($user_id, $subscription->id, $ff['a3']) .'">';
    
    		// Remainder of the easy bits
    
    		//$form .= '<input type="hidden" name="return" value="' . get_option('home') . '">';
    		//$form .= '<input type="hidden" name="cancel_return" value="' . get_option('home') . '">';
    
    		//$form .= '<input type="hidden" name="lc" value="' . esc_attr(get_option( $this->gateway . "_paypal_site" )) . '">';
    		//$form .= '<input type="hidden" name="notify_url" value="' . trailingslashit(get_option('home')) . 'paymentreturn/' . esc_attr($this->gateway) . '">';
    
    		//$button = get_option( $this->gateway . "_paypal_button", 'https://www.paypal.com/en_US/i/btn/btn_subscribe_LG.gif' );
    
    		//$form .= '<!-- Display the payment button. --> <input type="image" name="submit" border="0" src="' . $button . '" alt="PayPal - The safer, easier way to pay online">';
    		//$form .= '<img alt="" border="0" width="1" height="1" src="https://www.paypal.com/en_US/i/scr/pixel.gif" >';
    		$form .= '</form>';
    
    		return $form;
    		*/
    	}
    
    	function build_subscribe_button($subscription, $pricing, $user_id) {
    
    		if(!empty($pricing)) {
    
    			// check to make sure there is a price in the subscription
    			// we don't want to display free ones for a payment system
    			$free = true;
    			foreach($pricing as $key => $price) {
    				if(!empty($price['amount']) && $price['amount'] > 0 ) {
    					$free = false;
    				}
    			}
    
    			if(!$free) {
    				if(count($pricing) == 1) {
    					// A basic price or a single subscription
    					if(in_array($pricing[0]['type'], array('indefinite','finite'))) {
    						// one-off payment
    						return $this->single_sub_button($pricing, $subscription, $user_id, true);
    					} else {
    						// simple subscription
    						return $this->single_sub_button($pricing, $subscription, $user_id);
    					}
    				} else {
    					// something much more complex
    
    					return $this->complex_sub_button($pricing, $subscription, $user_id);
    
    				}
    			}
    
    		}
    
    	}
    
    	function display_subscribe_button($subscription, $pricing, $user_id) {
    		echo $this->build_subscribe_button($subscription, $pricing, $user_id);
    
    	}
    
    	function update() {
    
    		if(isset($_POST['braintree_merchant_id'])) {
    			update_option( $this->gateway . "_braintree_merchant_id", $_POST[ 'braintree_merchant_id' ] );
    			update_option( $this->gateway . "_braintree_public_key", $_POST[ 'braintree_public_key' ] );
    			update_option( $this->gateway . "_braintree_private_key", $_POST[ 'braintree_private_key' ] );
    			update_option( $this->gateway . "_braintree_environment", $_POST[ 'braintree_environment' ] );
    		}
    
    		// default action is to return true
    		return true;
    
    	}
    
    	// Payment Notification
    	function handle_braintree_return() {
    		// Handle response from Braintree
    
    		//Set up Braintree
    		Braintree_Configuration::environment(get_option( $this->gateway . "_braintree_environment" ));
    		Braintree_Configuration::merchantId(get_option( $this->gateway . "_braintree_merchant_id" ));
    		Braintree_Configuration::publicKey(get_option( $this->gateway . "_braintree_public_key" ));
    		Braintree_Configuration::privateKey(get_option( $this->gateway . "_braintree_private_key" ));
    
    		$customerresult = Braintree_TransparentRedirect::confirm($_SERVER['QUERY_STRING']);
    
    		//check for succussful customer creation
    		if($customerresult->success){
    			if (isset($customerresult->customer)){
    				//customer created, use the payment method token to create the subscription.
    				$cards = $customerresult->customer->creditCards;
    				$custom_fields = $customerresult->customer->customFields;
    				foreach($cards as $card){
    					if($card->default){
    						$token = $card->token;
    					}
    				}
    
    				foreach($custom_fields as $key => $value){
    					switch ($key) {
    						case "sub_code":
    							$sub_code = $value;
    							break;
    						case "sub_id":
    							$sub_id = $value;
    							break;
    						default:
    						//do nothing
    					}
    				}
    
    				//create the subscription with Braintree
    				$subscriptionresult = Braintree_Subscription::create(
    					array(
    						'paymentMethodToken' => $token,
    						'planId' => $sub_code
    					)
    				);
    				//check for successful subscription creation
    				if($subscriptionresult->success){
    					$subscription = $subscriptionresult->subscription;
    
    					//Grab the amount of the transaction
    					$amount = $subscription->price;
    					//Loop through the transaction details to get the currency type
    					foreach($subscription->transactions as $transaction){
    						if($transaction->type == "sale"){
    							$currency = $transaction->currencyIsoCode;
    							$user_id = $transaction->customer->id;
    							$txn_id = $transaction->id;
    							$payment_status = $transaction->processorResponseText;
    						}
    					}
    					//Set the transaction timestamp. In the sandbox the timestamp for the transaction is blank
    					//so for now, just setting it to the current date/time.
    					$timestamp = date("n/j/Y g:i:s a");
    
    					$this->record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $txn_id, $payment_status, '');
    
    					// Added for affiliate system link
    					do_action('membership_payment_processed', $user_id, $sub_id, $amount, $currency, $_POST['txn_id']);
    
    					// create_subscription
    					$member = new M_Membership($user_id);
    					if($member) {
    						$member->create_subscription($sub_id);
    					}
    
    					do_action('membership_payment_subscr_signup', $user_id, $sub_id);
    				}
    				else{
    					//subscription creation failed
    
    				}
    			}
    		}
    		else{
    			//customer creation failed.
    			$errors = explode(".",$customerresult->message,-1);
    			var_dump($errors);//something went wrong, need to handle errors.
    			die();
    
    			$note = 'Last transaction has been reversed. Reason: Payment Denied';
    			//$amount = $_POST['mc_gross'];
    			//$currency = $_POST['mc_currency'];
    			//list($timestamp, $user_id, $sub_id, $key) = explode(':', $_POST['custom']);
    
    			//$this->record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $_POST['txn_id'], $_POST['payment_status'], $note);
    
    			//$member = new M_Membership($user_id);
    			//if($member) {
    			//	$member->expire_subscription($sub_id);
    			//	$member->deactivate();
    			//}
    
    			//do_action('membership_payment_denied', $user_id, $sub_id, $amount, $currency, $_POST['txn_id']);
    			//break;
    
    		}
    
    /*		if ((isset($_POST['payment_status']) || isset($_POST['txn_type'])) && isset($_POST['custom'])) {
    
    			if (get_option( $this->gateway . "_braintree_environment" ) == 'sandbox') {
    				$domain = 'https://www.paypal.com';
    			} else {
    				$domain = 'https://www.sandbox.paypal.com';
    			}
    
    			$req = 'cmd=_notify-validate';
    			if (!isset($_POST)) $_POST = $HTTP_POST_VARS;
    			foreach ($_POST as $k => $v) {
    				if (get_magic_quotes_gpc()) $v = stripslashes($v);
    				$req .= '&' . $k . '=' . $v;
    			}
    
    			$header = 'POST /cgi-bin/webscr HTTP/1.0' . "\r\n"
    					. 'Content-Type: application/x-www-form-urlencoded' . "\r\n"
    					. 'Content-Length: ' . strlen($req) . "\r\n"
    					. "\r\n";
    
    			@set_time_limit(60);
    			if ($conn = @fsockopen($domain, 80, $errno, $errstr, 30)) {
    				fputs($conn, $header . $req);
    				socket_set_timeout($conn, 30);
    
    				$response = '';
    				$close_connection = false;
    				while (true) {
    					if (feof($conn) || $close_connection) {
    						fclose($conn);
    						break;
    					}
    
    					$st = @fgets($conn, 4096);
    					if ($st === false) {
    						$close_connection = true;
    						continue;
    					}
    
    					$response .= $st;
    				}
    
    				$error = '';
    				$lines = explode("\n", str_replace("\r\n", "\n", $response));
    				// looking for: HTTP/1.1 200 OK
    				if (count($lines) == 0) $error = 'Response Error: Header not found';
    				else if (substr($lines[0], -7) != ' 200 OK') $error = 'Response Error: Unexpected HTTP response';
    				else {
    					// remove HTTP header
    					while (count($lines) > 0 && trim($lines[0]) != '') array_shift($lines);
    
    					// first line will be empty, second line will have the result
    					if (count($lines) < 2) $error = 'Response Error: No content found in transaction response';
    					else if (strtoupper(trim($lines[1])) != 'VERIFIED') $error = 'Response Error: Unexpected transaction response';
    				}
    
    				if ($error != '') {
    					echo $error;
    					exit;
    				}
    			}
    
    			// handle cases that the system must ignore
    			//if ($_POST['payment_status'] == 'In-Progress' || $_POST['payment_status'] == 'Partially-Refunded') exit;
    			$new_status = false;
    			// process PayPal response
    			switch ($_POST['payment_status']) {
    				case 'Partially-Refunded':
    					break;
    
    				case 'In-Progress':
    					break;
    
    				case 'Completed':
    				case 'Processed':
    					// case: successful payment
    					$amount = $_POST['mc_gross'];
    					$currency = $_POST['mc_currency'];
    					list($timestamp, $user_id, $sub_id, $key) = explode(':', $_POST['custom']);
    
    					$this->record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $_POST['txn_id'], $_POST['payment_status'], '');
    
    					// Added for affiliate system link
    					do_action('membership_payment_processed', $user_id, $sub_id, $amount, $currency, $_POST['txn_id']);
    					break;
    
    				case 'Reversed':
    					// case: charge back
    					$note = 'Last transaction has been reversed. Reason: Payment has been reversed (charge back)';
    					$amount = $_POST['mc_gross'];
    					$currency = $_POST['mc_currency'];
    					list($timestamp, $user_id, $sub_id, $key) = explode(':', $_POST['custom']);
    
    					$this->record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $_POST['txn_id'], $_POST['payment_status'], $note);
    
    					$member = new M_Membership($user_id);
    					if($member) {
    						$member->expire_subscription($sub_id);
    						$member->deactivate();
    					}
    
    					do_action('membership_payment_reversed', $user_id, $sub_id, $amount, $currency, $_POST['txn_id']);
    					break;
    
    				case 'Refunded':
    					// case: refund
    					$note = 'Last transaction has been reversed. Reason: Payment has been refunded';
    					$amount = $_POST['mc_gross'];
    					$currency = $_POST['mc_currency'];
    					list($timestamp, $user_id, $sub_id, $key) = explode(':', $_POST['custom']);
    
    					$this->record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $_POST['txn_id'], $_POST['payment_status'], $note);
    
    					$member = new M_Membership($user_id);
    					if($member) {
    						$member->expire_subscription($sub_id);
    					}
    
    					do_action('membership_payment_refunded', $user_id, $sub_id, $amount, $currency, $_POST['txn_id']);
    					break;
    
    				case 'Denied':
    					// case: denied
    					$note = 'Last transaction has been reversed. Reason: Payment Denied';
    					$amount = $_POST['mc_gross'];
    					$currency = $_POST['mc_currency'];
    					list($timestamp, $user_id, $sub_id, $key) = explode(':', $_POST['custom']);
    
    					$this->record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $_POST['txn_id'], $_POST['payment_status'], $note);
    
    					$member = new M_Membership($user_id);
    					if($member) {
    						$member->expire_subscription($sub_id);
    						$member->deactivate();
    					}
    
    					do_action('membership_payment_denied', $user_id, $sub_id, $amount, $currency, $_POST['txn_id']);
    					break;
    
    				case 'Pending':
    					// case: payment is pending
    					$pending_str = array(
    						'address' => 'Customer did not include a confirmed shipping address',
    						'authorization' => 'Funds not captured yet',
    						'echeck' => 'eCheck that has not cleared yet',
    						'intl' => 'Payment waiting for aproval by service provider',
    						'multi-currency' => 'Payment waiting for service provider to handle multi-currency process',
    						'unilateral' => 'Customer did not register or confirm his/her email yet',
    						'upgrade' => 'Waiting for service provider to upgrade the PayPal account',
    						'verify' => 'Waiting for service provider to verify his/her PayPal account',
    						'*' => ''
    						);
    					$reason = @$_POST['pending_reason'];
    					$note = 'Last transaction is pending. Reason: ' . (isset($pending_str[$reason]) ? $pending_str[$reason] : $pending_str['*']);
    					$amount = $_POST['mc_gross'];
    					$currency = $_POST['mc_currency'];
    					list($timestamp, $user_id, $sub_id, $key) = explode(':', $_POST['custom']);
    
    					$this->record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $_POST['txn_id'], $_POST['payment_status'], $note);
    
    					do_action('membership_payment_pending', $user_id, $sub_id, $amount, $currency, $_POST['txn_id']);
    					break;
    
    				default:
    					// case: various error cases
    			}
    
    			//check for subscription details
    			switch ($_POST['txn_type']) {
    				case 'subscr_signup':
    					// start the subscription
    					list($timestamp, $user_id, $sub_id, $key) = explode(':', $_POST['custom']);
    
    					// create_subscription
    					$member = new M_Membership($user_id);
    					if($member) {
    						$member->create_subscription($sub_id);
    					}
    
    					do_action('membership_payment_subscr_signup', $user_id, $sub_id);
    				  break;
    
    				case 'subscr_cancel':
    					// mark for removal
    					list($timestamp, $user_id, $sub_id, $key) = explode(':', $_POST['custom']);
    
    					$member = new M_Membership($user_id);
    					if($member) {
    						$member->mark_for_expire($sub_id);
    					}
    
    					do_action('membership_payment_subscr_cancel', $user_id, $sub_id);
    				  break;
    
    				case 'new_case':
    					// a dispute
    					if($_POST['case_type'] == 'dispute') {
    						// immediately suspend the account
    						$member = new M_Membership($user_id);
    						if($member) {
    							$member->deactivate();
    						}
    					}
    
    					do_action('membership_payment_new_case', $user_id, $sub_id, $_POST['case_type']);
    					break;
    			}
    
    		} else {
    			// Did not find expected POST variables. Possible access attempt from a non PayPal site.
    			header('Status: 404 Not Found');
    			echo 'Error: Missing POST variables. Identification is not possible.';
    			exit;
    		}
    */
    	}
    }
    
    M_register_gateway('btree', 'btree');
    
    ?>
  • Barry

    @chimpsy -

    The problem is what to do after I get the response. If successful, I'd like to display the user some sort of confirmation, if not, I'd like to return them to the subscription form.

    The problem is that the payment return could happen asynchronously, that is certainly the case with PayPal (i.e. it happens behind the scenes - server to server, and not necessarily immediately).

    I'd suggest taking a different approach - have the payment gateway handle the payment, but on the front end have a check to see if the payment has been completed and do the redirect there.

  • chimpsy

    Right, that makes sense. The reason I'm approaching it the way that I am is because this gateway offers a way to post payment through a method they call "Transparent Redirect". This means that the CC information will never touch our servers as it's posted directly to the gateway and then the user is immediately redirected back to your site. This reduces a lot of the risk associated with PCI compliance. What you're suggesting is that I post server to server, which can be done, but I'd like to avoid that if possible.

  • chimpsy

    Yes, that's correct. There are a number of parameters appended to the URL. Along with a gateway is an API provided by the vendor. The querystring passed back is then processed by the API (I'm fairly certain it does a web service call back to the gateway) and the rest of the transaction information is then returned.

    You can see this in my code in the handle_braintree_return function. Shortly after the API is initialized there is this call:

    $customerresult = Braintree_TransparentRedirect::confirm($_SERVER['QUERY_STRING']);

    The resultant $customerresult object contains all of the information about the transaction.

    Thanks for all of your help!

Thank NAME, for their help.

Let NAME know exactly why they deserved these points.

Gift a custom amount of points.