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 NmiEcheck.php000064400000027605152101614250007112 0ustar00addText('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 getEcheckTypeOptions() { return array('business' => 'business', 'personal' => 'personal'); } public function _doBill(Invoice $invoice, $doFirst, EcheckRecord $echeck, Am_Paysystem_Result $result) { $user = $invoice->getUser(); if ($doFirst) // not recurring sale { $trAdd = new Echeck_Networkmerchants_AddCustomer($this, $invoice, $echeck); $trAdd->run($result); $customerVaultId = $trAdd->getCustomerVaultId(); $user->data()->set($this->getCustomerVaultVariable(), $customerVaultId)->update(); if (!(float)$invoice->first_total) // first - free { $trFree = new Am_Paysystem_Transaction_Free($this); $trFree->setInvoice($invoice); $trFree->process(); $result->setSuccess($trFree); return; } } else { $customerVaultId = $user->data()->get($this->getCustomerVaultVariable()); if (!$customerVaultId) { return $result->setFailed(array("No saved reference transaction for customer")); } } $trSale = new Echeck_Networkmerchants_Sale($this, $invoice, $doFirst, $customerVaultId); $trSale->run($result); } public function storeEcheck(EcheckRecord $echeck, Am_Paysystem_Result $result) { $user = $this->getDi()->userTable->load($echeck->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 && ($echeck->echeck_ban != '0000000000000000')) { $storedCc = $this->getDi()->echeckRecordTable->findFirstByUserId($user->pk()); if ($storedCc && ($storedCc->echeck != $echeck->maskBan($echeck->echeck_ban))) { $user->data()->set($this->getCustomerVaultVariable(), null)->update(); $customerVaultId = null; } } if (!$customerVaultId) { $trAdd = new Echeck_Networkmerchants_AddCustomer($this, $invoice, $echeck); $trAdd->run($result); $customerVaultId = $trAdd->getCustomerVaultId(); if (!$customerVaultId) { return $result->setFailed(array("NMI ACH Plugin: Bad add response.")); } $user->data()->set($this->getCustomerVaultVariable(), $customerVaultId)->update(); } /// $echeck->echeck = $echeck->maskBan($echeck->echeck_ban); $echeck->echeck_ban = '0000000000000000'; $echeck->echeck_aba = '0000000000000000'; if ($echeck->pk()) $echeck->update(); else $echeck->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 Echeck_Networkmerchants_Refund($this, $payment->getInvoice(), $payment->receipt_id, $amount, $customerVaultId); $tr->run($result); } } class Am_Paysystem_Transaction_Echeck_Networkmerchants extends Am_Paysystem_Transaction_Echeck { protected $parsedResponse = array(); public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst) { $request = new Am_HttpRequest($plugin->getGatewayURL(), Am_HttpRequest::METHOD_POST); parent::__construct($plugin, $invoice, $request, $doFirst); $this->addRequestParams(); } private function getUser() { return (!$this->plugin->getConfig('testMode')) ? $this->plugin->getConfig('user') : 'demo'; } private function getPass() { return (!$this->plugin->getConfig('testMode')) ? $this->plugin->getConfig('pass') : 'password'; } public function getAmount() { return $this->doFirst ? $this->invoice->first_total : $this->invoice->second_total; } protected function addRequestParams() { $this->request->addPostParameter('username', $this->getUser()); $this->request->addPostParameter('password', $this->getPass()); } public function getUniqId() { return $this->parsedResponse->transactionid; } public function parseResponse() { parse_str($this->response->getBody(), $this->parsedResponse); $this->parsedResponse = (object)$this->parsedResponse; } public function validate() { switch ($this->parsedResponse->response) { case 1: break; case 2: $err = "Transaction Declined."; break; case 3: $err = "Error in transaction data or system error."; break; default: $err = "Unknown error num: " . $this->parsedResponse->response . "."; break; } if (!empty($err)) { return $this->result->setFailed(array($err, $this->parsedResponse->responsetext)); } $this->result->setSuccess($this); } protected function setEcheck(EcheckRecord $echeck) { $this->request->addPostParameter('checkaccount', $echeck->echeck_ban); $this->request->addPostParameter('checkaba', $echeck->echeck_aba); $this->request->addPostParameter('checkname', $echeck->echeck_bank_name); $this->request->addPostParameter('account_holder_type', $echeck->echeck_type); $this->request->addPostParameter('account_type', 'checking'); $this->request->addPostParameter('payment', 'check'); $this->request->addPostParameter('firstname', $echeck->echeck_name_f); $this->request->addPostParameter('lastname', $echeck->echeck_name_l); $this->request->addPostParameter('address1', $echeck->echeck_street); $this->request->addPostParameter('city', $echeck->echeck_city); $this->request->addPostParameter('state', $echeck->echeck_state); $this->request->addPostParameter('zip', $echeck->echeck_zip); $this->request->addPostParameter('country', $echeck->echeck_country); $this->request->addPostParameter('phone', $echeck->echeck_phone); } public function getCustomerVaultId() { return $this->parsedResponse->customer_vault_id; } } class Echeck_Networkmerchants_AddCustomer extends Am_Paysystem_Transaction_Echeck_Networkmerchants { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, EcheckRecord $echeck) { parent::__construct($plugin, $invoice, true); $this->setEcheck($echeck); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('customer_vault', 'add_customer'); } public function processValidated(){} // no process payment } class Echeck_Networkmerchants_Sale extends Am_Paysystem_Transaction_Echeck_Networkmerchants { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst, $customerVaultId) { parent::__construct($plugin, $invoice, $doFirst); $this->request->addPostParameter('customer_vault_id', $customerVaultId); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('amount', $this->getAmount()); $this->request->addPostParameter('type', 'sale'); } } class Echeck_Networkmerchants_Refund extends Am_Paysystem_Transaction_Echeck_Networkmerchants { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $transactionId, $amount, $customerVaultId) { $this->amount = $amount; parent::__construct($plugin, $invoice, true); $this->request->addPostParameter('transactionid', $transactionId); $this->request->addPostParameter('customer_vault_id', $customerVaultId); } public function getAmount() { return $this->amount; } public function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('type', 'refund'); $this->request->addPostParameter('amount', $this->getAmount()); } public function processValidated(){} // no process payment } class Echeck_Networkmerchants_Authorization extends Am_Paysystem_Transaction_Echeck_Networkmerchants { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, EcheckRecord $echeck, $amount = '1.00') { $this->amount = $amount; parent::__construct($plugin, $invoice, true); $this->setEcheck($echeck); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('type', 'auth'); $this->request->addPostParameter('amount', $this->amount); $this->request->addPostParameter('customer_vault', 'add_customer'); } public function processValidated(){} // no process payment } class Echeck_Networkmerchants_Void extends Am_Paysystem_Transaction_Echeck_Networkmerchants { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $transactionId, $customerVaultId) { parent::__construct($plugin, $invoice, true); $this->request->addPostParameter('transactionid', $transactionId); $this->request->addPostParameter('customer_vault_id', $customerVaultId); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('type', 'void'); $this->request->addPostParameter('amount', 1.00); } public function processValidated(){} // no process payment } class Echeck_Networkmerchants_Capture extends Am_Paysystem_Transaction_Echeck_Networkmerchants { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst, $transactionid) { parent::__construct($plugin, $invoice, $doFirst); $this->request->addPostParameter('transactionid', $transactionid); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('amount', $this->getAmount()); $this->request->addPostParameter('type', 'capture'); } } Nmi.php000064400000013473152101614250006005 0ustar00addText('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); } } Action/Form.php000064400000003025152101614250007372 0ustar00 headers and common fields * like "confirm" * * @package Am_Paysystem */ class Am_Paysystem_Action_Form extends Am_Paysystem_Action_Redirect { protected $_autoSubmit = true; protected $_displayReceipt = false; protected $_prolog; protected $_epilog; function getVars() { return $this->_params; } function getUrl() { return $this->_url; } function setDisplayReceipt(Invoice $invoice) { $this->_displayReceipt = $invoice; return $this; } function setProlog($html) { $this->_prolog = $html; return $this; } function setEpilog($html) { $this->_epilog = $html; return $this; } function setAutoSubmit($flag) { $this->_autoSubmit = $flag; return $this; } /** * @param Am_Mvc_Controller $action * @throws Am_Exception_Redirect */ public function process(/*Am_Mvc_Controller*/ $action = null) { $action->view->url = $this->getURL(); $action->view->prolog = $this->_prolog; $action->view->epilog = $this->_epilog; $action->view->vars = $this->getVars(); if ($this->_displayReceipt) { $action->view->invoice = $this->_displayReceipt; } $action->view->autoSubmit = $this->_autoSubmit; $action->render('payment', '', true); throw new Am_Exception_Redirect($this->getURL()); } }Action/HtmlTemplate.php000064400000002213152101614250011065 0ustar00_template = $template; } /** * @param Am_Mvc_Controller $action * @throws Am_Exception_Redirect */ public function process(/*Am_Mvc_Controller*/ $action = null) { $action->view->assign($this->getVars()); $action->renderScript($this->_template); throw new Am_Exception_Redirect; } function getVars() { $ret = array(); foreach ($this as $k => $v) if ($k[0] != '_') $ret[$k] = $v; return $ret; } public function toXml(XMLWriter $x) { $x->startElement('template');$x->text($this->_template);$x->endElement(); $x->startElement('params'); foreach ($this->getVars() as $k => $v) { $x->startElement('param'); $x->writeAttribute('name', $k); $x->text($v); $x->endElement(); } $x->endElement(); } }Action/Redirect.php000064400000003627152101614250010240 0ustar00setUrl($url); } function __set($k, $v) { return $this->addParam($k, $v); } function __get($k) { return array_key_exists($k, $this->_params) ? $this->_params[$k] : null; } function filterEmpty() { foreach ($this->_params as $k => $v) if (!strlen($v)) unset($this->_params[$k]); return $this; } function getUrl() { $url = $this->_url; if ($this->_params) { $url = rtrim($url, '?&'); $url .= strpos($url, '?')===false ? '?' : '&'; foreach ($this->_params as $k => $v) $url .= urlencode($k) . '=' . urlencode($v) . '&'; $url = rtrim($url, '&') ; } return $url; } function setUrl($url) { $this->_url = $url; } function addParam($k,$v) { $this->_params[$k] = $v; return $this; } /** * @param Am_Mvc_Controller $controller */ public function process(/*Am_Mvc_Controller*/ $controller = null) { if ($controller === null) Am_Mvc_Response::redirectLocation ($this->getUrl()); else $controller->getResponse()->redirectLocation($this->getUrl()); } public function toXml(XMLWriter $x) { $x->writeElement('url', $this->_url); $x->startElement('params'); foreach ($this->_params as $k => $v) { $x->startElement('param'); $x->writeAttribute('name', $k); $x->text($v); $x->endElement(); } $x->endElement(); } } Description.php000064400000002044152101614250007535 0ustar00paysys_id = $id; $this->title = $title; $this->description = $description; $this->recurring = $recurring; } function setPublic($flag){ $this->public = (bool)$flag; } function getId(){ return $this->paysys_id; } function getTitle(){ return $this->title; } function getDescription(){ return $this->description; } function isPublic(){ return (bool)$this->public; } function isRecurring(){ return (bool)$this->recurring; } function toArray(){ return (array)$this; } function fromArray(array $arrayDesc){ foreach ($arrayDesc as $k => $v) $this->$k = $v; } }Abstract.php000064400000056415152101614250007030 0ustar00signup processflow * @var bool */ protected $_canAutoCreate = false; /** * Set to true in subclass to enable IPN resending * @var bool */ protected $_canResendPostback = false; /** * Constructor * @param array $config */ function __construct(Am_Di $di, array $config) { parent::__construct($di, $config); /** @todo remove this crap */ $ps = new Am_Paysystem_Description( $this->getId(), $this->getTitle(), $this->getDescription(), $this->isRecurring()); $ps->setPublic(true); $di->paysystemList->add($ps); ///////////////////////////// } function storesCcInfo(){ return false; } /** * Ability to change amount for refunds from the backend */ function allowPartialRefunds() { return false; } /** * If payment failed customer will be redirected to /cancel page with invoice_id as parameter */ function supportsCancelPage() { return true ; } /** * @return array of 3-letter ISO currency codes supported by this payment system like array('USD', 'EUR'); */ function getSupportedCurrencies() { return array(Am_Currency::getDefault()); } protected function _afterInitSetupForm(Am_Form_Setup $form) { if ($this->canAutoCreate()) $form->addAdvCheckbox('auto_create')->setLabel(___("Accept Direct Payments\n". "handle payments made on payment system side\n". "(without signup to aMember first)")); if($this->supportsCancelPage()) $form->addMagicselect('cancel_paysys_list') ->setLabel(___("Fallback paysystems\n". "if invoice was started with %s \n". "and user canceled payment process OR payment was failed.\n". "By default all enabled paysystems will be listed", $this->getTitle())) ->loadOptions(Am_Di::getInstance()->paysystemList->getOptionsPublic()); return parent::_afterInitSetupForm($form); } protected function _beforeInitSetupForm() { $form = parent::_beforeInitSetupForm(); $form->setTitle($this->getConfigPageId()); $plugin = $this->getId(); $form->addText('title', array('class' => 'el-wide')) ->setLabel(___('Payment System Title')); $form->setDefault("payment.$plugin.title", @$this->defaultTitle); $form->addText('description', array('class' => 'el-wide')) ->setLabel(___('Payment System Description')); $form->setDefault("payment.$plugin.description", @$this->defaultDescription); /* $form->addAdvCheckbox("disable_postback_log") ->setLabel(___("Disable PostBack messages Logging (not recommended)\n". "By default aMember logs all payment system postback messages\n". "you can disable it by changing this configuration value" )); */ if ($this->canResendPostback()) { $gr = $form->addGroup(); $gr->setLabel(___("Resend Postback\nenter list of URLs to resend incoming postback")); $gr->addAdvCheckbox('resend_postback', array('id' => 'resend_postback')); $gr->addTextarea('resend_postback_urls', array('rows' => 3, 'cols' => 70, 'class'=>'one-per-line')); $gr->addScript() ->setScript(<<defaultTitle, $this->getId(true)); } /** * @return bool */ function isRecurring() { return $this->getRecurringType() && $this->getRecurringType() != self::REPORTS_NOT_RECURRING; } function isRefundable(InvoicePayment $payment) { $rm = new ReflectionMethod(get_class($this), 'processRefund'); return $rm->getDeclaringClass()->getName() != __CLASS__; } /** * Must report one from Am_Payment_Abstract::REPORTS_xx constants * @abstract */ function getRecurringType() {} /** * get payment system title * @return string */ function getTitle() { return $this->getConfig('title', $this->defaultTitle); } /** * get payment system description * @return string */ function getDescription(){ return $this->getConfig('description', $this->defaultDescription); } /** * Check if the $invoice can be processed by given * payment processor. Example checks may be for changed * price of products, user country, recurring billing, * trials, and so on. * * Default function checks if given invoice has no not-zero amounts * * @param Invoice $invoice * @return null|array of translated error messages if process could not be used */ public function isNotAcceptableForInvoice(Invoice $invoice) { if ($invoice->isZero()) return array(___('This payment system could not handle zero-total invoices')); $supportedCurrencies = $this->getSupportedCurrencies(); if ($supportedCurrencies && !in_array($invoice->currency, $supportedCurrencies)) return array(___('This payment system could not handle payments in [%s] currency', $invoice->currency)); if ((float)$invoice->second_total && !$this->isRecurring()) { return array(___('This payment system could not handle recurring subscriptions')); } } /** * process payment * this will set $result to * action with * Zend_Form with defined defaults (with result code?!) * Zend_Response_Http with redirect ? * and/or * OK result code with Am_Paysystem_Transaction_Abstract * Fatal Failure result code Am_Paysystem_Transaction_Abstract * Fixable Failure result code with Am_Paysystem_Transaction_Abstract * @param Invoice invoice record * @param Am_Mvc_Request $request * @param Am_Paysystem_Result $result * @return must be ignored * @access protected will be called from * @abstract */ function _process(/*Invoice*/ $invoice, /*Am_Mvc_Request*/ $request, /*Am_Paysystem_Result*/ $result){} /** * Process refund if that is possible * @param InvoicePayment $payment * @param Am_Paysystem_Result $result - returned result * @param amount - refund amount, in payment currency * @throws Am_Exception_Paysystem_NotImplemented * * @return nothing, check $result */ function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { throw new Am_Exception_Paysystem_NotImplemented; } /** * Process payment * this is final, override @see process method instead * @param Invoice invoice record * @param Am_Mvc_Request $request user submitted data * @param Am_Paysystem_Result $ret may be skipped, then processInvoice will instantiate new * @return Am_Paysystem_Result */ public function processInvoice(/*Invoice*/ $invoice, /*Am_Mvc_Request*/ $request, /*Am_Paysystem_Result*/ & $ret = null){ if (null == $ret) $ret = new Am_Paysystem_Result; $this->_setInvoice($invoice); $errors = $this->isNotAcceptableForInvoice($invoice); if (null != $errors) return $ret->setFailed($errors); try { $this->_process($invoice, $request, $ret); } catch (Am_Exception_Redirect $e) { // pass } catch (Am_Exception $e) { $this->log("Exception in " . __METHOD__, $e); $ret->setFailed(array( ___("Payment error: ") . $e->getPublicError() )); } return $ret; } /** * Log something related to paysystem * Why is it a separate function here? to make payment plugins working * outside of aMember ... someday * @param string $logTitle * @param array|string $logDetails * @param string $logType 'request', 'response', 'other' * @access protected * @return InvoiceLog */ private function log($logTitle, $logDetails, $logType = self::LOG_REQUEST) { $log = $this->getDi()->invoiceLogTable->createRecord(); if ($this->getConfig('disable_postback_log')) $log->toggleDisablePostbackLog(true); if ($this->invoice) $log->setInvoice($this->invoice); $log->paysys_id = $this->getId(); $log->remote_addr = $_SERVER['REMOTE_ADDR']; $log->type = $logType; $log->title = $logTitle; $log->add($logDetails); return $log; } function logRequest($logDetails, $logTitle="REQUEST") { return $this->log($logTitle, $logDetails, self::LOG_REQUEST); } function logResponse($logDetails, $logTitle="RESPONSE") { return $this->log($logTitle, $logDetails, self::LOG_RESPONSE); } function logError($logTitle, $logDetails = null) { return $this->log($logTitle, $logDetails, self::LOG_ERROR); } function logOther($logTitle, $logDetails = null) { return $this->log($logTitle, $logDetails, self::LOG_OTHER); } /** * Lazy-init the http client * @return Am_HttpRequest */ function createHttpRequest() { if (!empty($this->_httpRequest)) if (count($this->_httpRequest) == 1) // last one return clone(current($this->_httpRequest)); else return array_shift($this->_httpRequest); return new Am_HttpRequest; } /** * Set the next http client to use (mostly for unit-testing) */ function _setHttpRequest(Am_HttpRequest $httpRequest) { $this->_httpRequest[] = $httpRequest; } /** * Url where customer should be returned by paysystem * in case of successful payment * @param Am_Mvc_Request $request * @return string Full URL */ function getReturnUrl(/*Am_Mvc_Request*/ $request = null) { return $this->getRootUrl() . "/thanks?id=" . $this->invoice->getSecureId("THANKS"); } /** * Url where customer should be returned by paysystem * in case of cancelled payment * @param Am_Mvc_Request $request * @return string Full URL */ function getCancelUrl(/*Am_Mvc_Request*/ $request = null) { return $this->getRootUrl() . "/cancel?id=" . $this->invoice->getSecureId('CANCEL'); } /** * Return Root URL of aMember script * use it instead of global methods to make plugins more universal * (that it can be used in other enviroments) * @return string root_url */ function getRootUrl() { return ROOT_SURL; } /** * Return URL of plugin "self-handled" page * @param action (will be passed as 'a' parameter) * @return string */ function getPluginUrl($action = null) { $ret = $this->getDi()->url('payment/' . $this->getId(), null, false, true); if ($action !== null) $ret .= '/' . urlencode($action); return $ret; } /** * For testing purproses */ function _setInvoice(Invoice $invoice = null) { $this->invoice = $invoice; } /** Create transaction object based on request or return null if nope * @param Am_Mvc_Request $request * @param Am_Mvc_Response $response * @param array $invokeArgs * @return Am_Paysystem_Transaction_Incoming|null * @abstract */ function createTransaction(/*Am_Mvc_Request*/ $request, /*Am_Mvc_Response*/ $response, array $invokeArgs) {} /** * Override this function to handle /amember/payment/paysysid/thanks page * @param Am_Mvc_Request $request * @param Am_Mvc_Response $response * @param array $invokeArgs * @link thanksAction */ function createThanksTransaction(/*Am_Mvc_Request*/ $request, /*Am_Mvc_Response*/ $response, array $invokeArgs) { throw new Am_Exception_Paysystem_NotImplemented("To handle [thanks] requests, paysystem plugin must override " . __METHOD__); } /** * By default this method handles request as IPN * If actionName=='thanks', it is handled by thanksAction() handler (override createThanksTransaction for that) * @param Am_Mvc_Request $request * @param Am_Mvc_Response $response * @param array $invokeArgs */ public function directAction(/*Am_Mvc_Request*/ $request, /*Am_Mvc_Response*/ $response, $invokeArgs) { $actionName = $request->getActionName(); switch ($actionName) { case 'thanks': $this->thanksAction($request, $response, $invokeArgs); break; case 'cancel': $invoice = $this->getDi()->invoiceTable->findBySecureId($request->getFiltered('id'), 'STOP'.$this->getId()); if (!$invoice) throw new Am_Exception_InputError("No invoice found [$id]"); $result = new Am_Paysystem_Result(); $result->setSuccess(); $this->cancelAction($invoice, $request->getActionName(), $result); if ($result->isSuccess()) { $invoice->setCancelled(true); return $response->redirectLocation($this->getDi()->url('member/payment-history',null,false)); } elseif ($result->isAction()) { $action = $result->getAction(); $action->process(); // I cannot imaginge anything but redirect here... yet? :) } else { throw new Am_Exception_InputError(___("Unable to cancel subscription: " . $result->getLastError())); } break; default: // standard action handling via transactions $invoiceLog = $this->_logDirectAction($request, $response, $invokeArgs); $transaction = $this->createTransaction($request, $response, $invokeArgs); if (!$transaction) { throw new Am_Exception_InputError("Request not handled - createTransaction() returned null"); } $transaction->setInvoiceLog($invoiceLog); try { $transaction->process(); } catch (Exception $e) { if ($invoiceLog) $invoiceLog->add($e); throw $e; } if ($invoiceLog) $invoiceLog->setProcessed(); } } /** * Handle thanks action request * @param Am_Mvc_Request $request * @param Am_Mvc_Response $response * @param array $invokeArgs */ public function thanksAction(/*Am_Mvc_Request*/ $request, /*Am_Mvc_Response*/ $response, array $invokeArgs) { $log = $this->logRequest($request); try { $transaction = $this->createThanksTransaction($request, $response, $invokeArgs); } catch (Am_Exception_Paysystem_NotImplemented $e) { $this->logError("[thanks] page handling is not implemented for this plugin. Define [createThanksTransaction] method in plugin"); throw $e; } $transaction->setInvoiceLog($log); try { $transaction->process(); } catch (Am_Exception_Paysystem_TransactionAlreadyHandled $e) { // ignore this error, as it happens in "thanks" transaction // we may have received IPN about the payment earlier } catch (Exception $e) { throw $e; } $log->setInvoice($transaction->getInvoice())->update(); $this->invoice = $transaction->getInvoice(); $response->setRedirect($this->getReturnUrl()); } /** Handle user-triggered cancel of recurring subscription */ public function cancelAction(Invoice $invoice, $actionName, Am_Paysystem_Result $result) { $result->setAction( new Am_Paysystem_Action_Redirect( $actionName == 'cancel-admin' ? $this->getAdminCancelUrl($invoice) : $this->getUserCancelUrl($invoice) ) ); } /** * @param Am_Mvc_Request $request * @param Am_Mvc_Response $response * @param array $invokeArgs * @return type */ protected function _logDirectAction(/*Am_Mvc_Request*/ $request, /*Am_Mvc_Response*/ $response, array $invokeArgs) { return $this->logRequest ($request, "POSTBACK [" . htmlentities($request->getActionName()) . "]"); } public function toggleDisablePostbackLog($flag) { $prev = (bool)@$this->config['disable_postback_log']; $this->config['disable_postback_log'] = (bool)$flag; return $prev; } /** * Return a link to stop recurring subscription * @param Invoice $invoice * @return string|null */ public function getAdminCancelUrl(Invoice $invoice) { $m = new ReflectionMethod($this, 'cancelAction'); if ($m->getDeclaringClass()->getName() == __CLASS__) return; return $this->getDi()->url('admin-user-payments/stop-recurring/user_id/'.$invoice->user_id,array('invoice_id'=>$invoice->pk())); } /** * Return a link to stop recurring subscription * @param Invoice $invoice * @return string|null */ public function getUserCancelUrl(Invoice $invoice) { $m = new ReflectionMethod($this, 'cancelAction'); if ($m->getDeclaringClass()->getName() == __CLASS__) return; return $this->getDi()->url('payment/'.$this->getId().'/cancel',array('id'=>$invoice->getSecureId('STOP'.$this->getId()))); } public function getUserRestoreUrl(Invoice $invoice) { try{ $newInvoice = $invoice->doRestoreRecurring($invoice); $newInvoice->setPaysystem($this->getId()); $err = $newInvoice->validate(); if ($err) throw new Am_Exception_InputError($err[0]); }catch(Exception $e){ return; // Unable to get Restored invoice for some reason. Do not show link; } return $this->getDi()->url('member/restore-recurring',array('invoice_id'=>$invoice->public_id)); } public function changeSubscription(Invoice $invoice, InvoiceItem $item, BillingPlan $newBillingPlan) { } /** * Return array of brick names to be hidden and not validated if * given payment system is selected * @example array('name', 'email') * @return array of brick names */ public function hideBricks() { return array(); } /** * Display Thanks page for given Invoice * @param type $request * @param Am_Mvc_Response $response * @param array $invokeArgs * @param Invoice $invoice * @return type */ protected function displayThanks(/*Am_Mvc_Request*/ $request, /*Am_Mvc_Response*/ $response, array $invokeArgs, Invoice $invoice = null) { if ($invoice !== null) $request->setParam('id', $invoice->getSecureId('THANKS')); /// require_once AM_APPLICATION_PATH . '/default/controllers/ThanksController.php'; $request->setActionName('index'); $c = new ThanksController($request, $response, $invokeArgs); return $c->run($request, $response); } /** @return bool true if plugin is able to create customers without signup */ public function canAutoCreate() { return $this->_canAutoCreate; } public function canResendPostback() { return $this->_canResendPostback; } /** * * @param Invoice $invoice * @param BillingPlan $from * @param BillingPlan $to * @return boolean */ public function canUpgrade(Invoice $invoice, InvoiceItem $item, ProductUpgrade $upgrade) { return true; } /** * Upgrade billing plan in subscription from one to another * @param Invoice $invoice * @param BillingPlan $from * @param BillingPlan $to * @throws Am_Exception_NotImplemented */ public function doUpgrade(Invoice $invoice, InvoiceItem $item, Invoice $newInvoice, ProductUpgrade $upgrade) { throw new Am_Exception_NotImplemented("doUpgrade not implemented"); } } PaypalApiRequest.php000064400000034740152101614250010513 0ustar00plugin = $plugin; $cert_file = AM_APPLICATION_PATH . '/configs/cert_key_pem.txt'; if(is_file($cert_file) && is_readable($cert_file)){ $this->use_cert = true; } if(!$this->use_cert) $url = $this->plugin->getConfig('testing') ? self::SANDBOX_URL : self::LIVE_URL; else $url = $this->plugin->getConfig('testing') ? self::CERT_SANDBOX_URL : self::CERT_LIVE_URL; parent::__construct($url, self::METHOD_POST); if ($adapter = $this->plugin->createHttpRequest()->getConfig('adapter')) $this->setConfig('adapter', $adapter); // Check certificate file. if($this->use_cert) $this->setConfig('ssl_local_cert', $cert_file); // Check certificate file. $ca_file = AM_APPLICATION_PATH . "/configs/api_cert_chain.crt"; if($this->use_cert && is_file($ca_file) && is_readable($ca_file)) $this->setConfig('ssl_cafile', $cert_file); if(!$this->use_cert) $this->addPostParameter('SIGNATURE', $this->plugin->getConfig('api_signature')); $this->addPostParameter('VERSION', '63.0') ->addPostParameter('BUTTONSOURCE', 'CgiCentral.aMemberPro') ->addPostParameter('USER', $this->plugin->getConfig('api_username')) ->addPostParameter('PWD', $this->plugin->getConfig('api_password')); } /** * Fills all but user and cc properties of request * @param Am_HttpRequest $this * @param Invoice $invoice */ function doSale(Invoice $invoice, CcRecord $cc) { $this->addPostParameter('METHOD', 'DoDirectPayment'); if ($invoice->first_total > 0 ) { $this->addPostParameter('PAYMENTACTION', 'Sale'); $this->addPostParameter('AMT', $invoice->first_total); } else { $this->addPostParameter('PAYMENTACTION', 'Sale'); $this->addPostParameter('AMT', 0.01); } $this->addPostParameter('DESC', $invoice->getLineDescription()); $this->addPostParameter('RETURNFMFDETAILS', 0); $this->addPostParameter('INVNUM', $invoice->getSecureId('paypal')); $this->setCc($invoice, $cc); if($this->plugin->getConfig('send_shipping')){ $this->setShippingAddress($invoice); } } function setShippingAddress(Invoice $invoice){ $this->addPostParameter('SHIPTONAME', $invoice->getName()); $this->addPostParameter('SHIPTOSTREET', $invoice->getStreet()); $this->addPostParameter('SHIPTOCITY', $invoice->getCity()); $this->addPostParameter('SHIPTOSTATE', $invoice->getState()); $this->addPostParameter('SHIPTOZIP', $invoice->getZip()); $this->addPostParameter('SHIPTOCOUNTRY', $invoice->getCountry()); $this->addPostParameter('SHIPTOPHONENUM', $invoice->getPhone()); } function setCc(Invoice $invoice, CcRecord $cc) { $this->addPostParameter('IPADDRESS', $_SERVER['REMOTE_ADDR']); $this->addPostParameter('CREDITCARDTYPE', $cc->cc_type); $this->addPostParameter('ACCT', $cc->cc_number); $this->addPostParameter('CURRENCYCODE', $invoice->currency); // @todo $this->addPostParameter('EXPDATE', $cc->getExpire("%02d20%02d")); $this->addPostParameter('CVV2', $cc->getCvv()); $this->addPostParameter('FIRSTNAME', $cc->cc_name_f); $this->addPostParameter('LASTNAME', $cc->cc_name_l); $this->addPostParameter('STREET', $cc->cc_street); $this->addPostParameter('CITY', $cc->cc_city); $this->addPostParameter('STATE', $cc->cc_state); $this->addPostParameter('ZIP', $cc->cc_zip); $this->addPostParameter('PHONENUM', $cc->cc_phone); $this->addPostParameter('COUNTRYCODE', strtoupper($cc->cc_country)); $this->addPostParameter('EMAIL', $invoice->getEmail()); return $this; } function cancelRecurringPaymentProfile(Invoice $invoice, $profile_id){ $this->addPostParameter('METHOD', 'ManageRecurringPaymentsProfileStatus'); $this->addPostParameter('ACTION', 'Cancel'); $this->addPostParameter('PROFILEID', $profile_id); $this->addPostParameter('Note', sprintf('Cancelled by customer IP: %s', Am_Di::getInstance()->request->getHttpHost())); } function createRecurringPaymentProfile(Invoice $invoice, CcRecord $cc = null, $token = null, $payerId = null) { if (!$cc && !$token) throw new Am_Exception_Paysystem("Either [token] or [cc] must be specified for " . __METHOD__ ); $periodConvert = array( Am_Period::DAY => 'Day', Am_Period::MONTH => 'Month', Am_Period::YEAR => 'Year', ); $this->addPostParameter('METHOD', 'CreateRecurringPaymentsProfile'); if ($token) { $this->addPostParameter('TOKEN', $token); $this->addPostParameter('PAYERID', $payerId); } $this->addPostParameter('DESC', $invoice->getTerms()); $this->addPostParameter('PROFILESTARTDATE', gmdate('Y-m-d\TH:i:s.00\Z' , strtotime($invoice->calculateRebillDate(1) . ' 00:00:01') )); $this->addPostParameter('PROFILEREFERENCE', $invoice->getRandomizedId('site')); //$this->addPostParameter('MAXFAILEDPAYMENTS', ''); //$this->addPostParameter('AUTOBILLOUTAMT', 'AddToNextBilling'); $p = new Am_Period($invoice->first_period); $pp = $periodConvert[$p->getUnit()]; if (!$pp) throw new Am_Exception_Configuration("Could not find billing unit for invoice#{$invoice->invoice_id}.first_period: {$invoice->first_period}"); /// first period - removed as handled with START_DATE //$this->addPostParameter('TRIALBILLINGPERIOD', $pp); //$this->addPostParameter('TRIALBILLINGFREQUENCY', $p->getCount()); //$this->addPostParameter('TRIALTOTALBILLINGCYCLES', '1'); //$this->addPostParameter('TRIALAMT', $invoice->second_total); // bill at the end of trial period // it may take up to 24hours to process it! so enabled only for credit card payments if ($cc && ($invoice->first_total > 0)) $this->addPostParameter('INITAMT', $invoice->first_total); // bill right now /// second period $p = new Am_Period($invoice->second_period); $pp = $periodConvert[$p->getUnit()]; if (!$pp) throw new Am_Exception_Configuration("Could not find billing unit for invoice#{$invoice->invoice_id}.second_period: {$invoice->second_period}"); $this->addPostParameter('BILLINGPERIOD', $pp); $this->addPostParameter('BILLINGFREQUENCY', $p->getCount()); if ($invoice->rebill_times != IProduct::RECURRING_REBILLS) $this->addPostParameter('TOTALBILLINGCYCLES', $invoice->rebill_times); $this->addPostParameter('AMT', $invoice->second_total - $invoice->second_tax); // bill at end of each payment period $this->addPostParameter('TAXAMT', $invoice->second_tax); $this->addPostParameter('CURRENCYCODE', $invoice->currency); // @todo $this->addPostParameter('NOTIFYURL', $this->plugin->getPluginUrl('ipn')); $i = 0; foreach ($invoice->getItems() as $item) { /* @var $item InvoiceItem */ $this->addPostParameter("L_PAYMENTREQUEST_0_NAME$i", $item->item_title); $this->addPostParameter("L_PAYMENTREQUEST_0_NUMBER$i", $item->item_id); $this->addPostParameter("L_PAYMENTREQUEST_0_QTY$i", $item->qty); $i++; } $this->addPostParameter('L_BILLINGTYPE0', 'RecurringPayments'); $this->addPostParameter('L_BILLINGAGREEMENTDESCRIPTION0', $invoice->getTerms()); if ($cc) $this->setCC($invoice, $cc); if($this->plugin->getConfig('send_shipping')) $this->setShippingAddress($invoice); return $this; } function _setExpressAmounts(Invoice $invoice) { $this->addPostParameter('PAYMENTREQUEST_0_AMT', $invoice->first_total); $this->addPostParameter('PAYMENTREQUEST_0_CURRENCYCODE', $invoice->currency); // @todo $this->addPostParameter('PAYMENTREQUEST_0_ITEMAMT', $invoice->first_total - $invoice->first_tax); // $this->addPostParameter('PAYMENTREQUEST_0_SHIPPINGAMT', $invoice->first_shipping); $this->addPostParameter('PAYMENTREQUEST_0_TAXAMT', $invoice->first_tax); $this->addPostParameter('PAYMENTREQUEST_0_INVNUM', $invoice->getSecureId('paypal')); $this->addPostParameter('PAYMENTREQUEST_0_NOTIFYURL', $this->plugin->getPluginUrl('ipn')); $this->addPostParameter('PAYMENTREQUEST_0_PAYMENTACTION', 'Sale'); $i = 0; foreach ($invoice->getItems() as $item) { /* @var $item InvoiceItem */ $this->addPostParameter('L_PAYMENTREQUEST_0_NAME'.$i, $item->item_title); $this->addPostParameter('L_PAYMENTREQUEST_0_AMT'.$i, moneyRound(($item->first_total - $item->first_tax )/ $item->qty)); // $this->addPostParameter('L_PAYMENTREQUEST_0_ITEMAMT'.$i, $item->getFirstSubtotal()); // $this->addPostParameter('L_PAYMENTREQUEST_0_NUMBER'.$i, $item->item_id); $this->addPostParameter('L_PAYMENTREQUEST_0_QTY'.$i, $item->qty); // $this->addPostParameter('L_PAYMENTREQUEST_0_TAXAMT'.$i, $item->first_tax); /// The unique non-changing identifier for the seller at the marketplace site. This ID is not displayed. //$this->addPostParameter('L_PAYMENTREQUEST_0_SELLERID'.$i, ); // PAYMENTREQUEST_n_SELLERPAYPALACCOUNTID $i++; } if ($invoice->rebill_times) { $this->addPostParameter('L_BILLINGTYPE0', 'RecurringPayments'); $this->addPostParameter('L_BILLINGAGREEMENTDESCRIPTION0', $invoice->getTerms()); } } function setExpressCheckout(Invoice $invoice) { $this->addPostParameter('METHOD', 'SetExpressCheckout'); $this->addPostParameter('RETURNURL', $this->plugin->getPluginUrl('express-checkout')); $this->addPostParameter('CANCELURL', $this->plugin->getCancelUrl()); //$this->addPostParameter('REQCONFIRMSHIPPING', 0); if (!$invoice->hasShipping()) $this->addPostParameter('NOSHIPPING', 1); //$this->addPostParameter('LOCALECODE', ''); //$this->addPostParameter('PAGESTYLE', ''); // htmlvariable page_style // $this->addPostParameter('HDRIMG', ''); // $this->addPostParameter('EMAIL', $invoice->getEmail()); $this->addPostParameter('SOLUTIONTYPE', 'Sole'); $this->addPostParameter('LANDINGPAGE', 'Billing'); $this->addPostParameter('CURRENCYCODE', $invoice->currency); $this->_setExpressAmounts($invoice); } public function getExpressCheckoutDetails($token) { $this->addPostParameter('METHOD', 'GetExpressCheckoutDetails'); $this->addPostParameter('TOKEN', $token); } public function doExpressCheckout(Invoice $invoice, $token, $payerId) { $this->addPostParameter('METHOD', 'DoExpressCheckoutPayment'); $this->addPostParameter('TOKEN', $token); $this->addPostParameter('PAYERID', $payerId); $this->addPostParameter('PAYMENTREQUEST_0_NOTIFYURL', $this->plugin->getPluginUrl('ipn')); //$this->addPostParameter('PAYMENTACTION', 'Sale'); $this->addPostParameter('PAYMENTREQUEST_0_PAYMENTACTION', 'Sale'); $this->_setExpressAmounts($invoice); } public function refundTransaction(InvoicePayment $payment, $amount = null) { $this->addPostParameter('METHOD', 'RefundTransaction'); $this->addPostParameter('TRANSACTIONID', $payment->transaction_id); if (!is_null($amount) && $payment->amount != $amount) { $this->addPostParameter('REFUNDTYPE', 'Partial'); $this->addPostParameter('AMT', $amount); $this->addPostParameter('CURRENCYCODE', $payment->currency); } else { $this->addPostParameter('REFUNDTYPE', 'Full'); } $this->addPostParameter('NOTE', sprintf('Transaction Refund from aMember (IP: %s)', Am_Di::getInstance()->request->getClientIp())); } /*** * Add fields specific for PayPal API */ static function initSetupForm(Am_Form_Setup $form) { $form->addText("business", array('size'=>40))->setLabel("Primary Paypal E-Mail Address"); $form->addTextarea("alt_business", array('cols'=>40, 'rows'=>3,)) ->setLabel("Alternate PayPal account emails (one per line)"); $form->addText("api_username", array('size'=>40))->setLabeL("API Username"); $form->addPassword("api_password")->setLabel("API Password"); $form->addText("api_signature", array('size'=>60))->setLabel("API Signature"); $form->addAdvCheckbox("testing")->setLabel("Sandbox", "is test or live transaction"); $form->addText("brandname", array('class'=>'el-wide'))->setLabel("Brand Name\nshown on Paypal checkout page as 'Return to {NAME}'.\nDefault is Paypal account name."); $form->addAdvCheckbox("landingpage_login")->setLabel("Expand 'Login to Paypal' first on Paypal\nby default Paypal expands the long non-account (guest) form"); } /** * Send response handle failure, return parsed array * @return array */ public function sendRequest(InvoiceLog $log) { $log->paysys_id = $this->plugin->getId(); $log->add($this); $response = $this->send(); $log->add($response); if ($response->getStatus()!=200) throw new Am_Exception_InputError("Error communicating to PayPal, unable to finish transaction. Your account was not billed, please try again"); parse_str($response->getBody(), $vars); if (!count($vars)) throw new Am_Exception_InputError("Error communicating to PayPay, unable to parse response "); return $vars; } }Action.php000064400000000702152101614250006466 0ustar00_di = $di; } /** * Trigger loading of all plugin to get * entires added */ protected function loadAllEnabled() { if ($this->loaded) return; $this->_di->plugins_payment->loadEnabled(); $this->_di->plugins_payment->getAllEnabled(); $this->loaded = true; } function getList() { return $this->list; } /** * @param Am_Paysystem_Description $record */ function add($record) { array_push($this->list, $record); } function makeFirst($id) { $first = null; foreach ($this->list as $k => $p) if ($p->getId() == $id) { $first = $p; unset($this->list[$k]); break; } if ($first) array_unshift($this->list, $first); } /** * @param string */ function delete($id) { foreach ($this->list as $k => $p) if ($p->getId() == $id) unset($this->list[$k]); } /** * @return Am_Paysystem_Description * @param id */ function get($id) { $this->loadAllEnabled(); foreach ($this->list as $k => $p) if ($p->getId() == $id) return $p; } function getAll() { $this->loadAllEnabled(); return $this->list; } function getAllPublic() { $ret = array(); $free = null; foreach ($this->getAll() as $p) { if ($p->isPublic()) if ($p->getId() == 'free') $free = $p; else $ret[] = $p; } // usually we hide 'free' option, but if no options, we will show it if (!$ret && $free) $ret[] = $free; return $ret; } /** * @return true if enabled and public */ function isPublic($paysysId) { foreach ($this->getAll() as $p) if ($p->isPublic() && $p->getId() == $paysysId) return true; return false; } function getAllPublicAsArrays() { return array_map(function($p) {return $p->toArray();}, $this->getAllPublic()); } /** * @return array key=>title for admin */ function getOptions() { $ret = array(); foreach ($this->getAll() as $p) $ret[ $p->getId() ] = $p->getTitle(); return $ret; } /** * @return array key=>title for customers */ function getOptionsPublic() { $ret = array(); foreach ($this->getAllPublic() as $p) $ret[ $p->getId() ] = $p->getTitle(); return $ret; } function getTitle($id) { foreach ($this->getAll() as $desc) if ($desc->paysys_id == $id) return $desc->title; return $id; } }PayProcessMediator.php000064400000006077152101614250011041 0ustar00controller = $controller; $this->invoice = $invoice; } public function setOnAction($callback) { $this->onAction = $callback; return $this; } public function setOnSuccess($callback) { $this->onSuccess = $callback; return $this; } public function setOnFailure($callback) { $this->onFailure = $callback; return $this; } /** * This function is likely never returns * but anyway handle result and exceptions * @return Am_Paysystem_Result */ function process() { Am_Di::getInstance()->hook->call(Am_Event::INVOICE_BEFORE_PAYMENT, array( 'invoice' => $this->invoice, 'controller' => $this->controller, )); $plugin = Am_Di::getInstance()->plugins_payment->loadGet($this->invoice->paysys_id); $this->result = new Am_Paysystem_Result(); $plugin->processInvoice($this->invoice, $this->controller->getRequest(), $this->result); if ($this->result->isSuccess() || $this->result->isFailure()) if ($transaction = $this->result->getTransaction()) { $transaction->setInvoice($this->invoice); $transaction->process(); } if ($this->result->isSuccess()) { if(method_exists($this->controller, 'getForm')) $this->controller->getForm()->getSessionContainer()->destroy(); $url = Am_Di::getInstance()->url("thanks",array('id'=> $this->invoice->getSecureId('THANKS')),false); $this->callback($this->onSuccess); Am_Mvc_Response::redirectLocation($url); // no return // Am_Exception_Redirect only for AM_APPLICATION_ENV = 'testing' } elseif ($this->result->isAction()) { if(method_exists($this->controller, 'getForm')) $this->controller->getForm()->getSessionContainer()->destroy(); $this->callback($this->onAction); $this->result->getAction()->process($this->controller); // no return // Am_Exception_Redirect only for AM_APPLICATION_ENV = 'testing' } else {// ($result->isFailure()) { $this->callback($this->onFailure); } return $this->result; } protected function callback($callback) { if ($callback) call_user_func($callback, $this->invoice, $this->controller, $this, $this->result); } }Result.php000064400000007021152101614250006530 0ustar00status = null; $this->errorMessages = array(); $this->action = null; $this->transaction = null; } function getStatus() { return (integer)$this->status; } /** * @return Am_Paysystem_Result provides fluent interface */ function setStatus($status) { $this->status = (integer)$status; return $this; } /** * @return Am_Paysystem_Result provides fluent interface */ function setSuccess(Am_Paysystem_Transaction_Interface $transaction = null) { if ($this->errorMessages) throw new Am_Exception_InternalError("Could not set SUCCESS status on transaction with errors. Remove errors first. Errors: " . implode(",", $this->getErrorMessages())); $this->status = self::SUCCESS; $this->action = null; $this->transaction = $transaction; return $this; } /** * @return Am_Paysystem_Result provides fluent interface */ function setFailed($errors) { $this->status = self::FAILURE; $this->errorMessages = (array)$errors; return $this; } function getErrorMessages() { return $this->errorMessages; } function getLastError() { reset($this->errorMessages); return current($this->errorMessages); } /** @return Am_Paysystem_Transaction_Abstract */ function getTransaction() { return $this->transaction; } /** * @return Am_Paysystem_Result provides fluent interface */ function setErrorMessages(array $errorMessages = null) { $this->errorMessages = $errorMessages; return $this; } function getAction() { return $this->action; } /** * Sets action and status to ACTION * @param Am_Paysystem_Action $action * @return Am_Paysystem_Result provides fluent interface */ function setAction(Am_Paysystem_Action $action) { $this->action = $action; $this->status = self::ACTION; return $this; } /** * Sets action and status to ERROR_ACTION * @param Am_Paysystem_Action $action * @return Am_Paysystem_Result provides fluent interface */ function setErrorAction(Am_Paysystem_Action $action) { $this->action = $action; $this->status = self::ERROR_ACTION; return $this; } /** * @return Am_Paysystem_Result provides fluent interface */ function addErrorMessage($error) { $this->errorMessages[] = $error; return $this; } function isSuccess() { return $this->status === self::SUCCESS; } function isFailure() { return $this->status === self::FAILURE; } function isAction() { return $this->status === self::ACTION || $this->status === self::ERROR_ACTION; } }Transaction/Paypal.php000064400000015626152101614250010777 0ustar00request->isPost()) $this->request = new Am_Mvc_Request($_POST, 'POST'); $this->txn_type = $this->request->getFiltered('txn_type'); } public function findTime() { $time = get_first( $this->request->get('subscr_date'), $this->request->get('payment_date')); if (!$time) return parent::findTime(); $d = new DateTime($time); $d->setTimezone(new DateTimeZone(date_default_timezone_get())); return $d; } public function getUniqId() { return $this->request->getFiltered('txn_id', $this->txn_type . '-' . $this->getTime()->format('YmdHis')); } function emailRegardingWrongBusiness($incoming, array $businesses){ $root_url = ROOT_URL; $businesses = implode(',', $businesses); $msg = << Configuration -> Setup/Configuration -> PayPal $businesses If it is really your transaction and your primary PayPal email address is $incoming, go to aMember CP -> Configuration -> Setup/Configuration -> PayPal and set PayPal email address as $incoming Once you have fixed the configuration, please visit Amember CP -> Utilities -> Logs -> Invoice, find this transaction (invoice #{$this->request->invoice}), and press "Retry Processing". -- Your aMember Pro script P.S. If you have any questions, resend this email to support@cgi-central.net with server access details. CUT; $mail = $this->getPlugin()->getDi()->mail; $mail->toAdmin(); $mail->setBodyText($msg); $mail->setSubject('*** PayPal plugin error in Amember ***'); $mail->send(); } public function validateSource() { // validate if that is genuine POST coming from PayPal if (!$this->plugin->getConfig('dont_verify')) { try { $req = $this->plugin->createHttpRequest(); $domain = $this->plugin->getConfig('testing') ? 'www.sandbox.paypal.com' : 'www.paypal.com'; $req->setConfig('ssl_verify_peer', false); $req->setConfig('ssl_verify_host', false); $req->setUrl('https://'.$domain.'/cgi-bin/webscr'); $req->addPostParameter('cmd','_notify-validate'); foreach ($this->request->getRequestOnlyParams() as $key => $value) $req->addPostParameter($key, $value); $req->setMethod(Am_HttpRequest::METHOD_POST); $resp = $req->send(); if ($resp->getStatus() != 200 || $resp->getBody()!=="VERIFIED") throw new Am_Exception_Paysystem("Wrong IPN received, paypal [_notify-validate] answers: ".$resp->getBody().'='.$resp->getStatus()); } catch(HTTP_Request2_ConnectionException $e) { Am_Di::getInstance()->errorLogTable->logException($e); header('HTTP/1.1 500 Internal Server Error'); die; } } /// validate business return $this->validateBusiness(); } public function validateBusiness(){ $businesses = array_merge(array($this->plugin->getConfig('business')), preg_split('|[\r\n]+|', $this->plugin->getConfig('alt_business'))); $businesses = array_filter(array_map('trim', $businesses), 'strlen'); $businesses = array_map("strtolower", $businesses); $incoming = strtolower($this->request->get('business',$this->request->get('receiver_email'))); if(!$incoming) return; // Nothing to validate foreach ($businesses as $e) if ($incoming === $e && $e) return true; // no match found $this->emailRegardingWrongBusiness($incoming, $businesses); throw new Am_Exception_Paysystem("IPN transaction comes for foreign business e-mail: " . htmlentities($incoming)); } public function processValidated() { switch ($this->txn_type) { // case self::TXN_SUBSCR_SIGNUP: // if ($this->invoice->first_total <= 0) // no payment will be reported // if ($this->invoice->status == Invoice::PENDING) // handle only once // $this->invoice->addAccessPeriod($this); // add first trial period // break; case self::TXN_SUBSCR_EOT: $this->invoice->stopAccess($this); break; case self::TXN_RECURRING_PAYMENT_PROFILE_CANCEL: case self::TXN_SUBSCR_CANCEL: $this->invoice->setCancelled(true); break; // case self::TXN_WEB_ACCEPT: case self::TXN_CART: case self::TXN_RECURRING_PAYMENT: switch ($this->request->payment_status) { case 'Completed': $this->invoice->addPayment($this); break; default: } break; } switch($this->request->payment_status){ case 'Refunded': case 'Chargeback': $this->invoice->addRefund($this, $this->request->parent_txn_id, $this->getAmount()); break; } } function getAmount() { return $this->request->get('mc_gross', $this->request->get('payment_gross')); } public function validateStatus() { return true; } public function validateTerms() { return true; } public function findInvoiceId() { $invoiceId = $this->request->getFiltered('invoice', $this->request->getFiltered('rp_invoice_id')); if ($invoiceId) return $invoiceId; // for paypal-pro/express if ($profileId = $this->request->get('recurring_payment_id')) { if ($invoice = Am_Di::getInstance()->invoiceTable->findFirstByData( 'paypal-profile-id', $profileId)) { return $invoice->public_id; } } } }Transaction/Abstract.php000064400000007335152101614250011312 0ustar00 * $transaction = new Am_Paysystem_Transaction_Abstract($plugin, $request); * $transaction->process(); * * * or if we know exact invoice and it is valid * * $transaction = new Am_Paysystem_Transaction_Abstract($plugin, $request); * $transaction->setInvoice($invoice)->processValidated(); * * * @package Am_Paysystem */ abstract class Am_Paysystem_Transaction_Abstract implements Am_Paysystem_Transaction_Interface { /** @var Invoice */ public $invoice; /** @var Am_Paysystem_Abstract */ protected $plugin; /** @var DateTime @see findTime */ private $time = null; /** * @param array of variables coming from the paysystem */ function __construct(Am_Paysystem_Abstract $plugin){ $this->plugin = $plugin; $this->init(); } function init() {} function process() { $this->validate(); $this->processValidated(); } /** * Function must return receipt id of the payment - it is the payment reference# * as returned from payment system. By default it just calls @see getUniqId, * but this can be overriden * @return string */ function getReceiptId(){ return $this->getUniqId(); } /** * Return the related plugin * @return Am_Paysystem_Abstract */ public function getPlugin(){ return $this->plugin; } /** * Return date/time/zone object from the request, if that is impossible, * then returns current date/time * @see findTime() * @return DateTime */ public function getTime(){ if (!$this->time) $this->time = $this->findTime(); return $this->time; } public function setTime(DateTime $time) { $this->time = $time; return $this; } /** * Returns to timestamp of transaction * Be careful with timezones, etc. May be it is even better * to keep it as is * @return DateTime */ public function findTime(){ return $this->getPlugin()->getDi()->dateTime; } public function validate() { } /** * Return payment amount of the transaction * @throws Am_Exception_Paysystem if it is not a payment transaction * @return double|null number or null to use default value from invoice */ public function getAmount(){ return null; } /** * Once the process of IPN validation is completed, we can do * actions in this method. Use @link Invoice::startAccessNow(), * @link Invoice::stopAccessNow(), @link Invoice::addPaymentAndAccess() * @link Invoice::stopAccessAfterPaidPeriodOver() */ public function processValidated() { $this->invoice->addPayment($this); } public function setInvoice(Invoice $invoice) { $this->invoice = $invoice; return $this; } /** set invoice log record for futher update with found details */ public function setInvoiceLog(InvoiceLog $log) { $this->log = $log; } /** * Get Invoice associated with transacion. * @return Invoice $invoice; */ public function getInvoice(){ return $this->invoice; } public function getRecurringType() { return $this->getPlugin()->getRecurringType(); } public function getPaysysId() { return $this->getPlugin()->getId(); } }Transaction/Interface.php000064400000001627152101614250011445 0ustar00config->get('auto_login_after_signup')) Am_Di::getInstance()->auth->setUser($this->invoice->getUser(), $this->request->getClientIp()); } }Transaction/Incoming.php000064400000036410152101614250011306 0ustar00 transactionField */ protected $_autoCreateMap = array(); /** * @param Am_Paysystem_Abstract $plugin * @param Am_Mvc_Request $request * @param Am_Mvc_Response $response * @param type $invokeArgs */ public function __construct(/*Am_Paysystem_Abstract*/ $plugin, /*Am_Mvc_Request*/ $request, /*Am_Mvc_Response*/ $response, $invokeArgs) { $this->request = $request; $this->response = $response; $this->invokeArgs = $invokeArgs; parent::__construct($plugin); } /** * By transaction : * - find or create user * - find or create invoice * - return created invoice * @return Invoice */ function autoCreateInvoice() { $invoiceExternalId = $this->generateInvoiceExternalId(); $invoice = Am_Di::getInstance()->invoiceTable->findFirstByData('external_id', $invoiceExternalId); $products = $this->autoCreateGetProducts(); if (!$invoice && !$products) return null; // If we are able to retrive invoice but doesn;t have products, // we should get products from invoice in order to handle situations when invoice was imported into amember; if($invoice && !$products) { $products = $invoice->getProducts(); } if (!is_array($products)) $products = array($products); $userTable = $this->getPlugin()->getDi()->userTable; $userInfo = $this->fetchUserInfo(); $externalId = $this->generateUserExternalId($userInfo); $user = null; if ($externalId) $user = $userTable->findFirstByData('external_id', $externalId); if (!$user) { $user = $userTable->findFirstByEmail($userInfo['email']); if ($user) $user->data()->set('external_id', $externalId)->update(); } if (!$user) { $user = $userTable->createRecord($userInfo); if(!$user->login) $user->generateLogin(); if(!$user->pass) $user->generatePassword(); else $user->setPass($user->pass); $user->data()->set('external_id', $externalId); $user->insert(); if ($this->getPlugin()->getDi()->config->get('registration_mail')) $user->sendRegistrationEmail(); if ($this->getPlugin()->getDi()->config->get('registration_mail_admin')) $user->sendRegistrationToAdminEmail(); } // if ($invoice) { if ($invoice->user_id != $user->user_id) { $invoice = null; // strange!!! } else { $invoice->_autoCreated = true; } } /// if (!$invoice) { $invoice = $this->getPlugin()->getDi()->invoiceRecord; $invoice->setUser($user); foreach ($products as $pr) $invoice->add($pr, $this->autoCreateGetProductQuantity($pr)); $invoice->calculate(); $invoice->data()->set('external_id', $invoiceExternalId); $invoice->paysys_id = $this->plugin->getId(); $invoice->insert(); $invoice->_autoCreated = true; } if ($invoice && $this->log) { $this->log->updateQuick(array( 'invoice_id' => $invoice->pk(), 'user_id' => $user->user_id, )); } return $invoice; } /** * find matching products according to request * @return array */ function autoCreateGetProducts() { throw new Am_Exception_NotImplemented("autoCreateGetProducts not implemented"); } function autoCreateGetProductQuantity(Product $pr){ return 1; } /** * Find out user info from transaction details * @see $_autoCreateMap * @return array */ function fetchUserInfo() { if (!$this->_autoCreateMap) throw new Am_Exception_NotImplemented("fetchUserInfo not implemented"); $ret = array(); foreach ($this->_autoCreateMap as $field => $valKey) switch ($field) { case 'user_external_id': case 'invoice_external_id': break; case 'name': @list($ret['name_f'], $ret['name_l']) = preg_split('/\s+/', $this->request->get($valKey), 2); break; default: $ret[$field] = $this->request->get($valKey); } return $ret; } function fillInUserFields(User $user) { $info = $this->fetchUserInfo(); if (!$info) return; $updated = 0; foreach ($info as $k => $v) { if (''==$user->get($k)) { $user->set($k, $v); $updated++; } } if ($updated) $user->update(); } /** * @see $_autoCreateMap * @return string unique id of user - so user will not be re-generated even if he changes email */ function generateUserExternalId(array $userInfo) { $field = @$this->_autoCreateMap['user_external_id']; if (!empty($field)) return $this->request->getFiltered($field); if (!empty($userInfo['email'])) return md5($userInfo['email']); throw new Am_Exception_Paysystem_TransactionInvalid("Could not generate externalId"); } /** * Must return the same value for single rebill sequence * @see $_autoCreateMap * @return string unique id of invoice - so rebills can be added to the same invoice */ function generateInvoiceExternalId() { $field = @$this->_autoCreateMap['invoice_external_id']; if (!empty($field)){ if(is_array($field)) { foreach($field as $v) if($res = $this->request->getFiltered($v)) return $res; } return $this->request->getFiltered($field); } throw new Am_Exception_Paysystem_NotImplemented("Not Implemented"); } function resendPostback() { if ($this->plugin->getConfig('resend_postback')) { $urls = $this->plugin->getConfig('resend_postback_urls'); $urls = explode("\n", $urls); $urls = array_filter(array_map('trim', $urls)); foreach ($urls as $url) try { $tm = microtime(true); $this->log->add("Resending postback to [$url]"); if ($url == $this->plugin->getPluginUrl('ipn')) { throw new Am_Exception_Configuration("DO NOT CONFIGURE RESENDING IPN TO ITSELF!"); } $req = new Am_HttpRequest($url); $req->setConfig('connect_timeout', 1000); $req->setConfig('timeout', 2000); $method = strtoupper($this->request->getMethod()); $req->setMethod($method); if ($method == 'POST') { foreach ($this->request->getPost() as $k => $v) $req->addPostParameter($k, $v); } else { $arr = $this->request->getQuery(); $req->setUrl($req->getUrl() . '?' . http_build_query($arr, '', '&')); } $req->send(); $tm = sprintf('%.3f', microtime(true) - $tm); $this->log->add("Postback resent successfully ($tm sec)"); } catch (Exception $e) { $tm = sprintf('%.3f', microtime(true) - $tm); $this->log->add("Cannot resend postback ($tm sec)"); $this->log->add($e); } } } function process() { // resend postback first as exception may be raised below $this->resendPostback(); parent::process(); // all went OK, with no exceptions, lets try to update customer info if (!empty($this->invoice->_autoCreated) && ($user = $this->invoice->getUser())) $this->fillInUserFields($user); } /** @return Invoice|null */ public function loadInvoice($invoiceId) { $invoiceId = preg_replace('/-[^-]*$/', '', $invoiceId); $invoice = Am_Di::getInstance()->invoiceTable->findFirstByPublicId($invoiceId); // update invoice_id in the log record if ($invoice && $this->log) { $this->log->updateQuick(array( 'invoice_id' => $invoice->pk(), 'user_id' => $invoice->user_id, )); } return $invoice; } protected function autoCreate() { try { $invoiceId = $this->findInvoiceId(); if ($invoiceId === null) throw new Am_Exception_Paysystem_TransactionEmpty("Looks like an invalid IPN post - no Invoice# passed"); $invoiceId = filterId($invoiceId); if (!strlen($invoiceId)) throw new Am_Exception_Paysystem_TransactionInvalid("Could not load Invoice related to this transaction, passed id is not a valid Invoice#[$invoiceId]"); if (!$this->invoice = $this->loadInvoice($invoiceId)) throw new Am_Exception_Paysystem_TransactionUnknown("Unknown transaction: related invoice not found #[$invoiceId]"); } catch (Am_Exception_Paysystem $e) { if (!$this->plugin->getConfig('auto_create')) throw $e; // try auto-create invoice $invoice = $this->autoCreateInvoice(); if ($invoice) $this->invoice = $invoice; else throw $e; } $this->setTime($this->findTime()); } /** * Validates if transaction comes from trusted source, * and if it matches found invoice * @throws Am_Exception_Paysystem * @return null must throw exception if it is not OK */ public function validate() { if (!$this->validateSource()) throw new Am_Exception_Paysystem_TransactionSource("IPN seems to be received from unknown source, not from the paysystem"); $this->autoCreate(); if($this->invoice->paysys_id != $this->getPaysysId()) throw new Am_Exception_Paysystem_TransactionInvalid("Invoice was created by another payment plugin."); if (empty($this->invoice->_autoCreated) && !$this->validateTerms()) throw new Am_Exception_Paysystem_TransactionInvalid("Subscriptions terms in the IPN does not match subscription terms in our Invoice"); if (!$this->validateStatus()) throw new Am_Exception_Paysystem_TransactionInvalid("Payment status is invalid, this IPN is not regarding a completed payment"); } /** * Make sure transaction is coming from the payment system and not from the fraudient source * @return true if OK, false if not */ abstract public function validateSource(); /** * Make sure the transaction have the same terms (at least paid amount as the @see $invoice ) * NOTE: if invoice is auto-created, its terms will NOT be validated * @return true if OK, false if not */ abstract public function validateTerms(); /** * Make sure this transaction is not regarding a pending or failed payment * @return true if OK, false if not */ abstract public function validateStatus(); /** * Find Invoice related with the current post as passed to the paysystem * must return null if no invoice_id present in the post * @return int|null id of invoice, null if not found */ public function findInvoiceId() { throw new Am_Exception_NotImplemented("findInvoiceId is not implemented"); } /** * Compare request IP with configured in plugin * and raise exception if that is wrong * @param mixed $ip string will be parsed using this format: $ip1_start [- $ip1_end][\n$ip2_start [- $ip2_end]] etc... * Array should have this format: array( array('start1', 'stop1'), single_ip, array('start2', 'stop2')) * also it may automatically check for hostname belonging to subdomain like * .worldpay.com */ public function _checkIp($ip) { $got = $this->request->getClientIp(false); if(!is_array($ip)) { $expected = array(); foreach(explode("\n", $ip) as $l){ if(strpos($l, "-")!== false){ list($k, $v) = explode("-", $l); $expected[] = array(trim($k),trim($v)); }else{ $expected[] = trim($l); } } }else $expected = $ip; $expected = array_filter($expected); if(empty($expected)) throw new Am_Exception_InputError("{$this->plugin->getId()} configuration error. Expected IP address array is empty!"); $found =false; $hostname = null; foreach ($expected as $v) { if (is_array($v)) { if(ip2long($got) >= ip2long($v[0]) && ip2long($got) <= ip2long($v[1])) { $found = true; break; } } else { if($got == $v) { $found = true; break; } if ($v[0] == '.') { if (!$hostname) $hostname = gethostbyaddr($got); if (preg_match($x='|'.preg_quote($v).'$|', $hostname)) { $found = true; break; } } } } if (!$found) throw new Am_Exception_Paysystem_TransactionSource("{$this->plugin->getId()} post comes from unknown IP [$got]"); } public function assertAmount($expected, $got, $whereMsg = "amount") { if ($e=moneyRound($expected) != $g=moneyRound($got)) throw new Am_Exception_Paysystem_TransactionInvalid("Transaction $whereMsg [$g] does not match expected [$e]"); } }Transaction/Free.php000064400000001323152101614250010417 0ustar00invoice = $invoice; return $this; } public function processValidated() { // Update Rebill Date is not required here. It will be executed from addAccessPeriod; $this->invoice->addAccessPeriod($this); } public function getUniqId() { return $_SERVER['REMOTE_ADDR'] . '-' . $this->plugin->getDi()->time; } } Transaction/Saved.php000064400000002721152101614250010603 0ustar00Amount = $transaction->getAmount(); $this->PaysysId = $transaction->getPaysysId(); $this->ReceiptId = $transaction->getReceiptId(); $this->RecurringType= $transaction->getRecurringType(); $this->Time = $transaction->getTime()->format('Y-m-d H:i:s'); $this->TimeZone = $transaction->getTime()->getTimezone()->getName(); $this->UniqId = $transaction->getUniqId(); } public function getAmount() { return $this->Amount; } public function getPaysysId() { return $this->PaysysId; } public function getReceiptId() { return $this->ReceiptId; } public function getRecurringType() { return $this->RecurringType; } public function getTime() { $t = new DateTime($this->Time); $t->setTimezone(new DateTimeZone($this->TimeZone)); return $t; } public function getUniqId() { return $this->UniqId; } }Transaction/Manual.php000064400000001500152101614250010750 0ustar00plugin = $plugin; } function setAmount($amount) { $this->amount = moneyRound($amount); return $this; } function getAmount() { return $this->amount; } function getReceiptId() { return $this->receiptId; } function setReceiptId($receiptId) { $this->receiptId = (string)$receiptId; return $this; } public function getUniqId() { return $this->getReceiptId().'-m-'.time().rand(1000,9999); } }Echeck.php000064400000052561152101666610006454 0ustar00getPluginUrl(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'); } } }Transaction/Echeck.php000064400000006045152101666610010735 0ustar00setInvoice($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; } }Transaction/CreditCard.php000064400000006032152101666610011553 0ustar00setInvoice($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; } }Transaction/Maxmind/Minfraud.php000064400000007027152101666610012716 0ustar00body = $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 } } Transaction/Maxmind/Number.php000064400000002104152101666610012370 0ustar00body = $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 } } Transaction/Maxmind/Phone.php000064400000001620152101666610012213 0ustar00body = $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 } } CreditCard.php000064400000105403152101666610007270 0ustar00getPluginUrl(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; } } Networkmerchants/Transaction/Refund.php000064400000001527152101666610014334 0ustar00amount = $amount; parent::__construct($plugin, $invoice, true); $this->request->addPostParameter('transactionid', $transactionId); $this->request->addPostParameter('customer_vault_id', $customerVaultId); } public function getAmount() { return $this->amount; } public function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('type', 'refund'); $this->request->addPostParameter('amount', $this->getAmount()); } public function processValidated(){} // no process payment } Networkmerchants/Transaction/AddCustomer.php000064400000001040152101666610015311 0ustar00setCcRecord($cc); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('customer_vault', 'add_customer'); } public function processValidated(){} // no process payment } Networkmerchants/Transaction/Capture.php000064400000001121152101666610014502 0ustar00request->addPostParameter('transactionid', $transactionid); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('amount', $this->getAmount()); $this->request->addPostParameter('type', 'capture'); } } Networkmerchants/Transaction/Sale.php000064400000001123152101666610013765 0ustar00request->addPostParameter('customer_vault_id', $customerVaultId); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('amount', $this->getAmount()); $this->request->addPostParameter('type', 'sale'); } } Networkmerchants/Transaction/Void.php000064400000001321152101666610014002 0ustar00request->addPostParameter('transactionid', $transactionId); $this->request->addPostParameter('customer_vault_id', $customerVaultId); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('type', 'void'); $this->request->addPostParameter('amount', 1.00); } public function processValidated(){} // no process payment } Networkmerchants/Transaction/Authorization.php000064400000001322152101666610015742 0ustar00amount = $amount; parent::__construct($plugin, $invoice, true); $this->setCcRecord($cc); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('type', 'auth'); $this->request->addPostParameter('amount', $this->amount); $this->request->addPostParameter('customer_vault', 'add_customer'); } public function processValidated(){} // no process payment } Networkmerchants/Transaction.php000064400000005600152101666610013105 0ustar00getGatewayURL(), Am_HttpRequest::METHOD_POST); parent::__construct($plugin, $invoice, $request, $doFirst); $this->addRequestParams(); } private function getUser() { return (!$this->plugin->getConfig('testMode')) ? $this->plugin->getConfig('user') : 'demo'; } private function getPass() { return (!$this->plugin->getConfig('testMode')) ? $this->plugin->getConfig('pass') : 'password'; } public function getAmount() { return $this->doFirst ? $this->invoice->first_total : $this->invoice->second_total; } protected function addRequestParams() { $this->request->addPostParameter('username', $this->getUser()); $this->request->addPostParameter('password', $this->getPass()); } public function getUniqId() { return $this->parsedResponse->transactionid; } public function parseResponse() { parse_str($this->response->getBody(), $this->parsedResponse); $this->parsedResponse = (object)$this->parsedResponse; } public function validate() { switch ($this->parsedResponse->response) { case 1: break; case 2: $err = "Transaction Declined."; break; case 3: $err = "Error in transaction data or system error."; break; default: $err = "Unknown error num: " . $this->parsedResponse->response . "."; break; } if (!empty($err)) { return $this->result->setFailed(array($err, $this->parsedResponse->responsetext)); } $this->result->setSuccess($this); } protected function setCcRecord(CcRecord $cc) { $this->request->addPostParameter('ccnumber', $cc->cc_number); $this->request->addPostParameter('ccexp', $cc->cc_expire); $this->request->addPostParameter('cvv', $cc->getCvv()); $this->request->addPostParameter('firstname', $cc->cc_name_f); $this->request->addPostParameter('lastname', $cc->cc_name_l); $this->request->addPostParameter('address1', $cc->cc_street); $this->request->addPostParameter('city', $cc->cc_city); $this->request->addPostParameter('state', $cc->cc_state); $this->request->addPostParameter('zip', $cc->cc_zip); $this->request->addPostParameter('country', $cc->cc_country); $this->request->addPostParameter('phone', $cc->cc_phone); } public function getCustomerVaultId() { return $this->parsedResponse->customer_vault_id; } }