Bruce Gust
asked on
How does this login script produce the view that you see after everything is validated (Part II)?
Here's the login page:
...and here's the code behind it:
The login script begins on line 40. The "getbyCode" SQL looks like this:
So, it seems you're getting all of the data you need to validate the user from that SQL.
It's all routed to a specific domain based on lines 111 and 112, yet on line 126 you've got the $this->load->view('patient portal/sub domaingate ) thing and that code looks like this:
So, the page is posting all of its data back to itself, but where does it go and how am I getting this page:
How am I getting there?
Thanks!
...and here's the code behind it:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class PatientPortal extends MY_Controller {
private $debug = false; //Setting this to true will trigger a krumo of the session in a small console window at the bottom of your screen.
private $payment_threshold = 30; // The minimum payment we will take through the portal
public function index(){
//$this->pay();
//$this->login();
}
/** Login Function ----------------------------------------------------------------------------------------------------------
*
*
* This endpoint serves the login form of the patient portal.
*
| In the case that there is no session variable 'guarantor_session' set (and we're not accessing via an admin subdomain),
| this becomes the root endpoint
*
* Posting in all of these variables will treat this as a login attempt and will try to validate the data:
* pin - The Statement Pin / Accountid associated with an account.
* lastfour - The last four digits of the guarantor OR patient associated with the account or with a charge
* year - The birth year of the guarantor OR patient associated with the account or with a charge
*
* Sending in a pin variable in your access assumes that you've clicked this link from an email and will autopopulate and lock the statement pin field.
*
*
-----------------------------------------------------------------------------------------------------------------------------*/
public function login($pin = ''){
//Start with client colors
$h = explode('.',$_SERVER['HTTP_HOST']);
$clientcode = $h[0];
$client = Clients::getByCode($clientcode);
$clientid = '-1';
session_start();
if($client) $clientid = $client['ClientID'];
$_SESSION['colors'] = ClientPreferences::getClientColors($clientid);
//$this->setSessionData($accountid);
//if(isset($_SESSION['guarantor_session'])) redirect('/patientportal/pay'); //On the offchance this is accessed directly with a session, reroute to pay.
$lockpin = (isset($_REQUEST['lockpin']) && $_REQUEST['lockpin'] === "0")?false:($pin?true:false);
$pin = $pin?$pin:(isset($_REQUEST['pin'])? $_REQUEST['pin'] : false);
$lastfour = isset($_REQUEST['lastfour'])? $_REQUEST['lastfour'] : false;
$year = isset($_REQUEST['year'])? $_REQUEST['year'] : false;
$redirect = isset($_REQUEST['uri'])?$_REQUEST['uri']:'/';
$data = array(
'lockpin' => $lockpin,
'pin' => $pin,
'lastfour' => $lastfour,
'year' => $year,
'redirect' => $redirect,
'debug' => $this->debug,
'new_statement' => ClientPreferences::getClientPreference($clientid, 'NewStatements')
);
if($pin && $lastfour && $year){
//If these variables are all set, assume validation attempt.
//1 -> get accountid from pin
if(strpos($pin,'T') !==false || strpos($pin,'t') !==false){
$statementid = ltrim($pin,"Tt");
$_SESSION['statementid'] = $statementid;
$accountid = Accounts::getAccountidFromStatementid($statementid);
}else{
$accountid = $pin;
}
//2 -> use accountid to get accountdetails
$accounts = Accounts::getAccountInfoFromPin($accountid);
//krumo($accounts); exit();
$ssns = array();
$dobs = array();
if($accounts)foreach($accounts as $account){
//NOTE the getAccountInfoFromPin function returns 3 records, trying to coalesce
//the information from Patient, Claim, and txn tables.
//I'm organizing the data here so I can run an in_array on the fields.
if($account['SSN'] && !in_array($account['SSN'], $ssns)) array_push($ssns, $account['SSN']);
if($account['PatientDOB'] && !in_array($account['PatientDOB'], $dobs)) array_push($dobs, substr($account['PatientDOB'], 0,4));
}
//$details = Accounts::getDetails($accountid); <-- Might not need this
//3 -> Check to see if account CAN be verified
if($accounts && count($ssns) && count($dobs)){
//4 -> Check to make sure our data = the provided data
if(in_array($lastfour, $ssns) && in_array($year, $dobs)){
//Validated!!!
//Guarantor Session
$this->setSessionData($accountid);
$data['clientcode'] = $_SESSION['guarantor_session']['clientcode'];
//Are we on the right domain?
$host = explode('.',$_SERVER['SERVER_NAME']);
$sub = $host[0];
if(strtolower($sub) == strtolower($data['clientcode']) || base_url() == 'https://dig.local/' || base_url() == 'https://localhost:8080/' || base_url() == 'https://digdev.patientfocus.com/' || base_url() == 'https://dig2.patientfocus.com/' || base_url() == 'https://staging.patientfocus.com/'){
//This is the correct domain.
redirect($redirect);
} else {
//Destroy existing session.
unset($_SESSION['guarantor_session']);
unset($_SESSION['userdata']);
unset($_SESSION['roles']);
unset($_SESSION['rights']);
unset($_SESSION['colors']);
unset($_SESSION['statementid']);
session_destroy();
//Post into the correct subdomain.
$this->load->view('patientportal/subdomaingate',$data);
}
//exit();
}
} else if($accounts) {
//Account cannot be verified, Give a specific call to action.
$data['errorheader'] = 'Account Incomplete';
$data['errormessage'] = 'There is not enough information in your account to verify your identity through the online portal. If you would like to access your account online, please <a class="cantlogin">let us know</a>, and one of our Patient Account Representatives will help you complete your account.';
}
}
if($client){
$data['clientcode'] = $clientcode;
$data['clientname'] = $client['ClientName'];
}
session_write_close();
$this->load->view('patientportal/login', $data);
}
/** Pay Function ----------------------------------------------------------------------------------------------------------
*
*
* This endpoint serves the ad hoc payment form of the patient portal.
*
| In the case that there IS a session variable 'guarantor_session' set (and we're not accessing via an admin subdomain),
| this becomes the root endpoint
*
* Posting in all of these variables will treat this as a payment attempt and will try to process the data:
*
REQUIRED (for any form processing)
*
* paymentamount - This is either a dollar amount or a 'specialamount' flag, which then looks to the 'specialamount' manually entered field for the payment amount.
* specialamount - Should paymentamount = the string 'specialamount', this process pulls the value from the specialamount variable.
* email - the email address of the user, required for sending a receipt.
* cardorbank - Set to the string 'card' if this is a credit card process. Set to 'bank' if it's an echeck
*
REQUIRED FOR CREDIT CARD (if cardorbank = 'card')
*
* cardnum - The number on the front of your card. IF THIS BEGINS WITH A * then we process the payment off the stored BRIC if it is available
* cvcode - The 3-4 digit security code on the back of the card.
* cardname - The name on the front of the card
* cardmonth - the two digit card expiration month
*
REQUIRED FOR E CHECK (if cardorbank = 'bank')
*
* accountnum - the bank account number (in the case that this starts with *, use the associated bank BRIC.
* routingnumb - the routing number of the bank. If we're using a brick, Use the bric's associated routing number.
* accname - The name on the account.
*
*
ON FAILED FORM PROCESSING,
* Reload the form view, populated where you left off, and provide an error that states the problem clearly.
*
ON SUCCESS,
* Load the payment success view.
*
*
---------------------------------------------------------------------------------------------------------------------------*/
public function pay($refreshdata = true){
$data = $this->getData($refreshdata);
if(isset($_REQUEST['paymentamount'])){
//Process the payment
$success = false;
$amount = $_REQUEST['paymentamount'] == 'specialamount' ? str_replace('$', '', $_REQUEST['specialamount']) : $_REQUEST['paymentamount'];
$amount = number_format($amount,2,'.','');
$email = isset($_REQUEST['email'])? $_REQUEST['email'] : false;
$createdby = $_SESSION['userdata']['id'];
if($amount >= $data['minpayment']){
if($amount <= $data['lefttopay']){
if($_REQUEST['cardorbank'] == "card"){
$cardinfo = $data['cardinfo'];
//Process as Credit Card
$cardnum = isset($_REQUEST['cardnum'])? $_REQUEST['cardnum'] : false; //ACCOUNT FOR ************XXXX STYLE ENTRIES - IMPLYING TO USE THE CARD ON FILE.
$bric = substr($cardnum,0,1) == '*' && $cardinfo?$cardinfo['bric']:false;
$cv = isset($_REQUEST['cvcode'])? $_REQUEST['cvcode'] : false;
$cardname = $bric?$cardinfo['cardname']:(isset($_REQUEST['name'])? $_REQUEST['name'] : false);
$cardmonth = isset($_REQUEST['cardmonth'])? $_REQUEST['cardmonth'] : false;
$cardyear = isset($_REQUEST['cardyear'])? $_REQUEST['cardyear'] : false;
$cardexp = $this->getEPXExpirationString($cardmonth, $cardyear);
//Ensure that the pieces are all available.
if( ($bric || $cardnum) && $cv && $amount && $cardname && $cardexp && $data['accountid'] && $email){
if(!$bric || ($cardexp == $cardinfo['cardexp'] && $cv == $cardinfo['cardcv'])){
if(filter_var($email,FILTER_VALIDATE_EMAIL)){
if(strtolower($email) != strtolower($data['email'])) Accounts::update($data['accountid'], array(array('field'=>'email','newvalue'=>$email)));
//PROCESS THE EPX TRANSACTION AND TURN THE RESULTING XML INTO AN ARRAY
$xmlstr = '';
if($bric){
//PROCESS FROM BRIC
$xmlstr = $this->epx->processCreditCardBRIC($bric, $cv, $amount, $cardname, $cardexp,array('AccountID'=>$data['accountid']),$data['dbanbr']);
} else {
//PROCESS FROM CARD NUMBER
$xmlstr = $this->epx->processCreditCard($cardnum, $cv, $amount, $cardname, $cardexp,array('AccountID'=>$data['accountid']),$data['dbanbr'],$data['address']);
}
$xmlobj = simplexml_load_string($xmlstr);
$xmlarr = array();
if($xmlobj) foreach($xmlobj->FIELDS->FIELD as $field){
$xmlarr[(string)$field->attributes()->KEY] = (string)$field;
}
if(isset($xmlarr['AUTH_RESP']) && $xmlarr['AUTH_RESP'] == '00'){
/**
Successfully paid on card
**/
//This is for the paymentmethodcode.
$this->load->library('CreditCard');
$card = $bric?array('abbr'=>$cardinfo['cardabbr'],'name' => 'card'):$this->creditcard->cardNameFromNumber($cardnum);
$cardfour = substr($cardnum, strlen($cardnum)-4);
$description = 'Credit card ending in ' . $cardfour;
//Create the paymentpending entry.
$paymentpending = PaymentPendings::addPaymentPending(date('Y-m-d H:i:s'), $xmlarr['AMOUNT'],$card['abbr'], $data['accountid'], $data['patientid'], 'PR', 'PT', $createdby, $xmlarr['AUTH_GUID'], $xmlarr['AUTH_CODE'],$xmlarr['AUTH_RESP'],$description);
if($paymentpending){
$success = true;
$data['successcardtype'] = $card['name'];
$data['successemail'] = $email;
$data['successcardlastfour'] = $cardfour;
$data['confirmation'] = $paymentpending['PaymentPendingID'];
}
} else {
//Error -> epx response
$data['errorcode'] = isset($xmlarr['AUTH_RESP'])? $xmlarr['AUTH_RESP'] : 'PF4';
$data['errormsg'] = isset($xmlarr['AUTH_RESP_TEXT'])?ucfirst($xmlarr['AUTH_RESP_TEXT']):'No response from EPX.';
}
} else {
$data['errorcode'] = 'PF8';
$data['errormsg'] = 'Email invalid.';
}
} else {
//Error -> CARDEXP DOESN'T MATCH ONE ON FILE OR CCV DOESN'T MATCH
$data['errorcode'] = 'PF5';
$data['errormsg'] = 'Card validation mismatch.';
}
} else {
//Error Incomplete Form.
$data['errorcode'] = 'PF3';
$data['errormsg'] = 'Payment form incomplete.';
}
//$this->epx->processCreditcard();
} else if ($_REQUEST['cardorbank'] == "bank"){
$bankinfo = $data['bankinfo'];
//Process as E Check
$accnum = isset($_REQUEST['accountnum'])? $_REQUEST['accountnum'] : false; //ACCOUNT FOR ************XXXX STYLE ENTRIES - IMPLYING TO USE THE CARD ON FILE.
$bric = substr($accnum,0,1) == '*' && $bankinfo?$bankinfo['bric']:false;
$name = $bric?$bankinfo['accname']:(isset($_REQUEST['account_name'])? $_REQUEST['account_name'] : false);
$routing = $bric?$bankinfo['accrouting']:(isset($_REQUEST['routingnumb'])? $_REQUEST['routingnumb'] : false);
//$bankname = $bric?$bankinfo['accbankname']:(isset($_REQUEST['cardmonth'])? $_REQUEST['cardmonth'] : false);
//Ensure that the pieces are all available.
if(($bric || $accnum) && $routing && $amount && $name && $data['accountid'] && $email){
if(filter_var($email,FILTER_VALIDATE_EMAIL)){
if(strtolower($email) != strtolower($data['email'])) Accounts::update($data['accountid'], array(array('field'=>'email','newvalue'=>$email)));
//PROCESS THE EPX TRANSACTION AND TURN THE RESULTING XML INTO AN ARRAY
$xmlstr = '';
if($bric){
//PROCESS FROM BRIC
$xmlstr = $this->epx->processCheckBRIC($bric, $routing, $amount, $name, array('AccountID'=>$data['accountid']),$data['dbanbr']);
} else {
//PROCESS FROM ACC NUMBER
$xmlstr = $this->epx->processCheck($accnum, $routing, $amount, $name, array('AccountID'=>$data['accountid']),$data['dbanbr'],$data['address']);
}
$xmlobj = simplexml_load_string($xmlstr);
$xmlarr = array();
if($xmlobj) foreach($xmlobj->FIELDS->FIELD as $field){
$xmlarr[(string)$field->attributes()->KEY] = (string)$field;
}
if(isset($xmlarr['AUTH_RESP']) && $xmlarr['AUTH_RESP'] == '00'){
/**
Successfully paid from bank
**/
$description = 'Bank account ending in ' . substr($accnum,-4);
//Create the paymentpending entry.
$paymentpending = PaymentPendings::addPaymentPending(date('Y-m-d H:i:s'), $xmlarr['AMOUNT'],'EF', $data['accountid'], $data['patientid'], 'PR', 'PT', $createdby, $xmlarr['AUTH_GUID'], $xmlarr['AUTH_CODE'],$xmlarr['AUTH_RESP'], $description);
if($paymentpending){
$success = true;
$data['successcardtype'] = 'bank account';
$data['successemail'] = $email;
$data['successcardlastfour'] = substr($accnum,-4);
$data['confirmation'] = $paymentpending['PaymentPendingID'];
}
} else {
//Error -> epx response
$data['errorcode'] = isset($xmlarr['AUTH_RESP'])? $xmlarr['AUTH_RESP'] : 'PF4';
$data['errormsg'] = isset($xmlarr['AUTH_RESP_TEXT'])?ucfirst($xmlarr['AUTH_RESP_TEXT']):'No response from EPX.';
}
} else {
$data['errorcode'] = 'PF8';
$data['errormsg'] = 'Email invalid.';
}
} else {
//Error Incomplete Form.
$data['errorcode'] = 'PF3';
$data['errormsg'] = 'Payment form incomplete.';
}
} else {
//Error as unrecognized type
$data['errorcode'] = 'PF1';
$data['errormsg'] = 'Could not process transaction type';
}
} else {
//Error as Amount above Maximum
$data['errorcode'] = 'PF2';
$data['errormsg'] = 'Can\'t pay more than you owe.';
}
} else {
//Error as Amount Below Minimum
$data['errorcode'] = 'PF2';
$data['errormsg'] = 'Amount below the minimum ' . $data['minpaymentformatted'] . ' payment.';
}
if($success){
//On Success load the next view.
$data['successamount'] = $amount;
$data = array_merge($data, $this->getData());
$this->load->library('Mandrill');
$message['html'] = $this->load->view('email/receipt',$data,true);
$message['text'] = "Thank you! Your payment of \$$amount was successfully charged to your $data[successcardtype] ending in $data[successcardlastfour]. Your confirmation number is $data[confirmation].";
$message['subject'] = "Your payment has been received!";
$message['from_email'] = 'noreply@patientfocus.com';
$message['from_name'] = $data['clientname'];
$message['to'][0] = array('email' => $email);
$mandrill_response = $this->mandrill->sendemail($message);
if($mandrill_response){
$e['templateid'] = 0;
$e['content'] = pfsql::scrub($message['html']);
$e['email'] = pfsql::scrub($email);
$e['systememail'] = $message['from_email'];
$e['response'] = pfsql::scrub(json_encode($mandrill_response));
$e['direction'] = 'outbound';
$e['accountid'] = $data['accountid'];
$data['receiptemailed'] = true;
$f = Emails::create($e);
} else $data['receiptemailed'] = false;
//echo json_encode($mandrill_response); exit();
$data['view'] = 'paymentsuccess';
} else {
//On Fail load same view.
$data['view'] = 'makeapayment';
}
} else if($data['lefttopay'] <= 0){
$data['view'] = 'creditbalance';
}else{
$data['view'] = 'makeapayment';
}
$this->loadView($data);
}
protected function getEPXExpirationString($month, $year) {
$exp = false;
//Month should be 2 digits.
if($month && $year) {
$month = str_pad($month, 2, '00', STR_PAD_LEFT);
$year = substr($year, -2);
if (strlen($year) === 2 && strlen($month) === 2) {
$exp = $year . $month;
}
}
return $exp;
}
/** PaymentPlan ---------------------------------------------------------------------------------------------------------------------------
*
*
* This function handles Payment Plans
*
*
*
--------------------------------------------------------------------------------------------------------------------------- */
public function paymentplan(){
$data = $this->getData();
if(!$data['paymentplan'] && $data['lefttopay'] < 90){
//This is only accessible if you have an active payment plan OR if you have a balance >= 90 dollars.
//redirect home
header ("location: /");
}else{
//are we processing a form?
if(isset($_REQUEST['cardorbank'])){ //we are processing a form.
//Declare Needed Vars
$payment_amount; //NUMBER - The amount of the monthly payment. Must be greater or equal to the minimum payment.
$is_new_plan; //BOOL - If there is an existing paymentplan, this is false. Otherwise this is true.
$is_autodraft; //BOOL - If this is true we are autodrafting.
$autodraft_type; //STRING - either "card" or "bank"
$bank = array(); //ARRAY
$bank['name'] = null; //STRING - The name on the account
$bank['accountnum'] = null; //NUMBER - The account number
$bank['acclastfour']= null; //NUMBER - The last four of the account number
$bank['routingnum'] = null; //NUMBER - The routing number
$card = array(); //ARRAY
$card['name'] = null; //STRING - The name on the card
$card['number'] = null; //NUMBER - The number on the front of the card
$card['lastfour'] = null; //NUMBER - The last four of the card number
$card['exp'] = null; //NUMBER - The expiration date in YYMM format
$card['cv'] = null; //NUMBER - The three or four digit security code on the back of the card
$bric; //STRING - The AUTH GUID provided by EPX
$plan_date; //NUMBER - Between 1 and 28 (inclusive), the date the payment is owed or autodrafted
$email; //STRING - User submitted email
$plan_starting_balance; //NUMBER - The balance at the start of this particular payment plan. In the case that there is an
$number_of_payments; //NUMBER - The number of payments there should be.
$payment_method_code; //STRING - EF for check, PP for promise to pay, Card specific versions for card autodrafts.
$pmtdetails = array(); //ARRAY - Formatted for payment plan creation function.
//Collect Data and validate form.
try {
$is_new_plan = $data['paymentplan'] === false ?
true
: false;
$payment_amount = $is_new_plan ?
$_REQUEST['paymentplanamount']
: $data['paymentplan']['paymentamount'];
if($payment_amount < $data['minpayment'])
throw new Exception('Payment Plan amount below minimum '. $data['minpaymentformatted'] . ' payment amount.',1);
$plan_starting_balance = $is_new_plan ?
$data['lefttopay']
: $data['paymentplan']['startingbalance'];
$number_of_payments = ceil( $data['lefttopay'] / floatval($payment_amount) );
$pmtdetails['NbrOfPayments'] = $number_of_payments;
$is_autodraft = isset($_REQUEST['autodraft']) ?
true
: false;
$plan_date = $is_autodraft ?
$_REQUEST['autodraftdate']
: (
$is_new_plan ? // If it IS NOT AN AUTODRAFT and it IS A NEW PLAN, set it to today, otherwise if it IS NOT A NEW PLAN, set it to the stored value.
(
intval(Date('d')) > 28? 1 : Date('d')
)
: $data['paymentplan']['duedate']
);
if(intval($plan_date) > 28 or intval($plan_date) < 1)
throw new Exception('Plan date out of range', 2);
if($is_autodraft){
if(isset($_REQUEST['email'])){
$email = $_REQUEST['email'];
} else throw new Exception('Incomplete Form', 4);
if(filter_var($email,FILTER_VALIDATE_EMAIL)) Accounts::update($data['accountid'], array(array('field'=>'email','newvalue'=>$email)));
else throw new Exception('Email Invalid', 8);
}
if($is_autodraft){
switch($_REQUEST['cardorbank']){
case 'card':
$autodraft_type = 'card';
//collect card values
if(substr($_REQUEST['cardnum'],0,1) == '*' && $data['cardinfo']){
//assume stored number
$cardinfo = $data['cardinfo'];
$card['name'] = $cardinfo['cardname'];
$card['lastfour'] = $cardinfo['cardlastfour'];
$card['exp'] = $cardinfo['exp'];
$card['cv'] = $cardinfo['cardcv'];
$bric = $cardinfo['bric'];
$payment_method_code = $cardinfo['cardabbr'];
} else {
//Check credit card entries
if(
isset($_REQUEST['name']) && $_REQUEST['name']
&& isset($_REQUEST['cardnum']) && $_REQUEST['cardnum']
&& isset($_REQUEST['cardmonth'])&& $_REQUEST['cardmonth']
&& isset($_REQUEST['cardyear']) && $_REQUEST['cardyear']
&& isset($_REQUEST['cvcode']) && $_REQUEST['cvcode']
) {
//use provided number, preauth required.
$card['name'] = $_REQUEST['name'];
$card['number'] = $_REQUEST['cardnum'];
$card['lastfour'] = substr($card['number'],strlen($card['number'])-4);
$card['exp'] = substr($_REQUEST['cardyear'],2,2) . $_REQUEST['cardmonth'];
$card['cv'] = $_REQUEST['cvcode'];
$this->load->library('CreditCard');
$exp = $this->parseEPXResponse($this->epx->preAuthCreditCard($card['number'],$card['cv'],'.01',$card['name'],$card['exp'],$data['accountid'],$data['dbanbr'],$data['address']));
if(isset($exp['AUTH_RESP']) && $exp['AUTH_RESP'] == '00'){
//SUCCESSFULLY PREAUTHED!
$extra = $this->creditcard->cardNameFromNumber($card['number']);
$payment_method_code = $extra['abbr'];
$bric = $exp['AUTH_GUID'];
} else {
throw new Exception (
isset($exp['AUTH_RESP_TEXT'])?ucfirst($exp['AUTH_RESP_TEXT']).'.':'Communication error, unable to preauthorize your card.',
isset($exp['AUTH_RESP'])?intval('90'.$exp['AUTH_RESP']):5
);
}
} else throw new Exception('Incomplete Form', 4);
}
//pmtdetails object.
$pmtdetails['ccname'] = pfsql::scrub($card['name']);
$pmtdetails['cclast4']= pfsql::scrub($card['lastfour']);
$pmtdetails['ccv2'] = pfsql::scrub($card['cv']);
$pmtdetails['ccyear'] = pfsql::scrub(substr($card['exp'],0,2));
$pmtdetails['ccmonth']= pfsql::scrub(substr($card['exp'],2));
break;
case 'bank':
$autodraft_type = 'bank';
$payment_method_code = 'EF';
//collect bank values
if(substr($_REQUEST['accountnum'],0,1) == '*'){
//assume stored number
$bankinfo = $data['bankinfo'];
$bank['name'] = $bankinfo['accname'];
$bank['acclastfour']= $bankinfo['acclastfour'];
$bank['routingnum'] = $bankinfo['accroutingnum'];
$bank['bank'] = $bankinfo['accbankname'];
$bric = $bankinfo['bric'];
} else {
//assume provided number, preauth required.
if(
isset($_REQUEST['account_name']) && $_REQUEST['account_name']
&& isset($_REQUEST['routingnumb']) && $_REQUEST['routingnumb']
&& isset($_REQUEST['accountnum']) && $_REQUEST['accountnum']
) {
//use provided number, preauth required.
$bank['name'] = $_REQUEST['account_name'];
$bank['accountnum'] = $_REQUEST['accountnum'];
$bank['acclastfour']= substr($bank['accountnum'],strlen($bank['accountnum'])-4);
$bank['routingnum'] = $_REQUEST['routingnumb'];
$bank['bank'] = '';
$exp = $this->parseEPXResponse($this->epx->preAuthCheck($bank['accountnum'],$bank['routingnum'],'.01',$bank['name'],$data['accountid'],$data['dbanbr'],$data['address']));
if(isset($exp['AUTH_RESP']) && $exp['AUTH_RESP'] == '00'){
//SUCCESSFULLY PREAUTHED!
$bric = $exp['AUTH_GUID'];
} else {
throw new Exception (
isset($exp['AUTH_RESP_TEXT'])?ucfirst($exp['AUTH_RESP_TEXT']).'.':'Communication error, unable to preauthorize your card.',
isset($exp['AUTH_RESP'])?intval('90'.$exp['AUTH_RESP']):5
);
}
} else throw new Exception('Incomplete Form', 4);
}
//pmtdetails object.
$pmtdetails['BankName'] = pfsql::scrub($bank['bank']);
$pmtdetails['accountname'] = pfsql::scrub($bank['name']);
$pmtdetails['accountlast4'] = pfsql::scrub($bank['acclastfour']);
$pmtdetails['routingnum'] = pfsql::scrub($bank['routingnum']);
break;
default:
throw new Exception('Autodraft source unrecognized.', 3);
break;
}
} else {
$payment_method_code = 'PP';
$bric = false;
}
//Create paymentplan.
if(PaymentPlans::endAllPlansForPatient($data['patientid'])){
} else throw new Exception('Failed closing old payment plan.',6);
$createdby = $_SESSION['userdata']['id'];
if(PaymentPlans::create($data['patientid'], $payment_method_code, $pmtdetails, $bric, $plan_starting_balance, $createdby, $plan_date, $payment_amount)){
} else throw new Exception('Failed starting new payment plan.',7);
//refresh data.
$data = $this->getData();
} catch (Exception $e) {
$code = $e->getCode();
$data['errorcode'] = $code >= 90 ? substr($code, 2):'PF'.$code;
$data['errormsg'] = $e->getMessage();
}
}
//IS THERE AN EXISTING PAYMENTPLAN?
if($data['paymentplan']){
//YES
$data['view'] = 'editpaymentplan';
} else {
//NO
$data['view'] = 'setuppaymentplan';
}
//Load the view
$this->loadView($data);
}
}
/** Details---------------------------------------------------------------------------------------------------------------------------
*
*
* This is a read only view, that lets you see your existing account activity. That's all.
*
*
--------------------------------------------------------------------------------------------------------------------------- */
public function details(){
$data = $this->getData();
$data['details'] = Accounts::patientPortalBalanceDetails($data['accountid']);
//krumo($data['details']); exit();
$data['view'] = 'balancedetails';
$this->loadView($data);
}
/** Statements ---------------------------------------------------------------------------------------------------------------------------
*
*
* This is a read only view, that lets you access your existing statements.
*
*
*
--------------------------------------------------------------------------------------------------------------------------- */
public function statements(){
$data = $this->getData();
$data['statements'] = Statements::getStatements($data['accountid'],'S', false, true);
$data['view'] = 'viewstatements';
$this->loadView($data);
}
/** Statement ---------------------------------------------------------------------------------------------------------------------------
*
*
* NOTE:: THIS IS ROUTED TO THE /statements/XXXXXXX URI,
* This endpoint, when accessed with a statementid, compares the accountid on the statement entry
* with the accountid in the current session, and should they match and the file exists, serves the pdf to the browser.
*
*
*
--------------------------------------------------------------------------------------------------------------------------- */
public function statement($statementid){
$statement = Statements::getStatementByID($statementid);
//krumo($_SESSION);exit();
if($statement && $_SESSION['guarantor_session']['accountid'] == $statement['AccountID']){
$data['path'] = '/p/' . (ENVIRONMENT === "production" ? "prod" : "dev") . '/statements/'.$statement['ExportID'].'/'.$statement['AccountID'].'-'.$statement['StatementID'].'.pdf';
if(file_exists($data['path'])){
$this->load->view('olddig/viewstatement', $data);
} else if ($_SESSION['guarantor_session']['clientcode'] == 'demo') {
//Generate statement on-the-fly for demo accounts
$data2['accountid'] = $_SESSION['guarantor_session']['accountid'];
$data2['templateid'] = 4;
$_REQUEST = $data2;
$template = Templates::getTemplate($data2['templateid']);
$this->load->view($template['content'], $data);
} else {
show_404();
}
} else {
show_404();
}
}
/** Contact ---------------------------------------------------------------------------------------------------------------------------
*
*
* This is a read only view, it uses the practice the current session is associated with to
* deliver the TwilioPhone entry to the view.
*
*
--------------------------------------------------------------------------------------------------------------------------- */
public function contact(){
$data = $this->getData();
$data['view'] = 'contactus';
$this->loadView($data);
}
/**
---------------------------------------------------------------------------------------------------------------------------
THESE ARE PRIVATE (and therefore URL inaccessable) FUNCTIONS VVVVV
(...beyond here monsters be)
---------------------------------------------------------------------------------------------------------------------------
*/
/** Settings ---------------------------------------------------------------------------------------------------------------------------
*
*
* THIS ENDPOINT IS CURRENTLY INACTIVE
*
* The intention of this endpoint is to provide an interface for an account to manage their account preferences
* and contact information. Currently there is a data model change required to ensure there will be no loss
* of data by using this endpoint.
*
*
--------------------------------------------------------------------------------------------------------------------------- */
public function settings(){
//This will be public when:
//1 - The statement backend respects prefs while the account is active (Default behavior if a preference HAS NOT BEEN SET is to respect the CCE choice).
//2 - When we have a data structure in place that will ensure that Phone Numbers and Mailing Addresses cannot be deleted by the patient ( an email table and an address table ).
$data = $this->getData();
if(pf::post('action') === 'save') {
$results = array();
if(pf::post('email')) array_push($results,$this->saveEmail());
$data['results'] = $results;
}
$data['settings'] = Accounts::getGuarantorControllableValues($data['accountid']);
$data['view'] = 'settings';
$this->loadView($data);
}
protected function saveEmail(){
$email = pf::post('email');
$gogreen = pf::post('gogreen') ? 1 : 0;
$data = $this->getData();
if (filter_var($email,FILTER_VALIDATE_EMAIL)) {
if(strtolower($email) != strtolower($data['email'])) {
$result = Accounts::update($data['accountid'], array(array('field'=>'email','newvalue'=>$email)));
if($result) $email = $result[0]['email'];
else $email = false;
}
} else {
$email = false;
}
$success = false;
if( $email ) {
$success = true;
$data['settings'] = Accounts::getGuarantorControllableValues($data['accountid']);
if ($gogreen !== $data['settings']['gogreen']) {
$success = false;
//Make sure this person is pref E
$this->load->model('Communications');
if ($gogreen) {
if ($this->Communications->prefin($data['accountid'], 'E')) {
$success = true;
}
} else {
if ($this->Communications->prefout($data['accountid'], 'E')) {
$success = true;
}
}
}
}
return array('success'=>$success, 'reason' => $email ? ($success ? 'Everything updated successfully!' : 'There was an error saving your go green preference. Please try again later or <a href="/contact">call us</a> if you continue to experience problems.') : 'There was a problem processing your email... Please make sure you\'re providing a valid email address, and <a href="/contact">call us</a> if you continue to experience problems.');
}
/** loadView ---------------------------------------------------------------------------------------------------------------------------
*
*
* This processes the $data object that we build before loading our views.
* (Any nested views use the $_ci_vars object, which represents the same information, for consistant variables)
* It's basically a controller-specific template function.
*
*
--------------------------------------------------------------------------------------------------------------------------- */
private function loadView($data){
//utility patientportal view loader, takes care of templating.
$this->load->view('patientportal/include/header', $data);
$this->load->view('patientportal/'.$data['view'], $data);
$this->load->view('patientportal/include/footer');
}
/** getData ---------------------------------------------------------------------------------------------------------------------------
*
*
* This snags and returns the data from the session (and from any controller constants).
* If $refresh is set to true (default behavior)
* Then it uses the accountid in the session to refresh the data from the database.
*
*
--------------------------------------------------------------------------------------------------------------------------- */
private function getData($refresh = true){
//Retrieves the appropriate data from the session, formated nicely and ready to go.
$data;
if(isset($_SESSION['guarantor_session']) && $_SESSION['guarantor_session']){
//Refreshes the data
if($refresh) $this->setSessionData($_SESSION['guarantor_session']['accountid']);
$data = $_SESSION['guarantor_session'];
}
$data['view'] = '';
$data['debug'] = $this->debug;
return $data;
}
/** setSessionData ---------------------------------------------------------------------------------------------------------------------------
*
*
* This unsets any existing session, then reaches out to the database and creates a nicely formatted session object
* with all sorts of account information. This is usually called once per request.
*
*
--------------------------------------------------------------------------------------------------------------------------- */
private function setSessionData($accountid){
//Refreshes session data from the database.
//In the case that there is no session data, it creates the object.
//Call to the database
$sess = Accounts::getGuarantorSession($accountid);
if($sess){
//Check to see if this patient can use the patient portal first and foremost.
$can_login = ($sess['status1'] != 'RT' && ClientPreferences::getClientPreference($sess['clientid'], 'PatientsCanLoginToPatientPortal'));
if($can_login === true){
//Get Parameters for this client
$this->load->model('ClientParameters');
$params = $this->ClientParameters->read(array('name','value'), array('clientid'=>$sess['clientid'], 'voideddate'=>null));
$sess['parameters'] = array();
if($params) foreach($params as $p){
$sess['parameters'][$p['name']][] = $p['value'];
}
$sess['preferences'] = array();
$sess['preferences']['DisplayCreditBalancesInPatientPortal'] = $this->ClientPreferences->getClientPreference($sess['clientid'], 'DisplayCreditBalancesInPatientPortal');
//Additional fields not needed from the database
if($sess['preferences']['DisplayCreditBalancesInPatientPortal'] || $sess['lefttopay'] >= 0){
$sess['lefttopayformatted'] = pfmoney::toFromCents($sess['lefttopay']);
} else {
$sess['lefttopayformatted'] = pfmoney::toFromCents(0);
}
//Sets what the "Default" make-a-payment amount is. If there is no payment plan active, it defaults to the full amount
$sess['defaultpayment'] = min((($sess['paymentplan']&&$sess['paymentplan']['paymentamount'])?$sess['paymentplan']['paymentamount']:$sess['lefttopay']),$sess['lefttopay']);
//payment_threshold is the minimum payment we will accept through the portal (assuming the patient owes more than the threshold)
$sess['paythresh'] = $this->payment_threshold;
$sess['paythreshformatted'] = pfmoney::toFromCents($this->payment_threshold);
//Is left to pay representative of the full amount owed?
if($sess['lefttopay'] != $sess['totalbalance']){
$sess['balancesmatch'] = false;
} else {
$sess['balancesmatch'] = true;
}
//Minimum payment
$sess['minpayment'] = ($sess['lefttopay'] < $this->payment_threshold ? $sess['lefttopay'] : $this->payment_threshold);
//If the default payment is less than this ^^ set it to the default payment instead.
if($sess['defaultpayment'] < $sess['minpayment']) $sess['defaultpayment'] = $sess['minpayment'];
//Minimum payment formatted
$sess['minpaymentformatted'] = pfmoney::toFromCents($sess['minpayment']);
//Color Scheme
$sess['colors'] = ClientPreferences::getClientColors($sess['clientid']);
//Unset any potentially remaining patient session.
//Might be a bit heavy handed... May want to account for spoofs.
if(isset($_SESSION['guarantor_session'])){
unset($_SESSION['guarantor_session']);
unset($_SESSION['userdata']);
unset($_SESSION['rights']);
unset($_SESSION['roles']);
//unset($_SESSION['colors']);
//unset($_SESSION['statementid']);
}
$_SESSION['guarantor_session'] = $sess;
//Userdata object
$ud = Users::getUserByEmail('patient@patientfocus.com');
$_SESSION['userdata'] = $ud;
$_SESSION['userdata']['accountid'] = $sess['accountid'];
//Roles n rights
$_SESSION['roles'][]= array('id'=>'14', 'name' => 'Patient');
$_SESSION['rights'] = Rights::getRightsForRolesArray($_SESSION['roles']);
} else {
//Destroy session
unset($_SESSION['guarantor_session']);
unset($_SESSION['userdata']);
unset($_SESSION['roles']);
unset($_SESSION['rights']);
unset($_SESSION['colors']);
unset($_SESSION['statementid']);
session_destroy();
//Get forwarding Message
$data = array();
$data['forwarding_message'] = $this->Clients->getForwardingMessageForPortal($sess['clientid']);
//Show forwarding Message and break the current flow.
echo $this->load->view('patientportal/forwardingmessage',$data, true);
//NOTE:: Echoing this here ensures that this doesn't enter the CI output buffer
// and writes straight to the output, so we can exit on the next line.
exit();
}
}
}
/** parseEPXResponse ---------------------------------------------------------------------------------------------------------------------------
*
*
* Converts an xml response string from EPX to an associated array.
*
*
--------------------------------------------------------------------------------------------------------------------------- */
private function parseEPXResponse($xmlstr){
$xmlobj = simplexml_load_string($xmlstr);
$xmlarr = array();
if($xmlobj) foreach($xmlobj->FIELDS->FIELD as $field){
$xmlarr[(string)$field->attributes()->KEY] = (string)$field;
}
return $xmlarr;
}
}
?>
The login script begins on line 40. The "getbyCode" SQL looks like this:
select
client.ClientID,
client.ClientName,
client.ClientTetrisId,
client.ClientPrimaryContact,
client.ClientBankAcctNumber,
client.ClientBankRoutingNumber,
client.ClientStatusCode,
client.Specialty,
client.Address,
client.PhoneNumber,
client.Email,
client.PaymentProcessingDBANbr,
client.CreatedDate,
client.LastModifiedDate,
client.ModifiedBy,
client.Address1,
client.Address2,
client.City,
client.State,
client.Zip,
client.clientcode,
cast(dbo.ufn_ClientPreference(clientid,'WithholdFeesOnInvoice') as bit) withhold,
client.phrase,
client.type,
client.ClientNotes,
client.SitePaymentFee
from client where clienttetrisid = '$clientcode'"
So, it seems you're getting all of the data you need to validate the user from that SQL.
It's all routed to a specific domain based on lines 111 and 112, yet on line 126 you've got the $this->load->view('patient
<!DOCTYPE html>
<html>
<head>
<title>Redirecting...</title>
</head>
<body>
<form action="https://<?=$clientcode?>.patientfocus.com/patientportal/login" method="post">
<!--<form action="patientportal/login" method="post">-->
<input type="hidden" name="pin" value="<?=$pin?>"/>
<input type="hidden" name="lastfour" value="<?=$lastfour?>"/>
<input type="hidden" name="year" value="<?=$year?>"/>
</form>
<script type="text/javascript">
document.forms[0].submit();
</script>
</body>
</html>
So, the page is posting all of its data back to itself, but where does it go and how am I getting this page:
How am I getting there?
Thanks!
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.