http://www.mediawiki.org/wiki/Special:Code/MediaWiki/100133
Revision: 100133 Author: khorn Date: 2011-10-18 17:44:56 +0000 (Tue, 18 Oct 2011) Log Message: ----------- Merging the fundraising branch of DonationInterface back into trunk. Verified that payflowpro_gateway.body.php did in fact merge properly. As this is a mid-merge commit, the extension will be broken until the merge is completed. Modified Paths: -------------- trunk/extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.body.php Modified: trunk/extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.body.php =================================================================== --- trunk/extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.body.php 2011-10-18 17:36:59 UTC (rev 100132) +++ trunk/extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.body.php 2011-10-18 17:44:56 UTC (rev 100133) @@ -1,47 +1,13 @@ <?php -class PayflowProGateway extends UnlistedSpecialPage { +class PayflowProGateway extends GatewayForm { /** - * Defines the action to take on a PFP transaction. - * - * Possible values include 'process', 'challenge', - * 'review', 'reject'. These values can be set during - * data processing validation, for instance. - * - * Hooks are exposed to handle the different actions. - * - * Defaults to 'process'. - * @var string - */ - public $action = 'process'; - - /** - * Holds the PayflowPro response from a transaction - * @var array - */ - public $payflow_response = array(); - - /** - * A container for the form class - * - * Used to loard the form object to display the CC form - * @var object - */ - public $form_class; - - /** - * An array of form errors - * @var array - */ - public $errors = array(); - - /** * Constructor - set up the new special page */ public function __construct() { - parent::__construct( 'PayflowProGateway' ); - $this->errors = $this->getPossibleErrors(); + $this->adapter = new PayflowProAdapter(); + parent::__construct(); //the next layer up will know who we are. } /** @@ -50,405 +16,67 @@ * @param $par Mixed: parameter passed to the page or null */ public function execute( $par ) { - global $wgRequest, $wgOut, $wgScriptPath, - $wgPayFlowProGatewayCSSVersion, - $wgPayflowGatewaySalt; + global $wgRequest, $wgOut, $wgExtensionAssetsPath; + $CSSVersion = $this->adapter->getGlobal( 'CSSVersion' ); - $scriptVars = array( - 'payflowproGatewayErrorMsgJs' => wfMsg( 'payflowpro_gateway-error-msg-js' ), - 'payflowproGatewayErrorMsgEmail' => wfMsg( 'payflowpro_gateway-error-msg-email' ), - 'payflowproGatewayErrorMsgAmount' => wfMsg( 'payflowpro_gateway-error-msg-amount' ), - 'payflowproGatewayErrorMsgEmailAdd' => wfMsg( 'payflowpro_gateway-error-msg-emailAdd' ), - 'payflowproGatewayErrorMsgFname' => wfMsg( 'payflowpro_gateway-error-msg-fname' ), - 'payflowproGatewayErrorMsgLname' => wfMsg( 'payflowpro_gateway-error-msg-lname' ), - 'payflowproGatewayErrorMsgStreet' => wfMsg( 'payflowpro_gateway-error-msg-street' ), - 'payflowproGatewayErrorMsgCity' => wfMsg( 'payflowpro_gateway-error-msg-city' ), - 'payflowproGatewayErrorMsgState' => wfMsg( 'payflowpro_gateway-error-msg-state' ), - 'payflowproGatewayErrorMsgZip' => wfMsg( 'payflowpro_gateway-error-msg-zip' ), - 'payflowproGatewayErrorMsgCountry' => wfMsg( 'payflowpro_gateway-error-msg-country' ), - 'payflowproGatewayErrorMsgCardNum' => wfMsg( 'payflowpro_gateway-error-msg-card_num' ), - 'payflowproGatewayErrorMsgExpiration' => wfMsg( 'payflowpro_gateway-error-msg-expiration' ), - 'payflowproGatewayErrorMsgCvv' => wfMsg( 'payflowpro_gateway-error-msg-cvv' ), - 'payflowproGatewayCVVExplain' => wfMsg( 'payflowpro_gateway-cvv-explain' ), - ); + // Hide unneeded interface elements + $wgOut->addModules( 'donationInterface.skinOverride' ); - $wgOut->addScript( Skin::makeVariablesScript( $scriptVars ) ); + $gateway_id = $this->adapter->getIdentifier(); - // find out if amount was a radio button or textbox, set amount - if ( isset( $_REQUEST['amount'] ) && preg_match( '/^\d+(\.(\d+)?)?$/', $wgRequest->getText( 'amount' ) ) ) { - $amount = $wgRequest->getText( 'amount' ); - } elseif ( isset( $_REQUEST['amountGiven'] ) && preg_match( '/^\d+(\.(\d+)?)?$/', $wgRequest->getText( 'amountGiven' ) ) ) { - $amount = number_format( $wgRequest->getText( 'amountGiven' ), 2, '.', '' ); - } elseif ( isset( $_REQUEST['amount'] ) ) { - $amount = '0.00'; - } elseif ( $wgRequest->getText( 'amount' ) == '-1' ) { - $amount = $wgRequest->getText( 'amountOther' ); - } else { - $amount = '0.00'; - } - - // track the number of attempts the user has made - $numAttempt = $wgRequest->getVal( 'numAttempt', 0 ); - - // Get array of default account values necessary for Payflow - require_once( 'includes/payflowUser.inc' ); - - $payflow_data = payflowUser(); - - // make a log entry if the user has submitted the cc form - if ( $wgRequest->wasPosted() && $wgRequest->getText( 'process', 0 )) { - self::log( $payflow_data[ 'order_id' ] . " Transaction initiated." ); - } else { - self::log( $payflow_data[ 'order_id' ] . " " . $payflow_data[ 'i_order_id' ] . " Initial credit card form request.", 'payflowpro_gateway', LOG_DEBUG ); - } - - // if _cache_ is requested by the user, do not set a session/token; dynamic data will be loaded via ajax - if ( $wgRequest->getText( '_cache_', false ) ) { - self::log( $payflow_data[ 'order_id' ] . " " . $payflow_data[ 'i_order_id' ] . " Cache requested", 'payflowpro_gateway', LOG_DEBUG ); - $cache = true; - $token = ''; - $token_match = false; - - // if we have squid caching enabled, set the maxage - global $wgUseSquid, $wgPayflowSMaxAge; - if ( $wgUseSquid ) { - self::log( $payflow_data[ 'order_id' ] . " " . $payflow_data[ 'i_order_id' ] . " Setting s-max-age: " . $wgPayflowSMaxAge, 'payflowpro_gateway', LOG_DEBUG ); - $wgOut->setSquidMaxage( $wgPayflowSMaxAge ); - } - } else { - $cache = false; - - // establish the edit token to prevent csrf - $token = self::fnPayflowEditToken( $wgPayflowGatewaySalt ); - - self::log( $payflow_data[ 'order_id' ] . " " . $payflow_data[ 'i_order_id' ] . " fnPayflowEditToken: " . $token, 'payflowpro_gateway', LOG_DEBUG ); - - // match token - $token_check = ( $wgRequest->getText( 'token' ) ) ? $wgRequest->getText( 'token' ) : $token; - $token_match = $this->fnPayflowMatchEditToken( $token_check, $wgPayflowGatewaySalt ); - if ( $wgRequest->wasPosted() ) { - self::log( $payflow_data[ 'order_id' ] . " " . $payflow_data[ 'i_order_id' ] . " Submitted edit token: " . $wgRequest->getText( 'token', 'None' ), 'payflowpro_gateway', LOG_DEBUG); - self::log( $payflow_data[ 'order_id' ] . " " . $payflow_data[ 'i_order_id' ] . " Token match: " . ($token_match ? 'true' : 'false' ), 'payflowpro_gateway', LOG_DEBUG ); - } - } - $this->setHeaders(); - // Populate form data - $data = $this->fnGetFormData( $amount, $numAttempt, $token, $payflow_data['order_id'], $payflow_data['i_order_id'] ); - /** * handle PayPal redirection * - * if paypal redirection is enabled ($wgPayflowGatewayPaypalURL must be defined) + * if paypal redirection is enabled ($wgPayflowProGatewayPaypalURL must be defined) * and the PaypalRedirect form value must be true */ if ( $wgRequest->getText( 'PaypalRedirect', 0 ) ) { - $this->paypalRedirect( $data ); + $this->paypalRedirect(); return; } + //TODO: This is short-circuiting what I really want to do here. + //so stop it. + $data = $this->adapter->getDisplayData(); + // dispatch forms/handling - if ( $token_match ) { - if ( $data['payment_method'] == 'processed' ) { - - // increase the count of attempts - ++$data['numAttempt']; - // Check form for errors and redisplay with messages - $form_errors = $this->fnPayflowValidateForm( $data, $this->errors ); + if ( $this->adapter->checkTokens() ) { + if ( $this->adapter->posted && $data['payment_method'] == 'processed' ) { + // The form was submitted and the payment method has been set + $this->adapter->log( "Form posted and payment method set." ); + // Check form for errors + $form_errors = $this->fnValidateForm( $data, $this->errors ); + // If there were errors, redisplay form, otherwise proceed to next step if ( $form_errors ) { - $this->fnPayflowDisplayForm( $data, $this->errors ); + $this->displayForm( $data, $this->errors ); } else { // The submitted form data is valid, so process it - // allow any external validators to have their way with the data - self::log( $data[ 'order_id' ] . " Preparing to query MaxMind" ); - wfRunHooks( 'PayflowGatewayValidate', array( &$this, &$data ) ); - self::log( $data[ 'order_id' ] . ' Finished querying Maxmind' ); + $result = $this->adapter->do_transaction( 'Card' ); - // if the transaction was flagged for review - if ( $this->action == 'review' ) { - // expose a hook for external handling of trxns flagged for review - wfRunHooks( 'PayflowGatewayReview', array( &$this, &$data )); - } - - // if the transaction was flagged to be 'challenged' - if ( $this->action == 'challenge' ) { - // expose a hook for external handling of trxns flagged for challenge (eg captcha) - wfRunHooks( 'PayflowGatewayChallenge', array( &$this, &$data ) ); - } - // if the transaction was flagged for rejection - if ( $this->action == 'reject' ) { - // expose a hook for external handling of trxns flagged for rejection - wfRunHooks( 'PayflowGatewayReject', array( &$this, &$data ) ); - + if ( $this->adapter->action == 'reject' ) { $this->fnPayflowDisplayDeclinedResults( '' ); - $this->fnPayflowUnsetEditToken(); } - // if the transaction was flagged for processing - if ( $this->action == 'process' ) { - // expose a hook for external handling of trxns ready for processing - wfRunHooks( 'PayflowGatewayProcess', array( &$this, &$data ) ); - $this->fnPayflowProcessTransaction( $data, $payflow_data ); + if ( $this->adapter->action == 'process' ) { + $this->fnPayflowDisplayResults( $result ); } - - // expose a hook for any post processing - wfRunHooks( 'PayflowGatewayPostProcess', array( &$this, &$data ) ); + $this->displayResultsForDebug( $result ); } } else { // Display form for the first time - $this->fnPayflowDisplayForm( $data, $this->errors ); + $this->displayForm( $data, $this->errors ); } } else { - if ( !$cache ) { + if ( !$this->adapter->isCache() ) { // if we're not caching, there's a token mismatch - $this->errors['general']['token-mismatch'] = wfMsg( 'payflowpro_gateway-token-mismatch' ); + $this->errors['general']['token-mismatch'] = wfMsg( $gateway_id . '_gateway-token-mismatch' ); } - $this->fnPayflowDisplayForm( $data, $this->errors ); + $this->displayForm( $data, $this->errors ); } } /** - * Build and display form to user - * - * @param $data Array: array of posted user input - * @param $error Array: array of error messages returned by validate_form function - * - * The message at the top of the form can be edited in the payflow_gateway.i18n.php file - */ - public function fnPayflowDisplayForm( &$data, &$error ) { - global $wgOut, $wgRequest; - - // save contrib tracking id early to track abondonment - if ( $data[ 'numAttempt' ] == '0' && ( !$wgRequest->getText( 'utm_source_id', false ) || $wgRequest->getText( '_nocache_' ) == 'true' ) ) { - $tracked = $this->fnPayflowSaveContributionTracking( $data ); - if ( !$tracked ) { - $when = time(); - self::log( $data[ 'order_id' ] . ' Unable to save data to the contribution_tracking table ' . $when ); - } - } - - $form_class = $this->getFormClass(); - $form_obj = new $form_class( $data, $error ); - $form = $form_obj->getForm(); - $wgOut->addHTML( $form ); - } - - /** - * Set the form class to use to generate the CC form - * - * @param string $class_name The class name of the form to use - */ - public function setFormClass( $class_name = NULL ) { - if ( !$class_name ) { - global $wgRequest, $wgPayflowGatewayDefaultForm; - $form_class = $wgRequest->getText( 'form_name', $wgPayflowGatewayDefaultForm ); - - // make sure our form class exists before going on, if not try loading default form class - $class_name = "PayflowProGateway_Form_" . $form_class; - if ( !class_exists( $class_name ) ) { - $class_name_orig = $class_name; - $class_name = "PayflowProGateway_Form_" . $wgPayflowGatewayDefaultForm; - if ( !class_exists( $class_name ) ) { - throw new MWException( 'Could not load form ' . $class_name_orig . ' nor default form ' . $class_name ); - } - } - } - $this->form_class = $class_name; - } - - /** - * Get the currently set form class - * - * Will set the form class if the form class not already set - * Using logic in setFormClass() - * @return string - */ - public function getFormClass( ) { - if ( !isset( $this->form_class ) ) { - $this->setFormClass(); - } - return $this->form_class; - } - - /** - * Checks posted form data for errors and returns array of messages - */ - private function fnPayflowValidateForm( &$data, &$error ) { - global $wgPayflowPriceFloor, $wgPayflowPriceCieling; - - // begin with no errors - $error_result = '0'; - - // create the human-speak message for required fields - // does not include fields that are not required - $msg = array( - 'amount' => wfMsg( 'payflowpro_gateway-error-msg-amount' ), - 'emailAdd' => wfMsg( 'payflowpro_gateway-error-msg-emailAdd' ), - 'fname' => wfMsg( 'payflowpro_gateway-error-msg-fname' ), - 'lname' => wfMsg( 'payflowpro_gateway-error-msg-lname' ), - 'street' => wfMsg( 'payflowpro_gateway-error-msg-street' ), - 'city' => wfMsg( 'payflowpro_gateway-error-msg-city' ), - 'state' => wfMsg( 'payflowpro_gateway-error-msg-state' ), - 'zip' => wfMsg( 'payflowpro_gateway-error-msg-zip' ), - 'card_num' => wfMsg( 'payflowpro_gateway-error-msg-card_num' ), - 'expiration' => wfMsg( 'payflowpro_gateway-error-msg-expiration' ), - 'cvv' => wfMsg( 'payflowpro_gateway-error-msg-cvv' ), - ); - - // find all empty fields and create message - foreach ( $data as $key => $value ) { - if ( $value == '' || ($key == 'state' && $value == 'YY' )) { - // ignore fields that are not required - if ( isset( $msg[$key] ) ) { - $error[$key] = "**" . wfMsg( 'payflowpro_gateway-error-msg', $msg[$key] ) . "**<br />"; - $error_result = '1'; - } - } - } - - // check amount - if ( !preg_match( '/^\d+(\.(\d+)?)?$/', $data[ 'amount' ] ) || - ( (float) $this->convert_to_usd( $data[ 'currency' ], $data[ 'amount' ] ) < (float) $wgPayflowPriceFloor || - (float) $this->convert_to_usd( $data[ 'currency' ], $data[ 'amount' ] ) > (float) $wgPayflowPriceCieling ) ) { - $error['invalidamount'] = wfMsg( 'payflowpro_gateway-error-msg-invalid-amount' ); - $error_result = '1'; - } - - // is email address valid? - $isEmail = User::isValidEmailAddr( $data['email'] ); - - // create error message (supercedes empty field message) - if ( !$isEmail ) { - $error['emailAdd'] = wfMsg( 'payflowpro_gateway-error-msg-email' ); - $error_result = '1'; - } - - // validate that credit card number entered is correct and set the card type - if ( preg_match( '/^3[47][0-9]{13}$/', $data[ 'card_num' ] ) ) { // american express - $data[ 'card' ] = 'american'; - } elseif ( preg_match( '/^5[1-5][0-9]{14}$/', $data[ 'card_num' ] ) ) { // mastercard - $data[ 'card' ] = 'mastercard'; - } elseif ( preg_match( '/^4[0-9]{12}(?:[0-9]{3})?$/', $data[ 'card_num' ] ) ) {// visa - $data[ 'card' ] = 'visa'; - } elseif ( preg_match( '/^6(?:011|5[0-9]{2})[0-9]{12}$/', $data[ 'card_num' ] ) ) { // discover - $data[ 'card' ] = 'discover'; - } else { // an invalid credit card number was entered - $error_result = '1'; - $error[ 'card_num' ] = wfMsg( 'payflowpro_gateway-error-msg-card-num' ); - } - - return $error_result; - } - - /** - * Sends a name-value pair string to Payflow gateway - * - * @param $data Array: array of user input - * @param $payflow_data Array: array of necessary Payflow variables to - * include in string (i.e. Vendor, password) - */ - private function fnPayflowProcessTransaction( $data, $payflow_data ) { - global $wgOut, $wgDonationTestingMode, $wgPayflowGatewayUseHTTPProxy, $wgPayflowGatewayHTTPProxy, $wgPayflowProTimeout; - - // update contribution tracking - $this->updateContributionTracking( $data, defined( 'OWA' ) ); - - // create payflow query string, include string lengths - $queryArray = array( - 'TRXTYPE' => $payflow_data['trxtype'], - 'TENDER' => $payflow_data['tender'], - 'USER' => $payflow_data['user'], - 'VENDOR' => $payflow_data['vendor'], - 'PARTNER' => $payflow_data['partner'], - 'PWD' => $payflow_data['password'], - 'ACCT' => $data['card_num'], - 'EXPDATE' => $data['expiration'], - 'AMT' => $data['amount'], - 'FIRSTNAME' => $data['fname'], - 'LASTNAME' => $data['lname'], - 'STREET' => $data['street'], - 'CITY' => $data['city'], - 'STATE' => $data['state'], - 'COUNTRY' => $data['country'], - 'ZIP' => $data['zip'], - 'INVNUM' => $data['order_id'], - 'CVV2' => $data['cvv'], - 'CURRENCY' => $data['currency'], - 'VERBOSITY' => $payflow_data['verbosity'], - 'CUSTIP' => $payflow_data['user_ip'], - ); - - foreach ( $queryArray as $name => $value ) { - $query[] = $name . '[' . strlen( $value ) . ']=' . $value; - } - - $queryString = implode( '&', $query ); - - $payflow_query = $queryString; - - // assign header data necessary for the curl_setopt() function - $user_agent = Http::userAgent(); - $headers[] = 'Content-Type: text/namevalue'; - $headers[] = 'Content-Length : ' . strlen( $payflow_query ); - $headers[] = 'X-VPS-Client-Timeout: 45'; - $headers[] = 'X-VPS-Request-ID:' . $data['order_id']; - $ch = curl_init(); - $paypalPostTo = isset ( $wgDonationTestingMode ) ? 'testingurl' : 'paypalurl'; - curl_setopt( $ch, CURLOPT_URL, $payflow_data[ $paypalPostTo ] ); - curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers ); - curl_setopt( $ch, CURLOPT_USERAGENT, $user_agent ); - curl_setopt( $ch, CURLOPT_HEADER, 1 ); - curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); - curl_setopt( $ch, CURLOPT_TIMEOUT, $wgPayflowProTimeout ); - curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 0 ); - curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 ); - curl_setopt( $ch, CURLOPT_POSTFIELDS, $payflow_query ); - curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 2 ); - curl_setopt( $ch, CURLOPT_FORBID_REUSE, true ); - curl_setopt( $ch, CURLOPT_POST, 1 ); - - // set proxy settings if necessary - if ( $wgPayflowGatewayUseHTTPProxy ) { - curl_setopt( $ch, CURLOPT_HTTPPROXYTUNNEL, 1 ); - curl_setopt( $ch, CURLOPT_PROXY, $wgPayflowGatewayHTTPProxy ); - } - - // As suggested in the PayPal developer forum sample code, try more than once to get a response - // in case there is a general network issue - $i = 1; - - while ( $i++ <= 3 ) { - self::log( $data[ 'order_id' ] . ' Preparing to send transaction to PayflowPro' ); - $result = curl_exec( $ch ); - $headers = curl_getinfo( $ch ); - - if ( $headers['http_code'] != 200 && $headers['http_code'] != 403 ) { - self::log( $data[ 'order_id' ] . ' Failed sending transaction to PayflowPro, retrying' ); - sleep( 1 ); - } elseif ( $headers['http_code'] == 200 || $headers['http_code'] == 403 ) { - self::log( $data[ 'order_id' ] . ' Finished sending transaction to PayflowPro' ); - break; - } - } - - if ( $headers['http_code'] != 200 ) { - $wgOut->addHTML( '<h3>No response from credit card processor. Please try again later!</h3><p>' ); - $when = time(); - self::log( $data[ 'order_id' ] . ' No response from credit card processor: ' . curl_error( $ch ) ); - curl_close( $ch ); - return; - } - - curl_close( $ch ); - - // get result string - $result = strstr( $result, 'RESULT' ); - - // parse string and display results to the user - $this->fnPayflowGetResults( $data, $result ); - } - - /** * "Reads" the name-value pair result string returned by Payflow and creates corresponding error messages * * @param $data Array: array of user input @@ -456,178 +84,56 @@ * * Credit: code modified from payflowpro_example_EC.php posted (and supervised) on the PayPal developers message board */ - private function fnPayflowGetResults( $data, $result ) { - // prepare NVP response for sorting and outputting - $responseArray = array(); - - /** - * The result response string looks like: - * RESULT=7&PNREF=E79P2C651DC2&RESPMSG=Field format error&HOSTCODE=10747&DUPLICATE=1 - * We want to turn this into an array of key value pairs, so explode on '&' and then - * split up the resulting strings into $key => $value - */ - $result_arr = explode( "&", $result ); - foreach ( $result_arr as $result_pair ) { - list( $key, $value ) = preg_split( "/=/", $result_pair ); - $responseArray[ $key ] = $value; + private function fnPayflowDisplayResults( $result ) { + if ( is_array( $result ) && array_key_exists( 'errors', $result ) && is_array( $result['errors'] ) ) { + foreach ( $result['errors'] as $key => $value ) { + $errorCode = $key; + $responseMsg = $value; + break; //we just want the top, and this is probably the fastest way. + } } - // store the response array as an object property for easy retrival/manipulation elsewhere - $this->payflow_response = $responseArray; + $oid = $this->adapter->getData( 'order_id' ); + $i_oid = $this->adapter->getData( 'i_order_id' ); + $data = $this->adapter->getData(); - // errors fall into three categories, "try again please", "sorry it didn't work out", and "approved" - // get the result code for response array - $resultCode = $responseArray['RESULT']; - - // initialize response message - $responseMsg = ''; - - // interpret result code, return - // approved (1), denied (2), try again (3), general error (4) - $errorCode = $this->fnPayflowGetResponseMsg( $resultCode, $responseMsg ); - - // log that the transaction is essentially complete - self::log( $data[ 'order_id' ] . " Transaction complete." ); - // if approved, display results and send transaction to the queue if ( $errorCode == '1' ) { - self::log( $data[ 'order_id' ] . " " . $data[ 'i_order_id' ] . " Transaction approved.", 'payflowpro_gateway', LOG_DEBUG ); - $this->fnPayflowDisplayApprovedResults( $data, $responseArray, $responseMsg ); + $this->log( $oid . " " . $i_oid . " Transaction approved.", LOG_DEBUG ); + $this->fnPayflowDisplayApprovedResults( $data, $responseMsg ); // give user a second chance to enter incorrect data } elseif ( ( $errorCode == '3' ) && ( $data['numAttempt'] < '5' ) ) { - self::log( $data[ 'order_id' ] . " " . $data[ 'i_order_id' ] . " Transaction unsuccessful (invalid info).", 'payflowpro_gateway', LOG_DEBUG ); + $this->log( $oid . " " . $i_oid . " Transaction unsuccessful (invalid info).", LOG_DEBUG ); // pass responseMsg as an array key as required by displayForm $this->errors['retryMsg'] = $responseMsg; - $this->fnPayflowDisplayForm( $data, $this->errors ); + $this->displayForm( $data, $this->errors ); // if declined or if user has already made two attempts, decline } elseif ( ( $errorCode == '2' ) || ( $data['numAttempt'] >= '3' ) ) { - self::log( $data[ 'order_id' ] . " " . $data[ 'i_order_id' ] . " Transaction declined.", 'payflowpro_gateway', LOG_DEBUG ); + $this->log( $oid . " " . $i_oid . " Transaction declined.", LOG_DEBUG ); $this->fnPayflowDisplayDeclinedResults( $responseMsg ); } elseif ( ( $errorCode == '4' ) ) { - self::log( $data[ 'order_id' ] . " " . $data[ 'i_order_id' ] . " Transaction unsuccessful.", 'payflowpro_gateway', LOG_DEBUG ); + $this->log( $oid . " " . $i_oid . " Transaction unsuccessful.", LOG_DEBUG ); $this->fnPayflowDisplayOtherResults( $responseMsg ); } elseif ( ( $errorCode == '5' ) ) { - self::log( $data[ 'order_id' ] . " " . $data[ 'i_order_id' ] . " Transaction pending.", 'payflowpro_gateway', LOG_DEBUG ); - $this->fnPayflowDisplayPending( $data, $responseArray, $responseMsg ); + $this->log( $oid . " " . $i_oid . " Transaction pending.", LOG_DEBUG ); + $this->fnPayflowDisplayPending( $data, $responseMsg ); + } elseif ( ( $errorCode == '1000000' ) ) { //TODO: This is temporary until we can decide on the actual error codes WE control. + $this->log( $oid . " " . $i_oid . " Transaction unsuccessful (communication failure).", LOG_DEBUG ); + $this->fnPayflowDisplayOtherResults( $responseMsg ); + $this->errors['retryMsg'] = $responseMsg; + $this->displayForm( $data, $this->errors ); } - - }// end display results - - /** - * Interpret response code, return - * 1 if approved - * 2 if declined - * 3 if invalid data was submitted by user - * 4 all other errors - */ - function fnPayflowGetResponseMsg( $resultCode, &$responseMsg ) { - $responseMsg = wfMsg( 'payflowpro_gateway-response-default' ); - - switch( $resultCode ) { - case '0': - $responseMsg = wfMsg( 'payflowpro_gateway-response-0' ); - $errorCode = '1'; - break; - case '126': - $responseMsg = wfMsg( 'payflowpro_gateway-response-126-2' ); - $errorCode = '5'; - break; - case '12': - $responseMsg = wfMsg( 'payflowpro_gateway-response-12' ); - $errorCode = '2'; - break; - case '13': - $responseMsg = wfMsg( 'payflowpro_gateway-response-13' ); - $errorCode = '2'; - break; - case '114': - $responseMsg = wfMsg( 'payflowpro_gateway-response-114' ); - $errorCode = '2'; - break; - case '4': - $responseMsg = wfMsg( 'payflowpro_gateway-response-4' ); - $errorCode = '3'; - break; - case '23': - $responseMsg = wfMsg( 'payflowpro_gateway-response-23' ); - $errorCode = '3'; - break; - case '24': - $responseMsg = wfMsg( 'payflowpro_gateway-response-24' ); - $errorCode = '3'; - break; - case '112': - $responseMsg = wfMsg( 'payflowpro_gateway-response-112' ); - $errorCode = '3'; - break; - case '125': - $responseMsg = wfMsg( 'payflowpro_gateway-response-125-2' ); - $errorCode = '3'; - break; - default: - $responseMsg = wfMsg( 'payflowpro_gateway-response-default' ); - $errorCode = '4'; - } - - return $errorCode; } /** - * Prepares the transactional message to be sent via Stomp to queueing service - * - * @param array $data - * @param array $resposneArray - * @param array $responseMsg - * @return array - */ - public function prepareStompTransaction( $data, $responseArray, $responseMsg ) { - $countries = $this->getCountries(); - - $transaction = array(); - - // include response message - $transaction['response'] = $responseMsg; - - // include date - $transaction['date'] = time(); - - // put all data into one array - $optout = $this->determineOptOut( $data ); - $data[ 'anonymous' ] = $optout[ 'anonymous' ]; - $data[ 'optout' ] = $optout[ 'optout' ]; - - $transaction += array_merge( $data, $responseArray ); - - return $transaction; - } - - /** - * Fetch an array of country abbrevs => country names - */ - public static function getCountries() { - require_once( 'includes/countryCodes.inc' ); - return countryCodes(); - } - - /** * Display response message to user with submitted user-supplied data * * @param $data Array: array of posted data from form * @param $responseMsg String: message supplied by getResults function */ - function fnPayflowDisplayApprovedResults( $data, $responseArray, $responseMsg ) { + function fnPayflowDisplayApprovedResults( $data, $responseMsg ) { global $wgOut, $wgExternalThankYouPage; - $transaction = $this->prepareStompTransaction( $data, $responseArray, $responseMsg ); - - /** - * hook to call stomp functions - * - * Sends transaction to Stomp-based queueing service, - * eg ActiveMQ - */ - wfRunHooks( 'gwStomp', array( $transaction ) ); - if ( $wgExternalThankYouPage ) { $wgOut->redirect( $wgExternalThankYouPage . "/" . $data['language'] ); } else { @@ -635,7 +141,7 @@ $wgOut->addHTML( '<h3 class="response_message">' . $responseMsg . '</h3>' ); // translate country code into text - $countries = $this->getCountries(); + $countries = GatewayForm::getCountries(); $rows = array( 'title' => array( wfMsg( 'payflowpro_gateway-post-transaction' ) ), @@ -648,8 +154,6 @@ // if we want to show the response $wgOut->addHTML( Xml::buildTable( $rows, array( 'class' => 'submitted-response' ) ) ); } - // unset edit token - $this->fnPayflowUnsetEditToken(); } /** @@ -665,9 +169,6 @@ // display response message $wgOut->addHTML( '<h3 class="response_message">' . $declinedDefault . ' ' . $responseMsg . '</h3>' ); - - // unset edit token - $this->fnPayflowUnsetEditToken(); } /** @@ -676,685 +177,50 @@ * @param $responseMsg String: message supplied by getResults function */ function fnPayflowDisplayOtherResults( $responseMsg ) { - global $wgOut; - - // general decline message - $declinedDefault = wfMsg( 'php-response-declined' ); - - // display response message - $wgOut->addHTML( '<h3 class="response_message">' . $declinedDefault . ' ' . $responseMsg . '</h3>' ); - - // unset edit token - $this->fnPayflowUnsetEditToken(); + //I have collapsed it like this because the contents were identical. + //TODO: Determine if we need to be switching on anything else in the display here. + $this->fnPayflowDisplayDeclinedResults( $responseMsg ); } - function fnPayflowDisplayPending( $data, $responseArray, $responseMsg ) { + function fnPayflowDisplayPending( $responseMsg ) { global $wgOut; - $transaction = $this->prepareStompTransaction( $data, $responseArray, $responseMsg ); - - // hook to call stomp functions - wfRunHooks( 'gwPendingStomp', array( $transaction ) ); - $thankyou = wfMsg( 'payflowpro_gateway-thankyou' ); // display response message $wgOut->addHTML( '<h2 class="response_message">' . $thankyou . '</h2>' ); $wgOut->addHTML( '<p>' . $responseMsg ); - - // unset edit token - $this->fnPayflowUnsetEditToken(); } - /** - * Determine proper opt-out settings for contribution tracking - * - * because the form elements for comment anonymization and email opt-out - * are backwards (they are really opt-in) relative to contribution_tracking - * (which is opt-out), we need to reverse the values - */ - public static function determineOptOut( $data ) { - $optout[ 'optout' ] = ( isset( $data[ 'email-opt' ] ) && $data[ 'email-opt' ] == "1" ) ? '0' : '1'; - $optout[ 'anonymous' ] = ( isset( $data[ 'comment-option' ] ) && $data[ 'comment-option' ] == "1" ) ? '0' : '1'; - return $optout; - } + //TODO: Remember why the heck I decided to leave this here... + //arguably, it's because it's slightly more "view" related, but... still, shouldn't you get stashed + //in the new GatewayForm class so we can override in children if we feel like it? Odd. + function addErrorMessageScript() { + global $wgOut; + $gateway_id = $this->adapter->getIdentifier(); - function fnPayflowSaveContributionTracking( &$data ) { - // determine opt-out settings - $optout = self::determineOptOut( $data ); - - $tracked_contribution = array( - 'note' => $data['comment'], - 'referrer' => $data['referrer'], - 'anonymous' => $optout[ 'anonymous' ], - 'utm_source' => $data['utm_source'], - 'utm_medium' => $data['utm_medium'], - 'utm_campaign' => $data['utm_campaign'], - 'optout' => $optout[ 'optout' ], - 'language' => $data['language'], - 'ts' => '', + $scriptVars = array( + $gateway_id . 'GatewayErrorMsgJs' => wfMsg( $gateway_id . '_gateway-error-msg-js' ), + $gateway_id . 'GatewayErrorMsgEmail' => wfMsg( $gateway_id . '_gateway-error-msg-email' ), + $gateway_id . 'GatewayErrorMsgAmount' => wfMsg( $gateway_id . '_gateway-error-msg-amount' ), + $gateway_id . 'GatewayErrorMsgEmailAdd' => wfMsg( $gateway_id . '_gateway-error-msg-emailAdd' ), + $gateway_id . 'GatewayErrorMsgFname' => wfMsg( $gateway_id . '_gateway-error-msg-fname' ), + $gateway_id . 'GatewayErrorMsgLname' => wfMsg( $gateway_id . '_gateway-error-msg-lname' ), + $gateway_id . 'GatewayErrorMsgStreet' => wfMsg( $gateway_id . '_gateway-error-msg-street' ), + $gateway_id . 'GatewayErrorMsgCity' => wfMsg( $gateway_id . '_gateway-error-msg-city' ), + $gateway_id . 'GatewayErrorMsgState' => wfMsg( $gateway_id . '_gateway-error-msg-state' ), + $gateway_id . 'GatewayErrorMsgZip' => wfMsg( $gateway_id . '_gateway-error-msg-zip' ), + $gateway_id . 'GatewayErrorMsgCountry' => wfMsg( $gateway_id . '_gateway-error-msg-country' ), + $gateway_id . 'GatewayErrorMsgCardType' => wfMsg( $gateway_id . '_gateway-error-msg-card_type' ), + $gateway_id . 'GatewayErrorMsgCardNum' => wfMsg( $gateway_id . '_gateway-error-msg-card_num' ), + $gateway_id . 'GatewayErrorMsgExpiration' => wfMsg( $gateway_id . '_gateway-error-msg-expiration' ), + $gateway_id . 'GatewayErrorMsgCvv' => wfMsg( $gateway_id . '_gateway-error-msg-cvv' ), + $gateway_id . 'GatewayCVVExplain' => wfMsg( $gateway_id . '_gateway-cvv-explain' ), ); - // insert tracking data and get the tracking id - $data['contribution_tracking_id'] = self::insertContributionTracking( $tracked_contribution ); - - if ( !$data[ 'contribution_tracking_id' ] ) { - return false; - } - return true; + $wgOut->addScript( Skin::makeVariablesScript( $scriptVars ) ); } - /** - * Insert a record into the contribution_tracking table - * - * @param array $tracking_data The array of tracking data to insert to contribution_tracking - * NOTE: this should probably be run thru self::cleanTrackingData to ensure data integrity - * @return mixed Contribution tracking ID or false on failure - */ - public static function insertContributionTracking( $tracking_data ) { - $db = payflowGatewayConnection(); +} - if ( !$db ) { return false; } - - // set the time stamp if it's not already set - if ( !isset( $tracking_data[ 'ts' ] ) || !strlen( $tracking_data[ 'ts' ] ) ) { - $tracking_data[ 'ts' ] = $db->timestamp(); - } - - // Store the contribution data - if ( $db->insert( 'contribution_tracking', $tracking_data ) ) { - return $db->insertId(); - } else { - return false; - } - } - - /** - * Clean array of tracking data to contain valid fields - * - * Compares tracking data array to list of valid tracking fields and - * removes any extra tracking fields/data. Also sets empty values to - * 'null' values. - * @param array $tracking_data - * @param bool $clean_opouts If true, form opt-out values will be run through $this->determineOptOut - * for cleanup. - */ - public static function cleanTrackingData( $tracking_data, $clean_optouts = false ) { - // clean up the optout values if necessary - if ( $clean_optouts ) { - $optouts = self::determineOptOut( $tracking_data ); - $tracking_data[ 'optout' ] = $optouts[ 'optout' ]; - $tracking_data[ 'anonymous' ] = $optouts[ 'anonymous' ]; - } - - // define valid tracking fields - $tracking_fields = array( - 'note', - 'referrer', - 'anonymous', - 'utm_source', - 'utm_medium', - 'utm_campaign', - 'optout', - 'language', - 'ts' - ); - - // loop through tracking data and clean it up - foreach ( $tracking_data as $key => $value ) { - // Make sure we only have valid fields - if ( !in_array( $key, $tracking_fields ) ) { - unset( $tracking_data[ $key ] ); - } - - // Make all empty strings NULL - if ( !strlen( $value ) ) { - $tracking_data[$key] = null; - } - } - - return $tracking_data; - } - - function fnPayflowReturnCurrencies() { - - $payflowCurrencies = array( - 'GBP' => 'GBP: British Pound', - 'EUR' => 'EUR: Euro', - 'USD' => 'USD: U.S. Dollar', - 'AUD' => 'AUD: Australian Dollar', - 'CAD' => 'CAD: Canadian Dollar', - 'JPY' => 'JPY: Japanese Yen', - ); - - return $payflowCurrencies; - } - - /** - * Establish an 'edit' token to help prevent CSRF, etc - * - * We use this in place of $wgUser->editToken() b/c currently - * $wgUser->editToken() is broken (apparently by design) for - * anonymous users. Using $wgUser->editToken() currently exposes - * a security risk for non-authenticated users. Until this is - * resolved in $wgUser, we'll use our own methods for token - * handling. - * - * @var mixed $salt - * @return string - */ - public static function fnPayflowEditToken( $salt = '' ) { - - // make sure we have a session open for tracking a CSRF-prevention token - self::fnPayflowEnsureSession(); - - if ( !isset( $_SESSION[ 'payflowEditToken' ] ) ) { - // generate unsalted token to place in the session - $token = self::fnPayflowGenerateToken(); - $_SESSION[ 'payflowEditToken' ] = $token; - } else { - $token = $_SESSION[ 'payflowEditToken' ]; - } - - if ( is_array( $salt ) ) { - $salt = implode( "|", $salt ); - } - return md5( $token . $salt ) . EDIT_TOKEN_SUFFIX; - } - - /** - * Generate a token string - * - * @var mixed $salt - * @return string - */ - public static function fnPayflowGenerateToken( $salt = '' ) { - $token = dechex( mt_rand() ) . dechex( mt_rand() ); - return md5( $token . $salt ); - } - - /** - * Determine the validity of a token - * - * @var string $val - * @var mixed $salt - * @return bool - */ - function fnPayflowMatchEditToken( $val, $salt = '' ) { - // fetch a salted version of the session token - $sessionToken = self::fnPayflowEditToken( $salt ); - if ( $val != $sessionToken ) { - wfDebug( "PayflowproGateway::fnPayflowMatchEditToken: broken session data\n" ); - } - return $val == $sessionToken; - } - - /** - * Unset the payflow edit token from a user's session - */ - function fnPayflowUnsetEditToken() { - unset( $_SESSION[ 'payflowEditToken' ] ); - } - - /** - * Ensure that we have a session set for the current user - * - * If we do not have a session set for the current user, - * start the session. - */ - public static function fnPayflowEnsureSession() { - // if the session is already started, do nothing - if ( session_id() ) return; - - // otherwise, fire it up using global mw function wfSetupSession - wfSetupSession(); - } - - -/** - * Fetches ID for reference URL for OWA tracking - * - * In the event that the URL is not already in the database, insert it - * and return it's id. Otehrewise, just return its id. - * @param string $ref The reference URL - * @return int The id for the reference URL - 0 if not found - */ - function get_owa_ref_id( $ref ) { - if ( !defined( 'OWA' ) ) { - return 0; - } - // Replication lag means sometimes a new event will not exist in the table yet - $dbw = payflowGatewayConnection(); - $id_num = $dbw->selectField( - 'contribution_tracking_owa_ref', - 'id', - array( 'url' => $ref ), - __METHOD__ - ); - // Once we're on mysql 5, we can use replace() instead of this selectField --> insert or update hooey - if ( $id_num === false ) { - $dbw->insert( - 'contribution_tracking_owa_ref', - array( 'url' => (string) $ref ), - __METHOD__ - ); - $id_num = $dbw->insertId(); - } - return $id_num === false ? 0 : $id_num; - } - - /** - * Populate the $data array for the credit card form - * - * Provides a way to prepopulate the form with test data using $wgPayflowGatewayTest - * @return array - */ - public function fnGetFormData( $amount, $numAttempt, $token, $order_id, $i_order_id=0 ) { - global $wgPayflowGatewayTest, $wgRequest; - - // fetch ID for the url reference for OWA tracking - $owa_ref = $wgRequest->getText( 'owa_ref', null ); - if( $owa_ref != null && !is_numeric( $owa_ref )){ - $owa_ref = $this->get_owa_ref_id( $owa_ref ); - } - - // if we're in testing mode and an action hasn't yet be specified, prepopulate the form - if ( !$wgRequest->getText( 'action', false ) && !$wgRequest->getText( 'process', 0 ) && $wgPayflowGatewayTest ) { - // define arrays of cc's and cc #s for random selection - $cards = array( 'american' ); - $card_nums = array( - 'american' => array( - 378282246310005 - ), - ); - - // randomly select a credit card - $card_index = array_rand( $cards ); - - // randomly select a credit card # - $card_num_index = array_rand( $card_nums[ $cards[ $card_index ]] ); - - $data = array( - 'amount' => ( $amount != "0.00" ) ? $amount : "35", - 'amountOther' => '', - 'email' => 't...@example.com', - 'fname' => 'Tester', - 'mname' => 'T.', - 'lname' => 'Testington', - 'street' => '548 Market St.', - 'city' => 'San Francisco', - 'state' => 'CA', - 'zip' => '94104', - 'country' => 'US', - 'fname2' => 'Testy', - 'lname2' => 'Testerson', - 'street2' => '123 Telegraph Ave.', - 'city2' => 'Berkeley', - 'state2' => 'CA', - 'zip2' => '94703', - 'country2' => 'US', - 'size' => 'small', - 'premium_language' => 'es', - 'card_num' => $card_nums[ $cards[ $card_index ]][ $card_num_index ], - 'card' => $cards[ $card_index ], - 'expiration' => date( 'my', strtotime( '+1 year 1 month' ) ), - 'cvv' => '001', - 'currency' => 'USD', - 'payment_method' => $wgRequest->getText( 'payment_method' ), - 'order_id' => $order_id, - 'i_order_id' => $i_order_id, - 'numAttempt' => $numAttempt, - 'referrer' => 'http://www.baz.test.com/index.php?action=foo&action=bar', - 'utm_source' => self::getUtmSource(), - 'utm_medium' => $wgRequest->getText( 'utm_medium' ), - 'utm_campaign' => $wgRequest->getText( 'utm_campaign' ), - 'language' => 'en', - 'comment-option' => $wgRequest->getText( 'comment-option' ), - 'comment' => $wgRequest->getText( 'comment' ), - 'email-opt' => $wgRequest->getText( 'email-opt' ), - 'test_string' => $wgRequest->getText( 'process' ), - 'token' => $token, - 'contribution_tracking_id' => $wgRequest->getText( 'contribution_tracking_id' ), - 'data_hash' => $wgRequest->getText( 'data_hash' ), - 'action' => $wgRequest->getText( 'action' ), - 'gateway' => 'payflowpro', - 'owa_session' => $wgRequest->getText( 'owa_session', null ), - 'owa_ref' => $owa_ref, - ); - } else { - $data = array( - 'amount' => $amount, - 'amountOther' => $wgRequest->getText( 'amountOther' ), - 'email' => $wgRequest->getText( 'emailAdd' ), - 'fname' => $wgRequest->getText( 'fname' ), - 'mname' => $wgRequest->getText( 'mname' ), - 'lname' => $wgRequest->getText( 'lname' ), - 'street' => $wgRequest->getText( 'street' ), - 'city' => $wgRequest->getText( 'city' ), - 'state' => $wgRequest->getText( 'state' ), - 'zip' => $wgRequest->getText( 'zip' ), - 'country' => $wgRequest->getText( 'country' ), - 'fname2' => $wgRequest->getText( 'fname' ), - 'lname2' => $wgRequest->getText( 'lname' ), - 'street2' => $wgRequest->getText( 'street' ), - 'city2' => $wgRequest->getText( 'city' ), - 'state2' => $wgRequest->getText( 'state' ), - 'zip2' => $wgRequest->getText( 'zip' ), - /** - * For legacy reasons, we might get a 0-length string passed into the form for country2. If this happens, we need to set country2 - * to be 'country' for downstream processing (until we fully support passing in two separate addresses). I thought about completely - * disabling country2 support in the forms, etc but realized there's a chance it'll be resurrected shortly. Hence this silly hack. - */ - 'country2' => ( strlen( $wgRequest->getText( 'country2' ))) ? $wgRequest->getText( 'country2' ) : $wgRequest->getText( 'country' ), - 'size' => $wgRequest->getText( 'size' ), - 'premium_language' => $wgRequest->getText( 'premium_language', "en" ), - 'card_num' => str_replace( ' ', '', $wgRequest->getText( 'card_num' ) ), - 'card' => $wgRequest->getText( 'card' ), - 'expiration' => $wgRequest->getText( 'mos' ) . substr( $wgRequest->getText( 'year' ), 2, 2 ), - 'cvv' => $wgRequest->getText( 'cvv' ), - 'currency' => $wgRequest->getText( 'currency_code' ), - 'payment_method' => $wgRequest->getText( 'payment_method' ), - 'order_id' => $order_id, - 'i_order_id' => $i_order_id, - 'numAttempt' => $numAttempt, - 'referrer' => ( $wgRequest->getVal( 'referrer' ) ) ? $wgRequest->getVal( 'referrer' ) : $wgRequest->getHeader( 'referer' ), - 'utm_source' => self::getUtmSource(), - 'utm_medium' => $wgRequest->getText( 'utm_medium' ), - 'utm_campaign' => $wgRequest->getText( 'utm_campaign' ), - // try to honor the user-set language (uselang), otherwise the language set in the URL (language) - 'language' => $wgRequest->getText( 'uselang', $wgRequest->getText( 'language' ) ), - 'comment-option' => $wgRequest->getText( 'comment-option' ), - 'comment' => $wgRequest->getText( 'comment' ), - 'email-opt' => $wgRequest->getText( 'email-opt' ), - 'test_string' => $wgRequest->getText( 'process' ), // for showing payflow string during testing - 'token' => $token, - 'contribution_tracking_id' => $wgRequest->getText( 'contribution_tracking_id' ), - 'data_hash' => $wgRequest->getText( 'data_hash' ), - 'action' => $wgRequest->getText( 'action' ), - 'gateway' => 'payflowpro', // this may need to become dynamic in the future - 'owa_session' => $wgRequest->getText( 'owa_session', null ), - 'owa_ref' => $owa_ref, - ); - } - - // sanitize user input - array_walk( $data, array( $this, 'sanitizeInput' ) ); - - return $data; - } - - /** - * Sanitize user input - * - * Intended to be used with something like array_walk - * - * @param $value The value of the array - * @param $key The key of the array - * @param $flags The flag constant for htmlspecialchars - * @param $double_encode Whether or not to double-encode strings - */ - public function sanitizeInput( &$value, $key, $flags=ENT_COMPAT, $double_encode=false ) { - $value = htmlspecialchars( $value, $flags, 'UTF-8', $double_encode ); - } - - public function getPossibleErrors() { - return array( - 'general' => '', - 'retryMsg' => '', - 'invalidamount' => '', - 'card_num' => '', - 'card' => '', - 'cvv' => '', - 'fname' => '', - 'lname' => '', - 'city' => '', - 'country' => '', - 'street' => '', - 'state' => '', - 'zip' => '', - 'emailAdd' => '', - ); - } - - /** - * Get the utm_source string - * - * Checks to see if the utm_source is set properly for the credit card - * form including any cc form variants (identified by utm_source_id). If - * anything cc form related is out of place for the utm_source, this - * will fix it. - * - * the utm_source is structured as: banner.landing_page.payment_instrument - * - * @param string $utm_source The utm_source for tracking - if not passed directly, - * we try to figure it out from the request object - * @param int $utm_source_id The utm_source_id for tracking - if not passed directly, - * we try to figure it out from the request object - * @return string The full utm_source - */ - public static function getUtmSource( $utm_source = null, $utm_source_id = null ) { - global $wgRequest; - - /** - * fetch whatever was passed in as the utm_source - * - * if utm_source was not passed in as a param, we try to divine it from - * the request. if it's not set there, no big deal, we'll just be - * missing some tracking data. - */ - if ( is_null( $utm_source ) ) { - $utm_source = $wgRequest->getText( 'utm_source' ); - } - - /** - * if we have a utm_source_id, then the user is on a single-step credit card form. - * if that's the case, we treat the single-step credit card form as a landing page, - * which we label as cc#, where # = the utm_source_id - */ - if ( is_null( $utm_source_id ) ) { - $utm_source_id = $wgRequest->getVal( 'utm_source_id', 0 ); - } - - // this is how the CC portion of the utm_source should be defined - $correct_cc_source = ( $utm_source_id ) ? 'cc' . $utm_source_id . '.cc' : 'cc'; - - // check to see if the utm_source is already correct - if so, return - if ( preg_match( '/' . str_replace( ".", "\.", $correct_cc_source ) . '$/', $utm_source ) ) { - return $utm_source; - } - - // split the utm_source into its parts for easier manipulation - $source_parts = explode( ".", $utm_source ); - - // if there are no sourceparts element, then the banner portion of the string needs to be set. - // since we don't know what it is, set it to an empty string - if ( !count( $source_parts ) ) $source_parts[0] = ''; - - // if the utm_source_id is set, set the landing page portion of the string to cc# - $source_parts[1] = ( $utm_source_id ) ? 'cc' . $utm_source_id : ( isset( $source_parts[1] ) ? $source_parts[1] : '' ); - - // the payment instrument portion should always be 'cc' if this method is being accessed - $source_parts[2] = 'cc'; - - // return a reconstructed string - return implode( ".", $source_parts ); - } - - /** - * Update contribution_tracking table - * - * @param array $data Form data - * @param bool $force If set to true, will ensure that contribution tracking is updated - */ - public function updateContributionTracking( &$data, $force = false ) { - // ony update contrib tracking if we're coming from a single-step landing page - // which we know with cc# in utm_source or if force=true or if contribution_tracking_id is not set - if ( !$force && - !preg_match( "/cc[0-9]/", $data[ 'utm_source' ] ) && - is_numeric( $data[ 'contribution_tracking_id' ] ) ) { - return; - } - - - // determine opt-out settings - $optout = self::determineOptOut( $data ); - - $db = payflowGatewayConnection(); - - if ( !$db ) { return true ; } - - $tracked_contribution = array( - 'note' => $data['comment'], - 'referrer' => $data['referrer'], - 'anonymous' => $optout[ 'anonymous' ], - 'utm_source' => $data['utm_source'], - 'utm_medium' => $data['utm_medium'], - 'utm_campaign' => $data['utm_campaign'], - 'owa_session' => $data['owa_session'], - 'owa_ref' => $data['owa_ref'], - 'optout' => $optout[ 'optout' ], - 'language' => $data['language'], - ); - - // Make all empty strings NULL - foreach ( $tracked_contribution as $key => $value ) { - if ( $value === '' ) { - $tracked_contribution[$key] = null; - } - } - - // if contrib tracking id is not already set, we need to insert the data, otherwise update - if ( !$data[ 'contribution_tracking_id' ] ) { - $data[ 'contribution_tracking_id' ] = $this->insertContributionTracking( $tracked_contribution ); - } else { - $db->update( 'contribution_tracking', $tracked_contribution, array( 'id' => $data[ 'contribution_tracking_id' ] ) ); - } - } - - /** - * Handle redirection of form content to PayPal - * - * @fixme If we can update contrib tracking table in ContributionTracking - * extension, we can probably get rid of this method and just submit the form - * directly to the paypal URL, and have all processing handled by ContributionTracking - * This would make this a lot less hack-ish - */ - public function paypalRedirect( &$data ) { - global $wgPayflowGatewayPaypalURL, $wgOut; - - // if we don't have a URL enabled throw a graceful error to the user - if ( !strlen( $wgPayflowGatewayPaypalURL ) ) { - $this->errors['general'][ 'nopaypal' ] = wfMsg( 'payflow_gateway-error-msg-nopaypal' ); - return; - } - - // update the utm source to set the payment instrument to pp rather than cc - $utm_source_parts = explode( ".", $data[ 'utm_source' ] ); - $utm_source_parts[2] = 'pp'; - $data[ 'utm_source' ] = implode( ".", $utm_source_parts ); - $data[ 'gateway' ] = 'paypal'; - $data[ 'currency_code' ] = $data[ 'currency' ]; - /** - * update contribution tracking - */ - $this->updateContributionTracking( $data, true ); - - $wgPayflowGatewayPaypalURL .= "/" . $data[ 'language' ] . "?gateway=paypal"; - - // submit the data to the paypal redirect URL - $wgOut->redirect( $wgPayflowGatewayPaypalURL . '&' . http_build_query( $data ) ); - } - - public static function log( $msg, $identifier='payflowpro_gateway', $log_level=LOG_INFO ) { - global $wgPayflowGatewayUseSyslog; - - // if we're not using the syslog facility, use wfDebugLog - if ( !$wgPayflowGatewayUseSyslog ) { - wfDebugLog( $identifier, $msg ); - return; - } - - // otherwise, use syslogging - openlog( $identifier, LOG_ODELAY, LOG_SYSLOG ); - syslog( $log_level, $msg ); - closelog(); - } - - /** - * Convert an amount for a particular currency to an amount in USD - * - * This is grosley rudimentary and likely wildly inaccurate. - * This mimicks the hard-coded values used by the WMF to convert currencies - * for validatoin on the front-end on the first step landing pages of their - * donation process - the idea being that we can get a close approximation - * of converted currencies to ensure that contributors are not going above - * or below the price ceiling/floor, even if they are using a non-US currency. - * - * In reality, this probably ought to use some sort of webservice to get real-time - * conversion rates. - * - * @param $currency_code - * @param $amount - * @return unknown_type - */ - public function convert_to_usd( $currency_code, $amount ) { - switch ( strtoupper( $currency_code ) ) { - case 'USD': - $usd_amount = $amount / 1; - break; - case 'GBP': - $usd_amount = $amount / 1; - break; - case 'EUR': - $usd_amount = $amount / 1; - break; - case 'AUD': - $usd_amount = $amount / 2; - break; - case 'CAD': - $usd_amount = $amount / 1; - break; - case 'CHF': - $usd_amount = $amount / 1; - break; - case 'CZK': - $usd_amount = $amount / 20; - break; - case 'DKK': - $usd_amount = $amount / 5; - break; - case 'HKD': - $usd_amount = $amount / 10; - break; - case 'HUF': - $usd_amount = $amount / 200; - break; - case 'JPY': - $usd_amount = $amount / 100; - break; - case 'NZD': - $usd_amount = $amount / 2; - break; - case 'NOK': - $usd_amount = $amount / 10; - break; - case 'PLN': - $usd_amount = $amount / 5; - break; - case 'SGD': - $usd_amount = $amount / 2; - break; - case 'SEK': - $usd_amount = $amount / 10; - break; - case 'ILS': - $usd_amount = $amount / 5; - break; - default: - $usd_amount = $amount; - break; - } - - return $usd_amount; - } -} // end class +// end class _______________________________________________ MediaWiki-CVS mailing list MediaWiki-CVS@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs