v7‰PNG
IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ±üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0AºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT
PK <\
HM M db.xmlnu [
PK <\H3eJ J library/CcRebill.phpnu [ 'Started',
self::NO_CC => 'No Credit Card saved',
self::ERROR => 'Error',
self::SUCCESS => 'OK',
self::EXCEPTION => 'Exception!',
);
return $arr[$status];
}
function setStatus($status, $message)
{
$this->updateQuick(array(
'status' => (int)$status,
'status_msg' => $message,
'status_tm' => $this->getDi()->sqlDateTime,
));
return $this;
}
}
class CcRebillTable extends Am_Table
{
protected $_table = '?_cc_rebill';
protected $_key = 'cc_rebill_id';
public function insert(array $values, $returnInserted = false)
{
if (empty($values['tm_added']))
$values['tm_added'] = $this->getDi()->sqlDateTime;
return parent::insert($values, $returnInserted);
}
}PK <\ library/EcheckRecord.phpnu [ maskBan($arr['echeck_ban']);
}
$arr['echeck_aba'] = preg_replace('/\D+/', '', $arr['echeck_aba']);
foreach ($this->_encryptedFields as $f)
if (array_key_exists($f, $arr))
$arr[$f] = $this->_table->encrypt($arr[$f]);
return $arr;
}
public function fromRow(array $arr)
{
// fields to decrypt
foreach ($this->_encryptedFields as $f)
if (array_key_exists($f, $arr))
$arr[$f] = $this->_table->decrypt($arr[$f]);
return parent::fromRow($arr);
}
/**
* Delete existing record for this user_id, then insert this one
* @return EcRecord provides fluent interface
*/
function replace()
{
if (empty($this->user_id) || $this->user_id <= 0)
throw new Am_Exception_InternalError("this->user_id is empty in " . __METHOD__);
$this->_table->deleteByUserId($this->user_id);
return $this->insert();
}
}
class EcheckRecordTable extends Am_Table
{
protected $_crypt;
protected $_key = 'echeck_id';
protected $_table = '?_echeck';
protected $_recordClass = 'EcheckRecord';
function encrypt($s){
return $this->_getCrypt()->encrypt($s);
}
function decrypt($s){
return $this->_getCrypt()->decrypt($s);
}
function _getCrypt(){
if (empty($this->_crypt))
$this->_crypt = Am_Di::getInstance ()->crypt;
return $this->_crypt;
}
function setCrypt(Am_Crypt $crypt)
{
$this->_crypt = $crypt;
}
}
PK <\߽O; ; library/Am/Paysystem/Nmi.phpnu [ addText('user')
->setLabel("Your username\n" .
'Username assigned to merchant account')
->addRule('required');
$form->addPassword('pass')
->setLabel("Your password\n" .
'Password for the specified username')
->addRule('required');
$form->addAdvCheckbox('testMode')
->setLabel("Test Mode\n" .
'Test account data will be used');
}
public function isConfigured()
{
return $this->getConfig('user') && $this->getConfig('pass');
}
public function loadCreditCard(Invoice $invoice)
{
if($cc = parent::loadCreditCard($invoice))
return $cc;
return $this->getDi()->CcRecordTable->createRecord(); // return fake record for rebill
}
public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result)
{
$user = $invoice->getUser();
if ($doFirst) // not recurring sale
{
if (!(float)$invoice->first_total) // first - free
{
$trAuth = new Am_Paysystem_Networkmerchants_Transaction_Authorization($this, $invoice, $cc);
$trAuth->run($result);
$transactionId = $trAuth->getUniqId();
$customerVaultId = $trAuth->getCustomerVaultId();
if (!$transactionId || !$customerVaultId)
{
return $result->setFailed(array("NMI Plugin: Bad auth response."));
}
$trVoid = new Am_Paysystem_Networkmerchants_Transaction_Void($this, $invoice, $transactionId, $customerVaultId);
$trVoid->run($result);
$trFree = new Am_Paysystem_Transaction_Free($this);
$trFree->setInvoice($invoice);
$trFree->process();
$result->setSuccess($trFree);
} else
{
$trAuth = new Am_Paysystem_Networkmerchants_Transaction_Authorization($this, $invoice, $cc, $invoice->first_total);
$trAuth->run($result);
$transactionId = $trAuth->getUniqId();
$customerVaultId = $trAuth->getCustomerVaultId();
if (!$transactionId || !$customerVaultId)
{
return $result->setFailed(array("NMI Plugin: Bad auth response."));
}
$trSale = new Am_Paysystem_Networkmerchants_Transaction_Capture($this, $invoice, $doFirst, $transactionId);
$trSale->run($result);
}
$user->data()->set($this->getCustomerVaultVariable(), $customerVaultId)->update();
} else
{
$customerVaultId = $user->data()->get($this->getCustomerVaultVariable());
if (!$customerVaultId)
{
return $result->setFailed(array("No saved reference transaction for customer"));
}
$trSale = new Am_Paysystem_Networkmerchants_Transaction_Sale($this, $invoice, $doFirst, $customerVaultId);
$trSale->run($result);
}
}
public function storeCreditCard(CcRecord $cc, Am_Paysystem_Result $result)
{
$user = $this->getDi()->userTable->load($cc->user_id);
$customerVaultId = $user->data()->get($this->getCustomerVaultVariable());
if ($this->invoice)
{ // to link log records with current invoice
$invoice = $this->invoice;
} else { // updating credit card info?
$invoice = $this->getDi()->invoiceRecord;
$invoice->invoice_id = 0;
$invoice->user_id = $user->pk();
}
// compare stored cc for that user may be we don't need to refresh?
if ($customerVaultId && ($cc->cc_number != '0000000000000000'))
{
$storedCc = $this->getDi()->ccRecordTable->findFirstByUserId($user->pk());
if ($storedCc && (($storedCc->cc != $cc->maskCc($cc->cc_number)) || ($storedCc->cc_expire != $cc->cc_expire)))
{
$user->data()->set($this->getCustomerVaultVariable(), null)->update();
$customerVaultId = null;
}
}
if (!$customerVaultId)
{
$trAdd = new Am_Paysystem_Networkmerchants_Transaction_AddCustomer ($this, $invoice, $cc);
$trAdd->run($result);
$customerVaultId = $trAdd->getCustomerVaultId();
if (!$customerVaultId)
{
return $result->setFailed(array("PSWW Plugin: Bad add response."));
}
$user->data()->set($this->getCustomerVaultVariable(), $customerVaultId)->update();
}
///
$cc->cc = $cc->maskCc(@$cc->cc_number);
$cc->cc_number = '0000000000000000';
if ($cc->pk())
$cc->update();
else
$cc->replace();
$result->setSuccess();
}
public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount)
{
$customerVaultId = $this->getDi()->userTable->load($payment->user_id)->data()->get($this->getCustomerVaultVariable());
$tr = new Am_Paysystem_Networkmerchants_Transaction_Refund($this, $payment->getInvoice(), $payment->receipt_id, $amount, $customerVaultId);
$tr->run($result);
}
}
PK <\#+qU qU library/Am/Paysystem/Echeck.phpnu [ getPluginUrl(self::ACTION_ECHECK) );
$action->id = $invoice->getSecureId($this->getId());
$result->setAction($action);
}
public function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs){}
public function directAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
{
switch ($request->getActionName())
{
case self::ACTION_IPN:
return parent::directAction($request, $response, $invokeArgs);
case self::ACTION_UPDATE:
return $this->updateAction($request, $response, $invokeArgs);
case self::ACTION_CANCEL:
return $this->doCancelAction($request, $response, $invokeArgs);
case self::ACTION_ECHECK:
default:
return $this->echeckAction($request, $response, $invokeArgs);
}
}
protected function echeckActionValidateSetInvoice(Am_Mvc_Request $request, array $invokeArgs)
{
$invoiceId = $request->getFiltered('id');
if (!$invoiceId)
throw new Am_Exception_InputError("invoice_id is empty - seems you have followed wrong url, please return back to continue");
$invoice = $this->getDi()->invoiceTable->findBySecureId($invoiceId, $this->getId());
if (!$invoice)
throw new Am_Exception_InputError('You have used wrong link for payment page, please return back and try again');
if ($invoice->isCompleted())
throw new Am_Exception_InputError(sprintf(___('Payment is already processed, please go to %sMembership page%s'), "",""));
if ($invoice->paysys_id != $this->getId())
throw new Am_Exception_InputError("You have used wrong link for payment page, please return back and try again");
if ($invoice->tm_added < sqlTime('-30 days'))
throw new Am_Exception_InputError("Invoice expired - you cannot open invoice after 30 days elapsed");
$this->invoice = $invoice; // set for reference
}
/**
* Show echeck info input page, validate it if submitted
*/
public function echeckAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
{
$this->echeckActionValidateSetInvoice($request, $invokeArgs);
$p = $this->createController($request, $response, $invokeArgs);
$p->setPlugin($this);
$p->setInvoice($this->invoice);
$p->run();
}
/**
* Process echeck update request
* @param Am_Mvc_Request $request
* @param Am_Mvc_Response $response
* @param array $invokeArgs
*/
public function updateAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
{
$this->getDi()->auth->requireLogin($this->getDi()->url('member',null,false));
$p = $this->createController($request, $response, $invokeArgs);
$p->setPlugin($this);
$p->run();
}
/**
* Process "cancel recurring" request
* @param Am_Mvc_Request $request
* @param Am_Mvc_Response $response
* @param array $invokeArgs
*/
public function doCancelAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
{
$id = $request->getFiltered('id');
$invoice = $this->getDi()->invoiceTable->findBySecureId($id, 'STOP' . $this->getId());
if (!$invoice)
throw new Am_Exception_InputError("No invoice found [$id]");
if ($invoice->user_id != $this->getDi()->auth->getUserId())
throw new Am_Exception_InternalError("User tried to access foreign invoice: [$id]");
if (method_exists($this, 'cancelInvoice'))
$this->cancelInvoice($invoice);
$invoice->setCancelled();
$response->setRedirect($this->getDi()->url('member/payment-history',null,false));
}
/**
* To be overriden in children classes
* @param Am_Mvc_Request $request
* @param Am_Mvc_Response $response
* @param array $invokeArgs
* @return Am_Mvc_Controller_Echeck
*/
protected function createController(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
{
return new Am_Mvc_Controller_Echeck($request, $response, $invokeArgs);
}
/**
* Method must return array of self::ECHECK_xxx constants to control which
* additional fields will be displayed in the form
* @return array
*/
public function getFormOptions()
{
$ret = array(self::ECHECK_ADDRESS, self::ECHECK_COUNTRY, self::ECHECK_STATE, self::ECHECK_CITY, self::ECHECK_STREET, self::ECHECK_ZIP);
return $ret;
}
/**
* You can do form customization necessary for the plugin
* here
*/
public function onFormInit(Am_Form_Echeck $form)
{
}
/**
* You can do custom form validation here. If errors found,
* call $form->getElementById('xx-0')->setError('xxx') and
* return false
* @return bool
*/
public function onFormValidate(Am_Form_Echeck $form)
{
return true;
}
public function doBill(Invoice $invoice, $doFirst, EcheckRecord $echeck = null)
{
$this->invoice = $invoice;
$this->echeck = $echeck;
$result = new Am_Paysystem_Result();
$this->_doBill($invoice, $doFirst, $echeck, $result);
return $result;
}
abstract public function _doBill(Invoice $invoice, $doFirst, EcheckRecord $echeck, Am_Paysystem_Result $result);
/**
* Function can be overrided to change behaviour
*/
public function storeEcheck(EcheckRecord $echeck, Am_Paysystem_Result $result)
{
if ($this->storesCcInfo())
{
$echeck->replace();
$result->setSuccess();
}
return $this;
}
/**
* Method defined for overriding in child classes where EC info is not stored locally
* @return EcRecord
* @param Invoice $invoice
* @throws Am_Exception
*/
public function loadEcheck(Invoice $invoice)
{
if ($this->storesCcInfo())
return $this->getDi()->echeckRecordTable->findFirstByUserId($invoice->user_id);
}
public function prorateInvoice(Invoice $invoice, EcheckRecord $echeck, Am_Paysystem_Result $result, $date)
{
/** @todo use "reattempt" config **/
$reattempt = array_filter($this->getConfig('reattempt', array()));
sort($reattempt);
if (!$reattempt) {
$invoice->setStatus(Invoice::RECURRING_FAILED);
$invoice->updateQuick('rebill_date', null);
return;
}
$first_failure = $invoice->data()->get(self::FIRST_REBILL_FAILURE);
if (!$first_failure)
{
$invoice->data()->set(self::FIRST_REBILL_FAILURE, $date)->update();
$first_failure = $date;
}
$days_diff = (strtotime($date) - strtotime($first_failure)) / (24*3600);
foreach ($reattempt as $day)
if ($day > $days_diff) break; // we have found next rebill date to jump
if ($day <= $days_diff){
// Several rebilling attempts failed already.
// change status to RECURRING_FAILED;
$invoice->setStatus(Invoice::RECURRING_FAILED);
$invoice->updateQuick('rebill_date', null);
return;
}
$invoice->updateQuick('rebill_date', date('Y-m-d', strtotime($first_failure." +$day days")));
$tr = new Am_Paysystem_Transaction_Manual($this);
if ($invoice->getAccessExpire() < $invoice->rebill_date)
$invoice->extendAccessPeriod($invoice->rebill_date);
}
public function onRebillFailure(Invoice $invoice, EcheckRecord $echeck, Am_Paysystem_Result $result, $date)
{
$this->prorateInvoice($invoice, $echeck, $result, $date);
if($this->getDi()->config->get('cc.rebill_failed'))
$this->sendRebillFailedToUser($invoice, $result->getLastError(), $invoice->rebill_date);
}
function sendRebillFailedToUser(Invoice $invoice, $failedReason, $nextRebill)
{
try
{
if($et = Am_Mail_Template::load('cc.rebill_failed'))
{
$et->setError($failedReason);
$et->setUser($invoice->getUser());
$et->setInvoice($invoice);
$et->setProrate(
($nextRebill > $this->getDi()->sqlDate) ?
sprintf(___('Our system will try to charge your card again on %s'), amDate($nextRebill)) : ""
);
$et->setMailPeriodic(Am_Mail::USER_REQUESTED);
$et->send($invoice->getUser());
}
}catch(Exception $e)
{
// No mail exceptions when rebilling;
$this->getDi()->errorLogTable->logException($e);
}
}
function sendRebillSuccessToUser(Invoice $invoice)
{
try
{
if($et = Am_Mail_Template::load('cc.rebill_success'))
{
$et->setUser($invoice->getUser());
$et->setInvoice($invoice);
$et->setAmount($invoice->second_total);
$et->setRebill_date($invoice->rebill_date ? amDate($invoice->rebill_date) : ___('NEVER'));
$et->setMailPeriodic(Am_Mail::USER_REQUESTED);
$et->send($invoice->getUser());
}
}
catch(Exception $e)
{
// No mail exceptions when rebilling;
$this->getDi()->errorLogTable->logException($e);
}
}
public function onRebillSuccess(Invoice $invoice, EcheckRecord $echeck, Am_Paysystem_Result $result, $date)
{
if ($invoice->data()->get(self::FIRST_REBILL_FAILURE))
{
$invoice->addToRebillDate(false, $invoice->data()->get(self::FIRST_REBILL_FAILURE));
$invoice->data()->set(self::FIRST_REBILL_FAILURE, null)->update();
}
if($this->getDi()->config->get('cc.rebill_success'))
$this->sendRebillSuccessToUser($invoice);
}
// called from Bootstrap_Cc
public function ccRebill($date = null)
{
/**
* If plugin can't rebill payments itself, leave it alone.
*/
if($this->getRecurringType() != self::REPORTS_CRONREBILL) return;
$rebillTable = $this->getDi()->ccRebillTable;
$processedCount = 0;
foreach ($this->getDi()->invoiceTable->findForRebill($date, $this->getId()) as $invoice)
{
// Invoice must have status RECURRING_ACTIVE in order to be rebilled;
if($invoice->status != Invoice::RECURRING_ACTIVE)
continue;
// If we already have all payments for this invoice unset rebill_date and update invoice status;
if($invoice->getPaymentsCount() >= $invoice->getExpectedPaymentsCount())
{
$invoice->recalculateRebillDate();
$invoice->updateStatus();
continue;
}
/* @var $invoice Invoice */
try {
$rebill = $rebillTable->createRecord(array(
'paysys_id' => $this->getId(),
'invoice_id' => $invoice->invoice_id,
'rebill_date' => $date,
'status' => CcRebill::STARTED,
'status_msg' => "Not Processed",
));
//only one attempt to rebill per day
try {
$rebill->insert();
} catch (Am_Exception_Db_NotUnique $e) {
continue;
}
$echeck = $this->getDi()->echeckRecordTable->createRecord();
if ($this->storesCcInfo())
{
$echeck = $this->loadEcheck($invoice);
if (!$echeck)
{
$rebill->setStatus(CcRebill::NO_CC, "No credit card/echeck saved, cannot rebill");
continue;
}
}
$result = $this->doBill($invoice, false, $echeck);
if (!$result->isSuccess())
$this->onRebillFailure($invoice, $echeck, $result, $date);
else
$this->onRebillSuccess($invoice, $echeck, $result, $date);
$rebill->setStatus($result->isSuccess() ? CcRebill::SUCCESS : CcRebill::ERROR,
current($result->getErrorMessages()));
$processedCount++;
} catch (Exception $e) {
if (stripos(get_class($e), 'PHPUnit_')===0) throw $e;
$rebill->setStatus(CcRebill::EXCEPTION,
"Exception " . get_class($e) . " : " . $e->getMessage());
// if there was an exception in billing (say internal error),
// we set rebill_date to tomorrow
$invoice->updateQuick('rebill_date', date('Y-m-d', strtotime($invoice->rebill_date . ' +1 day')));
$this->getDi()->errorLogTable->logException($e);
$this->logError("Exception on rebilling", $e, $invoice);
unset($this->invoice);
}
}
// Send message only if rebill executed by cron;
if($this->getDi()->config->get('cc.admin_rebill_stats')
&& (is_null($date) || $date == $this->getDi()->sqlDate)
&& $processedCount)
$this->sendStatisticsEmail();
}
protected function getStatisticsRow(Array $r)
{
if($r['status']!= CcRebill::SUCCESS)
{
$failed = "Reason: ".$r['status_msg'];
if($r['rebill_date']>$this->getDi()->sqlDate)
$failed .= "\t Next Rebill Date: ".$r['rebill_date'];
}else
$failed = '';
$row = sprintf("%s, %s, %s %s, \nInvoice: %s\tAmount: %s\t%s\n%s\n\n",
$r['email'], $r['login'], $r['name_f'], $r['name_l'],
$r['public_id'], $r['second_total'], $failed,
$this->getDi()->url(array('admin-user-payments/index/user_id/%s#invoice-%s', $r['user_id'], $r['invoice_id']))
);
return $row;
}
protected function sendStatisticsEmail()
{
$date = $this->getDi()->sqlDate;
$success = $failed = "";
$success_count = $failed_count = $success_amount = $failed_amount = 0;
if ($et = Am_Mail_Template::load('cc.admin_rebill_stats'))
{
foreach($this->getDi()->db->selectPage($total, "
SELECT r.*, i.second_total, i.user_id, i.invoice_id, i.public_id, i.rebill_date, u.name_f, u.name_l, u.email, u.login
FROM ?_cc_rebill r LEFT JOIN ?_invoice i USING(invoice_id) LEFT JOIN ?_user u ON(i.user_id = u.user_id)
WHERE status_tm>? and status_tm<=? and r.paysys_id=?
", $date, $this->getDi()->sqlDateTime, $this->getId()) as $r)
{
if($r['status'] == CcRebill::SUCCESS)
{
$success_count++; $success_amount+=$r['second_total'];
$success .= $this->getStatisticsRow($r);
}else{
$failed_count++; $failed_amount += $r['second_total'];
$failed .= $this->getStatisticsRow($r);;
}
}
if($success || $failed)
{
$currency = $this->getDi()->config->get('currency');
$et->setShort_stats(sprintf(___('Success: %d (%0.2f %s) Failed: %d (%0.2f %s)'),
$success_count, $success_amount, $currency, $failed_count, $failed_amount, $currency));
$et->setRebills_success(!empty($success) ? $success : ___('No items in this section'));
$et->setRebills_failed(!empty($failed) ? $failed : ___('No items in this section'));
$et->setPlugin($this->getId());
$et->setMailPeriodic(Am_Mail::ADMIN_REQUESTED);
$et->sendAdmin();
}
}
}
protected function _afterInitSetupForm(Am_Form_Setup $form)
{
// insert title, description fields
$form->setTitle(ucfirst(toCamelCase($this->getId())));
$el = $form->addMagicSelect('reattempt', array('multiple'=>'multiple'));
$options = array();
for ($i=1;$i<60;$i++) $options[$i] = ___("on %d-th day", $i);
$el->loadOptions($options);
$el->setLabel(___("Retry On Failure\n".
"if the recurring billing has failed,\n".
"aMember can repeat it after several days,\n".
"and extend customer subscription for that period\n".
"enter number of days to repeat billing attempt"));
if($this->storesCcInfo() && !$this->_pciDssNotRequired)
{
$text = "
WARNING! Every application processing e-check information, must be certified\n" .
"as PA-DSS compliant, and every website processing credit cards must\n" .
"be certified as PCI-DSS compliant.
";
$text.= "aMember Pro is not yet certified as PA-DSS compliant. We will start certification process\n".
"once we get 4.2.0 branch released and stable. This plugins is provided solely for TESTING purproses\n".
"Use it for anything else but testing at your own risk.
";
$form->addProlog(<<
$text
CUT
);
}
$keyFile = defined('AM_KEYFILE') ? AM_KEYFILE : AM_APPLICATION_PATH . '/configs/key.php';
if (!is_readable($keyFile))
{
$random = $this->getDi()->security->randomString(78);
$text = "To use credit card plugins, you need to create a key file that contains unique\n";
$text .= "encryption key for your website. It is necessary even if the plugin does not\n";
$text .= "store sensitive information.
";
$text .= "In a text editor, create file with the following content (one-line, no spaces before opening <?php):\n";
$text .= "
<?php return '$random';
\n";
$text .= "
save the file as key.php, and upload to amember/application/configs/ folder.\n";
$text .= "This warning will disappear once you do it correctly.";
$text .= "KEEP A BACKUP COPY OF THE key.php FILE (!)
";
$form->addProlog(<<
$text
CUT
);
}
return parent::_afterInitSetupForm($form);
}
/**
* If plugin require special actions to cancel invoice, cancelInvoice will be called
* after Invoice will actually be cancelled by CreditCard Controller.
* do nothing by default;
* @throws Am_Exception_InputError if failure;
* @param Invoice $invoice
*/
function cancelInvoice(Invoice $invoice)
{
return true;
}
public function cancelAction(Invoice $invoice, $actionName, Am_Paysystem_Result $result)
{
$invoice->setCancelled(true);
}
public function getUpdateEcheckLink($user)
{
if ($this->storesCcInfo() &&
$this->getDi()->echeckRecordTable->findFirstByUserId($user->user_id))
{
return $this->getPluginUrl('update');
}
}
}PK <\Mb-% % + library/Am/Paysystem/Transaction/Echeck.phpnu [ setInvoice($invoice);
$this->request = $request;
$this->doFirst = $doFirst;
}
public function run(Am_Paysystem_Result $result)
{
$this->result = $result;
$log = $this->getInvoiceLog();
$log->add($this->request);
$this->response = $this->request->send();
$log->add($this->response);
$this->validateResponseStatus($this->result);
if ($this->result->isFailure())
return;
try {
$this->parseResponse();
// validate function must set success status
$this->validate();
if ($this->result->isSuccess())
$this->processValidated();
} catch (Exception $e) {
if ($e instanceof PHPUnit_Framework_Error)
throw $e;
if ($e instanceof PHPUnit_Framework_Asser )
throw $e;
if (!$result->isFailure())
$result->setFailed(___("Payment failed"));
$log->add($e);
}
}
/**
* Must operate $this->result to set error status or call
* $result->setSuccess if all ok
*/
public function validate()
{
}
/**
* Parse response and return it, it will be placed to @link $this->vars
* @return mixed
*/
abstract public function parseResponse();
public function validateResponseStatus(Am_Paysystem_Result $result)
{
if ($this->response->getStatus() != 200)
{
$result->setFailed(array("Received invalid response from payment server: " . $this->response->getStatus()));
}
}
/** @return InvoiceLog */
function getInvoiceLog()
{
if (!$this->log)
{
$this->log = $this->plugin->getDi()->invoiceLogRecord;
if ($this->invoice)
{
$this->log->invoice_id = $this->invoice->invoice_id;
$this->log->user_id = $this->invoice->user_id;
}
$this->log->paysys_id = $this->getPlugin()->getId();
$this->log->remote_addr = $_SERVER['REMOTE_ADDR'];
foreach ($this->plugin->getConfig() as $k => $v)
if (is_scalar($v) && (strlen($v) > 4))
$this->log->mask($v);
}
return $this->log;
}
}PK <\/ / library/Am/Paysystem/Transaction/CreditCard.phpnu [ setInvoice($invoice);
$this->request = $request;
$this->doFirst = $doFirst;
}
public function run(Am_Paysystem_Result $result)
{
$this->result = $result;
$log = $this->getInvoiceLog();
$log->add($this->request);
$this->response = $this->request->send();
$log->add($this->response);
$this->validateResponseStatus($this->result);
if ($this->result->isFailure())
return;
try {
$this->parseResponse();
// validate function must set success status
$this->validate();
if ($this->result->isSuccess())
$this->processValidated();
} catch (Exception $e) {
if ($e instanceof PHPUnit_Framework_Error)
throw $e;
if ($e instanceof PHPUnit_Framework_Asser )
throw $e;
if (!$result->isFailure())
$result->setFailed(___("Payment failed"));
$log->add($e);
}
}
/**
* Must operate $this->result to set error status or call
* $result->setSuccess if all ok
*/
public function validate()
{
}
/**
* Parse response and return it, it will be placed to @link $this->vars
* @return mixed
*/
abstract public function parseResponse();
public function validateResponseStatus(Am_Paysystem_Result $result)
{
//nop, it is not necessary to validate it here. In most cases REST API
//can return different status with detailed
//error messages instead of our common one
}
/** @return InvoiceLog */
function getInvoiceLog()
{
if (!$this->log)
{
$this->log = $this->plugin->getDi()->invoiceLogRecord;
if ($this->invoice)
{
$this->log->invoice_id = $this->invoice->invoice_id;
$this->log->user_id = $this->invoice->user_id;
}
$this->log->paysys_id = $this->getPlugin()->getId();
$this->log->remote_addr = $_SERVER['REMOTE_ADDR'];
foreach ($this->plugin->getConfig() as $k => $v)
if (is_scalar($v) && (strlen($v) > 4))
$this->log->mask($v);
}
return $this->log;
}
}PK <\ޜ 5 library/Am/Paysystem/Transaction/Maxmind/Minfraud.phpnu [ body = $this->response->getBody();
$this->vars = array();
$list = explode(';', $this->body);
foreach ($list as $l)
{
list($key, $value) = explode('=', $l);
$this->vars[$key] = $value;
}
}
public function isEmpty()
{
if (!@array_key_exists('countryMatch', (array) $this->vars))
return false;
return empty($this->body);
}
public function validate()
{
$this->riskscore = $this->vars['riskScore'];
$payment_records_edit_log = array();
if ($this->vars['carderEmail'] == 'Yes')
$payment_records_edit_log[] = 'Email is in database of high risk e-mails';
if ($this->vars['countryMatch'] == 'No' && !$this->plugin->getConfig('maxmind_allow_country_not_matched'))
$payment_records_edit_log[] = 'Country of IP address not matches billing address country';
if ($this->vars['highRiskCountry'] == 'Yes' && !$this->plugin->getConfig('maxmind_allow_high_risk_country'))
$payment_records_edit_log[] = 'IP address or billing address country is in high risk countries list';
if ($this->vars['anonymousProxy'] == 'Yes' && !$this->plugin->getConfig('maxmind_allow_anonymous_proxy'))
$payment_records_edit_log[] = 'Anonymous proxy are not allowed';
if ($this->vars['freeMail'] == 'Yes' && !$this->plugin->getConfig('maxmind_allow_free_mail'))
$payment_records_edit_log[] = 'E-mail from free e-mail provider are not allowed';
if ($this->vars['queriesRemaining'] > 0 && $this->vars['queriesRemaining'] < 10)
Am_Di::getInstance()->errorLogTable->log("MaxMind queriesRemaining: " . $this->vars['queriesRemaining']);
$ccfd_warnings = array(
'IP_NOT_FOUND',
'COUNTRY_NOT_FOUND',
'CITY_NOT_FOUND',
'CITY_REQUIRED',
'POSTAL_CODE_REQUIRED',
'POSTAL_CODE_NOT_FOUND'
);
$ccfd_fatal_errors = array(
'INVALID_LICENSE_KEY',
'MAX_REQUESTS_PER_LICENSE',
'IP_REQUIRED',
'LICENSE_REQUIRED',
'COUNTRY_REQUIRED',
'MAX_REQUESTS_REACHED'
);
if (count($payment_records_edit_log))
$this->riskscore = 99;
if ($this->vars['err'] || count($payment_records_edit_log))
{
if (in_array($this->vars['err'], $ccfd_warnings))
Am_Di::getInstance()->errorLogTable->log("MaxMind warning: " . $this->vars['err'] . " maxmindID: " . $this->vars['maxmindID']);
if (in_array($this->vars['err'], $ccfd_fatal_errors))
{
$payment_records_edit_log[] = $this->vars['err'];
}
if (count($payment_records_edit_log))
{
$this->getInvoiceLog()->add($payment_records_edit_log);
return $this->result->setFailed(___('Payment failed'));
}
}
if($this->riskscore > $this->getPlugin()->getConfig('maxmind_risk_score'))
return $this->result->setFailed(___('Payment failed'));
$this->result->setSuccess($this);
}
public function getRiskScore()
{
return $this->riskscore;
}
public function processValidated()
{
//do nothing
}
}
PK <\jD D 3 library/Am/Paysystem/Transaction/Maxmind/Number.phpnu [ body = $this->response->getBody();
$this->vars = array();
$list = explode(';', $this->body);
foreach ($list as $l)
{
list($key, $value) = explode('=', $l);
$this->vars[$key] = $value;
}
}
public function isEmpty()
{
if (!array_key_exists('phoneType', $this->vars))
return false;
return empty($this->body);
}
public function validate()
{
if ($this->vars['err'])
return $this->result->setFailed(___('Payment failed'));
if(!in_array($this->vars['phoneType'], $this->getPlugin()->getConfig('maxmind_tni_phone_types')))
return $this->result->setFailed(___('Payment failed'));
$this->result->setSuccess($this);
}
public function processValidated()
{
//do nothing
}
}
PK <\9j 2 library/Am/Paysystem/Transaction/Maxmind/Phone.phpnu [ body = $this->response->getBody();
$this->vars = array();
$list = explode(';', $this->body);
foreach ($list as $l)
{
list($key, $value) = explode('=', $l);
$this->vars[$key] = $value;
}
}
public function isEmpty()
{
if (!array_key_exists('refid', $this->vars))
return false;
return empty($this->body);
}
public function validate()
{
if ($this->vars['err'])
return $this->result->setFailed(___('Payment failed'));
$this->result->setSuccess($this);
}
public function processValidated()
{
//do nothing
}
}
PK <\
V1 # library/Am/Paysystem/CreditCard.phpnu [ getPluginUrl(self::ACTION_CC) );
$action->id = $invoice->getSecureId($this->getId());
$result->setAction($action);
}
public function createTransaction(/*Am_Mvc_Request */$request, /*Am_Mvc_Response */$response, array $invokeArgs){}
public function directAction(/*Am_Mvc_Request */$request, /*Am_Mvc_Response */$response, $invokeArgs)
{
switch ($request->getActionName())
{
case self::ACTION_IPN:
return parent::directAction($request, $response, $invokeArgs);
case self::ACTION_UPDATE:
return $this->updateAction($request, $response, $invokeArgs);
case self::ACTION_CANCEL:
return $this->doCancelAction($request, $response, $invokeArgs);
case self::ACTION_CANCEL_PAYMENT:
return $this->cancelPaymentAction($request, $response, $invokeArgs);
case self::ACTION_THANKS:
return $this->thanksAction($request, $response, $invokeArgs);
case self::ACTION_CC:
default:
return $this->ccAction($request, $response, $invokeArgs);
}
}
protected function ccActionValidateSetInvoice(Am_Mvc_Request $request, array $invokeArgs)
{
$invoiceId = $request->getFiltered('id');
if (!$invoiceId)
throw new Am_Exception_InputError("invoice_id is empty - seems you have followed wrong url, please return back to continue");
$invoice = $this->getDi()->invoiceTable->findBySecureId($invoiceId, $this->getId());
if (!$invoice)
throw new Am_Exception_InputError('You have used wrong link for payment page, please return back and try again');
if ($invoice->isCompleted())
throw new Am_Exception_InputError(sprintf(___('Payment is already processed, please go to %sMembership page%s'), "",""));
if ($invoice->paysys_id != $this->getId())
throw new Am_Exception_InputError("You have used wrong link for payment page, please return back and try again");
if ($invoice->tm_added < sqlTime('-30 days'))
throw new Am_Exception_InputError("Invoice expired - you cannot open invoice after 30 days elapsed");
$this->invoice = $invoice; // set for reference
}
/**
* Show credit card info input page, validate it if submitted
*/
public function ccAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
{
$this->ccActionValidateSetInvoice($request, $invokeArgs);
$p = $this->createController($request, $response, $invokeArgs);
$p->setPlugin($this);
$p->setInvoice($this->invoice);
$p->run();
}
/**
* Process credit card update request
* @param Am_Mvc_Request $request
* @param Am_Mvc_Response $response
* @param array $invokeArgs
*/
public function updateAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
{
$this->getDi()->auth->requireLogin($this->getDi()->url('member',null,false));
$p = $this->createController($request, $response, $invokeArgs);
$p->setPlugin($this);
$p->run();
}
/**
* Process "cancel recurring" request
* @param Am_Mvc_Request $request
* @param Am_Mvc_Response $response
* @param array $invokeArgs
*/
public function doCancelAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
{
$id = $request->getFiltered('id');
$invoice = $this->getDi()->invoiceTable->findBySecureId($id, 'STOP'.$this->getId());
if (!$invoice)
throw new Am_Exception_InputError("No invoice found [$id]");
if ($invoice->user_id != $this->getDi()->auth->getUserId())
throw new Am_Exception_InternalError("User tried to access foreign invoice: [$id]");
if (method_exists($this, 'cancelInvoice'))
$this->cancelInvoice($invoice);
$invoice->setCancelled();
$response->setRedirect($this->getDi()->surl('member/payment-history', false));
}
public function cancelPaymentAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
{
$id = $request->getFiltered('id');
if (!$id && isset($_GET['id'])) $id = filterId($_GET['id']);
$invoice = $this->getDi()->invoiceTable->findFirstByPublicId($id);
if (!$invoice)
throw new Am_Exception_InputError("No invoice found [$id]");
if ($invoice->user_id != $this->getDi()->auth->getUserId())
throw new Am_Exception_InternalError("User tried to access foreign invoice: [$id]");
$this->invoice = $invoice;
// find invoice and redirect to default "cancel" page
$response->setRedirect($this->getCancelUrl());
}
/**
* To be overriden in children classes
* @param Am_Mvc_Request $request
* @param Am_Mvc_Response $response
* @param array $invokeArgs
* @return \Am_Mvc_Controller_CreditCard
*/
protected function createController(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
{
return new Am_Mvc_Controller_CreditCard($request, $response, $invokeArgs);
}
/**
* Method must return array of self::CC_xxx constants to control which
* additional fields will be displayed in the form
* @return array
*/
public function getFormOptions(){
$ret = array(self::CC_CODE, self::CC_ADDRESS, self::CC_COUNTRY, self::CC_STATE, self::CC_CITY, self::CC_STREET, self::CC_ZIP);
if ($this->getCreditCardTypeOptions()) $ret[] = self::CC_TYPE_OPTIONS;
return $ret;
}
/**
*
*/
public function getCreditCardTypeOptions(){
return array();
}
/**
* You can do form customization necessary for the plugin
* here
*/
public function onFormInit(Am_Form_CreditCard $form)
{
}
/**
* You can do custom form validation here. If errors found,
* call $form->getElementById('xx-0')->setError('xxx') and
* return false
* @return bool
*/
public function onFormValidate(Am_Form_CreditCard $form)
{
return true;
}
/**
* Filter and validate cc#
* @return null|string null if ok, error message if error
*/
public function validateCreditCardNumber($cc){
require_once 'ccvs.php';
$validator = new CreditCardValidationSolution;
if (!$validator->validateCreditCard($cc))
return $validator->CCVSError;
/** @todo translate error messages from ccvs.php */
return null;
}
public function doBill(Invoice $invoice, $doFirst, CcRecord $cc = null)
{
$this->invoice = $invoice;
$this->cc = $cc;
$result = new Am_Paysystem_Result();
$this->_doBill($invoice, $doFirst, $cc, $result);
return $result;
}
public function doMaxmindCheck(Invoice $invoice, CcRecord $cc)
{
$result = new Am_Paysystem_Result();
$user = $invoice->getUser();
$server = array(
'minfraud.maxmind.com',
'minfraud-us-east.maxmind.com',
'minfraud-us-west.maxmind.com'
);
$i = 0;
if (isset($_SERVER["HTTP_X_FORWARDED_FOR"]))
{
$client_ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
$forwarded_ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
}
else
{
if (isset($_SERVER["HTTP_CLIENT_IP"]))
$client_ip = $_SERVER["HTTP_CLIENT_IP"];
else
$client_ip = $_SERVER["REMOTE_ADDR"];
$forwarded_ip = '';
}
$ps = new stdclass;
$ps->license_key = $this->getConfig('maxmind_license_key');
$ps->bin = substr($cc->cc_number, 0, 6);
$ps->i = $client_ip;
$ps->forwardedIP = $forwarded_ip;
$ps->city = $cc->cc_city;
$ps->region = $cc->cc_state;
$ps->country = $cc->cc_country;
$ps->postal = $cc->cc_zip;
list($acc, $domain) = @explode('@', $user->email, 2);
$ps->domain = $domain;
$ps->emailMD5 = md5(strtolower($user->email));
$ps->usernameMD5 = md5($user->login);
$ps->custPhone = $cc->cc_phone;
$ps->requested_type = $this->getConfig('maxmind_requested_type');
$ps->shipAddr = $cc->cc_street;
$ps->shipCity = $cc->cc_city;
$ps->shipRegion = $cc->cc_city;
$ps->shipPostal = $cc->cc_city;
$ps->shipCountry = $cc->cc_city;
$ps->txnID = $invoice->public_id;
$ps->sessionID = md5(session_id());
$ps->user_agent = $_SERVER['HTTP_USER_AGENT'];
$request = $this->createHttpRequest();
$request->addPostParameter((array) $ps);
$request->setMethod(Am_HttpRequest::METHOD_POST);
do
{
//minfraud verification
$request->setUrl('https://' . $server[$i] . '/app/ccv2r');
$transaction = new Am_Paysystem_Transaction_Maxmind_Minfraud($this, $invoice, $request, true);
$transaction->run($result);
$i++;
}
while ($i < count($server) && $transaction->isEmpty());
$risk_score = $transaction->getRiskScore();
if ($this->getConfig('maxmind_use_telephone_verification') &&
$risk_score >= $this->getConfig('maxmind_risk_score') &&
$risk_score <= 10 &&
!empty($cc->cc_phone))
{
//number identification
if($this->getConfig('maxmind_use_number_identification'))
{
$result_number = new Am_Paysystem_Result();
$i = 0;
$ps = new stdclass;
$ps->l = $this->getConfig('maxmind_license_key');
$ps->phone = preg_replace("/[^\d]+/i", "", $cc->cc_phone);
$request_number = $this->createHttpRequest();
$request_number->addPostParameter((array) $ps);
$request_number->setMethod(Am_HttpRequest::METHOD_POST);
do
{
$request_number->setUrl('https://' . $server[$i] . '/app/phone_id_http');
$transaction = new Am_Paysystem_Transaction_Maxmind_Number($this, $invoice, $request_number, true);
$transaction->run($result_number);
$i++;
}
while ($i < count($server) && $transaction->isEmpty());
if($result_number->isFailure())
return $result_number;
}
//phone verification
/*if($this->getConfig('maxmind_use_telephone_verification'))
{
$result_phone= new Am_Paysystem_Result();
$i = 0;
$ps = new stdclass;
$ps->l = $this->getConfig('maxmind_license_key');
$ps->phone = preg_replace("/[^\d]+/i", "", $cc->cc_phone);
$request_phone = $this->createHttpRequest();
$request_phone->addPostParameter((array) $ps);
$request_phone->setMethod(Am_HttpRequest::METHOD_POST);
do
{
$request_phone->setUrl('https://' . $server[$i] . '/app/telephone_http');
$transaction = new Am_Paysystem_Transaction_Maxmind_Phone($this, $invoice, $request_phone, true);
$transaction->run($result_phone);
$i++;
}
while ($i < count($server) && $transaction->isEmpty());
if($result_phone->isFailure())
return $result_phone;
}*/
}
return $result;
}
abstract public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result);
/**
* Function can be overrided to change behaviour
*/
public function storeCreditCard(CcRecord $cc, Am_Paysystem_Result $result)
{
if ($this->storesCcInfo())
{
$cc->replace();
$result->setSuccess();
}
return $this;
}
/**
* Method defined for overriding in child classes where CC info is not stored locally
* @return CcRecord
* @param Invoice $invoice
* @throws Am_Exception
*/
public function loadCreditCard(Invoice $invoice)
{
if ($this->storesCcInfo())
return $this->getDi()->ccRecordTable->findFirstByUserId($invoice->user_id);
}
public function prorateInvoice(Invoice $invoice, CcRecord $cc, Am_Paysystem_Result $result, $date)
{
/** @todo use "reattempt" config **/
$reattempt = array_filter($this->getConfig('reattempt', array()));
sort($reattempt);
if (!$reattempt) {
$invoice->setStatus(Invoice::RECURRING_FAILED);
$invoice->updateQuick('rebill_date', null);
return;
}
$first_failure = $invoice->data()->get(self::FIRST_REBILL_FAILURE);
if (!$first_failure)
{
$invoice->data()->set(self::FIRST_REBILL_FAILURE, $date)->update();
$first_failure = $date;
}
$days_diff = (strtotime($date) - strtotime($first_failure)) / (24*3600);
foreach ($reattempt as $day)
if ($day > $days_diff) break; // we have found next rebill date to jump
if ($day <= $days_diff){
// Several rebilling attempts failed already.
// change status to RECURRING_FAILED;
$invoice->setStatus(Invoice::RECURRING_FAILED);
$invoice->updateQuick('rebill_date', null);
return;
}
$invoice->updateQuick('rebill_date', date('Y-m-d', strtotime($first_failure." +$day days")));
$tr = new Am_Paysystem_Transaction_Manual($this);
if ($invoice->getAccessExpire() < $invoice->rebill_date)
$invoice->extendAccessPeriod($invoice->rebill_date);
}
public function onRebillFailure(Invoice $invoice, CcRecord $cc, Am_Paysystem_Result $result, $date)
{
$this->prorateInvoice($invoice, $cc, $result, $date);
if($this->getDi()->config->get('cc.rebill_failed'))
$this->sendRebillFailedToUser($invoice, $result->getLastError(), $invoice->rebill_date);
}
function sendRebillFailedToUser(Invoice $invoice, $failedReason, $nextRebill)
{
try
{
if($et = Am_Mail_Template::load('cc.rebill_failed'))
{
$et->setError($failedReason);
$et->setUser($invoice->getUser());
$et->setInvoice($invoice);
$et->setProrate(
($nextRebill > $this->getDi()->sqlDate) ?
sprintf(___('Our system will try to charge your card again on %s'), amDate($nextRebill)) : ""
);
$et->setMailPeriodic(Am_Mail::USER_REQUESTED);
$et->send($invoice->getUser());
}
}catch(Exception $e)
{
// No mail exceptions when rebilling;
$this->getDi()->errorLogTable->logException($e);
}
}
function sendRebillSuccessToUser(Invoice $invoice)
{
try
{
if($et = Am_Mail_Template::load('cc.rebill_success'))
{
$et->setUser($invoice->getUser());
$et->setInvoice($invoice);
$et->setAmount($invoice->second_total);
$et->setRebill_date($invoice->rebill_date ? amDate($invoice->rebill_date) : ___('NEVER'));
$et->setMailPeriodic(Am_Mail::USER_REQUESTED);
$et->send($invoice->getUser());
}
}
catch(Exception $e)
{
// No mail exceptions when rebilling;
$this->getDi()->errorLogTable->logException($e);
}
}
public function onRebillSuccess(Invoice $invoice, CcRecord $cc, Am_Paysystem_Result $result, $date)
{
if ($invoice->data()->get(self::FIRST_REBILL_FAILURE))
{
$invoice->addToRebillDate(false, $invoice->data()->get(self::FIRST_REBILL_FAILURE));
$invoice->data()->set(self::FIRST_REBILL_FAILURE, null)->update();
}
if($this->getDi()->config->get('cc.rebill_success'))
$this->sendRebillSuccessToUser($invoice);
}
public function ccRebill($date = null)
{
/**
* If plugin can't rebill payments itself, leave it alone.
*/
if($this->getRecurringType() != self::REPORTS_CRONREBILL) return;
$rebillTable = $this->getDi()->ccRebillTable;
$processedCount = 0;
foreach ($this->getDi()->invoiceTable->findForRebill($date, $this->getId()) as $invoice)
{
// Invoice must have status RECURRING_ACTIVE in order to be rebilled;
if($invoice->status != Invoice::RECURRING_ACTIVE)
continue;
// If we already have all payments for this invoice unset rebill_date and update invoice status;
if($invoice->getPaymentsCount() >= $invoice->getExpectedPaymentsCount())
{
$invoice->recalculateRebillDate();
$invoice->updateStatus();
continue;
}
/* @var $invoice Invoice */
try {
$rebill = $rebillTable->createRecord(array(
'paysys_id' => $this->getId(),
'invoice_id' => $invoice->invoice_id,
'rebill_date' => $date,
'status' => CcRebill::STARTED,
'status_msg' => "Not Processed",
));
//only one attempt to rebill per day
try {
$rebill->insert();
} catch (Am_Exception_Db_NotUnique $e) {
continue;
}
$cc = $this->getDi()->CcRecordRecord;
if ($this->storesCcInfo())
{
$cc = $this->loadCreditCard($invoice);
if (!$cc)
{
$rebill->setStatus(CcRebill::NO_CC, "No credit card saved, cannot rebill");
continue;
}
}
$result = $this->doBill($invoice, false, $cc);
if (!$result->isSuccess())
$this->onRebillFailure($invoice, $cc, $result, $date);
else
$this->onRebillSuccess($invoice, $cc, $result, $date);
$rebill->setStatus($result->isSuccess() ? CcRebill::SUCCESS : CcRebill::ERROR,
current($result->getErrorMessages()));
$processedCount++;
} catch (Exception $e) {
if (stripos(get_class($e), 'PHPUnit_')===0) throw $e;
$rebill->setStatus(CcRebill::EXCEPTION,
"Exception " . get_class($e) . " : " . $e->getMessage());
// if there was an exception in billing (say internal error),
// we set rebill_date to tomorrow
$invoice->updateQuick('rebill_date', date('Y-m-d', strtotime($invoice->rebill_date . ' +1 day')));
$this->getDi()->errorLogTable->logException($e);
$this->logError("Exception on rebilling", $e, $invoice);
unset($this->invoice);
}
}
// Send message only if rebill executed by cron;
if($this->getDi()->config->get('cc.admin_rebill_stats')
&& (is_null($date) || $date == $this->getDi()->sqlDate)
&& $processedCount)
$this->sendStatisticsEmail();
}
protected function getStatisticsRow(Array $r)
{
if($r['status']!= CcRebill::SUCCESS)
{
$failed = "Reason: ".$r['status_msg'];
if($r['rebill_date']>$this->getDi()->sqlDate)
$failed .= "\t Next Rebill Date: ".$r['rebill_date'];
}else
$failed = '';
$row = sprintf("%s, %s, %s %s, \nInvoice: %s\tAmount: %s\t%s\n%s\n\n",
$r['email'], $r['login'], $r['name_f'], $r['name_l'],
$r['public_id'], $r['second_total'], $failed,
$this->getDi()->url(array('admin-user-payments/index/user_id/%s#invoice-%s', $r['user_id'], $r['invoice_id']))
);
return $row;
}
protected function sendStatisticsEmail()
{
$date = $this->getDi()->sqlDate;
$success = $failed = "";
$success_count = $failed_count = $success_amount = $failed_amount = 0;
if ($et = Am_Mail_Template::load('cc.admin_rebill_stats'))
{
foreach($this->getDi()->db->selectPage($total, "
SELECT r.*, i.second_total, i.user_id, i.invoice_id, i.public_id, i.rebill_date, u.name_f, u.name_l, u.email, u.login
FROM ?_cc_rebill r LEFT JOIN ?_invoice i USING(invoice_id) LEFT JOIN ?_user u ON(i.user_id = u.user_id)
WHERE status_tm>? and status_tm<=? and r.paysys_id=?
", $date, $this->getDi()->sqlDateTime, $this->getId()) as $r)
{
if($r['status'] == CcRebill::SUCCESS)
{
$success_count++; $success_amount+=$r['second_total'];
$success .= $this->getStatisticsRow($r);
}else{
$failed_count++; $failed_amount += $r['second_total'];
$failed .= $this->getStatisticsRow($r);;
}
}
if($success || $failed)
{
$currency = $this->getDi()->config->get('currency');
$et->setShort_stats(sprintf(___('Success: %d (%0.2f %s) Failed: %d (%0.2f %s)'),
$success_count, $success_amount, $currency, $failed_count, $failed_amount, $currency));
$et->setRebills_success(!empty($success) ? $success : ___('No items in this section'));
$et->setRebills_failed(!empty($failed) ? $failed : ___('No items in this section'));
$et->setPlugin($this->getId());
$et->setMailPeriodic(Am_Mail::ADMIN_REQUESTED);
$et->sendAdmin();
}
}
}
protected function _afterInitSetupForm(Am_Form_Setup $form)
{
// insert title, description fields
$form->setTitle(ucfirst(toCamelCase($this->getId())));
$el = $form->addMagicSelect('reattempt', array('multiple'=>'multiple'));
$options = array();
for ($i=1;$i<60;$i++) $options[$i] = ___("on %d-th day", $i);
$el->loadOptions($options);
$el->setLabel(___("Retry On Failure\n".
"if the recurring billing has failed,\n".
"aMember can repeat it after several days,\n".
"and extend customer subscription for that period\n".
"enter number of days to repeat billing attempt"));
if($this->canUseMaxmind())
{
$form->addFieldset()->setLabel(___('MaxMind Credit Card Fraud Detection'));
$form->addAdvCheckbox('use_maxmind')->setLabel(___('Use MaxMind Credit Card Fraud Detection'));
$form->addText('maxmind_license_key')->setLabel(
___("Maxmind License Key\n" .
"%sObtain a Free or Premium license key%s", '', ''));
$form->addSelect('maxmind_requested_type')->setLabel(
___("Requested Type\n" .
"To be used if you have multiple plans in one account\n" .
"and wish to select type of query you wish to make.\n" .
"By default the service uses the highest level available"))
->loadOptions(array(
"" => 'Default',
"free" => 'Free',
"city" => 'City (standard paid service)',
"premium" => 'Premium (premium paid service)'));
$form->addText('maxmind_risk_score')->setLabel(
___("Risk Score\n" .
"Overall %sRisk Score%s (decimal from 0 to 10)\n" .
"For orders that return a fraud score of 2.5 and above,\n" .
" it is recommended to hold for review,\n" .
" or require the validation with the Telephone Verification service\n", '', ''));
$form->setDefault('maxmind_risk_score', '2.5');
/*$form->addAdvCheckbox('maxmind_use_telephone_verification')->setLabel(
___("Telephone Verification\n" .
"Enable %sTelephone Verification%s service"
, '', ''));*/
$form->addAdvCheckbox('maxmind_use_number_identification')->setLabel(
___("Number Identification\n" .
"Enable %sTelephone Number Identification (TNI)%s service", '', ''));
$form->addMagicSelect('maxmind_tni_phone_types')->setLabel(
___("Allowed Phone Types\n" .
"The TNI service is able to categorize customer inputted US and Canadian\n" .
"phone numbers into %seight different phone types%s\n" .
"such as fixed land line, mobile, VoIP, and invalid phone numbers", '', ''))
->loadOptions(array(
'0' => 'Undetermined (Medium Risk Level)',
'1' => 'Fixed Line (Low Risk Level)',
'2' => 'Mobile (Low-Medium Risk Level)',
'3' => 'PrePaid Mobile (Medium-High Risk Level)',
'4' => 'Toll-Free (High Risk Level)',
'5' => 'Non-Fixed VoIP (High Risk Level)',
'8' => 'Invalid Number (High Risk Level)',
'9' => 'Restricted Number (High Risk Level)'));
$form->addAdvCheckbox('maxmind_allow_country_not_matched')->setLabel(
___("Allow payment if country not matched\n" .
"Whether country of IP address matches billing address country\n" .
"(mismatch = higher risk)"));
$form->addAdvCheckbox('maxmind_allow_high_risk_country')->setLabel(
___("Allow payment if high risk countries\n" .
"Whether IP address or billing address country is in\n" .
"Egypt, Ghana, Indonesia, Lebanon, Macedonia, Morocco, Nigeria,\n" .
"Pakistan, Romania, Serbia and Montenegro, Ukraine, or Vietnam"));
$form->addAdvCheckbox('maxmind_allow_anonymous_proxy')->setLabel(
___("Allow payment if anonymous proxy\n" .
"Whether IP address is %sAnonymous Proxy%s\n" .
"(anonymous proxy = very high risk)", '', ''));
$form->addAdvCheckbox('maxmind_allow_free_mail')->setLabel(
___("Allow payment if free e-mail\n" .
"Whether e-mail is from free e-mail provider\n" .
"(free e-mail = higher risk)"));
$form->addElement('script')->setScript(<<storesCcInfo() && !$this->_pciDssNotRequired)
{
$text = "WARNING! Every application processing credit card information, must be certified\n" .
"as PA-DSS compliant, and every website processing credit cards must\n" .
"be certified as PCI-DSS compliant.
";
$text.= "aMember Pro is not yet certified as PA-DSS compliant. ".
"This plugins is provided solely for TESTING purproses\n".
"Use it for anything else but testing at your own risk.
";
$form->addProlog(<<
$text
CUT
);
}
$keyFile = defined('AM_KEYFILE') ? AM_KEYFILE : AM_APPLICATION_PATH . '/configs/key.php';
if (!is_readable($keyFile))
{
$random = $this->getDi()->security->randomString(78);
$text = "To use credit card plugins, you need to create a key file that contains unique\n";
$text .= "encryption key for your website. It is necessary even if the plugin does not\n";
$text .= "store sensitive information.
";
$text .= "In a text editor, create file with the following content (one-line, no spaces before opening <?php):\n";
$text .= "
<?php return '$random';
\n";
$text .= "
save the file as key.php, and upload to amember/application/configs/ folder.\n";
$text .= "This warning will disappear once you do it correctly.";
$text .= "KEEP A BACKUP COPY OF THE key.php FILE (!)
";
$form->addProlog(<<
$text
CUT
);
}
return parent::_afterInitSetupForm($form);
}
/**
* If plugin require special actions to cancel invoice, cancelInvoice will be called
* after Invoice will actually be cancelled by CreditCard Controller.
* do nothing by default;
* @throws Am_Exception_InputError if failure;
* @param Invoice $invoice
*/
function cancelInvoice(Invoice $invoice){
return true;
}
public function cancelAction(Invoice $invoice, $actionName, Am_Paysystem_Result $result)
{
$result->setSuccess();
$invoice->setCancelled(true);
}
public function getUpdateCcLink($user)
{
if ($this->storesCcInfo() &&
$this->getDi()->ccRecordTable->findFirstByUserId($user->user_id))
{
return $this->getPluginUrl('update');
}
}
public function onDaily()
{
$this->sendCcExpireMessage();
}
public function sendCcExpireMessage()
{
// Send Message only if plugin is allowed to store CC info.
if(!$this->storesCcInfo()) return;
if(!$this->getDi()->config->get('cc.card_expire')) return;
$oRebillDate = $this->getDi()->dateTime;
$oRebillDate->modify(sprintf("+%d days", $this->getDi()->config->get('cc.card_expire_days', 5)));
foreach($this->getDi()->db->selectPage($total, "
SELECT i.invoice_id, c.cc_expire
FROM ?_invoice i LEFT JOIN ?_cc c using(user_id)
WHERE i.status = ? and i.rebill_date = ? and CONCAT(SUBSTR(c.cc_expire, 3,2), SUBSTR(c.cc_expire, 1,2)) < ?
and i.paysys_id = ?
", Invoice::RECURRING_ACTIVE, $oRebillDate->format('Y-m-d'), $oRebillDate->format('ym'), $this->getId()) as $r)
{
$invoice = $this->getDi()->invoiceTable->load($r['invoice_id']);
if($et = Am_Mail_Template::load('cc.card_expire'))
{
$et->setUser($invoice->getUser());
$et->setInvoice($invoice);
$et->setExpires(substr_replace($r['cc_expire'], '/', 2, 0));
$et->setMailPeriodic(Am_Mail::USER_REQUESTED);
$et->send($invoice->getUser());
}
}
}
public function canUseMaxmind()
{
return false;
}
}
PK <\RfW W <