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 db.xml000064400000001515152101601040005643 0ustar00
library/CcRebill.php000064400000002112152101601040010362 0ustar00 'Started', self::NO_CC => 'No Credit Card saved', self::ERROR => 'Error', self::SUCCESS => 'OK', self::EXCEPTION => 'Exception!', ); return $arr[$status]; } function setStatus($status, $message) { $this->updateQuick(array( 'status' => (int)$status, 'status_msg' => $message, 'status_tm' => $this->getDi()->sqlDateTime, )); return $this; } } class CcRebillTable extends Am_Table { protected $_table = '?_cc_rebill'; protected $_key = 'cc_rebill_id'; public function insert(array $values, $returnInserted = false) { if (empty($values['tm_added'])) $values['tm_added'] = $this->getDi()->sqlDateTime; return parent::insert($values, $returnInserted); } }library/EcheckRecord.php000064400000004403152101601040011231 0ustar00maskBan($arr['echeck_ban']); } $arr['echeck_aba'] = preg_replace('/\D+/', '', $arr['echeck_aba']); foreach ($this->_encryptedFields as $f) if (array_key_exists($f, $arr)) $arr[$f] = $this->_table->encrypt($arr[$f]); return $arr; } public function fromRow(array $arr) { // fields to decrypt foreach ($this->_encryptedFields as $f) if (array_key_exists($f, $arr)) $arr[$f] = $this->_table->decrypt($arr[$f]); return parent::fromRow($arr); } /** * Delete existing record for this user_id, then insert this one * @return EcRecord provides fluent interface */ function replace() { if (empty($this->user_id) || $this->user_id <= 0) throw new Am_Exception_InternalError("this->user_id is empty in " . __METHOD__); $this->_table->deleteByUserId($this->user_id); return $this->insert(); } } class EcheckRecordTable extends Am_Table { protected $_crypt; protected $_key = 'echeck_id'; protected $_table = '?_echeck'; protected $_recordClass = 'EcheckRecord'; function encrypt($s){ return $this->_getCrypt()->encrypt($s); } function decrypt($s){ return $this->_getCrypt()->decrypt($s); } function _getCrypt(){ if (empty($this->_crypt)) $this->_crypt = Am_Di::getInstance ()->crypt; return $this->_crypt; } function setCrypt(Am_Crypt $crypt) { $this->_crypt = $crypt; } } library/Am/Paysystem/Nmi.php000064400000013473152101601040011775 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); } } library/Am/Paysystem/Echeck.php000064400000052561152101601040012435 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'); } } }library/Am/Paysystem/Transaction/Echeck.php000064400000006045152101601040014716 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; } }library/Am/Paysystem/Transaction/CreditCard.php000064400000006032152101601040015534 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; } }library/Am/Paysystem/Transaction/Maxmind/Minfraud.php000064400000007027152101601040016677 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 } } library/Am/Paysystem/Transaction/Maxmind/Number.php000064400000002104152101601040016351 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 } } library/Am/Paysystem/Transaction/Maxmind/Phone.php000064400000001620152101601040016174 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 } } library/Am/Paysystem/CreditCard.php000064400000105403152101601040013251 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; } } library/Am/Paysystem/Networkmerchants/Transaction/Refund.php000064400000001527152101601040020315 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 } library/Am/Paysystem/Networkmerchants/Transaction/AddCustomer.php000064400000001040152101601040021272 0ustar00setCcRecord($cc); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('customer_vault', 'add_customer'); } public function processValidated(){} // no process payment } library/Am/Paysystem/Networkmerchants/Transaction/Capture.php000064400000001121152101601040020463 0ustar00request->addPostParameter('transactionid', $transactionid); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('amount', $this->getAmount()); $this->request->addPostParameter('type', 'capture'); } } library/Am/Paysystem/Networkmerchants/Transaction/Sale.php000064400000001123152101601040017746 0ustar00request->addPostParameter('customer_vault_id', $customerVaultId); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('amount', $this->getAmount()); $this->request->addPostParameter('type', 'sale'); } } library/Am/Paysystem/Networkmerchants/Transaction/Void.php000064400000001321152101601040017763 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 } library/Am/Paysystem/Networkmerchants/Transaction/Authorization.php000064400000001322152101601040021723 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 } library/Am/Paysystem/Networkmerchants/Transaction.php000064400000005600152101601040017066 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; } } library/Am/Form/Echeck.php000064400000022613152101601040011335 0ustar00plugin = $plugin; $this->formType = $formType; $this->payButtons = array( self::PAYFORM => ___('Subscribe And Pay'), self::ADMIN_UPDATE => ___('Update eCheck Info'), self::USER_UPDATE => ___('Update eCheck Info'), self::ADMIN_INSERT => ___('Update eCheck Info'), ); parent::__construct('ec'); } public function init() { parent::init(); $name = $this->addGroup() ->setLabel(___("Your Name\n" . 'your first and last name')); $name->setSeparator(' '); $name->addRule('required', ___('Please enter your name')); $name->addText('echeck_name_f', array('size' => 15)) ->addRule('required', ___('Please enter first name')) ->addRule('regex', ___('Please enter first name'), '|^[a-zA-Z_\' -]+$|'); $name->addText('echeck_name_l', array('size' => 15)) ->addRule('required', ___('Please enter your last name')) ->addRule('regex', ___('Please enter your last name'), '|^[a-zA-Z_\' -]+$|'); if ($this->formType == self::ADMIN_UPDATE) { $group = $this->addGroup()->setLabel(___("Bank Account Number\n" . 'Up to 20 digits')); $group->addStatic()->setContent('
'); $group->addStatic('echeck'); $group->addText('echeck_ban', array('autocomplete' => 'off', 'maxlength' => 20, 'style' => 'display:none')) ->addRule('regex', ___('Invalid Bank Account Number'), '/^[a-zA-Z0-9]{1,20}$/'); $group->addScript("")->setScript(<<addStatic()->setContent('
'); } else { $this->addText('echeck_ban', array('autocomplete' => 'off', 'maxlength' => 20)) ->setLabel(___("Your Bank Account Number\n" . 'Up to 20 digits')) ->addRule('required', ___('Please enter Account Number')) ->addRule('regex', ___('Invalid Account Number'), '/^[a-zA-Z0-9]{1,20}$/'); } $this->addText('echeck_aba', array('autocomplete' => 'off', 'maxlength' => 9)) ->setLabel(___("ABA Routing Number\n" . '9 digits')) ->addRule('required', ___('Please enter Routing Number')) ->addRule('regex', ___('Invalid Routing Number'), '/^[a-zA-Z0-9]{1,9}$/'); $options = $this->plugin->getFormOptions(); if (in_array(Am_Paysystem_Echeck::ECHECK_COMPANY, $options)) { $this->addText(Am_Paysystem_Echeck::ECHECK_COMPANY) ->setLabel(___("Company Name\n" . 'the company name associated with the billing address for ' . 'the transaction')); } if (in_array(Am_Paysystem_Echeck::ECHECK_TYPE_OPTIONS, $options)) { $type = $this->addSelect(Am_Paysystem_Echeck::ECHECK_TYPE_OPTIONS) ->setLabel(___("Bank Account Type\n" . 'please select one')) ->loadOptions(array_merge(array(''=>'-- ' . ___('Please choose') . ' --'), $this->plugin->getEcheckTypeOptions())); $type->addRule('required', ___('Please choose a Bank Account Type')); } if (in_array(Am_Paysystem_Echeck::ECHECK_BANK_NAME, $options)) { $this->addText(Am_Paysystem_Echeck::ECHECK_BANK_NAME, array('autocomplete' => 'off', 'maxlength' => 50)) ->setLabel(___('Bank Name')) ->addRule('required', ___('Please enter Bank Name')); } if (in_array(Am_Paysystem_Echeck::ECHECK_ACCOUNT_NAME, $options)) { $this->addText(Am_Paysystem_Echeck::ECHECK_ACCOUNT_NAME, array('autocomplete' => 'off', 'maxlength' => 50)) ->setLabel(___("Bank Account Name\n" . 'name associated with the bank account')) ->addRule('required', ___('Please enter Bank Account Name')); } if (in_array(Am_Paysystem_Echeck::ECHECK_ADDRESS, $options)) { $fieldSet = $this->addFieldset(___('Address Info')) ->setLabel(___("Address Info\n" . '(must match your credit card statement delivery address)')); if (in_array(Am_Paysystem_Echeck::ECHECK_STREET, $options)) { $street = $fieldSet->addText('echeck_street')->setLabel(___('Street Address')) ->addRule('required', ___('Please enter Street Address')); } if (in_array(Am_Paysystem_Echeck::ECHECK_STREET2, $options)) { $street2 = $fieldSet->addText('echeck_street2')->setLabel(___('Street Address (Second Line)')) ->addRule('required', ___('Please enter Street Address')); } if (in_array(Am_Paysystem_Echeck::ECHECK_CITY, $options)) { $city = $fieldSet->addText('echeck_city')->setLabel(___('City')) ->addRule('required', ___('Please enter City')); } if (in_array(Am_Paysystem_Echeck::ECHECK_ZIP, $options)) { $zip = $fieldSet->addText('echeck_zip')->setLabel(___('ZIP')) ->addRule('required', ___('Please enter ZIP code')); } if (in_array(Am_Paysystem_Echeck::ECHECK_COUNTRY, $options)) { $country = $fieldSet->addSelect('echeck_country')->setLabel(___('Country')) ->setId('f_cc_country') ->loadOptions(Am_Di::getInstance()->countryTable->getOptions(true)); $country->addRule('required', ___('Please enter Country')); } if (in_array(Am_Paysystem_Echeck::ECHECK_STATE, $options)) { $group = $fieldSet->addGroup()->setLabel(___('State')); $group->addRule('required', ___('Please enter State')); /** @todo load correct states */ $stateSelect = $group->addSelect('echeck_state') ->setId('f_cc_state') ->loadOptions($stateOptions = Am_Di::getInstance()->stateTable->getOptions(@$_REQUEST['echeck_country'], true)); $stateText = $group->addText('echeck_state')->setId('t_cc_state'); $disableObj = $stateOptions ? $stateText : $stateSelect; $disableObj->setAttribute('disabled', 'disabled')->setAttribute('style', 'display: none'); } if (in_array(Am_Paysystem_Echeck::ECHECK_PHONE, $options)) { $phone = $fieldSet->addText('echeck_phone', array('size'=>14))->setLabel(___('Phone')) ->addRule('required', ___('Please enter phone number')) ->addRule('regex', ___('Please enter phone number'), '|^[\d() +-]+$|'); } } $buttons = $this->addGroup(); $buttons->addSubmit('_echeck_', array('value' => ' ' . $this->payButtons[$this->formType] . ' ')); if ($this->formType == self::USER_UPDATE) { $buttons->addInputButton('_echeck_', array('value' => ' ' . ___("Back") . ' ', 'onclick' => 'goBackToMember()')); $this->addScript("")->setScript("function goBackToMember(){ window.location = amUrl('/member'); }"); } $this->plugin->onFormInit($this); } /** * Return array of default values based on $user record * @param User $user */ public function getDefaultValues(User $user) { return array( 'echeck_name_f' => $user->name_f, 'echeck_name_l' => $user->name_l, 'echeck_street' => $user->street, 'echeck_street2' => $user->street2, 'echeck_city' => $user->city, 'echeck_state' => $user->state, 'echeck_country' => $user->country, 'echeck_zip' => $user->zip, 'echeck_phone' => $user->phone, ); } public function validate() { return parent::validate() && $this->plugin->onFormValidate($this); } public function getValue() { $ret = parent::getValue(); array_walk_recursive($ret, function(&$v, $k) {$v=trim($v);}); if (!empty($ret['echeck_ban'])) $ret['echeck_ban'] = preg_replace('/\D/', '', $ret['echeck_ban']); return $ret; } public function toEcheckRecord(EcheckRecord $echeck) { $values = $this->getValue(); unset($values['a']); unset($values['id']); unset($values['action']); $echeck->setForInsert($values); } } library/Am/Form/CreditCard.php000064400000033134152101601040012157 0ustar00setSeparator(' '); $require = !$data['dont_require']; $years = @$data['years']; if (!$years) $years = 10; $m = $this->addSelect('m')->loadOptions($this->getMonthOptions()); if ($require) $m->addRule('required', ___('Invalid Expiration Date - Month')); $y = $this->addSelect('y')->loadOptions($this->getYearOptions($years)); if ($require) $y->addRule('required', ___('Invalid Expiration Date - Year')); } public function getMonthOptions() { $locale = Am_Di::getInstance()->locale; $months = $locale->getMonthNames('wide', false); foreach ($months as $k=>$v) $months[$k] = sprintf('(%02d) %s', $k, $v); $months[''] = ''; ksort($months); return $months; } public function getYearOptions($add){ $years = range(date('Y'), date('Y')+$add); array_unshift($years, ''); return array_combine($years, $years); } public function setValue($value) { if (is_string($value) && preg_match('/^\d{4}$/', $value)) { $value = array( 'm' => (int)substr($value, 0, 2), 'y' => '20' . substr($value, 2, 2), ); } return parent::setValue($value); } protected function updateValue() { $name = $this->getName(); foreach ($this->getDataSources() as $ds) { if (null !== ($value = $ds->getValue($name))) { $this->setValue($value); return; } } return parent::updateValue(); } } class Am_Form_CreditCard extends Am_Form { const PAYFORM = 'payform'; const USER_UPDATE = 'user-update'; const ADMIN_UPDATE = 'admin-update'; const ADMIN_INSERT = 'admin-insert'; protected $payButtons = array(); /** @var Am_Paysystem_CreditCard */ protected $plugin; protected $formType = self::PAYFORM; public function __construct(Am_Paysystem_CreditCard $plugin, $formType = self::PAYFORM) { $this->plugin = $plugin; $this->formType = $formType; $this->payButtons = array( self::PAYFORM => ___('Subscribe And Pay'), self::ADMIN_UPDATE => ___('Update Credit Card Info'), self::USER_UPDATE => ___('Update Credit Card Info'), self::ADMIN_INSERT => ___('Update Credit Card Info'), ); parent::__construct('cc'); } public function init() { parent::init(); $name = $this->addGroup() ->setLabel(___("Cardholder Name\n" . 'cardholder first and last name, exactly as on the card')); $name->setSeparator(' '); $name->addRule('required', ___('Please enter credit card holder name')); $name_f = $name->addText('cc_name_f', array('size'=>15)); $name_f->addRule('required', ___('Please enter credit card holder first name'))->addRule('regex', ___('Please enter credit card holder first name'), '/^[^=:<>{}()"]+$/D'); $name_l = $name->addText('cc_name_l', array('size'=>15)); $name_l->addRule('required', ___('Please enter credit card holder last name'))->addRule('regex', ___('Please enter credit card holder last name'), '/^[^=:<>{}()"]+$/D'); $options = $this->plugin->getFormOptions(); if (in_array(Am_Paysystem_CreditCard::CC_COMPANY, $options)) $company = $this->addText('cc_company') ->setLabel(___("Company Name\n" . 'the company name associated with the billing address for the transaction')); if (in_array(Am_Paysystem_CreditCard::CC_TYPE_OPTIONS, $options)) { $type = $this->addSelect('cc_type') ->setLabel(___("Credit Card Type\n" . 'please select one')) ->loadOptions(array_merge(array(''=>'-- ' . ___('Please choose') . ' --'), $this->plugin->getCreditCardTypeOptions())); $type->addRule('required', ___('Please choose a Credit Card Type')); } if ($this->formType == self::ADMIN_UPDATE) { $group = $this->addGroup() ->setLabel(___("Credit Card Number\n" . "for example: 1111-2222-3333-4444")); $group->addStatic()->setContent('
'); $group->addStatic('cc'); $cc = $group->addText('cc_number', array('autocomplete'=>'off', 'size'=>22, 'maxlength'=>22, 'style'=>'display:none')); $cc->addRule('regex', ___('Invalid Credit Card Number'), '/^[0-9 -]+$/') ->addRule('callback2', 'Invalid CC#', array($this->plugin, 'validateCreditCardNumber')); $group->addScript("")->setScript(<<addStatic()->setContent('
'); } else { $cc = $this->addText('cc_number', array('autocomplete'=>'off', 'size'=>22, 'maxlength'=>22)) ->setLabel(___("Credit Card Number\n" . 'for example: 1111-2222-3333-4444')); $cc->addRule('required', ___('Please enter Credit Card Number')) ->addRule('regex', ___('Invalid Credit Card Number'), '/^[0-9 -]+$/') ->addRule('callback2', 'Invalid CC#', array($this->plugin, 'validateCreditCardNumber')); } $expire = $this->addElement(new Am_Form_Element_CreditCardExpire('cc_expire')) ->setLabel(___("Card Expire\n" . 'Select card expiration date - month and year')) ->addRule('required'); if (in_array(Am_Paysystem_CreditCard::CC_CODE, $options)) { $code = $this->addPassword('cc_code', array('autocomplete'=>'off', 'size'=>4, 'maxlength'=>4)) ->setLabel(___("Credit Card Code\n" . 'The "Card Code" is a three- or four-digit security code ' . 'that is printed on the back of credit cards in the card\'s ' . 'signature panel (or on the front for American Express cards)')); $code->addRule('required', ___('Please enter Credit Card Code')) ->addRule('regex', ___('Please enter Credit Card Code'), '/^\s*\d{3,4}\s*$/'); } if (in_array(Am_Paysystem_CreditCard::CC_MAESTRO_SOLO_SWITCH, $options)) { $issue = $this->addText('cc_issuenum', array('autocomplete'=>'off', 'size'=>20, 'maxlength'=>22)) ->setLabel(___("Card Issue #\n" . 'is required for Maestro/Solo/Switch credit cards only')) ->addRule('regex', ___('Invalid Issue Number'), '/^\d+$/'); $this->addElement(new Am_Form_Element_CreditCardExpire('cc_startdate', null, array('dont_require'=>true, 'years'=>-10))) ->setLabel(___("Card Start Date\n" . 'is required for Maestro/Solo/Switch credit cards only')); } if (in_array(Am_Paysystem_CreditCard::CC_INPUT_BIN, $options)) { $fieldSet = $this->addFieldset()->setLabel(___('Bank Identification')); $fieldSet->addText('_cc_bin_name', array()) ->setLabel(___("Bank Name\n" . 'name of the bank which issued the credit card')); $fieldSet->addText('_cc_bin_phone', array()) ->setLabel(___("Bank Phone\n" . 'customer service phone number listed on back of your credit card')); } if (in_array(Am_Paysystem_CreditCard::CC_ADDRESS, $options)) { $fieldSet = $this->addFieldset(___('Billing Address')) ->setLabel(___("Billing Address\n" . 'must match your credit card statement delivery address')); if (in_array(Am_Paysystem_CreditCard::CC_STREET, $options)) { $street = $fieldSet->addText('cc_street', array('class'=>'el-wide'))->setLabel(___('Street Address')) ->addRule('required', ___('Please enter Street Address')); } if (in_array(Am_Paysystem_CreditCard::CC_STREET2, $options)) { $street2 = $fieldSet->addText('cc_street2', array('class'=>'el-wide'))->setLabel(___('Street Address (Second Line)')) ->addRule('required', ___('Please enter Street Address')); } if (in_array(Am_Paysystem_CreditCard::CC_HOUSENUMBER, $options)) { $house = $fieldSet->addText('cc_housenumber', array('size'=>15))->setLabel(___('Housenumber')) ->addRule('required', ___('Please enter housenumber')); } if (in_array(Am_Paysystem_CreditCard::CC_CITY, $options)) { $city = $fieldSet->addText('cc_city')->setLabel(___('City')) ->addRule('required', ___('Please enter City')); } if (in_array(Am_Paysystem_CreditCard::CC_PROVINCE_OUTSIDE_OF_US, $options)) { $province = $fieldSet->addText('cc_province', array('size'=>15)) ->setLabel(___("Billing International Province\n" . 'for international provinces outside of US & Canada ' . 'include the province name here')) ->addRule('required', ___('Please choose state')); } if (in_array(Am_Paysystem_CreditCard::CC_ZIP, $options)) { $zip = $fieldSet->addText('cc_zip')->setLabel(___('ZIP')) ->addRule('required', ___('Please enter ZIP code')); } if (in_array(Am_Paysystem_CreditCard::CC_COUNTRY, $options)) { $country = $fieldSet->addSelect('cc_country')->setLabel(___('Country')) ->setId('f_cc_country') ->loadOptions(Am_Di::getInstance()->countryTable->getOptions(true)); $country->addRule('required', ___('Please enter Country')); } if (in_array(Am_Paysystem_CreditCard::CC_STATE, $options)) { $group = $fieldSet->addGroup()->setLabel(___('State')); $group->addRule('required', ___('Please enter State')); /** @todo load correct states */ $stateSelect = $group->addSelect('cc_state') ->setId('f_cc_state') ->loadOptions($stateOptions = Am_Di::getInstance()->stateTable->getOptions(@$_REQUEST['cc_country'], true)); $stateText = $group->addText('cc_state')->setId('t_cc_state'); $disableObj = $stateOptions ? $stateText : $stateSelect; $disableObj->setAttribute('disabled', 'disabled')->setAttribute('style', 'display: none'); } if (in_array(Am_Paysystem_CreditCard::CC_PHONE, $options)) { $phone = $fieldSet->addText('cc_phone', array('size'=>14))->setLabel(___('Phone')) ->addRule('required', ___('Please enter phone number')) ->addRule('regex', ___('Please enter phone number'), '|^[\d() +-]+$|'); } } // if free trial set _TPL_CC_INFO_SUBMIT_BUT2 $buttons = $this->addGroup(); $buttons->setSeparator(' '); $buttons->addSubmit('_cc_', array('value'=> $this->payButtons[ $this->formType ])); if ($this->formType == self::USER_UPDATE) { $buttons->addStatic() ->setContent(sprintf('%s', ___("Back"))); $this->addScript("")->setScript("function goBackToMember(){ window.location = amUrl('/member'); }"); } $this->plugin->onFormInit($this); } /** * Return array of default values based on $user record * @param User $user */ public function getDefaultValues(User $user){ return array( 'cc_name_f' => $user->name_f, 'cc_name_l' => $user->name_l, 'cc_street' => $user->street, 'cc_street2' => $user->street2, 'cc_city' => $user->city, 'cc_state' => $user->state, 'cc_country' => $user->country, 'cc_zip' => $user->zip, 'cc_phone' => $user->phone, ); } public function validate() { return parent::validate() && $this->plugin->onFormValidate($this); } public function getValue() { $ret = parent::getValue(); array_walk_recursive($ret, function(&$v, $k) {$v=trim($v);}); if (!empty($ret['cc_number'])) $ret['cc_number'] = preg_replace('/\D/', '', $ret['cc_number']); return $ret; } public function toCcRecord(CcRecord $cc){ $values = $this->getValue(); foreach ($values as $k=>$v) if (is_array($v)) { if(!empty($v['m'])) $values[$k] = sprintf('%02d%02d', $v['m'], substr($v['y'], -2)); elseif(array_key_exists('m', $v)) $values[$k] = ''; } unset($values['_cc_bin_name']); unset($values['_cc_bin_phone']); unset($values['a']); unset($values['id']); if( !empty($values['cc_code'])) $cc->setCvv($values['cc_code']); unset($values['cc_code']); unset($values['action']); $cc->setForInsert($values); } }library/Am/Mvc/Controller/Echeck.php000064400000010322152101601040013274 0ustar00plugin = $plugin; } /** * Process the validated form and if ok, display thanks page, * if not ok, return false */ public function processEcheck() { $echeck = $this->getDi()->echeckRecordRecord; $this->form->toEcheckRecord($echeck); $echeck->user_id = $this->invoice->user_id; $result = $this->plugin->doBill($this->invoice, true, $echeck); if ($result->isSuccess()) { if (($this->invoice->rebill_times > 0) && !$echeck->pk()) $this->plugin->storeEcheck($echeck, new Am_Paysystem_Result); $this->_response->redirectLocation($this->plugin->getReturnUrl()); return true; } elseif ($result->isAction() && ($result->getAction() instanceof Am_Paysystem_Action_Redirect)) { $result->getAction()->process($this); // throws Am_Exception_Redirect (!) } else { $this->view->error = $result->getErrorMessages(); } } public function setInvoice(Invoice $invoice) { $this->invoice = $invoice; } public function echeckAction() { // invoice must be set to this point by the plugin if (!$this->invoice) throw new Am_Exception_InternalError('Empty invoice - internal error!'); $this->form = $this->createForm(); if ($this->form->isSubmitted() && $this->form->validate() && $this->processEcheck()) return; $this->view->form = $this->form; $this->view->invoice = $this->invoice; $this->view->display_receipt = true; $this->view->layoutNoMenu = true; $this->view->display('echeck/info.phtml'); } public function createForm() { $form = $this->plugin->createForm($this->_request->getActionName(), $this->invoice); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($form->getDefaultValues($this->invoice->getUser())) )); $form->addHidden(Am_Mvc_Controller::ACTION_KEY)->setValue($this->_request->getActionName()); $form->addHidden('id')->setValue($this->getFiltered('id')); return $form; } public function preDispatch() { if (!$this->plugin) throw new Am_Exception_InternalError("Payment plugin is not passed to " . __CLASS__); } public function createUpdateForm() { $form = new Am_Form_Echeck($this->plugin, Am_Form_CreditCard::USER_UPDATE); $user = $this->getDi()->auth->getUser(true); if (!$user) throw new Am_Exception_InputError("You are not logged-in"); $echeck = $this->getDi()->echeckRecordTable->findFirstByUserId($user->user_id); if (!$echeck) $echeck = $this->getDi()->echeckRecordRecord; $arr = $echeck->toArray(); unset($arr['echeck_ban']); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($arr) )); return $form; } public function updateAction() { $this->form = $this->createUpdateForm(); if ($this->form->isSubmitted() && $this->form->validate()) { $echeck = $this->getDi()->echeckRecordRecord; $this->form->toEcheckRecord($echeck); $echeck->user_id = $this->getDi()->auth->getUserId(); $result = new Am_Paysystem_Result(); $this->plugin->storeEcheck($echeck, $result); if ($result->isSuccess()) { return $this->_response->redirectLocation($this->getDi()->url('member',null,false)); } else { $this->form->getElementById('echeck_ban-0')->setError($result->getLastError()); } } $this->view->form = $this->form; $this->view->invoice = null; $this->view->display_receipt = false; $this->view->display('echeck/info.phtml'); } } library/Am/Mvc/Controller/CreditCard.php000064400000011111152101601040014113 0ustar00plugin = $plugin; } /** * Process the validated form and if ok, display thanks page, * if not ok, return false */ public function processCc() { $cc = $this->getDi()->ccRecordRecord; $this->form->toCcRecord($cc); $cc->user_id = $this->invoice->user_id; if($this->plugin->getConfig('use_maxmind')) { $checkresult = $this->plugin->doMaxmindCheck($this->invoice, $cc); if (!$checkresult->isSuccess()) { $this->view->error = $checkresult->getErrorMessages(); return; } } $result = $this->plugin->doBill($this->invoice, true, $cc); if ($result->isSuccess()) { if (($this->invoice->rebill_times > 0) && !$cc->pk()) $this->plugin->storeCreditCard($cc, new Am_Paysystem_Result); $this->_response->redirectLocation($this->plugin->getReturnUrl()); return true; } elseif ($result->isAction() && ($result->getAction() instanceof Am_Paysystem_Action_Redirect)) { $result->getAction()->process($this); // throws Am_Exception_Redirect (!) } else { $this->view->error = $result->getErrorMessages(); } } public function setInvoice(Invoice $invoice) { $this->invoice = $invoice; } public function ccAction() { // invoice must be set to this point by the plugin if (!$this->invoice) throw new Am_Exception_InternalError('Empty invoice - internal error!'); $this->form = $this->createForm(); $this->getDi()->hook->call(Bootstrap_Cc::EVENT_CC_FORM, array('form' => $this->form)); if ($this->form->isSubmitted() && $this->form->validate()) { if ($this->processCc()) return; } $this->view->form = $this->form; $this->view->invoice = $this->invoice; $this->view->display_receipt = true; $this->view->layoutNoMenu = true; $this->view->display('cc/info.phtml'); } public function createForm() { $form = $this->plugin->createForm($this->_request->getActionName(), $this->invoice); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($form->getDefaultValues($this->invoice->getUser())) )); $form->addHidden(Am_Mvc_Controller::ACTION_KEY)->setValue($this->_request->getActionName()); $form->addHidden('id')->setValue($this->getFiltered('id')); return $form; } public function preDispatch() { if (!$this->plugin) throw new Am_Exception_InternalError("Payment plugin is not passed to " . __CLASS__); } public function createUpdateForm() { $form = new Am_Form_CreditCard($this->plugin, Am_Form_CreditCard::USER_UPDATE); $user = $this->getDi()->auth->getUser(true); if (!$user) throw new Am_Exception_InputError("You are not logged-in"); $cc = $this->getDi()->ccRecordTable->findFirstByUserId($user->user_id); if (!$cc) $cc = $this->getDi()->ccRecordRecord; $arr = $cc->toArray(); unset($arr['cc_number']); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($arr) )); return $form; } public function updateAction() { $this->form = $this->createUpdateForm(); if ($this->form->isSubmitted() && $this->form->validate()) { $cc = $this->getDi()->ccRecordRecord; $this->form->toCcRecord($cc); $cc->user_id = $this->getDi()->auth->getUserId(); $result = new Am_Paysystem_Result(); $this->plugin->storeCreditCard($cc, $result); if ($result->isSuccess()) { return $this->_response->redirectLocation($this->getDi()->url('member',array('_msg'=>___('Your card details have been updated.')),false)); } else { $this->form->getElementById('cc_number-0')->setError($result->getLastError()); } } $this->view->form = $this->form; $this->view->invoice = null; $this->view->display_receipt = false; $this->view->display('cc/info.phtml'); } } library/Am/Controller/Echeck.php000064400000010232152101601040012547 0ustar00plugin = $plugin; } /** * Process the validated form and if ok, display thanks page, * if not ok, return false */ public function processEcheck() { $echeck = $this->getDi()->echeckRecordRecord; $this->form->toEcheckRecord($echeck); $echeck->user_id = $this->invoice->user_id; $result = $this->plugin->doBill($this->invoice, true, $echeck); if ($result->isSuccess()) { if (($this->invoice->rebill_times > 0) && !$echeck->pk()) $this->plugin->storeEcheck($echeck, new Am_Paysystem_Result); $this->redirectLocation($this->plugin->getReturnUrl()); return true; } elseif ($result->isAction() && ($result->getAction() instanceof Am_Paysystem_Action_Redirect)) { $result->getAction()->process($this); // throws Am_Exception_Redirect (!) } else { $this->view->error = $result->getErrorMessages(); } } public function setInvoice(Invoice $invoice) { $this->invoice = $invoice; } public function echeckAction() { // invoice must be set to this point by the plugin if (!$this->invoice) throw new Am_Exception_InternalError('Empty invoice - internal error!'); $this->form = $this->createForm(); if ($this->form->isSubmitted() && $this->form->validate() && $this->processEcheck()) return; $this->view->form = $this->form; $this->view->invoice = $this->invoice; $this->view->display_receipt = true; $this->view->layoutNoMenu = true; $this->view->display('echeck/info.phtml'); } public function createForm() { $form = $this->plugin->createForm($this->_request->getActionName(), $this->invoice); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($form->getDefaultValues($this->invoice->getUser())) )); $form->addHidden(Am_Controller::ACTION_KEY)->setValue($this->_request->getActionName()); $form->addHidden('id')->setValue($this->getFiltered('id')); return $form; } public function preDispatch() { if (!$this->plugin) throw new Am_Exception_InternalError("Payment plugin is not passed to " . __CLASS__); } public function createUpdateForm() { $form = new Am_Form_Echeck($this->plugin, Am_Form_CreditCard::USER_UPDATE); $user = $this->getDi()->auth->getUser(true); if (!$user) throw new Am_Exception_InputError("You are not logged-in"); $echeck = $this->getDi()->echeckRecordTable->findFirstByUserId($user->user_id); if (!$echeck) $echeck = $this->getDi()->echeckRecordRecord; $arr = $echeck->toArray(); unset($arr['echeck_ban']); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($arr) )); return $form; } public function updateAction() { $this->form = $this->createUpdateForm(); if ($this->form->isSubmitted() && $this->form->validate()) { $echeck = $this->getDi()->echeckRecord; $this->form->toEcheckRecord($echeck); $echeck->user_id = $this->getDi()->auth->getUserId(); $result = new Am_Paysystem_Result(); $this->plugin->storeEcheck($echeck, $result); if ($result->isSuccess()) { return $this->redirectLocation(REL_ROOT_URL . '/member'); } else { $this->form->getElementById('echeck_ban-0')->setError($result->getLastError()); } } $this->view->form = $this->form; $this->view->invoice = null; $this->view->display_receipt = false; $this->view->display('echeck/info.phtml'); } } library/Am/Controller/CreditCard.php000064400000010741152101601040013376 0ustar00plugin = $plugin; } /** * Process the validated form and if ok, display thanks page, * if not ok, return false */ public function processCc() { $cc = $this->getDi()->ccRecordRecord; $this->form->toCcRecord($cc); $cc->user_id = $this->invoice->user_id; if($this->plugin->getConfig('use_maxmind')) { $checkresult = $this->plugin->doMaxmindCheck($this->invoice, $cc); if (!$checkresult->isSuccess()) { $this->view->error = $checkresult->getErrorMessages(); return; } } $result = $this->plugin->doBill($this->invoice, true, $cc); if ($result->isSuccess()) { if (($this->invoice->rebill_times > 0) && !$cc->pk()) $this->plugin->storeCreditCard($cc, new Am_Paysystem_Result); $this->redirectLocation($this->plugin->getReturnUrl()); return true; } elseif ($result->isAction() && ($result->getAction() instanceof Am_Paysystem_Action_Redirect)) { $result->getAction()->process($this); // throws Am_Exception_Redirect (!) } else { $this->view->error = $result->getErrorMessages(); } } public function setInvoice(Invoice $invoice) { $this->invoice = $invoice; } public function ccAction() { // invoice must be set to this point by the plugin if (!$this->invoice) throw new Am_Exception_InternalError('Empty invoice - internal error!'); $this->form = $this->createForm(); $this->getDi()->hook->call(Bootstrap_Cc::EVENT_CC_FORM, array('form' => $this->form)); if ($this->form->isSubmitted() && $this->form->validate()) { if ($this->processCc()) return; } $this->view->form = $this->form; $this->view->invoice = $this->invoice; $this->view->display_receipt = true; $this->view->layoutNoMenu = true; $this->view->display('cc/info.phtml'); } public function createForm() { $form = $this->plugin->createForm($this->_request->getActionName(), $this->invoice); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($form->getDefaultValues($this->invoice->getUser())) )); $form->addHidden(Am_Controller::ACTION_KEY)->setValue($this->_request->getActionName()); $form->addHidden('id')->setValue($this->getFiltered('id')); return $form; } public function preDispatch() { if (!$this->plugin) throw new Am_Exception_InternalError("Payment plugin is not passed to " . __CLASS__); } public function createUpdateForm() { $form = new Am_Form_CreditCard($this->plugin, Am_Form_CreditCard::USER_UPDATE); $user = $this->getDi()->auth->getUser(true); if (!$user) throw new Am_Exception_InputError("You are not logged-in"); $cc = $this->getDi()->ccRecordTable->findFirstByUserId($user->user_id); if (!$cc) $cc = $this->getDi()->ccRecordRecord; $arr = $cc->toArray(); unset($arr['cc_number']); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($arr) )); return $form; } public function updateAction() { $this->form = $this->createUpdateForm(); if ($this->form->isSubmitted() && $this->form->validate()) { $cc = $this->getDi()->ccRecordRecord; $this->form->toCcRecord($cc); $cc->user_id = $this->getDi()->auth->getUserId(); $result = new Am_Paysystem_Result(); $this->plugin->storeCreditCard($cc, $result); if ($result->isSuccess()) { return $this->redirectLocation(REL_ROOT_URL . '/member'); } else { $this->form->getElementById('cc_number-0')->setError($result->getLastError()); } } $this->view->form = $this->form; $this->view->invoice = null; $this->view->display_receipt = false; $this->view->display('cc/info.phtml'); } } library/CcRecord.php000064400000006565152101601040010407 0ustar00_cc_code; } function setCvv($code) { $this->_cc_code = filterId($code); } function maskCc($number) { $number = preg_replace('/\D+/', '', $number); if (strlen($number)<8) return '****************'; return str_repeat('*', strlen($number)-4) . substr($number, -4, 4); } function toRow() { $arr = parent::toRow(); // fields to encrypt if (isset($arr['cc_number'])) { $arr['cc_number'] = preg_replace('/\D+/', '', $arr['cc_number']); if (empty($arr['cc']) || ($arr['cc_number'] != '0000000000000000')) $arr['cc'] = $this->maskCc($arr['cc_number']); } foreach ($this->_encryptedFields as $f) if (array_key_exists($f, $arr)) $arr[$f] = $this->_table->encrypt($arr[$f]); return $arr; } public function fromRow(array $arr) { // fields to decrypt foreach ($this->_encryptedFields as $f) if (array_key_exists($f, $arr)) $arr[$f] = $this->_table->decrypt($arr[$f]); return parent::fromRow($arr); } /** * Delete existing record for this user_id, then insert this one * @return CcRecord provides fluent interface */ function replace() { if (empty($this->user_id) || $this->user_id <= 0) throw new Am_Exception_InternalError("this->user_id is empty in " . __METHOD__); $this->_table->deleteByUserId($this->user_id); return $this->insert(); } function getExpire($format = "%02d%02d") { if ("" == $this->cc_expire) return ""; $m = substr($this->cc_expire, 0, 2); $y = substr($this->cc_expire, 2, 2); return sprintf($format, $m, $y); } } class CcRecordTable extends Am_Table { protected $_crypt; protected $_key = 'cc_id'; protected $_table = '?_cc'; protected $_recordClass = 'CcRecord'; function encrypt($s){ return $this->_getCrypt()->encrypt($s); } function decrypt($s){ return $this->_getCrypt()->decrypt($s); } function _getCrypt(){ if (empty($this->_crypt)) $this->_crypt = Am_Di::getInstance ()->crypt; return $this->_crypt; } function setCrypt(Am_Crypt $crypt) { $this->_crypt = $crypt; } } email-templates.xml000064400000004564152101601040010350 0ustar00 cc.admin_rebill_stats 2 en text %site_title%: %plugin% Rebill Statistics (%short_stats%) Outlined below are the results of latest rebilling attempt on %site_title% Here you can find statistics about SUCCESS rebills === %rebills_success% Here you can find statistics about FAILED rebills === %rebills_failed% cc.rebill_failed 1 en text %site_title%: Subscription Renewal Failed Your subscription was not renewed automatically by membership system due to payment failure: %error% %prorate% You may update your credit card info here:. %root_url%/member Thank you for attention! cc.rebill_success 1 en text %site_title%: Subscription Renewed Your subscription has been renewed automatically by our membership system. Your credit card was charged %amount% Next renewal date: %rebill_date% You may login to membership info page at : %root_url%/member cc.card_expire 1 en text %site_title%: Credit Card Expiration Your credit card that we have on file for recurring billing will expire on %expires%. Next recurring billing for invoice %invoice.public_id% is sheduled for %invoice.rebill_date|date%. To avoid any interruption of your subscription, please visit page %root_url%/member and update your credit card information. Bootstrap.php000064400000014442152101601040007225 0ustar00getDi()->plugins_payment->addPath(dirname(__FILE__) . '/plugins'); } function onSetupEmailTemplateTypes(Am_Event $event) { $event->addReturn(array( 'id' => 'cc.admin_rebill_stats', 'title' => 'Cc Rebill Rebill Stats', 'mailPeriodic' => Am_Mail::USER_REQUESTED, 'isAdmin' => true, ), 'cc.admin_rebill_stats'); $event->addReturn(array( 'id' => 'cc.rebill_failed', 'title' => 'Cc Rebill Failed', 'mailPeriodic' => Am_Mail::USER_REQUESTED, 'vars' => array('user'), ), 'cc.rebill_failed'); $event->addReturn(array( 'id' => 'cc.rebill_failed_admin', 'title' => 'Cc Rebill Failed Admin', 'mailPeriodic' => Am_Mail::USER_REQUESTED, 'isAdmin' => true, 'vars' => array('user'), ), 'cc.rebill_failed_admin'); $event->addReturn(array( 'id' => 'cc.rebill_success', 'title' => 'Cc Rebill Success', 'mailPeriodic' => Am_Mail::USER_REQUESTED, 'vars' => array('user'), ), 'cc.rebill_success'); } public function onAdminWarnings(Am_Event $event) { $plugins = $this->getDi()->plugins_payment->loadEnabled()->getAllEnabled(); $key_pl = ''; foreach($plugins as $pl) if($pl instanceof Am_Paysystem_CreditCard) $key_pl = $pl->getId(); $setupUrl = $this->getDi()->url('admin-setup'); ///check for configuration problems if (class_exists('Am_Paysystem_CreditCard', false)) { if(!$this->getDi()->config->get('use_cron')) { $event->addReturn(___('%sEnable%s and %sconfigure%s external cron if you are using credit card payment plugins', '', '', '', '')); } try { $crypt = $this->getDi()->crypt; } catch (Am_Exception_Crypt_Key $e) { if($key_pl) $event->addReturn("Encryption subsystem error: " . '' . $e->getMessage(). ''); else $event->addReturn("Encryption subsystem error: " . $e->getMessage()); } catch (Am_Exception_Crypt $e) { $event->addReturn("Encryption subsystem error: " . $e->getMessage()); } } } public function onHourly(Am_Event $event) { foreach ($this->getPlugins() as $ps) $ps->ccRebill($this->getDi()->sqlDate); } /** @return array of Am_Paysystem_CreditCard */ public function getPlugins() { $this->getDi()->plugins_payment->loadEnabled(); $ret = array(); foreach ($this->getDi()->plugins_payment->getAllEnabled() as $ps) if ($ps instanceof Am_Paysystem_CreditCard || $ps instanceof Am_Paysystem_Echeck) $ret[] = $ps; return $ret; } public function onUserAfterDelete(Am_Event_UserAfterDelete $event) { $this->getDi()->ccRecordTable->deleteByUserId($event->getUser()->pk()); $this->getDi()->echeckRecordTable->deleteByUserId($event->getUser()->pk()); } function onUserTabs(Am_Event_UserTabs $event) { if ($event->getUserId() > 0) { $event->getTabs()->addPage(array( 'id' => 'cc', 'module' => 'cc', 'controller' => 'admin', 'action' => 'info-tab', 'params' => array( 'user_id' => $event->getUserId(), ), 'label' => ___('Credit Cards'), 'order' => 900, 'resource' => 'cc', )); foreach ($this->getPlugins() as $ps) { if($ps instanceof Am_Paysystem_Echeck) { $event->getTabs()->addPage(array( 'id' => 'cc', 'module' => 'cc', 'controller' => 'admin', 'action' => 'info-tab-echeck', 'params' => array( 'user_id' => $event->getUserId(), ), 'label' => ___('Echeck'), 'order' => 901, 'resource' => 'cc', )); break; } } } } function onAdminMenu(Am_Event $event) { $parent = $event->getMenu()->findBy('id', 'utilites'); if (!$parent) $parent = $event->getMenu(); $parent->addPage(array( 'id' => 'ccrebills', 'module' => 'cc', 'controller' => 'admin-rebills', 'label' => ___('Credit Card Rebills'), 'resource' => 'cc', )); /* disabled until real-life tested if (count($this->getPlugins()) > 1) { $parent->addPage(array( 'id' => 'cc-change', 'module' => 'cc', 'controller' => 'admin', 'action' => 'change-paysys', 'label' => 'Change Paysystem', )); } * */ } function onGetPermissionsList(Am_Event $event) { $event->addReturn(___("Can view/edit customer Credit Card information and rebills"), 'cc'); } function onGetMemberLinks(Am_Event $event) { $user = $event->getUser(); if ($user->status == User::STATUS_PENDING) return ; foreach ($this->getPlugins() as $pl) { if($pl instanceof Am_Paysystem_CreditCard && ($link = $pl->getUpdateCcLink($user))) $event->addReturn(___("Update Credit Card Info"), $link); elseif($pl instanceof Am_Paysystem_Echeck && ($link = $pl->getUpdateEcheckLink($user))) $event->addReturn(___("Update Echeck Info"), $link); } } } module.xml000064400000000355152101601040006544 0ustar00 cc Credit Card Billing This module provides ability to bill credit cards directly on your website 4.7.0 controllers/AdminConvertController.php000064400000022121152101601040014244 0ustar00isSuper(); } /** * * @return Am_Crypt */ function getCurrentCryptObject() { $cname = $this->getDi()->getCryptClass(); return (new $cname); } /** * * @return Am_Crypt */ function getOldCryptObject() { $crypt = $this->getCurrentCryptObject(); $oldCrypt = $crypt; $oldSig = $oldCrypt->loadKeySignature(); $cname = Am_Crypt::getClassByMethod($oldSig); if (get_class($oldCrypt) != $cname) { $oldCrypt = new $cname; } if ($oldCrypt->getKeySignature() != $oldSig) { // Now try to load old key file; $path = APPLICATION_PATH . '/configs/key-old.php'; if (!file_exists($path)) throw new Am_Exception_Crypt_Key(___('Unable to find old keyfile. ' . 'Please upload it to %s ' . 'Then refresh page', $path)); // @todo comment $key = include $path; if (!strlen($key)) throw new Am_Exception_Crypt_Key('Key file has incorrect format or the key is empty'); // @todo comment if ($key == 'REPLACE THIS STRING TO YOUR KEYSTRING') throw new Am_Exception_Crypt_Key("You must define a valid key in the file [$path] instead of default"); $oldCrypt = new $cname($key); } return $oldCrypt; } function createBackup() { try { $this->getDi()->db->query("CREATE TABLE ?_cc_backup LIKE ?_cc"); $this->getDi()->db->query("INSERT INTO ?_cc_backup SELECT * FROM ?_cc"); $this->getDi()->db->query("CREATE TABLE ?_config_backup LIKE ?_config"); $this->getDi()->db->query("INSERT INTO ?_config_backup SELECT * FROM ?_config"); } catch (Exception $ex) { $this->getDi()->errorLogTable->logException($ex); throw new Am_Exception_InputError( ___('Unable to backup cc and config tables. ' . 'Check "aMember CP -> Error Log" for more info', $this->getDi()->url('admin-logs')) ); } } function dropBackup() { try { $this->getDi()->db->query('DROP TABLE ?_config_backup'); $this->getDi()->db->query('DROP TABLE ?_cc_backup'); } catch (Exception $ex) { $this->getDi()->errorLogTable->logException($ex); throw new Am_Exception_InputError( ___('Unable to delete backup tables. ' . 'Please delete cc_backup and config_backup tables manually ' . 'Check "aMember CP -> Error Log" for more info ', $this->getDi()->url('admin-logs')) ); } } function restoreBackup() { try { $this->getDi()->db->query('TRUNCATE TABLE ?_config'); $this->getDi()->db->query("INSERT INTO ?_config SELECT * FROM ?_config_backup"); $this->getDi()->db->query('TRUNCATE TABLE ?_cc'); $this->getDi()->db->query("INSERT INTO ?_cc SELECT * FROM ?_cc_backup"); } catch (Exception $ex) { $this->getDi()->errorLogTable->logException($ex); throw new Am_Exception_InputError( ___('Unable to restore back in cc_backup and config_backup tables' . 'Please restore it manually. ' . 'Check "aMember CP -> Error Log" for more info ', $this->getDi()->url('admin-logs')) ); } } function indexAction() { $crypt = $this->getCurrentCryptObject(); $oldCrypt = $this->getOldCryptObject(); $this->view->title = "Convert CC Database"; $startUrl = $this->getDi()->url('cc/admin-convert/do'); if (get_class($oldCrypt) != get_class($crypt)) { $this->view->content = << Encryption method was changed. You have to encrypt database using new encryption method.
You must backup your database before you'll run this tool. GGI-Central is not responsible for any damage this script may result to. If you have no backup saved, you may loose your data.
Make backup first, then return to this page.

CUT1; } else if ($crypt->getKeySignature() != $crypt->loadKeySignature()) { $path = $path = APPLICATION_PATH . '/configs/key-old.php'; $this->view->content = << Encryption key file was changed. Please upload old key file to {$path} and re-encrypt CC table.
You must backup your database before you'll run this tool. GGI-Central is not responsible for any damage this script may result to. If you have no backup saved, you may loose your data.
Make backup first, then return to this page.

CUT2; } else { $this->view->content = <<Key signature stored in database and key signature that was generated are equal. No further actions are required. CUT; } $this->view->display('admin/layout.phtml'); } function doAction() { $this->crypt = $this->getCurrentCryptObject(); $this->oldCrypt = $this->getOldCryptObject(); $this->context = $this->getParam('context'); if (!$this->context) $this->createBackup(); $batch = new Am_BatchProcessor(array($this, 'doWork')); $breaked = !$batch->run($this->context); $breaked ? $this->convertRedirect() : $this->convertComplete(); } function doWork(& $context, Am_BatchProcessor $batch) { $db = $this->getDi()->db; try { $this->done = $db->selectCell("select count(*) from ?_cc where cc_idtotal = $db->selectCell("select count(*) from ?_cc"); $q = $db->queryResultOnly("SELECT * FROM ?_cc WHERE cc_id > ?d", (int) $context); while ($r = $db->fetchRow($q)) { $context = $r['cc_id']; $ccRecord = $this->getDi()->CcRecordRecord; $ccRecord->getTable()->setCrypt($this->oldCrypt); $ccRecord->fromRow($r); if (preg_match('/[^\s\d-]/', $ccRecord->cc_number)) { throw new Am_Exception_InternalError( "Problem with converting to new encryption key. " . "cc record# {$ccRecord->cc_id} could not be converted, " . "it seems the old key has been specified incorrectly. Conversion cancelled."); return; } $ccRecord->getTable()->setCrypt($this->crypt); $ccRecord->update(); $this->done++; if (!$batch->checkLimits()) return; } } catch (Exception $ex) { $this->getDi()->errorLogTable->logException($ex); $this->restoreBackup(); $this->dropBackup(); throw new Am_Exception_InputError( ___('Got an error when attempting to re-encode CC record. ' . 'CC and config tables were restored from backup. ' . 'Check "aMember CP -> Error Log" for more info ', $this->getDi()->url('admin-logs')) ); } return true; } function convertRedirect() { $done = $this->done; $total = $this->total; $url = $this->getUrl('admin-convert', 'do', 'cc', array('context' => $this->context)); $text = $total > 0 ? (___('Converting CC info (%d from %d)', $done, $total) . '. ' . ___('Please wait')) : ___('Converting started'); $text .= '...'; $this->redirectHtml($url, $text, ___('Converting') . '...', false, $done, $total); } function convertComplete() { $this->crypt->saveKeySigunature(); $this->view->assign('title', ___('Done')); ob_start(); print '
'; print 'Records were re-encoded using new key/encryption method'; print '. '; $url = $this->getUrl('admin-convert', 'index', 'cc'); print '' . ___('Back') . '
'; $this->view->assign('content', ob_get_clean()); $this->view->display('admin/layout.phtml'); $this->getDi()->db->query("OPTIMIZE TABLE ?_cc"); $this->dropBackup(); } } controllers/AdminRebillsController.php000064400000030052152101601040014222 0ustar00hasPermission('cc'); } public function init() { parent::init(); $this->view->headScript()->appendScript($this->getJs()); $this->view->placeholder('after-content')->append( ""); } public function renderStatus($obj, $fieldname, $grid, $field) { return $obj->{$fieldname} ? $this->renderTd(sprintf('%d - %s', $obj->{$fieldname}, Am_Currency::render($obj->{$fieldname . '_amt'})), false) : $this->renderTd(''); } protected function createAdapter() { $q = new Am_Query($this->getDi()->ccRebillTable); $q->leftJoin('?_invoice', 'i', 't.invoice_id=i.invoice_id'); $q->clearFields(); $q->groupBy('rebill_date'); $q->addField('t.rebill_date'); $q->addField('(1)', 'is_log'); $q->addField('COUNT(t.rebill_date)', 'total'); $q->addField('SUM(i.second_total/i.base_currency_multi)', 'total_amt'); $q->addField('SUM(IF(t.status=0, 1, 0))', 'status_0'); $q->addField('SUM(IF(t.status=1, 1, 0))', 'status_1'); $q->addField('SUM(IF(t.status=2, 1, 0))', 'status_2'); $q->addField('SUM(IF(t.status=3, 1, 0))', 'status_3'); $q->addField('SUM(IF(t.status=4, 1, 0))', 'status_4'); $q->addField('SUM(IF(t.status=0, i.second_total/i.base_currency_multi, 0))', 'status_0_amt'); $q->addField('SUM(IF(t.status=1, i.second_total/i.base_currency_multi, 0))', 'status_1_amt'); $q->addField('SUM(IF(t.status=2, i.second_total/i.base_currency_multi, 0))', 'status_2_amt'); $q->addField('SUM(IF(t.status=3, i.second_total/i.base_currency_multi, 0))', 'status_3_amt'); $q->addField('SUM(IF(t.status=4, i.second_total/i.base_currency_multi, 0))', 'status_4_amt'); $u = new Am_Query($this->getDi()->invoiceTable, 'i'); $u->addWhere('i.paysys_id IN (?a)', array_merge(array('avoid-sql-error'), $this->getPlugins())); $u->groupBy('rebill_date'); $u->clearFields()->addField('i.rebill_date'); $u->addField('(0)', 'is_log'); $u->addField('COUNT(i.invoice_id)', 'total'); $u->addField('SUM(i.second_total/i.base_currency_multi)', 'total_amt'); for ($i = 1; $i < 11; $i++) $u->addField('(NULL)'); $u->leftJoin('?_cc_rebill', 't', 't.rebill_date=i.rebill_date'); $u->addWhere('i.rebill_date IS NOT NULL'); $u->addWhere('t.rebill_date IS NULL'); $q->addUnion($u); $q->addOrder('rebill_date', true); return $q; } public function createGrid() { $grid = new Am_Grid_ReadOnly('_r', 'Rebills by Date', $this->createAdapter(), $this->_request, $this->view); $grid->setPermissionId('cc'); $grid->addField('rebill_date', 'Date')->setRenderFunction(array($this, 'renderDate')); $grid->addField('status_0', 'Processing Not Finished')->setRenderFunction(array($this, 'renderStatus')); $grid->addField('status_1', 'No CC Saved')->setRenderFunction(array($this, 'renderStatus')); $grid->addField('status_2', 'Error')->setRenderFunction(array($this, 'renderStatus')); $grid->addField('status_3', 'Success')->setRenderFunction(array($this, 'renderStatus')); $grid->addField('status_4', 'Exception!')->setRenderFunction(array($this, 'renderStatus')); $grid->addField('total', 'Total')->setRenderFunction(array($this, 'renderTotal')); $grid->addField('_action', '')->setRenderFunction(array($this, 'renderLink')); $grid->addCallback(Am_Grid_ReadOnly::CB_TR_ATTRIBS, array($this, 'getTrAttribs')); return $grid; } public function getPlugins() { $this->getDi()->plugins_payment->loadEnabled(); $ret = array(); foreach ($this->getDi()->plugins_payment->getAllEnabled() as $ps) if ($ps instanceof Am_Paysystem_CreditCard || $ps instanceof Am_Paysystem_Echeck) $ret[] = $ps->getId(); return $ret; } public function renderDate(CcRebill $obj) { $raw = $obj->rebill_date; $d = amDate($raw); return $this->renderTd("$d", false); } public function getTrAttribs(& $ret, $record) { if ($record->rebill_date > sqlDate('now')) { $ret['class'] = isset($ret['class']) ? $ret['class'] . ' disabled' : 'disabled'; } } public function renderTotal(CcRebill $obj) { if ($obj->is_log) { return $this->renderTd(sprintf('%d - %s', $obj->total, Am_Currency::render($obj->total_amt)), false); } else { $url = $this->getDi()->url('default/admin-payments/p/invoices/index' , array( '_invoice_filter' => array( 'datf' => 'rebill_date', 'dat1' => amDate($obj->rebill_date), 'dat2' => amDate($obj->rebill_date) ), '_invoice_sort' => 'rebill_date' )); return $this->renderTd(sprintf('%d - %s', $url, $obj->total, Am_Currency::render($obj->total_amt)), false); } } public function renderLink(CcRebill $obj) { $linkRun = $linkDetail = ''; if ($obj->rebill_date <= sqlDate('now')) { if ($obj->status_3 < $obj->total) { $iconRun = $this->getDi()->view->icon('retry', ___('Run')); $back_url = $this->grid->makeUrl(); $linkRun = "$iconRun"; } if ($obj->is_log) { $iconDetail = $this->getDi()->view->icon('view', ___('Details')); $linkDetail = "$iconDetail"; } } return "$linkRun $linkDetail"; } public function renderInvoiceLink($record) { return 'user_id . "#invoice-" . $record->invoice_id) . '" target=_top >' . $record->invoice_id . '/' . $record->public_id . ''; } public function getJs() { $title = ___('Run Rebill Manually'); $title_details = ___('Details'); return <<'); div.load(amUrl("/cc/admin-rebills/detail?_r_d_date=") + date , function(){ div.dialog({ autoOpen: true ,width: 800 ,buttons: {} ,closeOnEscape: true ,title: "$title_details" ,modal: true ,open: function(){ div.ngrid(); } }); }); }); }); jQuery(function(){ jQuery(document).on('submit',"#run-form form", function(){ jQuery(this).ajaxSubmit({target: '#run-form'}); return false; }); }); CUT; } public function createRunForm() { $form = new Am_Form; $form->setAction($this->getUrl(null, 'run')); $s = $form->addSelect('paysys_id')->setLabel(___('Choose a plugin')); $s->addRule('required'); foreach ($this->getModule()->getPlugins() as $p) $s->addOption($p->getTitle(), $p->getId()); $form->addDate('date')->setLabel(___('Run Rebill Manually'))->addRule('required'); $form->addHidden('back_url'); $form->addSubmit('run', array('value' => ___('Run'))); return $form; } public function detailAction() { $date = $this->getFiltered('_r_d_date'); if (!$date) throw new Am_Exception_InputError('Wrong date'); $grid = $this->createDetailGrid($date); $grid->isAjax(false); $grid->runWithLayout('admin/layout.phtml'); } protected function createDetailGrid($date) { $q = new Am_Query($this->getDi()->ccRebillTable); $q->addWhere('t.rebill_date=?', $date); $q->leftJoin('?_invoice', 'i', 'i.invoice_id=t.invoice_id'); $q->leftJoin('?_user', 'u', 'u.user_id=i.user_id'); $q->addField('i.public_id'); $q->addField('u.name_f'); $q->addField('u.name_l'); $q->addField('u.email'); $q->addField('i.user_id'); $q->addField('i.second_total'); $q->addField('i.currency'); $grid = new Am_Grid_ReadOnly('_r_d', ___('Detailed Rebill Report for %s', amDate($date)), $q, $this->_request, $this->view); $grid->setPermissionId('cc'); $grid->addField(new Am_Grid_Field_Date('tm_added', 'Started', true)); $grid->addField('invoice_id', 'Invoice#', true, '', array($this, 'renderInvoiceLink')); $grid->addField('user', 'User')->setRenderFunction(function($obj, $fieldname, $grid, $field){ return sprintf("%s %s (%s)>", $this->getDi()->url('admin-users',array('_u_a'=>'edit','_u_id'=>$obj->user_id)), $obj->name_f, $obj->name_l, $obj->email); }); $grid->addField('paysys_id', ___('Payment System')); $grid->addField('second_total', ___('Amount'))->setRenderFunction(function($obj, $fieldname, $grid, $field){ return $grid->renderTd(Am_Currency::render($obj->second_total, $obj->currency), false); }); $grid->addField(new Am_Grid_Field_Date('rebill_date', 'Date', true))->setFormatDate(); $grid->addField('status', 'Status', true)->setFormatFunction(array('CcRebill', 'getStatusText')); $grid->addField('status_msg', 'Message'); $grid->setCountPerPage(10); return $grid; } public function runAction() { $date = $this->getFiltered('date'); if (!$date) throw new Am_Exception_InputError("Wrong date"); $form = $this->createRunForm(); if ($form->isSubmitted() && $form->validate()) { $value = $form->getValue(); $this->doRun($value['paysys_id'], $value['date']); echo sprintf('
%s
', ___('Rebill Operation Completed for %s', amDate($value['date']))); } else { echo $form; } } public function doRun($paysys_id, $date) { $this->getDi()->plugins_payment->load($paysys_id); $p = $this->getDi()->plugins_payment->get($paysys_id); // Delete all previous failed attempts for this date in order to rebill these invoices again. $this->getDi()->db->query(" DELETE FROM ?_cc_rebill WHERE rebill_date = ? AND paysys_id = ? AND status <> ? ", $date, $paysys_id, ccRebill::SUCCESS); $p->ccRebill($date); } }controllers/AdminController.php000064400000016146152101601040012715 0ustar00hasPermission('cc'); } function infoTabAction() { require_once AM_APPLICATION_PATH . '/default/controllers/AdminUsersController.php'; $this->setActiveMenu('users-browse'); $user_id = $this->_request->getInt('user_id'); if (!$user_id) throw Am_Exception_InputError("Empty [user_id] passsed"); $cc = $this->getDi()->ccRecordTable->findFirstByUserId($user_id); /* @var $cc CcRecord */ $this->view->cc = $cc; $this->view->addUrl = $this->getUrl(null, null, null, 'user_id', $this->getInt('user_id'), array('add'=>1)); if ($cc || $this->_request->getInt('add') || $this->_request->get('_save_')) { $form = $this->createAdminForm((bool)$cc); if ($form) { if ($form->isSubmitted() && $form->validate()) { if (!$cc) $cc = $this->getDi()->ccRecordTable->createRecord(); $form->toCcRecord($cc); $cc->user_id = $user_id; $cc->save(); } elseif ($cc) { $arr = $cc->toArray(); unset($arr['cc_number']); $form->addDataSource(new HTML_QuickForm2_DataSource_Array($arr)); } $this->view->form = $form; $this->view->form->setAction($this->_request->getRequestUri()); } } $this->view->display('admin/cc/info-tab.phtml'); } function infoTabEcheckAction() { require_once AM_APPLICATION_PATH . '/default/controllers/AdminUsersController.php'; $this->setActiveMenu('users-browse'); $user_id = $this->_request->getInt('user_id'); if (!$user_id) throw Am_Exception_InputError("Empty [user_id] passsed"); $echeck = $this->getDi()->echeckRecordTable->findFirstByUserId($user_id); $this->view->echeck = $echeck; $this->view->addUrl = $this->getUrl(null, null, null, 'user_id', $this->getInt('user_id'), array('add'=>1)); if ($echeck || $this->_request->getInt('add') || $this->_request->get('_save_')) { $form = $this->createEcheckAdminForm((bool)$echeck); if ($form) { if ($form->isSubmitted() && $form->validate()) { if (!$echeck) $echeck = $this->getDi()->echeckRecordTable->createRecord(); $form->toEcheckRecord($echeck); $echeck->user_id = $user_id; $echeck->save(); $this->_response->redirectLocation($this->_request->getRequestUri()); } elseif ($echeck) { $arr = $echeck->toArray(); unset($arr['echeck_ban']); $form->addDataSource(new HTML_QuickForm2_DataSource_Array($arr)); } $this->view->form = $form; $this->view->form->setAction($this->_request->getRequestUri()); } } $this->view->display('admin/echeck/info-tab.phtml'); // ???? } function createAdminForm($isUpdate) { $form = null; foreach ($this->getDi()->modules->get('cc')->getPlugins() as $ps) { if($ps instanceof Am_Paysystem_CreditCard) { $form = new Am_Form_CreditCard($ps, $isUpdate ? Am_Form_CreditCard::ADMIN_UPDATE : Am_Form_CreditCard::ADMIN_INSERT ); break; // first one } } return $form; } function createEcheckAdminForm($isUpdate) { $form = null; foreach ($this->getDi()->modules->get('cc')->getPlugins() as $ps) { if($ps instanceof Am_Paysystem_Echeck) { $form = new Am_Form_Echeck($ps, $isUpdate ? Am_Form_Echeck::ADMIN_UPDATE : Am_Form_Echeck::ADMIN_INSERT ); break; // first one } } return $form; } function changePaysysAction() { $form = new Am_Form_Admin; $form->setDataSources(array($this->_request)); $form->addStatic()->setContent(___( 'If you are moving from one payment processor, you can use this page to switch existing subscription from one payment processor to another. It is possible only if full credit card info is stored on aMember side.')); $ccPlugins = $echeckPlugins = array(); foreach ($this->getModule()->getPlugins() as $ps) { if($ps instanceof Am_Paysystem_CreditCard) $ccPlugins[$ps->getId()] = $ps->getTitle(); elseif($ps instanceof Am_Paysystem_Echeck) $echeckPlugins[$ps->getId()] = $ps->getTitle(); } if(count($ccPlugins) < 2) $ccPlugins = array(); if(count($echeckPlugins) < 2) $echeckPlugins = array(); $options = array('' => '-- ' . ___('Please select') . ' --') + ($ccPlugins ? array(___('Credit Card Plugins') => $ccPlugins) : array()) + ($echeckPlugins ? array(___('Echeck Plugins') => $echeckPlugins) : array()); $from = $form->addSelect('from', array('id' => 'paysys_from'))->setLabel('Move Active Invoices From')->loadOptions($options); $from->addRule('required'); $to = $form->addSelect('to', array('id' => 'paysys_to'))->setLabel('Move To')->loadOptions($options); $to->addRule('required'); $to->addRule('neq', ___('Values must not be equal'), $from); $form->addScript() ->setScript(<<addSaveButton(); if ($form->isSubmitted() && $form->validate()) { $vars = $form->getValue(); $updated = $this->getDi()->db->query("UPDATE ?_invoice SET paysys_id=? WHERE paysys_id=? AND status IN (?a)", $vars['to'], $vars['from'], array(Invoice::RECURRING_ACTIVE)); $this->view->content = "$updated rows changed. New rebills for these invoices will be handled with [{$vars['to']}]"; } else { $this->view->content = (string)$form; } $this->view->title = ___("Change Paysystem"); $this->view->display('admin/layout.phtml'); } } plugins/targetpay-wap.php000064400000016060152101601040011514 0ustar00addInteger("rtlo", array('maxlength' => 15, 'size' => 15)) ->setLabel("Subaccount (rtlo)") ->addRule('required'); $form->addInteger("service", array('maxlength' => 15, 'size' => 15)) ->setLabel("The ID of your service") ->addRule('required'); } public function isConfigured() { return strlen($this->getConfig('service')) && strlen($this->getConfig('rtlo')); } public function getSupportedCurrencies() { return array('EUR'); } public function storesCcInfo() { return false; } public function getRecurringType() { return self::REPORTS_CRONREBILL; } public function directAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { if ($request->getActionName() == 'ipn') { try { parent::directAction($request, $response, $invokeArgs); } catch (Exception $ex) { $this->getDi()->errorLogTable->logException($ex); } echo '45000'; } else parent::directAction($request, $response, $invokeArgs); } public function _process(Invoice $invoice, Am_Mvc_Request $request, Am_Paysystem_Result $result) { $user = $invoice->getUser(); $req = new Am_HttpRequest(self::LIVE_URL_START, Am_HttpRequest::METHOD_POST); $req->addPostParameter(array( 'service' => $this->getConfig('service'), 'ip' => $user->remote_addr ? $user->remote_addr : $_SERVER['REMOTE_ADDR'], 'amount' => $invoice->first_total * 100, 'returnurl' => $this->getReturnUrl(), 'cancelurl' => $this->getCancelUrl(), 'autofirstbilling' => 1, 'pnotifyurl' => $this->getPluginUrl('ipn'), 'notifyurl' => $this->getPluginUrl('ipn'), )); $tr = new Am_Paysystem_Transaction_TargetpayWap_GetOrderUrl($this, $invoice, $req, true); $res = new Am_Paysystem_Result(); $tr->run($res); if (!($url = $tr->getUniqId())) throw new Am_Exception_Paysystem("Could not get order url - [" . $tr->getErrorDescription() . "]"); $a = new Am_Paysystem_Action_Form($url); $result->setAction($a); } public function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Transaction_TargetpayWap($this, $request, $response, $invokeArgs); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { if (!($trxid = $invoice->getUser()->data()->get(self::INVOICE_TRANSACTION_ID))) throw new Am_Exception_Paysystem("Stored targetpay-wap trxid not found"); $request = new Am_HttpRequest(self::LIVE_URL_FOLLOWUP, Am_HttpRequest::METHOD_POST); $request->addPostParameter(array( 'trxid' => $trxid, 'service' => $this->getConfig('service'), 'amount' => $invoice->second_total * 100, 'rtlo' => $this->getConfig('rtlo'), 'description' => $invoice->getLineDescription(), )); $tr = new Am_paysystem_Transaction_TargetpayWap_Charge($this, $invoice, $request, $doFirst); $tr->run($result); } } class Am_Paysystem_Transaction_TargetpayWap extends Am_Paysystem_Transaction_Incoming { public function findInvoiceId() { if ($invoice = $this->getPlugin()->getDi()->invoiceTable->findFirstByData(Am_Paysystem_TargetpayWap::INVOICE_TRANSACTION_ID, $this->request->get('trxid'))) return $invoice->public_id; } public function getUniqId() { return $this->request->get('paymentid'); } public function validateSource() { return true; } public function validateStatus() { if($this->request->get('status') == 'closed') return true; return intval($this->request->get('errorcode', 0)) == 0; } public function validateTerms() { if($this->request->get('status') == 'closed') return true; return floatval($this->request->get('amount') / 100) == floatval($this->invoice->first_total); } public function processValidated() { if($this->request->get('status') == 'closed') { $this->invoice->setCancelled (); } else { $user = $this->invoice->getUser(); $user->data()->set(Am_Paysystem_TargetpayWap::INVOICE_TRANSACTION_ID, $this->request->get('trxid'))->update(); parent::processValidated(); } } } class Am_Paysystem_Transaction_TargetpayWap_GetOrderUrl extends Am_Paysystem_Transaction_CreditCard { public function getErrorDescription() { return $this->res; } public function getUniqId() { return $this->url; } public function parseResponse() { $this->res = $this->response->getBody(); } public function processValidated() { } public function validate() { $this->url = false; if ($this->response->getStatus() != 200) { $this->result->setFailed(___('Payment failed')); return; } list($code, $extra) = explode(" ", $this->res); if ($code != "000000") { $this->result->setFailed(___('Payment failed')); return; } list($transactionId, $orderUrl) = explode("|", $extra); $this->url = $orderUrl; $this->result->setSuccess($this); $this->invoice->data()->set(Am_Paysystem_TargetpayWap::INVOICE_TRANSACTION_ID, $transactionId)->update(); return true; } } class Am_paysystem_Transaction_TargetpayWap_Charge extends Am_Paysystem_Transaction_CreditCard { public function getUniqId() { return $this->payment_id; } public function parseResponse() { $this->res = $this->response->getBody(); } public function validate() { if ($this->response->getStatus() != 200) { $this->result->setFailed(___('Payment failed')); return; } list($code, $payment_id) = explode(" ", $this->res); if ($code != "000000") { $this->result->setFailed(___('Payment failed')); return; } $this->payment_id = $payment_id; $this->result->setSuccess($this); return true; } }plugins/eway-rapid3.php000064400000075053152101601040011063 0ustar00 'System Error', 'S5085' => 'Started 3dSecure', 'S5086' => 'Routed 3dSecure', 'S5087' => 'Completed 3dSecure', 'S5099' => 'Incomplete (Access Code in progress/incomplete)', 'V6000' => 'Validation error', 'V6001' => 'Invalid CustomerIP', 'V6002' => 'Invalid DeviceID', 'V6011' => 'Invalid Payment TotalAmount', 'V6012' => 'Invalid Payment InvoiceDescription', 'V6013' => 'Invalid Payment InvoiceNumber', 'V6014' => 'Invalid Payment InvoiceReference', 'V6015' => 'Invalid Payment CurrencyCode', 'V6016' => 'Payment Required', 'V6017' => 'Payment CurrencyCode Required', 'V6018' => 'Unknown Payment CurrencyCode', 'V6021' => 'EWAY_CARDHOLDERNAME Required', 'V6022' => 'EWAY_CARDNUMBER Required', 'V6023' => 'EWAY_CARDCVN Required', 'V6033' => 'Invalid Expiry Date', 'V6034' => 'Invalid Issue Number', 'V6035' => 'Invalid Valid From Date', 'V6040' => 'Invalid TokenCustomerID', 'V6041' => 'Customer Required', 'V6042' => 'Customer FirstName Required', 'V6043' => 'Customer LastName Required', 'V6044' => 'Customer CountryCode Required', 'V6045' => 'Customer Title Required', 'V6046' => 'TokenCustomerID Required', 'V6047' => 'RedirectURL Required', 'V6051' => 'Invalid Customer FirstName', 'V6052' => 'Invalid Customer LastName', 'V6053' => 'Invalid Customer CountryCode', 'V6058' => 'Invalid Customer Title', 'V6059' => 'Invalid RedirectURL', 'V6060' => 'Invalid TokenCustomerID', 'V6061' => 'Invalid Customer Reference', 'V6062' => 'Invalid Customer CompanyName', 'V6063' => 'Invalid Customer JobDescription', 'V6064' => 'Invalid Customer Street1', 'V6065' => 'Invalid Customer Street2', 'V6066' => 'Invalid Customer City', 'V6067' => 'Invalid Customer State', 'V6068' => 'Invalid Customer PostalCode', 'V6069' => 'Invalid Customer Email', 'V6070' => 'Invalid Customer Phone', 'V6071' => 'Invalid Customer Mobile', 'V6072' => 'Invalid Customer Comments', 'V6073' => 'Invalid Customer Fax', 'V6074' => 'Invalid Customer URL', 'V6075' => 'Invalid ShippingAddress FirstName', 'V6076' => 'Invalid ShippingAddress LastName', 'V6077' => 'Invalid ShippingAddress Street1', 'V6078' => 'Invalid ShippingAddress Street2', 'V6079' => 'Invalid ShippingAddress City', 'V6080' => 'Invalid ShippingAddress State', 'V6081' => 'Invalid ShippingAddress PostalCode', 'V6082' => 'Invalid ShippingAddress Email', 'V6083' => 'Invalid ShippingAddress Phone', 'V6084' => 'Invalid ShippingAddress Country', 'V6085' => 'Invalid ShippingAddress ShippingMethod', 'V6086' => 'Invalid ShippingAddress Fax', 'V6091' => 'Unknown Customer CountryCode', 'V6092' => 'Unknown ShippingAddress CountryCode', 'V6100' => 'Invalid EWAY_CARDNAME', 'V6101' => 'Invalid EWAY_CARDEXPIRYMONTH', 'V6102' => 'Invalid EWAY_CARDEXPIRYYEAR', 'V6103' => 'Invalid EWAY_CARDSTARTMONTH', 'V6104' => 'Invalid EWAY_CARDSTARTYEAR', 'V6105' => 'Invalid EWAY_CARDISSUENUMBER', 'V6106' => 'Invalid EWAY_CARDCVN', 'V6107' => 'Invalid EWAY_ACCESSCODE', 'V6108' => 'Invalid CustomerHostAddress', 'V6109' => 'Invalid UserAgent', 'V6110' => 'Invalid EWAY_CARDNUMBER' ); protected $responseCodesFailed = array( 'D4401' => 'Refer to Issuer', 'D4402' => 'Refer to Issuer, special', 'D4403' => 'No Merchant', 'D4404' => 'Pick Up Card', 'D4405' => 'Do Not Honour', 'D4406' => 'Errror', 'D4407' => 'Pick Up Card, Special', 'D4409' => 'Request In Progress', 'D4412' => 'Invalid Transaction', 'D4413' => 'Invalid Amount', 'D4414' => 'Invalid Card Number', 'D4415' => 'No Issuer', 'D4419' => 'Re-enter Last Transaction', 'D4421' => 'No Action Taken', 'D4422' => 'Suspected Malfunction', 'D4423' => 'Unacceptable Transaction Fee', 'D4425' => 'Unable to Locate Record On File', 'D4430' => 'Format Error', 'D4433' => 'Expired Card, Capture', 'D4434' => 'Suspected Fraud, Retain Card', 'D4435' => 'Card Acceptor, Contact Acquirer, Retain Card', 'D4436' => 'Restricted Card, Retain Card', 'D4437' => 'Contact Acquirer Security Department, Retain Card', 'D4438' => 'PIN Tries Exceeded, Capture', 'D4439' => 'No Credit Account', 'D4440' => 'Function Not Supported', 'D4441' => 'Lost Card', 'D4442' => 'No Universal Account', 'D4443' => 'Stolen Card', 'D4444' => 'No Investment Account', 'D4451' => 'Insufficient Funds', 'D4452' => 'No Cheque Account', 'D4453' => 'No Savings Account', 'D4454' => 'Expired Card', 'D4455' => 'Incorrect PIN', 'D4456' => 'No Card Record', 'D4457' => 'Function Not Permitted to Cardholder', 'D4458' => 'Function Not Permitted to Terminal', 'D4459' => 'Suspected Fraud', 'D4460' => 'Acceptor Contact Acquirer', 'D4461' => 'Exceeds Withdrawal Limit', 'D4462' => 'Restricted Card', 'D4463' => 'Security Violation', 'D4464' => 'Original Amount Incorrect', 'D4466' => 'Acceptor Contact Acquirer, Security', 'D4467' => 'Capture Card', 'D4475' => 'PIN Tries Exceeded', 'D4482' => 'CVV Validation Error', 'D4490' => 'Cut off In Progress', 'D4491' => 'Card Issuer Unavailable', 'D4492' => 'Unable To Route Transaction', 'D4493' => 'Cannot Complete, Violation Of The Law', 'D4494' => 'Duplicate Transaction', 'D4496' => 'System Error' ); protected $responseCodesBeagle = array( 'F7000' => 'Undefined Fraud Error', 'F7001' => 'Challenged Fraud', 'F7002' => 'Country Match Fraud', 'F7003' => 'High Risk Country Fraud', 'F7004' => 'Anonymous Proxy Fraud', 'F7005' => 'Transparent Proxy Fraud', 'F7006' => 'Free Email Fraud', 'F7007' => 'Inernational Transaction Fraud', 'F7008' => 'Risk Score Fraud', 'F7009' => 'Denied Fraud', 'F9010' => 'High Risk Billing Country', 'F9011' => 'High Risk Credit Card Country', 'F9012' => 'High Risk Customer IP Address', 'F9013' => 'High Risk Email Address', 'F9014' => 'High Risk Shipping Country', 'F9015' => 'Multiple card numbers for single email address', 'F9016' => 'Multiple card numbers for single location', 'F9017' => 'Multiple email addresses for single card number', 'F9018' => 'Multiple email addresses for single location', 'F9019' => 'Multiple locations for single card number', 'F9020' => 'Multiple locations for single email address', 'F9021' => 'Suspicious Customer First Name', 'F9022' => 'Suspicious Customer Last Name', 'F9023' => 'Transaction Declined', 'F9024' => 'Multiple transactions for same address with known credit card', 'F9025' => 'Multiple transactions for same address with new credit card', 'F9026' => 'Multiple transactions for same email with new credit card', 'F9027' => 'Multiple transactions for same email with known credit card', 'F9028' => 'Multiple transactions for new credit card', 'F9029' => 'Multiple transactions for known credi card', 'F9030' => 'Multiple transactions for same credit card', 'F9032' => 'Invalid Customer Last Name', 'F9033' => 'Invalid Billing Street', 'F9034' => 'Invalid Shipping Street' ); protected function _afterInitSetupForm(Am_Form_Setup $form) { parent::_afterInitSetupForm($form); $form->setTitle('eWay Rapid 3.1'); } function storesCcInfo(){ return false; } protected function createController(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Mvc_Controller_CreditCard_EwayRapid3($request, $response, $invokeArgs); } public function getErrorMessage($code) { $message = ''; if(!empty($this->messages[$code])) $message .= $this->messages[$code].", "; if(!empty($this->responseCodesFailed[$code])) $message .= $this->responseCodesFailed[$code]; if(!empty($this->responseCodesBeagle[$code])) $message .= $this->responseCodesBeagle[$code]; return $message; } public function createForm($actionName) { return new Am_Form_EwayRapid3($this); } public function directAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { if ($request->getActionName() == 'ipn') { $accessCode = $request->getFiltered('AccessCode'); $result = new Am_Paysystem_Result(); $transaction = new Am_Paysystem_Transaction_EwayRapid3($this, $accessCode); $transaction->run($result); if (!($invoice = $transaction->getInvoice())) throw new Am_Exception_InputError("Payment failed. Please contact webmaster for details."); $this->_setInvoice($invoice); if ($result->isSuccess()) { return $response->redirectLocation($this->getReturnUrl($invoice)); } else return $response->redirectLocation($this->getCancelUrl($invoice)); } else parent::directAction($request, $response, $invokeArgs); } public function getSupportedCurrencies() { return array('USD', 'CAD', 'GBP', 'EUR', 'AUD', 'NZD'); } public function getRecurringType() { return self::REPORTS_CRONREBILL; } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { $transaction = new Am_Paysystem_Transaction_EwayRapid3_Recurring($this, $invoice, $doFirst); $transaction->run($result); } public function _initSetupForm(Am_Form_Setup $form) { $form->addText('apikey', 'size=40')->setLabel('Api Key')->addRule('required'); $form->addText('password', 'size=40')->setLabel('Api Password')->addRule('required'); $form->addText('customer_id', 'size=20') ->setLabel("Eway Customer ID\n" . 'Your unique 8 digit eWAY customer ID ' . 'assigned to you when you join eWAY ' . 'e.g. 1xxxxxxx.')->addRule('required'); $form->addText('customer_username', 'size=20') ->setLabel("Eway Username\n" . 'Your username which is used to ' . 'login to eWAY Business Center.')->addRule('required'); $form->addText('customer_password', 'size=20') ->setLabel("Eway Password\n" . 'Your password which is used to ' . 'login to eWAY Business Center.')->addRule('required'); $form->addAdvCheckbox('testing')->setLabel("Test Mode Enabled"); } public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $transaction = new Am_Paysystem_Transaction_Ewayrapid3_Refund($this, $payment->getInvoice(), $payment->transaction_id, $amount); $transaction->run($result); } } class Am_Paysystem_Transaction_EwayRapid3_Recurring extends Am_Paysystem_Transaction_CreditCard { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice) { parent::__construct($plugin, $invoice, $plugin->createHttpRequest(), $doFirst); $header[] = "Content-Type: text/xml"; $header[] = 'SOAPAction: "https://www.eway.com.au/gateway/managedpayment/ProcessPayment"'; $this->request->setHeader($header); $this->request->setBody($this->createXml($invoice, $doFirst)); $this->request->setMethod(Am_HttpRequest::METHOD_POST); $this->request->setUrl($this->plugin->getConfig('testing') ? Am_Paysystem_EwayRapid3::API_TOKEN_SANDBOX_URL : Am_Paysystem_EwayRapid3::API_TOKEN_URL); } protected function createXml(Invoice $invoice, $doFirst) { $request = << CUT; $x = new SimpleXMLElement($request); $ns = $x->getNamespaces(); $b = $x->children($ns['soap'])->Body; $bb = $b->addChild('ProcessPayment',"", 'https://www.eway.com.au/gateway/managedpayment'); $h = $x->children($ns['soap'])->Header; $hh = $h->addChild('eWAYHeader',"", 'https://www.eway.com.au/gateway/managedpayment'); $hh->addChild('eWAYCustomerID', $this->plugin->getConfig('customer_id')); $hh->addChild('Username', $this->plugin->getConfig('customer_username')); $hh->addChild('Password', $this->plugin->getConfig('customer_password')); $bb->addChild('managedCustomerID', $invoice->getUser()->data()->get(Am_Paysystem_EwayRapid3::TOKEN)); $bb->addChild('amount', $invoice->second_total*100); $bb->addChild('invoiceReference', $invoice->public_id."-".sprintf("%03d", $invoice->getPaymentsCount())); $bb->addChild('invoiceDescription', $invoice->getLineDescription()); $xml = $x->asXML(); return $xml; } public function getUniqId() { return (string)$this->parsedResponse->ewayTrxnNumber; } public function parseResponse() { $xml = simplexml_load_string($this->response->getBody()); $this->parsedResponse = $xml->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->children('https://www.eway.com.au/gateway/managedpayment')->ProcessPaymentResponse->ewayResponse; } public function validate() { if(strtolower((string)$this->parsedResponse->ewayTrxnStatus) !== 'true') { $message = ''; $errors = explode(",", $this->parsedResponse->ewayTrxnError); foreach($errors as $error) $message .= $this->plugin->getErrorMessage($error).", "; $this->result->setFailed("Internal plugin's errors: ".substr($message, 0, -2)); return; } $this->result->setSuccess($this); return true; } } class Am_Mvc_Controller_CreditCard_EwayRapid3 extends Am_Mvc_Controller_CreditCard { public function createForm() { $invoice = $this->invoice; $result = new Am_Paysystem_Result(); $transaction = new Am_Paysystem_Transaction_EwayRapid3_RequestFormActionUrl($this->plugin, $invoice); $transaction->run($result); if (!$result->isSuccess()) { throw new Am_Exception_InputError(___('Error happened during payment process')); } $response = $transaction->getResponse(); $form = $this->plugin->createForm($this->_request->getActionName(), $this->invoice); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($form->getDefaultValues($this->invoice->getUser())) )); $form->addHidden(Am_Mvc_Controller::ACTION_KEY)->setValue($this->_request->getActionName()); $form->addHidden('EWAY_ACCESSCODE')->setValue((string) $response->AccessCode); $form->addHidden('EWAY_PAYMENTTYPE')->setValue('Credit Card'); $form->setAction((string) $response->FormActionURL); return $form; } } class Am_Paysystem_Transaction_EwayRapid3_RequestFormActionUrl extends Am_Paysystem_Transaction_CreditCard { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice) { parent::__construct($plugin, $invoice, $plugin->createHttpRequest(), true); $header[] = "Authorization: Basic " . base64_encode( $plugin->getConfig('apikey') . ':' . $plugin->getConfig('password')); $header[] = "Content-Type: text/xml"; $this->request->setHeader($header); $this->request->setBody($this->createXml($invoice)); $this->request->setMethod(Am_HttpRequest::METHOD_POST); $this->request->setUrl($this->plugin->getConfig('testing') ? Am_Paysystem_EwayRapid3::CREATE_ACCESS_CODE_SANDBOX_URL : Am_Paysystem_EwayRapid3::CREATE_ACCESS_CODE_URL); } public function getResponse() { return $this->xml; } public function getUniqId() { } public function parseResponse() { $this->xml = new SimpleXMLElement($this->response->getBody()); } protected function createXml(Invoice $invoice) { $user = $invoice->getUser(); $xml = new SimpleXMLElement(''); $xml->addChild('RedirectUrl', $this->plugin->getPluginUrl('ipn')); $xml->addChild('TransactionType', 'Purchase'); $xml->addChild('Method', 'TokenPayment'); $xml->addChild('Customer'); $xml->Customer->addChild('Reference', $user->user_id); $xml->Customer->addChild('FirstName', $user->name_f); $xml->Customer->addChild('LastName', $user->name_l); $xml->Customer->addChild('Street1', $user->street); $xml->Customer->addChild('City', $user->city); $xml->Customer->addChild('State', $user->state); $xml->Customer->addChild('PostalCode', $user->zip); $xml->Customer->addChild('Country', strtolower($user->country)); $xml->Customer->addChild('Email', $user->email); $xml->Customer->addChild('Phone', $user->phone); $xml->addChild('Items'); foreach ($invoice->getItems() as $item) { $xml->Items->addChild('LineItem'); $xml->Items->LineItem->addChild('Description', $item->item_title); } $xml->addChild('Payment'); $xml->Payment->addChild('TotalAmount', $invoice->first_total * 100); $xml->Payment->addChild('InvoiceNumber', $invoice->public_id); $xml->Payment->addChild('CurrencyCode', $invoice->currency); $xml->Payment->addChild('InvoiceDescription', $invoice->getLineDescription()); $xml->Payment->addChild('InvoiceReference', $invoice->public_id); return utf8_encode($xml->asXML()); } public function validate() { if ($this->xml->Errors) { $this->result->setFailed(); return; } $this->result->setSuccess($this); return true; } public function processValidated() { //do not add payment } } class Am_Form_Element_CreditCardExpire_EwayRapid3 extends HTML_QuickForm2_Container_Group { public function __construct($name = null, $attributes = null, $data = null) { parent::__construct($name, $attributes, $data); $this->setSeparator(' '); $require = !$data['dont_require']; $years = @$data['years']; if (!$years) $years = 10; $m = $this->addSelect('EWAY_CARDEXPIRYMONTH')->loadOptions($this->getMonthOptions()); if ($require) $m->addRule('required', ___('Invalid Expiration Date - Month')); $y = $this->addSelect('EWAY_CARDEXPIRYYEAR')->loadOptions($this->getYearOptions($years)); if ($require) $y->addRule('required', ___('Invalid Expiration Date - Year')); } public function getMonthOptions() { $locale = Am_Di::getInstance()->locale; $months = $locale->getMonthNames('wide', false); foreach ($months as $k => $v) $months[$k] = sprintf('(%02d) %s', $k, $v); $months[''] = ''; ksort($months); return $months; } public function getYearOptions($add) { $years = range(date('Y'), date('Y') + $add); array_unshift($years, ''); return array_combine($years, $years); } public function setValue($value) { if (is_string($value) && preg_match('/^\d{4}$/', $value)) { $value = array( 'm' => (int) substr($value, 0, 2), 'y' => '20' . substr($value, 2, 2), ); } return parent::setValue($value); } protected function updateValue() { $name = $this->getName(); foreach ($this->getDataSources() as $ds) { if (null !== ($value = $ds->getValue($name))) { $this->setValue($value); return; } } return parent::updateValue(); } } class Am_Form_EwayRapid3 extends Am_Form_CreditCard { public function init() { $name = $this->addText('EWAY_CARDNAME', array('size' => 30)) ->setLabel(___("Cardholder Name\n" . 'cardholder first and last name, exactly as on the card')); $name->addRule('required', ___('Please enter credit card holder first and last name'))->addRule('regex', ___('Please enter credit card holder first and last name'), '/^[^=:<>{}()"]+$/D'); $options = $this->plugin->getFormOptions(); if (in_array(Am_Paysystem_CreditCard::CC_COMPANY, $options)) $company = $this->addText('cc_company') ->setLabel(___("Company Name\n" . 'the company name associated with the billing address for ' . 'the transaction')); $cc = $this->addText('EWAY_CARDNUMBER', array('autocomplete' => 'off', 'size' => 22, 'maxlength' => 22)) ->setLabel(___("Credit Card Number\n" . 'for example: 1111-2222-3333-4444')); $cc->addRule('required', ___('Please enter Credit Card Number')) ->addRule('regex', ___('Invalid Credit Card Number'), '/^[0-9 -]+$/') ->addRule('callback2', 'Invalid CC#', array($this->plugin, 'validateCreditCardNumber')); $expire = $this->addElement(new Am_Form_Element_CreditCardExpire_EwayRapid3()) ->setLabel(___("Card Expire\n" . 'Select card expiration date - month and year')) ->addRule('required'); $code = $this->addPassword('EWAY_CARDCVN', array('autocomplete' => 'off', 'size' => 4, 'maxlength' => 4)) ->setLabel(___("Credit Card Code\n" . 'The "Card Code" is a three- or four-digit security code that ' . 'is printed on the back of credit cards in the card\'s signature ' . 'panel (or on the front for American Express cards).')); $code->addRule('required', ___('Please enter Credit Card Code')) ->addRule('regex', ___('Please enter Credit Card Code'), '/^\s*\d{3,4}\s*$/'); $buttons = $this->addGroup(); $buttons->addSubmit('_cc_', array('value' => ' ' . $this->payButtons[$this->formType] . ' ')); if ($this->formType == self::USER_UPDATE) { $buttons->addInputButton('_cc_', array('value' => ' ' . ___("Back") . ' ', 'onclick' => 'goBackToMember()')); $this->addScript("")->setScript("function goBackToMember(){ window.location = amUrl('/member'); }"); } $this->plugin->onFormInit($this); } } class Am_Paysystem_Transaction_EwayRapid3 extends Am_Paysystem_Transaction_CreditCard { public function __construct(Am_Paysystem_Abstract $plugin, $accessCode, $doFirst = true) { parent::__construct($plugin, null, $plugin->createHttpRequest(), $doFirst); $xml = new SimpleXMLElement(''); $xml->addChild('AccessCode', $accessCode); $xml = utf8_encode($xml->asXML()); $url = $plugin->getConfig('testing') ? Am_Paysystem_EwayRapid3::GET_RESULT_SANDBOX_URL : Am_Paysystem_EwayRapid3::GET_RESULT_URL; $header[] = "Authorization: Basic " . base64_encode( $plugin->getConfig('apikey') . ':' . $plugin->getConfig('password')); $header[] = "Content-Type: text/xml"; $this->request->setUrl($url); $this->request->setMethod(Am_HttpRequest::METHOD_POST); $this->request->setHeader($header); $this->request->setBody($xml); return $this->request; } protected $responseCodesSuccess = array( 'A2000' => 'Transaction Approved', 'A2008' => 'Honour With Identification', 'A2010' => 'Approved FOr Partial Amount', 'A2011' => 'Approved, VIP', 'A2016' => 'Approved, Update Track 3', ); public function validate() { // A2008 - is positive response according to documentation. https://go.eway.io/s/article/Rapid-Response-Code-A2008-Honour-with-Identification if (!array_key_exists((string) $this->parsedResponse->ResponseMessage, $this->responseCodesSuccess) || !in_array((string) $this->parsedResponse->ResponseMessage, array('A2000', 'A2008')) ) { $this->result->setFailed(); } else { if (!($this->invoice = Am_Di::getInstance()->invoiceTable->findFirstBy(array('public_id' => (string) $this->parsedResponse->InvoiceNumber)))) $this->result->setFailed(); else { $this->log->updateQuick(array( 'invoice_id' => $this->invoice->pk(), 'user_id' => $this->invoice->getUser()->user_id, )); $this->result->setSuccess($this); } } } public function parseResponse() { $this->parsedResponse = simplexml_load_string($this->response->getBody()); } public function getUniqId() { return (string) $this->parsedResponse->TransactionID; } public function processValidated() { $this->invoice->addPayment($this); $this->invoice->getUser()->data()->set(Am_Paysystem_EwayRapid3::TOKEN, (string) $this->parsedResponse->TokenCustomerID)->update(); } } class Am_Paysystem_Transaction_EwayRapid3_Refund extends Am_Paysystem_Transaction_CreditCard { protected $amount; protected $orig_id; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $billNumber, $amount) { parent::__construct($plugin, $invoice, $plugin->createHttpRequest(), true); $this->amount = $amount; $this->orig_id = $billNumber; $header[] = "Authorization: Basic " . base64_encode( $plugin->getConfig('apikey') . ':' . $plugin->getConfig('password')); $header[] = "Content-Type: text/xml"; $this->request->setHeader($header); $this->request->setBody($this->createXml($invoice)); $this->request->setMethod(Am_HttpRequest::METHOD_POST); $this->request->setUrl($this->plugin->getConfig('testing') ? Am_Paysystem_EwayRapid3::REFUND_SANDBOX_URL : Am_Paysystem_EwayRapid3::REFUND_URL); } public function getResponse() { return $this->xml; } public function getUniqId() { } public function parseResponse() { $this->xml = new SimpleXMLElement($this->response->getBody()); } protected function createXml(Invoice $invoice) { $user = $invoice->getUser(); $xml = new SimpleXMLElement(''); $xml->addChild('Refund'); $xml->Refund->addChild('TotalAmount', $this->amount * 10); $xml->Refund->addChild('InvoiceNumber', $this->invoice->public_id); $xml->Refund->addChild('InvoiceDescription', $this->invoice->getLineDescription()); $xml->Refund->addChild('InvoiceReference', $this->invoice->public_id); $xml->Refund->addChild('CurrencyCode', $this->invoice->currency); $xml->Refund->addChild('TransactionID', $this->orig_id); $xml->addChild('Customer'); $xml->Customer->addChild('Reference', $user->user_id); $xml->Customer->addChild('FirstName', $user->name_f); $xml->Customer->addChild('LastName', $user->name_l); $xml->Customer->addChild('Street1', $user->street); $xml->Customer->addChild('City', $user->city); $xml->Customer->addChild('State', $user->state); $xml->Customer->addChild('PostalCode', $user->zip); $xml->Customer->addChild('Country', strtolower($user->country)); $xml->Customer->addChild('Email', $user->email); $xml->Customer->addChild('Phone', $user->phone); /*$xml->addChild('Items'); foreach ($invoice->getItems() as $item) { $xml->Items->addChild('LineItem'); $xml->Items->LineItem->addChild('Description', $item->item_title); }*/ $xml->addChild('CustomerIP', $this->invoice->getUser()->remote_addr); return utf8_encode($xml->asXML()); } public function validate() { if ($this->xml->Errors) { $this->result->setFailed(); return; } $this->result->setSuccess($this); return true; } public function processValidated() { $this->invoice->addRefund($this, $this->orig_id); } public function getAmount() { return $this->amount; } } plugins/paymill.php000064400000070060152101601040010376 0ustar00 Settings -> API keys) CUT; } public function getSupportedCurrencies() { return array('EUR', 'GBP', 'USD', 'CHF'); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { $token = $invoice->getUser()->data()->get(self::TOKEN); if (!$token) return $result->setErrorMessages(array(___('Payment failed'))); if ($doFirst && (doubleval($invoice->first_total) <= 0)) { // free trial $tr = new Am_Paysystem_Transaction_Free($this); $tr->setInvoice($invoice); $tr->process(); $result->setSuccess($tr); } else { $tr = new Am_Paysystem_Transaction_Paymill($this, $invoice, $doFirst); $tr->run($result); } } public function getUpdateCcLink($user) { if ($user->data()->get(self::TOKEN)) return $this->getPluginUrl('update'); } public function storeCreditCard(CcRecord $cc, Am_Paysystem_Result $result) { } public function loadCreditCard(Invoice $invoice) { if ($invoice->getUser()->data()->get(self::TOKEN)) return $this->getDi()->CcRecordTable->createRecord(); // return fake record for rebill } protected function createController(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { $controller = new Am_Mvc_Controller_CreditCard_Paymill($request, $response, $invokeArgs); if($this->getConfig('testing')) $controller->setTestMode(); return $controller; } protected function _initSetupForm(Am_Form_Setup $form) { $form->addText('private_key', 'size=40')->setLabel('Private Key')->addRule('required'); $form->addText('public_key', 'size=40')->setLabel('Public Key')->addRule('required'); $form->addSelect("testing", array(), array('options' => array( ''=>'No', '1'=>'Yes' )))->setLabel('Test Mode'); } public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $tr = new Am_Paysystem_Transaction_Paymill_Refund($this, $payment->getInvoice(), $payment->receipt_id, $amount); $tr->run($result); } } class Am_Mvc_Controller_CreditCard_Paymill extends Am_Mvc_Controller { /** @var Invoice*/ protected $invoice; /** @var Am_Paysystem_Paymill */ protected $plugin; protected $testMode; public function setInvoice(Invoice $invoice) { $this->invoice = $invoice; } public function setPlugin($plugin) { $this->plugin = $plugin; } public function setTestMode() { $this->testMode = true; } public function createForm($label, $cc_mask = null) { $form = new Am_Form('cc-paymill'); $name = $form->addGroup() ->setLabel(___("Cardholder Name\n" . 'cardholder first and last name, exactly as on the card')); $name->addRule('required', ___('Please enter credit card holder name')); $name_f = $name->addText('cc_name_f', array('size'=>15, 'id' => 'cc_name_f')); $name_f->addRule('required', ___('Please enter credit card holder first name'))->addRule('regex', ___('Please enter credit card holder first name'), '|^[a-zA-Z_\' -]+$|'); $name_l = $name->addText('cc_name_l', array('size'=>15, 'id' => 'cc_name_l')); $name_l->addRule('required', ___('Please enter credit card holder last name'))->addRule('regex', ___('Please enter credit card holder last name'), '|^[a-zA-Z_\' -]+$|'); $cc = $form->addText('', array('autocomplete'=>'off', 'size'=>22, 'maxlength'=>22, 'id' => 'cc_number')) ->setLabel(___('Credit Card Number'), ___('for example: 1111-2222-3333-4444')); if ($cc_mask) $cc->setAttribute('placeholder', $cc_mask); $cc->addRule('required', ___('Please enter Credit Card Number')) ->addRule('regex', ___('Invalid Credit Card Number'), '/^[0-9 -]+$/'); class_exists('Am_Form_CreditCard', true); // preload element $expire = $form->addElement(new Am_Form_Element_CreditCardExpire('cc_expire')) ->setLabel(___("Card Expire\n" . 'Select card expiration date - month and year')); $code = $form->addPassword('', array('autocomplete'=>'off', 'size'=>4, 'maxlength'=>4, 'id' => 'cc_code')) ->setLabel(___('Credit Card Code'), sprintf(___('The "Card Code" is a three- or four-digit security code that is printed on the back of credit cards in the card\'s signature panel (or on the front for American Express cards).'),'
','
')); $code->addRule('required', ___('Please enter Credit Card Code')) ->addRule('regex', ___('Please enter Credit Card Code'), '/^\s*\d{3,4}\s*$/'); /**/ $fieldSet = $form->addFieldset(___('Address Info')) ->setLabel(___("Address Info\n" . '(must match your credit card statement delivery address)')); $street = $fieldSet->addText('cc_street')->setLabel(___('Street Address')) ->addRule('required', ___('Please enter Street Address')); $city = $fieldSet->addText('cc_city')->setLabel(___('City')) ->addRule('required', ___('Please enter City')); $zip = $fieldSet->addText('cc_zip')->setLabel(___('ZIP')) ->addRule('required', ___('Please enter ZIP code')); $country = $fieldSet->addSelect('cc_country')->setLabel(___('Country')) ->setId('f_cc_country') ->loadOptions(Am_Di::getInstance()->countryTable->getOptions(true)); $country->addRule('required', ___('Please enter Country')); $group = $fieldSet->addGroup()->setLabel(___('State')); $group->addRule('required', ___('Please enter State')); $stateSelect = $group->addSelect('cc_state') ->setId('f_cc_state') ->loadOptions($stateOptions = Am_Di::getInstance()->stateTable->getOptions(@$_REQUEST['cc_country'], true)); $stateText = $group->addText('cc_state')->setId('t_cc_state'); $disableObj = $stateOptions ? $stateText : $stateSelect; $disableObj->setAttribute('disabled', 'disabled')->setAttribute('style', 'display: none'); /**/ $form->addSubmit('', array('value' => $label)); $form->addHidden('id')->setValue($this->_request->get('id')); $form->addHidden('paymill_token', 'id=paymill_token')->addRule('required'); //$key = json_encode($this->plugin->getConfig('public_key')); $key = $this->plugin->getConfig('public_key'); $amount = $this->invoice->isFirstPayment() ? $this->invoice->first_total : $this->invoice->second_total; $amount = sprintf('%.02f', $amount)*100; $form->addProlog(''); $form->addScript()->setScript("var PAYMILL_PUBLIC_KEY = '".$key."';"); if($this->testMode) $form->addScript()->setScript("var PAYMILL_TEST_MODE = true;"); $form->addScript()->setScript(file_get_contents(dirname(__FILE__) . '/../../default/views/public/js/json2.min.js')); $form->addScript()->setScript(<< '') return true; // submit the form! event.stopPropagation(); paymill.createToken({ number: frm.find("#cc_number").val(), // required exp_month: frm.find("[name='cc_expire[m]']").val(), // required exp_year: frm.find("[name='cc_expire[y]']").val(), // required cvc: frm.find("#cc_code").val(), // required amount_int: {$amount}, // required, z.B. "4900" for 49.00 EUR currency: '{$this->invoice->currency}', // required cardholder: frm.find("#cc_name_f").val() + " " + frm.find("#cc_name_l").val() // optional }, function(error, result){ // handle response if (error) { frm.find("input[type=submit]").prop('disabled', null); var el = frm.find("#cc_number"); var cnt = el.closest(".element"); cnt.addClass("error"); cnt.find("span.error").remove(); var errorMessage = ''; switch (error.apierror) { case 'internal_server_error': errorMessage = 'Communication with PSP failed'; break; case 'invalid_public_key': errorMessage = 'Invalid Public Key'; break; case 'unknown_error': errorMessage = 'Unknown Error'; break; case '3ds_cancelled': errorMessage = 'Password Entry of 3-D Secure password was cancelled by the user'; break; case 'field_invalid_card_number': errorMessage = 'Missing or invalid creditcard number'; break; case 'field_invalid_card_exp_year': errorMessage = 'Missing or invalid expiry year'; break; case 'field_invalid_card_exp_month': errorMessage = 'Missing or invalid expiry month'; break; case 'field_invalid_card_exp': errorMessage = 'Card is no longer valid or has expired'; break; case 'field_invalid_card_cvc': errorMessage = 'Invalid checking number'; break; case 'field_invalid_card_holder': errorMessage = 'Invalid cardholder'; break; case 'field_invalid_amount_int': errorMessage = 'Invalid or missing amount for 3-D Secure'; break; case 'field_invalid_amount': errorMessage = 'Invalid or missing amount for 3-D Secure deprecated, see blog post'; break; case 'field_invalid_currency': errorMessage = 'Invalid or missing currency code for 3-D Secure'; break; case 'field_invalid_account_number': errorMessage = 'Missing or invalid bank account number'; break; case 'field_invalid_account_holder': errorMessage = 'Missing or invalid bank account holder'; break; case 'field_invalid_bank_code': errorMessage = 'Missing or invalid bank code '; break; default: errorMessage = error.apierror; } el.after("
"+errorMessage+"
"); } else { frm.find("input[name=paymill_token]").val(result.token); frm.submit(); } }); frm.find("input[type=submit]").prop('disabled', 'disabled'); return false; }); }); CUT ); return $form; } public function updateAction() { $user = $this->getDi()->user; $token = $user->data()->get(Am_Paysystem_Paymill::TOKEN); if (!$token) throw new Am_Exception_Paysystem("No credit card stored, nothing to update"); $this->invoice = $this->getDi()->invoiceTable->findFirstBy( array('user_id'=>$user->pk(), 'paysys_id'=>$this->plugin->getId()), 'invoice_id DESC'); if (!$this->invoice) throw new Am_Exception_Paysystem("No invoices found for user and paysystem"); $cc_last4 = $this->invoice->getUser()->data()->get(Am_Paysystem_Paymill::CC_LAST4); $cc_expires = $this->invoice->getUser()->data()->get(Am_Paysystem_Paymill::CC_EXPIRES); $this->form = $this->createForm(___('Update Credit Card Info'), 'XXXX XXXX XXXX ' . $cc_last4); $this->form->addDataSource(new HTML_QuickForm2_DataSource_Array(array( 'cc_street' => $user->street, 'cc_name_f' => $user->name_f, 'cc_name_l' => $user->name_l, 'cc_zip' => $user->zip, 'cc_expire' => $cc_expires, ))); $result = $this->ccFormAndSaveCustomer(); if ($result->isSuccess()) $this->_redirect($this->getDi()->url('member',null,false,true)); $this->form->getElementById('paymill_token')->setValue(''); $this->view->title = ___('Payment info'); $this->view->display_receipt = false; $this->view->form = $this->form; $this->view->display('cc/info.phtml'); } protected function ccFormAndSaveCustomer() { $vars = $this->form->getValue(); $result = new Am_Paysystem_Result(); if (!empty($vars['paymill_token'])) { if (!$vars['paymill_token']) throw new Am_Exception_Paysystem("No expected token id received"); $this->invoice->getUser()->data() ->set(Am_Paysystem_Paymill::TOKEN, $vars['paymill_token']) ->update(); $result->setSuccess(); // setup session to do not reask payment info within 30 minutes $s = new Zend_Session_Namespace($this->plugin->getId()); $s->setExpirationSeconds(60*30); // after 30 minutes we will reset the session $s->ccConfirmed = true; } return $result; } protected function displayReuse() { $result = new Am_Paysystem_Result; $cc_last4 = $this->invoice->getUser()->data()->get(Am_Paysystem_Paymill::CC_LAST4); $card = 'XXXX XXXX XXXX ' . $cc_last4; $text = ___('Click "Continue" to pay this order using stored credit card %s', $card); $continue = ___('Continue'); $cancel = ___('Cancel'); $action = $this->plugin->getPluginUrl('cc'); $id = Am_Html::escape($this->_request->get('id')); $action = Am_Html::escape($action); $this->view->content .= << $text
   
CUT; $this->view->display('layout.phtml'); } public function ccAction() { $this->view->title = ___('Payment info'); $this->view->display_receipt = true; $this->view->invoice = $this->invoice; // if we have credit card on file, we will try to use it but we // have to display confirmation first if ($this->invoice->getUser()->data()->get(Am_Paysystem_Paymill::TOKEN)) { $s = new Zend_Session_Namespace($this->plugin->getId()); $s->setExpirationSeconds(60*30); // after 30 minutes we will reset the session if ($this->_request->get('reuse_ok') || (@$s->ccConfirmed === true)) { $result = $this->plugin->doBill($this->invoice, true, $this->getDi()->CcRecordTable->createRecord()); if ($result->isSuccess()) { return $this->_redirect($this->plugin->getReturnUrl()); } else { $this->invoice->getUser()->data() ->set(Am_Paysystem_Paymill::TOKEN, null) ->set(Am_Paysystem_Paymill::CC_EXPIRES, null) ->set(Am_Paysystem_Paymill::CC_LAST4, null) ->set(Am_Paysystem_Paymill::CLIENT_ID, null) ->update(); $this->view->error = $result->getErrorMessages(); $s->ccConfirmed = false; // failed } } elseif ($this->_request->get('reuse_cancel') || (@$s->ccConfirmed === false)) { $s->ccConfirmed = false; } elseif (!isset($s->ccConfirmed)) { return $this->displayReuse(); } } $this->form = $this->createForm(___('Subscribe And Pay')); $result = $this->ccFormAndSaveCustomer(); if ($result->isSuccess()) { $result = $this->plugin->doBill($this->invoice, true, $this->getDi()->CcRecordTable->createRecord()); if ($result->isSuccess()) { return $this->_redirect($this->plugin->getReturnUrl()); } else { $this->invoice->getUser()->data() ->set(Am_Paysystem_Paymill::TOKEN, null) ->set(Am_Paysystem_Paymill::CC_EXPIRES, null) ->set(Am_Paysystem_Paymill::CC_LAST4, null) ->set(Am_Paysystem_Paymill::CLIENT_ID, null) ->update(); $this->view->error = $result->getErrorMessages(); } } $this->form->getElementById('paymill_token')->setValue(''); $this->view->form = $this->form; $this->view->display('cc/info.phtml'); } } class Am_Paysystem_Transaction_Paymill extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse = array(); public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst) { $request = new Am_HttpRequest(Am_Paysystem_Paymill::API_ENDPOINT . 'transactions/', 'POST'); $amount = $doFirst ? $invoice->first_total : $invoice->second_total; $request->setAuth($plugin->getConfig('private_key'), '') ->addPostParameter('amount', sprintf('%.02f', $amount)*100) ->addPostParameter('currency', $invoice->currency) ->addPostParameter('token', $invoice->getUser()->data()->get(Am_Paysystem_Paymill::TOKEN)) ->addPostParameter('description', 'Invoice #'.$invoice->public_id.': '.$invoice->getLineDescription()); parent::__construct($plugin, $invoice, $request, $doFirst); } public function getUniqId() { return $this->parsedResponse['data']['id']; } public function parseResponse() { $this->parsedResponse = json_decode($this->response->getBody(), true); } public function processValidated() { $this->invoice->getUser()->data() ->set(Am_Paysystem_Paymill::CC_EXPIRES, sprintf('%02d%02d', $this->parsedResponse['data']['payment']['expire_month'], $this->parsedResponse['data']['payment']['expire_year']-2000)) ->set(Am_Paysystem_Paymill::CC_LAST4, $this->parsedResponse['data']['payment']['last4']) ->set(Am_Paysystem_Paymill::CLIENT_ID, $this->parsedResponse['data']['client']['id']) ->update(); //$result = new Am_Paysystem_Result(); //$tr = new Am_Paysystem_Transaction_Paymill_UpdateCustomer($this->plugin, $this->invoice); //$tr->run($result); $this->invoice->addPayment($this); } public function validate() { if (@$this->parsedResponse['error']) { if ($message = $this->parsedResponse['error']) $this->result->setErrorMessages(array($message)); else $this->result->setErrorMessages(array(___('Payment failed'))); return false; } if (isset($this->parsedResponse['data']['response_code']) && $this->parsedResponse['data']['response_code'] != 20000) { $message = $this->getStatusByCode($this->parsedResponse['data']['response_code']); $this->result->setErrorMessages(array($message)); } if ($this->parsedResponse['data']['livemode'] || ($this->parsedResponse['mode'] == 'test' && $this->getPlugin()->getConfig('testing'))) $this->result->setSuccess($this); return true; } public function getStatusByCode($code) { $statusCodes = array( '10001' => 'General undefined response.', '10002' => 'Still waiting on something.', '20000' => 'General success response.', '40000' => 'General problem with data.', '40100' => 'Problem with creditcard data.', '40101' => 'Problem with cvv.', '40102' => 'Card expired or not yet valid.', '40103' => 'Limit exceeded.', '40104' => 'Card invalid.', '40105' => 'expiry date not valid.', '40200' => 'Problem with bank account data.', '40300' => 'Problem with 3d secure data.', '40301' => 'currency / amount mismatch.', '40400' => 'Problem with input data.', '40401' => 'Amount too low or zero.', '40402' => 'Usage field too long.', '40403' => 'Currency not allowed.', '50000' => 'General problem with backend.', '50001' => 'country blacklisted.', '50100' => 'Technical error with credit card.', '50101' => 'Error limit exceeded.', '50102' => 'Card declined by authorization system.', '50103' => 'Manipulation or stolen card.', '50104' => 'Card restricted.', '50105' => 'Invalid card configuration data.', '50200' => 'Technical error with bank account.', '50201' => 'Card blacklisted.', '50300' => 'Technical error with 3D secure.', '50400' => 'Decline because of risk issues.' ); return isset($statusCodes[$code]) ? $statusCodes[$code] : ''; } } class Am_Paysystem_Transaction_Paymill_CreateCustomer extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse = array(); public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice) { $request = new Am_HttpRequest(Am_Paysystem_Paymill::API_ENDPOINT . 'clients/', 'POST'); $request->setAuth($plugin->getConfig('private_key'), '') ->addPostParameter('email', $invoice->getEmail()) ->addPostParameter('description', 'Username: ' . $invoice->getUser()->login); parent::__construct($plugin, $invoice, $request, true); } public function getUniqId() { return $this->parsedResponse['data']['id']; } public function parseResponse() { $this->parsedResponse = json_decode($this->response->getBody(), true); } public function validate() { if (!@$this->parsedResponse['data']['id']) { $this->result->setErrorMessages(array('Error storing customer profile')); return false; } $this->result->setSuccess($this); return true; } public function processValidated() { $this->invoice->getUser()->data()->set(Am_Paysystem_Paymill::CLIENT_ID, $this->parsedResponse['data']['id'])->update(); } } class Am_Paysystem_Transaction_Paymill_UpdateCustomer extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse = array(); public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice) { $clientId = $invoice->getUser()->data()->get(Am_Paysystem_Paymill::CLIENT_ID); $request = new Am_HttpRequest(Am_Paysystem_Paymill::API_ENDPOINT . 'clients/' . $clientId, 'PUT'); $request->setAuth($plugin->getConfig('private_key'), '') ->addPostParameter('email', $invoice->getEmail()) ->addPostParameter('description', 'Username: ' . $invoice->getUser()->login); parent::__construct($plugin, $invoice, $request, true); } public function getUniqId() { return $this->parsedResponse['data']['id']; } public function parseResponse() { $this->parsedResponse = json_decode($this->response->getBody(), true); } public function validate() { if (!@$this->parsedResponse['data']['id']) { $this->result->setErrorMessages(array('Error storing customer profile')); return false; } $this->result->setSuccess($this); return true; } } class Am_Paysystem_Transaction_Paymill_GetCustomer extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse = array(); public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice) { $clientId = $invoice->getUser()->data()->get(Am_Paysystem_Paymill::CLIENT_ID); $request = new Am_HttpRequest(Am_Paysystem_Paymill::API_ENDPOINT . 'clients/' . $clientId , 'GET'); $request->setAuth($plugin->getConfig('private_key'), ''); parent::__construct($plugin, $invoice, $request, true); } public function getUniqId() { return $this->parsedResponse['data']['id']; } public function parseResponse() { $this->parsedResponse = json_decode($this->response->getBody(), true); } public function validate() { if (!@$this->parsedResponse['data']['id']) $this->result->setErrorMessages(array('Unable to fetch payment profile')); $this->result->setSuccess($this); } public function getInfo() { return $this->parsedResponse; } public function processValidated() { } } class Am_Paysystem_Transaction_Paymill_Refund extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse = array(); protected $transactionId; protected $amount; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $transactionId, $amount = null) { $this->transactionId = $transactionId; $this->amount = $amount > 0 ? $amount : null; $request = new Am_HttpRequest(Am_Paysystem_Paymill::API_ENDPOINT . 'refunds/' . $transactionId, 'POST'); $request->setAuth($plugin->getConfig('private_key'), ''); if ($this->amount > 0) $request->addPostParameter('amount', sprintf('%.02f', $amount)*100) ->addPostParameter('description', 'Refund from aMember script. Username: ' . $invoice->getUser()->login . ', invoice: ' . $invoice->public_id); parent::__construct($plugin, $invoice, $request, true); } public function getUniqId() { return $this->parsedResponse['data']['id']; } public function parseResponse() { $this->parsedResponse = json_decode($this->response->getBody(), true); } public function validate() { if (!@$this->parsedResponse['data']['id']) $this->result->setErrorMessages(array('Unable to fetch payment profile')); if (@$this->parsedResponse['data']['status'] == 'refunded') $this->result->setSuccess($this); elseif ($message = @$this->parsedResponse['error']) $this->result->setErrorMessages(array($message)); } public function processValidated() { $this->invoice->addRefund($this, $this->transactionId, $this->amount); } } plugins/graypay.php000064400000001735152101601040010406 0ustar00getTitle()) $form->setTitle($t); } protected function _initSetupForm(Am_Form_Setup $form) { $form->addText('PxPayUserId') ->setLabel('Your PxPayUserId') ->addRule('required'); $form->addText('PxPayKey', array('size' => 65)) ->setLabel('Your PxPayKey') ->addRule('required'); $form->addText('PostUsername') ->setLabel('Your PostUsername') ->addRule('required'); $form->addPassword('PostPassword') ->setLabel('Your PostPassword') ->addRule('required'); $form->addAdvCheckbox('debugMode') ->setLabel("Debug Mode Enable\n" . "write all requests/responses to log"); } public function isConfigured() { return $this->getConfig('PxPayUserId') && $this->getConfig('PxPayKey'); } public static function getAttributeValue($attr, $xmlStr, $which = 0) { if ($which == 1) { $spos = strpos($xmlStr, $attr . '="'); $xmlStr = substr($xmlStr, $spos + strlen($attr)); } if (false !== ($spos = strpos($xmlStr, $attr . '="')) && false !== ($epos = strpos($xmlStr, '"', $spos + strlen($attr) + 2))) return substr($xmlStr, $spos + strlen($attr) + 2, $epos - $spos - strlen($attr) - 2); return ''; } public static function getTagValue($tag, $xmlStr) { if (false !== strpos($xmlStr, '<' . $tag . '>') && false !== strpos($xmlStr, '')) { $xmlStr = strstr($xmlStr, '<' . $tag . '>'); $end = strpos($xmlStr, ''); return substr($xmlStr, strlen($tag) + 2, $end - (strlen($tag) + 2)); } return ''; } public function _process(Invoice $invoice, Am_Mvc_Request $request, Am_Paysystem_Result $result) { $xmlOut = ''; $xmlOut .= ''; $xmlOut .= '' . $this->getConfig('PxPayUserId') . ''; $xmlOut .= '' . $this->getConfig('PxPayKey') . ''; $xmlOut .= '' . $invoice->first_total . ''; // $xmlOut .= '' . (((float)$invoice->first_total) ? $invoice->first_total : '1.00'). ''; $xmlOut .= '' . $invoice->currency . ''; $xmlOut .= '' . $invoice->getLineDescription() . ''; $xmlOut .= 'Purchase'; // $xmlOut .= '' . (((float)$invoice->first_total) ? 'Purchase' : 'Auth') . ''; $xmlOut .= '' . $invoice->public_id . ''; $xmlOut .= ''; // $xmlOut .= '' . $invoice->getUserId() . ''; $xmlOut .= '' . (($invoice->second_total > 0) ? '1' : '0') . ''; $xmlOut .= '' . $this->getPluginUrl(self::RETURN_URL_SUCCESS) . ''; $xmlOut .= '' . $this->getPluginUrl(self::RETURN_URL_FAIL) . ''; $xmlOut .= ''; $req = new Am_HttpRequest_PaymentExpress($xmlOut, 'GenerateRequest', (bool)$this->getConfig('debugMode')); $xmlIn = $req->getResponseXML(); $url = self::getTagValue('URI', $xmlIn); if (!url) throw new Am_Exception_InternalError( "PaymentExpress[error]. URI is absent. /GenerateRequest/" ); $a = new Am_Paysystem_Action_Redirect(htmlspecialchars_decode($url)); $result->setAction($a); } public function directAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { switch ($request->getActionName()) { case self::RETURN_URL_SUCCESS: case self::RETURN_URL_FAIL: $log = $this->logRequest($request); $transaction = $this->createThanksTransaction($request, $response, $invokeArgs); $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; $this->getDi()->errorLogTable->logException($e); throw Am_Exception_InternalError(___("Error happened during transaction handling. Please contact website administrator")); } $log->setInvoice($transaction->getInvoice())->update(); $this->invoice = $transaction->getInvoice(); $this->invoice->data()->set(self::DPS_BILLING_ID, $transaction->getDpsBillingId())->update(); $response->setRedirect($this->getReturnUrl()); break; default : parent::directAction($request, $response, $invokeArgs); } } public function loadCreditCard(Invoice $invoice) { return $this->getDi()->CcRecordTable->createRecord(); // return fake record for rebill } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { if (!$doFirst) { $dpsBillingId = $invoice->data()->get(self::DPS_BILLING_ID); if(!$dpsBillingId) return $result->setFailed(array("No saved DPS_BILLING_ID for invoice")); $transaction = new Am_Paysystem_Transaction_CreditCard_PaymentExpress_Rebill($this, $invoice, $doFirst, $dpsBillingId); $transaction->run($result); } } public function createThanksTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Transaction_Incoming_PaymentExpress($this, $request, $response, $invokeArgs); } public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $dpsBillingId = $payment->getInvoice()->data()->get(self::DPS_BILLING_ID); if(!$dpsBillingId) return $result->setFailed(array("No saved DPS_BILLING_ID for invoice")); $transaction = new Am_Paysystem_Transaction_CreditCard_PaymentExpress_Refund($this, $payment->getInvoice(), $dpsBillingId, $amount); $transaction->run($result); } public function getReadme() { return <<Attention: Plugin does not support free-trial payments. Configure plugin at this page and click 'Save' CUT; } } class Am_Paysystem_Transaction_Incoming_PaymentExpress extends Am_Paysystem_Transaction_Incoming { private $xmlResponse; public function findInvoiceId() { return Am_Paysystem_PaymentExpress::getTagValue('TxnId', $this->xmlResponse); } public function getUniqId() { return Am_Paysystem_PaymentExpress::getTagValue('DpsTxnRef', $this->xmlResponse); } public function validateSource() { if(!$this->request->getParam('result')) throw new Am_Exception_InternalError("PaymentExpress[error]. Result is absent at response. /ProcessResponse/"); $xmlOut = ''; $xmlOut .= ''; $xmlOut .= '' . $this->plugin->getConfig('PxPayUserId') . ''; $xmlOut .= '' . $this->plugin->getConfig('PxPayKey') . ''; $xmlOut .= '' . $this->request->getParam('result') . ''; $xmlOut .= ''; $req = new Am_HttpRequest_PaymentExpress($xmlOut, 'ProcessResponse', (bool)$this->plugin->getConfig('debugMode')); $this->xmlResponse = $req->getResponseXML(); return true; } public function validateStatus() { return (Am_Paysystem_PaymentExpress::getTagValue('Success', $this->xmlResponse) > 0); } public function validateTerms() { $this->assertAmount($this->invoice->first_total, Am_Paysystem_PaymentExpress::getTagValue('AmountSettlement', $this->xmlResponse)); return true; } public function getDpsBillingId() { return Am_Paysystem_PaymentExpress::getTagValue('DpsBillingId', $this->xmlResponse); } } class Am_Paysystem_Transaction_CreditCard_PaymentExpress_Rebill extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse; protected $txnType = 'Purchase'; protected $requestType = 'Rebill'; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst, $dpsBillingId) { $xmlOut = ''; $xmlOut .= ''; $xmlOut .= ''. $plugin->getConfig('PostUsername') . ''; $xmlOut .= ''. $plugin->getConfig('PostPassword') . ''; $xmlOut .= '' . $invoice->second_total . ''; $xmlOut .= '' . $invoice->currency . ''; $xmlOut .= '' . $invoice->getLineDescription() . ''; $xmlOut .= '' . $this->txnType . ''; $xmlOut .= '' . $invoice->public_id . ''; $xmlOut .= ''; $xmlOut .= '' . $dpsBillingId . ''; $xmlOut .= ''; $request = new Am_HttpRequest_PaymentExpress($xmlOut, $this->requestType, (bool)$plugin->getConfig('debugMode')); $request->setUrl(Am_Paysystem_PaymentExpress::URL_RECURRING); parent::__construct($plugin, $invoice, $request, $doFirst); } public function getUniqId() { return Am_Paysystem_PaymentExpress::getTagValue('DpsTxnRef', $this->parsedResponse); } public function parseResponse() { $this->parsedResponse = $this->response->getBody(); } public function validate() { $success = Am_Paysystem_PaymentExpress::getTagValue('Success', $this->parsedResponse); if ($success != 1) return $this->result->setFailed(array('#'.$success.'-'.Am_Paysystem_PaymentExpress::getTagValue('ResponseText', $this->parsedResponse))); $this->result->setSuccess($this); } } class Am_Paysystem_Transaction_CreditCard_PaymentExpress_Refund extends Am_Paysystem_Transaction_CreditCard_PaymentExpress_Rebill { protected $txnType = 'Purchase'; protected $requestType = 'Rebill'; protected $amount; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $dpsBillingId, $amount) { $this->amount = $amount; parent::__construct($plugin, $invoice, false, $dpsBillingId); } public function getAmount() { return $this->amount; } public function processValidated(){} // no process payment } class Am_HttpRequest_PaymentExpress extends Am_HttpRequest { private $requestType; private $debugMode; public function __construct($xmlOut, $requestType, $debugMode = false) { $this->requestType = $requestType; $this->debugMode = $debugMode; parent::__construct(Am_Paysystem_PaymentExpress::URL, self::METHOD_POST); $this->setBody($xmlOut); } public function getResponseXML() { $response = $this->send(); $xmlIn = $response->getBody(); if (!Am_Paysystem_PaymentExpress::getAttributeValue('valid', $xmlIn)) throw new Am_Exception_InternalError( "PaymentExpress[error]. Invalid request. /{$this->requestType}/" ); return $xmlIn; } public function send() { if ($this->debugMode) Am_Di::getInstance()->errorLogTable->log("PaymentExpress[debug]. [{$this->requestType}]-request: " . $this->getBody()); $response = parent::send(); if ($response->getStatus() != 200) throw new Am_Exception_InternalError( "PaymentExpress[error]. Bad response status [{$response->getStatus()}]. /GenerateRequest/" ); $xmlIn = $response->getBody(); if (!$xmlIn) throw new Am_Exception_InternalError( "PaymentExpress[error]. Null response body. /{$this->requestType}/" ); if ($this->debugMode) Am_Di::getInstance()->errorLogTable->log("PaymentExpress[debug]. [{$this->requestType}]-response: " . $xmlIn); return $response; } }plugins/networkmerchants-hosted.php000064400000056756152101601040013631 0ustar00getActionName()) { case self::ACTION_PROCESSED: return $this->makepaymentAction($request, $response, $invokeArgs); case self::ACTION_PROCESS: return $this->processAction($request, $response, $invokeArgs); case self::ACTION_SUCCESS: return $this->succeedAction($request, $response, $invokeArgs); case self::ACTION_FAILED: return $this->failedAction($request, $response, $invokeArgs); default: return parent::directAction($request, $response, $invokeArgs); } } private function getInvoice($invoiceId) { if (!$invoiceId) { throw new Am_Exception_InputError("" . "invoice_id is empty - " . "seems you have followed wrong url, " . "please return back to continue"); } return $this->getDi()->invoiceTable->findFirstByPublicId($invoiceId); } private function handleResponse( $gwResponse, Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { $result = $gwResponse->result; switch ($result) { case self::NMI_SUCCESS: return $this->thanksAction($request, $response, $invokeArgs); case self::NMI_DECLINED: case self::NMI_FAILED: $message = (string)$gwResponse->{'result-text'}; $this->logError($message); return $this->cancelPaymentAction($request, $response, $invokeArgs); default: throw new Am_Exception("Unhandled NMI transaction result {$result}"); } } static function debug($data) { if (self::$plugin->getConfig('debug')) { self::$plugin->logRequest($data); error_log(print_r($data, 1)); } } public function getUpdateCcLink($user) { if ($user->data()->get('customer_vault_id')) { return $this->getPluginUrl('update'); } } private function failedAction( \Am_Mvc_Request $request, \Am_Mvc_Response $response, array $invokeArgs) { $param = $request->getParam('message'); $message = html_entity_decode($param); $view = $this->getDi()->view; $view->title = ___('Update failed'); $view->content = <<NMI Profile updated failed: {$message} CUT; $view->display('member/layout.phtml'); } private function succeedAction( \Am_Mvc_Request $request, \Am_Mvc_Response $response, array $invokeArgs) { $view = $this->getDi()->view; $view->title = ___('Successfull update'); $view->content = <<Thank you. Your credit card info has been successfully saved. CUT; $view->display('member/layout.phtml'); } private function processAction( \Am_Mvc_Request $request, \Am_Mvc_Response $response, array $invokeArgs) { $APIKey = $this->getConfig('api_key'); $tokenId = $request->get('token-id'); if (!$tokenId) { throw new Am_Exception('No token in request'); } $user = Am_Di::getInstance() ->auth ->getUser(); $xmlRequest = new DOMDocument('1.0','UTF-8'); $xmlRequest->formatOutput = true; $xmlCompleteTransaction = $xmlRequest->createElement('complete-action'); $this->appendXmlNode($xmlRequest, $xmlCompleteTransaction, 'api-key', $APIKey); $this->appendXmlNode($xmlRequest, $xmlCompleteTransaction, 'token-id', $tokenId); $xmlRequest->appendChild($xmlCompleteTransaction); self::debug($xmlRequest->saveXML()); // Process Step Three $data = $this->sendXMLrequest( $xmlRequest, self::GATEWAY_URL); self::debug($data); $gwResponse = @new SimpleXMLElement((string)$data); $result = $gwResponse->result; switch ($result) { case self::NMI_SUCCESS: $customer_id = (string)$gwResponse->{'customer-vault-id'}; if (!$customer_id) { throw new Am_Exception('No customer-vault-id returned on successful transaction'); } $fourlast = (string)$gwResponse->{'billing'}->{'cc-number'}; $user->data()->set('customer_vault_id', $customer_id); $user->data()->set('four_last', $this->cosherCCNum($fourlast)); $user->update(); $response->setRedirect($this->getPluginUrl(self::ACTION_SUCCESS)); break; case self::NMI_DECLINED: case self::NMI_FAILED: $message = (string)$gwResponse->{'result-text'}; $this->logError($message); $url = $this->getPluginUrl(self::ACTION_FAILED) . '?message=' . htmlentities($message); $response->setRedirect($url); break; default: throw new Am_Exception("Unhandled NMI transaction result {$result}"); } } function cancelPaymentAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { $id = $request->getFiltered('invoice_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()); } function cosherCCNum($num) { $subnum = substr($num, -4); return str_pad($subnum, 16, "*", STR_PAD_LEFT); } private function makepaymentAction( \Am_Mvc_Request $request, \Am_Mvc_Response $response, array $invokeArgs) { $tokenId = $request->get('token-id'); $APIKey = $this->getConfig('api_key'); $xmlRequest = new DOMDocument('1.0','UTF-8'); $xmlRequest->formatOutput = true; $xmlCompleteTransaction = $xmlRequest->createElement('complete-action'); $this->appendXmlNode($xmlRequest, $xmlCompleteTransaction, 'api-key', $APIKey); $this->appendXmlNode($xmlRequest, $xmlCompleteTransaction, 'token-id', $tokenId); $xmlRequest->appendChild($xmlCompleteTransaction); self::debug($xmlRequest->saveXML()); // Process Step Three $data = $this->sendXMLrequest($xmlRequest, self::GATEWAY_URL); self::debug($data); $gwResponse = @new SimpleXMLElement((string)$data); $customer_id = (string)$gwResponse->{'customer-vault-id'}; if ($customer_id) { $fourlast = (string)$gwResponse->{'billing'}->{'cc-number'}; $invoiceId = $request->getFiltered('invoice_id'); $invoice = $this->getInvoice($invoiceId); $invoice->getUser()->data()->set('customer_vault_id', $customer_id); $invoice->getUser()->data()->set('four_last', $this->cosherCCNum($fourlast)); $invoice->getUser()->update(); } self::debug('here3'); return $this->handleResponse($gwResponse, $request, $response, $invokeArgs); } protected function _initSetupForm(Am_Form_Setup $form) { $key = $form->addText('api_key') ->setLabel("API Key"); $key->addRule('required') ->addRule( 'regex', 'API Keys must be in form of 32 alphanumeric signs', '/^[a-zA-Z0-9]{32}$/'); $form->addText('user') ->setLabel("Your username\n" . 'Username assigned to merchant account') ->addRule('required'); $form->addPassword('pass') ->setLabel("Your password\n" . 'Password for the specified username') ->addRule('required'); $form->addAdvCheckbox('testMode') ->setLabel("Test Mode\n" . 'Test account data will be used'); $form->addAdvCheckbox('debug') ->setLabel("Write debug info"); } function isConfigured() { return $this->getConfig('api_key') && $this->getConfig('user') && $this->getConfig('pass'); } function configBaseForm($formUrl, Am_Form_Nmi $form) { $id = $form->getId(); $cc = $form->addText('billing-cc-number', array('autocomplete'=>'off', 'size'=>22, 'maxlength'=>22)) ->setLabel(___( "Credit Card Number\n" . "for example: 1111-2222-3333-4444")); $cc->addRule('required', ___('Please enter Credit Card Number')) ->addRule('regex', ___('Invalid Credit Card Number'), '/^[0-9 -]+$/') ->addRule('callback2', 'Invalid CC#', array($this, 'validateCreditCardNumber')); $form->addElement(new Am_Form_Element_CreditCardExpire('expiration')) ->setLabel(___("Card Expire\n" . 'Select card expiration date - month and year')) ->addRule('required'); $form->addHidden('billing-cc-exp') ->setId('hid-exp'); $code = $form->addPassword('cvv', array('autocomplete'=>'off', 'size'=>4, 'maxlength'=>4)) ->setLabel(___("Credit Card Code\n" . 'The "Card Code" is a three- or four-digit security code ' . 'that is printed on the back of credit cards in the card\'s ' . 'signature panel (or on the front for American Express cards)')); $code->addRule('required', ___('Please enter Credit Card Code')) ->addRule('regex', ___('Please enter Credit Card Code'), '/^\s*\d{3,4}\s*$/'); $form->setAction($formUrl); $form->addScript() ->setScript(<<data() ->get('customer_vault_id'); if ($customer_id) { $fourlast = $this->cosherCCNum( $user ->data() ->get('four_last')); $form->addAdvCheckbox('use_saved') ->setLabel(___( "Use saved card: $fourlast")); } $this->configBaseForm($formUrl, $form); $buttons = $form->addGroup(); $buttons->setSeparator(' '); $buttons->addSubmit('_cc_', array('value'=> 'Subscribe And Pay')); $form->addScript() ->setScript(<< 0 && index < 4) { if($('#use_saved-0').is(':checked')){ $(this).hide(); } else { $(this).show(); } } }); } $('#use_saved-0').prop('checked', true); reChech(); }) CUT ); $this->onFormInit($form); return $form; } function createController( Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Mvc_Controller_CreditCard_Nmi( $request, $response, $invokeArgs); } function appendXmlNode($domDocument, $parentNode, $name, $value) { $childNode = $domDocument->createElement($name); $childNodeValue = $domDocument->createTextNode($value); $childNode->appendChild($childNodeValue); $parentNode->appendChild($childNode); } function sendXMLrequest(DOMDocument $xmlRequest, $gatewayURL) { $xmlString = $xmlRequest->saveXML(); $request = new Am_HttpRequest($gatewayURL, Am_HttpRequest::METHOD_POST); $request->setConfig('timeout', 7); $request->setConfig('follow_redirects', true); $request->setHeader('Content-type', 'text/xml'); $request->setBody($xmlString); $result = $request->send(); if ($result->getStatus() != 200) { throw new Am_Exception( "Error on request: " . "{$result->getStatus()}: " . "{$result->getBody()}"); } return $result->getBody(); } function createXmlRequest( User $user, Invoice $invoice, $customer_vault_id = null) { $APIKey = $this->getConfig('api_key'); $redirect = $this->getPluginUrl(self::ACTION_PROCESSED) . "?invoice_id={$invoice->public_id}"; $xmlRequest = new DOMDocument('1.0','UTF-8'); $xmlRequest->formatOutput = true; $amount = $invoice->first_total; if (floatval($amount)) { $xmlRoot = $xmlRequest->createElement('sale'); } else { $xmlRoot = $xmlRequest->createElement('validate'); } // Amount, authentication, and Redirect-URL are typically the bare minimum. $this->appendXmlNode($xmlRequest, $xmlRoot,'api-key', $APIKey); $this->appendXmlNode($xmlRequest, $xmlRoot,'redirect-url', $redirect); $this->appendXmlNode($xmlRequest, $xmlRoot, 'ip-address', $user->remote_addr); $this->appendXmlNode($xmlRequest, $xmlRoot, 'currency', $invoice->currency); if (floatval($amount)) { $this->appendXmlNode($xmlRequest, $xmlRoot, 'amount', $amount); } if ($customer_vault_id) { $this->appendXmlNode( $xmlRequest, $xmlRoot, 'customer-vault-id', $customer_vault_id); } else { // Set the Billing and Shipping from what was collected on initial shopping cart form $xmlBillingAddress = $xmlRequest->createElement('billing'); $this->appendXmlNode($xmlRequest, $xmlBillingAddress,'first-name', $user->name_f); $this->appendXmlNode($xmlRequest, $xmlBillingAddress,'last-name', $user->name_l); $this->appendXmlNode($xmlRequest, $xmlBillingAddress,'address1', $user->street); $this->appendXmlNode($xmlRequest, $xmlBillingAddress,'city', $user->city); $this->appendXmlNode($xmlRequest, $xmlBillingAddress,'state', $user->state); $this->appendXmlNode($xmlRequest, $xmlBillingAddress,'postal', $user->zip); $this->appendXmlNode($xmlRequest, $xmlBillingAddress,'country', $user->country); $this->appendXmlNode($xmlRequest, $xmlBillingAddress,'email', $user->email); $this->appendXmlNode($xmlRequest, $xmlBillingAddress,'phone', $user->phone); $xmlRoot->appendChild($xmlBillingAddress); $xmlShippingAddress = $xmlRequest->createElement('shipping'); $this->appendXmlNode($xmlRequest, $xmlShippingAddress,'first-name', $user->name_f); $this->appendXmlNode($xmlRequest, $xmlShippingAddress,'last-name', $user->name_l); $this->appendXmlNode($xmlRequest, $xmlShippingAddress,'address1', $user->street); $this->appendXmlNode($xmlRequest, $xmlShippingAddress,'city', $user->city); $this->appendXmlNode($xmlRequest, $xmlShippingAddress,'state', $user->state); $this->appendXmlNode($xmlRequest, $xmlShippingAddress,'postal', $user->zip); $this->appendXmlNode($xmlRequest, $xmlShippingAddress,'country', $user->country); $this->appendXmlNode($xmlRequest, $xmlShippingAddress,'phone', $user->phone); $xmlRoot->appendChild($xmlShippingAddress); foreach($invoice->getItems() as $item) { // Products already chosen by user $xmlProduct = $xmlRequest->createElement('product'); $this->appendXmlNode($xmlRequest, $xmlProduct, 'product-code' , $item->item_id); $this->appendXmlNode($xmlRequest, $xmlProduct, 'description' , $item->item_description); $this->appendXmlNode($xmlRequest, $xmlProduct, 'unit-cost' , $item->first_price); $this->appendXmlNode($xmlRequest, $xmlProduct, 'quantity' , $item->qty); $this->appendXmlNode($xmlRequest, $xmlProduct, 'total-amount' , $item->first_total); $this->appendXmlNode($xmlRequest, $xmlProduct, 'tax-amount' , $item->first_tax); $this->appendXmlNode($xmlRequest, $xmlProduct, 'discount-amount', $item->first_discount); $this->appendXmlNode($xmlRequest, $xmlProduct, 'tax-type' , $item->tax_group); $xmlRoot->appendChild($xmlProduct); } $userVault = $xmlRequest->createElement('add-customer'); $xmlRoot->appendChild($userVault); } $xmlRequest->appendChild($xmlRoot); return $xmlRequest; } function _doBill( \Invoice $invoice, $doFirst, \CcRecord $cc, \Am_Paysystem_Result $result) { if ($doFirst) { throw new Am_Exception("Just rebill can be handled here"); } $user = $this->invoice->getUser(); $customer_id = $user->data()->get('customer_vault_id'); if (!$customer_id) { return $result->setFailed( array("No saved reference transaction for customer")); } $trSale = new Am_Paysystem_Networkmerchants_Transaction_Sale( $this, $invoice, false, $customer_id); $trSale->run($result); } function storesCcInfo() { return false; } } class Am_Paysystem_Networkmerchants_Transaction_Thanks extends Am_Paysystem_Transaction_Incoming_Thanks { public function getUniqId() { return time(); } public function validateSource() { return true; } public function validateStatus() { return true; } public function validateTerms() { return true; } function findInvoiceId() { return $this->request->get('invoice_id'); } function processValidated() { if (floatval($this->invoice->first_total)) { $this->invoice->addPayment($this); } else { $this->invoice->addAccessPeriod($this); } } } /** @property Am_Paysystem_NetworkmerchantsHosted $plugin Plugin */ class Am_Mvc_Controller_CreditCard_Nmi extends Am_Mvc_Controller_CreditCard { private function createXmlRequest($user, $apiKey, $customer_vault_id = null) { $redirect = $this->plugin->getPluginUrl(ThePlugin::ACTION_PROCESS); $xmlRequest = new DOMDocument('1.0','UTF-8'); $xmlRequest->formatOutput = true; if ($customer_vault_id) { $xmlSale = $xmlRequest->createElement('update-customer'); } else { $xmlSale = $xmlRequest->createElement('add-customer'); } // Amount, authentication, and Redirect-URL are typically the bare minimum. $this->plugin->appendXmlNode($xmlRequest, $xmlSale,'api-key', $apiKey); $this->plugin->appendXmlNode($xmlRequest, $xmlSale,'redirect-url', $redirect); if ($customer_vault_id) { ThePlugin::appendXmlNode( $xmlRequest, $xmlSale, 'customer-vault-id', $customer_vault_id); } else { // Set the Billing and Shipping from what was collected on initial shopping cart form $xmlBillingAddress = $xmlRequest->createElement('billing'); $this->plugin->appendXmlNode($xmlRequest, $xmlBillingAddress,'first-name', $user->name_f); $this->plugin->appendXmlNode($xmlRequest, $xmlBillingAddress,'last-name', $user->name_l); $this->plugin->appendXmlNode($xmlRequest, $xmlBillingAddress,'address1', $user->street); $this->plugin->appendXmlNode($xmlRequest, $xmlBillingAddress,'city', $user->city); $this->plugin->appendXmlNode($xmlRequest, $xmlBillingAddress,'state', $user->state); $this->plugin->appendXmlNode($xmlRequest, $xmlBillingAddress,'postal', $user->zip); $this->plugin->appendXmlNode($xmlRequest, $xmlBillingAddress,'country', $user->country); $this->plugin->appendXmlNode($xmlRequest, $xmlBillingAddress,'email', $user->email); $this->plugin->appendXmlNode($xmlRequest, $xmlBillingAddress,'phone', $user->phone); $xmlSale->appendChild($xmlBillingAddress); $xmlShippingAddress = $xmlRequest->createElement('shipping'); $this->plugin->appendXmlNode($xmlRequest, $xmlShippingAddress,'first-name', $user->name_f); $this->plugin->appendXmlNode($xmlRequest, $xmlShippingAddress,'last-name', $user->name_l); $this->plugin->appendXmlNode($xmlRequest, $xmlShippingAddress,'address1', $user->street); $this->plugin->appendXmlNode($xmlRequest, $xmlShippingAddress,'city', $user->city); $this->plugin->appendXmlNode($xmlRequest, $xmlShippingAddress,'state', $user->state); $this->plugin->appendXmlNode($xmlRequest, $xmlShippingAddress,'postal', $user->zip); $this->plugin->appendXmlNode($xmlRequest, $xmlShippingAddress,'country', $user->country); $this->plugin->appendXmlNode($xmlRequest, $xmlShippingAddress,'phone', $user->phone); $xmlSale->appendChild($xmlShippingAddress); } $xmlRequest->appendChild($xmlSale); return $xmlRequest; } function updateAction() { if (!$this->plugin->isConfigured()) { throw new Am_Exception( "Plugin networkmerchants-hosted " . "isn\'t configured"); } $APIKey = $this->plugin->getConfig('api_key'); $user = Am_Di::getInstance() ->auth ->getUser(); $customer_id = $user ->data() ->get('customer_vault_id'); $request = $this->createXmlRequest($user, $APIKey, $customer_id); ThePlugin::debug($request->saveXML()); $data = $this->plugin->sendXMLrequest($request, ThePlugin::GATEWAY_URL); ThePlugin::debug($data); // Parse Step One's XML response $gwResponse = @new SimpleXMLElement($data); if ((string)$gwResponse->result == 1) { // The form url for used in Step Two below $formURL = $gwResponse->{'form-url'}; } else { $message = (string)$gwResponse->{'result-text'}; Am_Di::getInstance() ->errorLogTable->log("NMI error, received: $message"); $url = $this->plugin->getPluginUrl(self::ACTION_FAILED) . '?message=' . htmlentities($message); return Am_Di::getInstance()->response->redirectLocation($url); } $form = new Am_Form_Nmi($this->plugin); if ($customer_id) { $ccNum = $user->data()->get('four_last'); $form->addStatic() ->setLabel('Current CC Number:') ->setContent($ccNum); } else { $form->addStatic() ->setContent('You dosn\'t have saved cc data yet'); } $this->plugin->configBaseForm($formURL, $form); $form->addSubmit('_cc_', array('value'=> 'Update')); $this->view->title = ___('Payment info'); $this->view->display_receipt = false; $this->view->form = $form; $this->view->display('cc/info.phtml'); } function createForm() { $user = $this->invoice->getUser(); $customer_id = $user->data()->get('customer_vault_id'); $request = $this->plugin->createXmlRequest($user, $this->invoice, $customer_id); ThePlugin::debug($request->saveXML()); $data = $this->plugin->sendXMLrequest($request, ThePlugin::GATEWAY_URL); ThePlugin::debug($data); // Parse Step One's XML response $gwResponse = @new SimpleXMLElement($data); if ((string)$gwResponse->result == 1) { // The form url for used in Step Two below $formURL = $gwResponse->{'form-url'}; } else { throw new Am_Exception("Error, received " . $data); } $form = $this->plugin->createForm($formURL, $user); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($form->getDefaultValues($user)) )); return $form; } } class Am_Form_Nmi extends Am_Form_CreditCard { function init() {} }plugins/paypal-pro.php000064400000031046152101601040011014 0ustar00 'Visa', 'MasterCard' => 'MasterCard', 'Discover' => 'Discover', 'Amex' => 'Amex', // 'Maestro' => 'Maestro', // 'Solo' => 'Solo', ); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { if(!$doFirst) return; // Recurring payments should not be handled by cron. if (!$invoice->rebill_times) { $request = new Am_Paysystem_PaypalApiRequest($this); $request->doSale($invoice, $cc); } else { $request = new Am_Paysystem_PaypalApiRequest($this); $request->createRecurringPaymentProfile($invoice, $cc); } $tr = new Am_Paysystem_Transaction_PaypalPro_CreateRecurringPaymentsProfile($this, $invoice, $request, $doFirst); $tr->run($result); // send payment request and check response } public function _initSetupForm(Am_Form_Setup $form) { Am_Paysystem_PaypalApiRequest::initSetupForm($form); $form->addAdvCheckbox('send_shipping')->setLabel("Send user's address as shipping address to PayPal"); } public function onSetupForms(Am_Event_SetupForms $event) { parent::onSetupForms($event); $event->getForm('paypal-pro')->removeElementByName('payment.'.$this->getId().'.reattempt'); } public function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Transaction_Incoming_PaypalPro($this, $request, $response, $invokeArgs); } public function cancelInvoice(Invoice $invoice) { $log = Am_Di::getInstance()->invoiceLogRecord; $log->title = "cancelRecurringPaymentProfile"; $log->paysys_id = $this->getId(); $apireq = new Am_Paysystem_PaypalApiRequest($this); $apireq->cancelRecurringPaymentProfile($invoice, $invoice->data()->get(self::PAYPAL_PROFILE_ID)); $result = $apireq->sendRequest($log); $log->setInvoice($invoice); $log->update(); if($result['ACK'] != 'Success') throw new Am_Exception_InputError('Transaction was not cancelled. Got error from paypal: '.$result['L_SHORTMESSAGE0']); } function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $log = Am_Di::getInstance()->invoiceLogRecord; $log->title = "refundTransaction"; $log->paysys_id = $this->getId(); $apireq = new Am_Paysystem_PaypalApiRequest($this); $apireq->refundTransaction($payment); $res = $apireq->sendRequest($log); $log->setInvoice($payment->getInvoice()); $log->update(); if($res['ACK'] != 'Success') throw new Am_Exception_InputError('Transaction was not refunded. Got error from paypal: '.$res['L_SHORTMESSAGE0']); $trans = new Am_Paysystem_Transaction_Manual($this); $trans->setAmount($amount); $trans->setReceiptId($res['REFUNDTRANSACTIONID']); $result->setSuccess(); } public function ccRebill($date = null) { /* Disable cron rebill process * Rebills will be handled through IPN. */ } public function getReadme() { $url = $this->getPluginUrl('ipn'); return <<PayPal PRO payment plugin installation Up to date instructions how to enable and configure PayPal PRO plugin you may find at http://www.amember.com/docs/Payment/PaypalPro IMPORTANT: You MUST set IPN url in your Paypal Profile to: $url CUT; } public function getUpdateCcLink($user) { return $this->getPluginUrl('update'); } public function storeCreditCard(CcRecord $cc, Am_Paysystem_Result $result) { $profiles = array(); //Find all profiles which should be updated. foreach($this->getDi()->db->selectPage($total, " SELECT d.value, i.invoice_id FROM ?_data d LEFT JOIN ?_invoice i ON d.`table` = 'invoice' AND d.`key` = ? AND d.id = i.invoice_id WHERE i.user_id = ? AND i.status = ?", self::PAYPAL_PROFILE_ID, $cc->user_id, Invoice::RECURRING_ACTIVE) as $profile) { $profiles[$profile['value']] = $profile['invoice_id']; } $failures = array(); foreach($profiles as $profile_id=>$invoice_id) { $request = new Am_Paysystem_PaypalApiRequest($this); $request->setCc($invoice = $this->getDi()->invoiceTable->load($invoice_id), $cc); $request->addPostParameter('METHOD', 'UpdateRecurringPaymentsProfile'); $request->addPostParameter('PROFILEID', $profile_id); $request->addPostParameter('NOTE', 'Update CC info, customer IP: '.$this->getDi()->request->getHttpHost()); $log = Am_Di::getInstance()->invoiceLogRecord; $log->title = "updateRecurringPaymentsProfile"; $log->paysys_id = $this->getId(); $res = $request->sendRequest($log); $log->setInvoice($invoice); $log->update(); if($res['ACK'] != 'Success') $failures[] = sprintf('CC info was not updated for profile: %s. Got error from paypal: %s', $profile_id, $res['L_SHORTMESSAGE0']); } if(count($failures)) $result->setFailed ($failures); else $result->setSuccess(); } function createController(\Am_Mvc_Request $request, \Am_Mvc_Response $response, array $invokeArgs) { return new Am_Mvc_Controller_CreditCard_PaypalPro($request, $response, $invokeArgs); } } /** * Overrided to avoid old profile updating on new payment. * Do not call update CC info when new payment is being processed. */ class Am_Mvc_Controller_CreditCard_PaypalPro extends Am_Mvc_Controller_CreditCard { public function processCc() { $cc = $this->getDi()->ccRecordRecord; $this->form->toCcRecord($cc); $cc->user_id = $this->invoice->user_id; if($this->plugin->getConfig('use_maxmind')) { $checkresult = $this->plugin->doMaxmindCheck($this->invoice, $cc); if (!$checkresult->isSuccess()) { $this->view->error = $checkresult->getErrorMessages(); return; } } $result = $this->plugin->doBill($this->invoice, true, $cc); if ($result->isSuccess()) { $this->_response->redirectLocation($this->plugin->getReturnUrl()); return true; } elseif ($result->isAction() && ($result->getAction() instanceof Am_Paysystem_Action_Redirect)) { $result->getAction()->process($this); // throws Am_Exception_Redirect (!) } else { $this->view->error = $result->getErrorMessages(); } } } class Am_Paysystem_Transaction_PaypalPro extends Am_Paysystem_Transaction_CreditCard { public function validate() { if (empty($this->vars['ACK'])) return $this->result->setFailed(___("Payment failed")); if (!in_array($this->vars['ACK'], array('Success', 'SuccessWithWarning'))) return $this->result->setFailed(___("Payment failed") . " : " . $this->vars['ACK']); // The next check is unnecessary. and it lead to an issue when paypal returns SuccessWithWarning response. // In this situation L_SHORTMESSAGE0 will have value. // // if (!empty($this->vars['L_SHORTMESSAGE0'])) // return $this->result->setFailed(___("Payment failed") . " : " . $this->vars['L_SHORTMESSAGE0']); $this->result->setSuccess(); } public function getUniqId() { return @$this->vars['TRANSACTIONID']; } public function parseResponse() { parse_str($this->response->getBody(), $this->vars); if (get_magic_quotes_gpc()) $this->vars = Am_Mvc_Request::ss($this->vars); } public function processValidated() { if ($this->invoice->first_total > 0) $this->invoice->addPayment($this); else $this->invoice->addAccessPeriod($this); // start free trial if (!empty($this->vars['PROFILEID'])) $this->invoice->data()->set(Am_Paysystem_PaypalPro::PAYPAL_PROFILE_ID, $this->vars['PROFILEID'])->update(); } } class Am_Paysystem_Transaction_PaypalPro_CreateRecurringPaymentsProfile extends Am_Paysystem_Transaction_PaypalPro { public function validate() { parent::validate(); // Need to do additional validations. // Sometimes paypal return transacton with ACK = Success ProfileStatus= PendingProfile and without transactionid. // In most situations this means that profile won't be created. So need to wait for IPN message to be sure. if( $this->result->isSuccess() && ($this->vars['PROFILESTATUS'] == 'PendingProfile') && empty($this->vars['TRANSACTIONID']) ) { $this->invoice->data()->set( Am_Paysystem_Transaction_Incoming_PaypalPro::IPN_STATUS_KEY, Am_Paysystem_Transaction_Incoming_PaypalPro::IPN_STATUS_WAITING )->update(); $sleep = 15; // Wait for 15 seconds; do{ sleep(1); $status = $this->invoice->data()->get(Am_Paysystem_Transaction_Incoming_PaypalPro::IPN_STATUS_KEY); }while((--$sleep) && ($status == Am_Paysystem_Transaction_Incoming_PaypalPro::IPN_STATUS_WAITING)); switch($status){ case Am_Paysystem_Transaction_Incoming_PaypalPro::IPN_STATUS_CANCELLED : case Am_Paysystem_Transaction_Incoming_PaypalPro::IPN_STATUS_WAITING : $this->result->reset(); $this->result->setFailed(___("Payment failed")); break; default: $this->vars['TRANSACTIONID'] = $status; } } return $this->result; } } class Am_Paysystem_Transaction_Incoming_PaypalPro extends Am_Paysystem_Transaction_Paypal { const TXN_RECURRING_PAYMENT_PROFILE_CREATED = "recurring_payment_profile_created"; const IPN_STATUS_KEY = 'paypal-ipn-status'; const IPN_STATUS_CANCELLED = 'cancelled'; const IPN_STATUS_WAITING = 'waiting'; function processValidated() { switch($this->txn_type){ case self::TXN_RECURRING_PAYMENT_PROFILE_CREATED : $this->invoice->data()->set(self::IPN_STATUS_KEY, $this->request->get('initial_payment_txn_id'))->update(); break; case self::TXN_RECURRING_PAYMENT_PROFILE_CANCEL : $this->invoice->data()->set(self::IPN_STATUS_KEY, self::IPN_STATUS_CANCELLED)->update(); break; } parent::processValidated(); } }plugins/payonlinesystem.php000064400000021123152101601040012166 0ustar00addText('merchant_id')->setLabel('Merchant ID'); $form->addText('security_key')->setLabel('Private Security Key'); $form->addSelect('language', '', array( 'options' => array( 'ru' => 'Русский', 'en' => 'English' ) ))->setLabel('Interface language'); $form->addAdvCheckbox('cc_form')->setLabel('Send User to CC form directly'); } function getSupportedCurrencies() { return array('RUB', 'USD', 'EUR'); } function isConfigured() { return $this->getConfig('merchant_id') && $this->getConfig('security_key'); } function getRedirectUrl() { return 'https://secure.payonlinesystem.com/' . $this->getConfig('language', 'ru') . '/payment/'.($this->getConfig('cc_form') ? '' : 'select/'); } public function getSecurityKey(Am_Paysystem_Action_Redirect $a) { return md5(sprintf('MerchantId=%s&OrderId=%s&Amount=%s&Currency=%s&OrderDescription=%s&PrivateSecurityKey=%s', $this->getConfig('merchant_id'), $a->OrderId, $a->Amount, $a->Currency, $a->OrderDescription, $this->getConfig('security_key') )); } public function getIncomingSecurityKey(Am_Mvc_Request $r) { return md5(sprintf('DateTime=%s&TransactionID=%s&OrderId=%s&Amount=%s&Currency=%s&PrivateSecurityKey=%s', $r->get('DateTime'), $r->get('TransactionID'), $r->get('OrderId'), $r->get('Amount'), $r->get('Currency'), $this->getConfig('security_key') )); } public function _process(Invoice $invoice, Am_Mvc_Request $request, Am_Paysystem_Result $result) { $a = new Am_Paysystem_Action_Redirect($this->getRedirectUrl()); $a->MerchantId = $this->getConfig('merchant_id'); $a->OrderId = $invoice->public_id; $a->Amount = $invoice->first_total; $a->Currency = $invoice->currency; $a->OrderDescription = $invoice->getLineDescription(); $a->ReturnUrl = $this->getReturnUrl(); $a->FailUrl = $this->getCancelUrl(); $a->SecurityKey = $this->getSecurityKey($a); $result->setAction($a); } public function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Transaction_PayonlineSystem($this, $request, $response, $invokeArgs); } public function getReadme() { return <<PayOnlineSystem plugin configuration instructions 1. Enable plugin: go to aMember CP -> Setup/Configuration -> Plugins and enable "payonlinesystem" payment plugin. 2. In plugin configuration page set you Merchant ID and Private Security Key ( you can get it from your PayOnlineSystem Account -> Integration Settings) 3. On PayOnlineSystem account -> Integration Settings page enable Callback URL for approved transactions. Callback URL should be set to: %root_surl%/payment/payonlinesystem/ipn NOTE: This is necessary to configure https url in aMember CP -> Setup -> Global -> License & Root URLS. Rebill notifications can be sent to https url only. CUT; } function storesCcInfo() { return false; } function getRefundSecurityKey(Array $vars) { return md5(sprintf('MerchantId=%s&TransactionId=%s&Amount=%s&PrivateSecurityKey=%s', $vars['MerchantId'], $vars['TransactionId'], $vars['Amount'], $this->getConfig('security_key') )); } function getRebillSecurityKey(Array $vars) { return md5(sprintf('MerchantId=%s&RebillAnchor=%s&OrderId=%s&Amount=%s&Currency=%s&PrivateSecurityKey=%s', $vars['MerchantId'], $varsp['RebillAnchor'], $vars['OrderId'], $vars['Amount'], $vars['Currency'], $this->getConfig('security_key') )); } public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $request = new Am_HttpRequest(self::REFUND_URL, Am_HttpRequest::METHOD_POST); $vars = array( 'MerchantId' => $this->getConfig('merchant_id'), 'TransactionId' => $payment->receipt_id, 'Amount' => $amount, 'ContentType' => 'text' ); $vars['SecurityKey'] = $this->getRefundSecurityKey($vars); foreach ($vars as $k => $v) { $request->addPostParameter($k, $v); } $this->logRequest($request); $response = $request->send(); $this->logResponse($response); if ($response->getStatus() != 200) throw new Am_Exception_InputError("An error occurred during refund request"); parse_str($response->getBody(), $parsed); if ($parsed['Result'] != 'Ok') throw new Am_Exception_InputError("An error occurred during refund request: " . $parsed['Message']); $trans = new Am_Paysystem_Transaction_Manual($this); $trans->setAmount($amount); $trans->setReceiptId($parsed['TransactionId'] . '-refund'); $result->setSuccess($trans); } public function isNotAcceptableForInvoice(Invoice $invoice) { if ($invoice->rebill_times && ($invoice->first_total != $invoice->second_total)) { return 'Rebilling amount should be the same as first payment amount.'; } } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { $r = new Am_HttpRequest(self::REBILL_URL, Am_HttpRequest::METHOD_POST); $vars = array( 'MerchantId' => $this->getConfig('merchant_id'), 'RebillAnchor' => $invoice->data()->get(self::REBILL_ANCHOR), 'OrderId' => $invoice->public_id . '-' . $invoice->getPaymentsCount(), 'Amount' => $invoice->second_total, 'Currency' => $invoice->currency, 'ContentType' => 'text' ); $vars['SecurityKey'] = $this->getRebillSecurityKey($vars); foreach ($vars as $k => $v) { $r->addPostParameter($k, $v); } $transaction = new Am_Paysystem_Transaction_PayonlineSystemRebill($this, $invoice, $r, $doFirst); $transaction->run($result); } } class Am_Paysystem_Transaction_PayonlineSystem extends Am_Paysystem_Transaction_Incoming { public function getUniqId() { return $this->request->get('TransactionID'); } public function findInvoiceId() { return $this->request->get('OrderId'); } public function validateSource() { return $this->getPlugin()->getIncomingSecurityKey($this->request) == $this->request->get('SecurityKey'); } public function validateStatus() { return true; } public function validateTerms() { return floatval($this->request->get('Amount')) === floatval($this->invoice->first_total); } public function processValidated() { parent::processValidated(); if ($this->request->get('RebillAnchor')) $this->invoice->data()->set(Am_Paysystem_Payonlinesystem::REBILL_ANCHOR, $this->request->get('RebillAnchor'))->update(); } } class Am_Paysystem_Transaction_PayonlineSystemRebill extends Am_Paysystem_Transaction_CreditCard { public function getUniqId() { return $this->response['TransactionId']; } public function parseResponse() { // TransactionId={TransactionId}&Operation=Rebill&Result={Result}&Status={Status}&Code={Code}&ErrorCode={ErrorCode} parse_str($this->response, $arr); $this->response = $arr; } public function validate() { if ($this->response['Result'] == 'Ok' && $this->response['Status'] != 'Declined') $this->result->setSuccess($this); else $this->result->setFailed('Error processing transaction: ' . $this->response['Status'] . ' - ' . $this->response['ErrorCode']); } }plugins/securepaycomau.php000064400000027245152101601040011763 0ustar00getConfig('merchant_id')) && strlen($this->getConfig('password')); } function get_xml($arr) { return $s = ''. $this->form_xml($arr); } function form_xml($arr) { $res = ''; foreach($arr as $k=>$v){ $l = preg_replace("/( .*)/i",'',$k); if(is_array($v)) $res.="<$k>".$this->form_xml($v).""; else $res.="<$k>$v"; } return $res; } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { $user = $invoice->getUser(); if ($cc->user_id != $user->pk()) throw new Am_Exception_Paysystem("Assertion failed: cc.user_id != user.user_id"); // will be stored only if cc# or expiration changed $this->storeCreditCard($cc, $result); if (!$result->isSuccess()) return; $user->refresh(); // we have both profile id and payment id, run the necessary transaction now if amount > 0 $result->reset(); if ($doFirst && !$invoice->first_total) { // free trial $tr = new Am_Paysystem_Transaction_Free($this); $tr->setInvoice($invoice); $tr->process(); $result->setSuccess($tr); } else { $tr = new Am_Paysystem_Transaction_Securepaycomau_Payment($this, $invoice, $doFirst); $tr->run($result); } } public function _initSetupForm(Am_Form_Setup $form) { $form->addText("merchant_id")->setLabel("Merchant ID\n" . '5 or 7-character merchant ID supplied by SecurePay'); $form->addText("password")->setLabel("Payment password\n" . 'Password used for authentication of the merchant\'s request message, supplied by SecurePay'); $form->addAdvCheckbox("testing")->setLabel("Test Mode Enabled"); } public function storeCreditCard(CcRecord $cc, Am_Paysystem_Result $result) { $user = $this->getDi()->userTable->load($cc->user_id); $clientId = $user->data()->get(self::CLIENT_ID); 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 ($clientId && ($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(self::CLIENT_ID, null)->update(); $clientId = null; } } if (!$clientId) { try { $tr = new Am_Paysystem_Transaction_Securepaycomau_CreateCustomerProfile($this, $invoice, $cc); $tr->run($result); if (!$result->isSuccess()) return; $user->data()->set(self::CLIENT_ID, $tr->getClientId())->update(); } catch (Am_Exception_Paysystem $e) { $result->setFailed($e->getPublicError()); return false; } } /// $cc->cc = $cc->maskCc(@$cc->cc_number); $cc->cc_number = '0000000000000000'; if ($cc->pk()) $cc->update(); else $cc->replace(); $result->setSuccess(); } /*public function getReadme() { return << Customer Information Manager -> Sign Up Now) This is a paid service. 1. Enable and configure plugin in aMember CP -> Setup -> Plugins 2. You NEED to use external cron with this plugins (See Setup/Configuration -> Advanced) CUT; }*/ function isRefundable(InvoicePayment $payment) { return false; } } abstract class Am_Paysystem_Transaction_Securepaycomau extends Am_Paysystem_Transaction_CreditCard { protected $response = null; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst = true) { parent::__construct($plugin, $invoice, $plugin->createHttpRequest(), $doFirst); $this->request->setHeader('Content-type', 'text/xml'); $this->request->setBody($this->createXml()); $this->request->setMethod(Am_HttpRequest::METHOD_POST); $this->request->setUrl(!$this->plugin->getConfig('testing') ? Am_Paysystem_Securepaycomau::LIVE_URL : Am_Paysystem_Securepaycomau::SANDBOX_URL); } public function parseResponse() { $p = xml_parser_create(); xml_parse_into_struct($p, $this->response->getBody(), $vals, $index); $res = array(); foreach($vals as $v) if(isset($v['value'])) $res[$v['tag']] = $v['value']; $this->response = $res; } } class Am_Paysystem_Transaction_Securepaycomau_CreateCustomerProfile extends Am_Paysystem_Transaction_Securepaycomau { /** @var CcRecord */ protected $cc; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, CcRecord $cc) { $this->cc = $cc; parent::__construct($plugin, $invoice); } protected function createXml() { $user = $this->invoice->getUser(); $z = date('Z')/60; if($z>=0)$z='+'.$z; $z = date('YdmHis')."000000$z"; $vars = array( 'SecurePayMessage'=>array( 'MessageInfo' => array( 'messageID' => substr(md5($this->invoice->public_id.time()),2), 'messageTimestamp' => $z, 'timeoutValue' => '60', 'apiVersion' => 'spxml-3.0' ), 'MerchantInfo'=>array( 'merchantID'=>$this->getPlugin()->getConfig('merchant_id'), 'password'=>$this->getPlugin()->getConfig('password') ), 'RequestType' => 'Periodic', 'Periodic' => array( 'PeriodicList count="1"' => array( 'PeriodicItem ID="1"' => array( 'actionType' => 'add', 'clientID' => substr(md5(time().$user->user_id),20), 'amount' => 1000, 'periodicType' => '4', 'CreditCardInfo'=>array( 'cardNumber'=>$this->cc->cc_number, 'expiryDate'=>substr($this->cc->cc_expire,0,2).'/'.substr($this->cc->cc_expire, 2,2), 'cvv' => $this->cc->getCvv() ), 'BuyerInfo' => array( 'ip' => $user->remote_addr, 'zipcode' => $user->zip, 'town' => $user->city, 'billingCountry' => $user->country, 'emailAddress' => $user->email, 'firstName' => $user->name_f, 'lastName'=>$user->name_l ) ) ) ) )); $xml = $this->getPlugin()->get_xml($vars); return $xml; } public function getClientId(){ return $this->response['CLIENTID']; } public function validate() { if ($this->response['STATUSCODE'] == '000' && in_array(intval($this->response['RESPONSECODE']),array(0,8,11,16,77))){ $this->result->setSuccess(); return true; } elseif(!@in_array(intval($this->response['RESPONSECODE']),array(0,8,11,16,77))) { $this->result->setFailed((string)$this->response['RESPONSETEXT'].' , ERROR CODE - '.intval($this->response['STATUSCODE'])); return; } else { $this->result->setFailed((string)$this->response['STATUSDESCRIPTION'].' , ERROR CODE - '.intval($this->response['STATUSCODE'])); return; } } function getProfileId() { return (string)$this->xml->customerProfileId; } function getPaymentId() { return (string)$this->xml->customerPaymentProfileIdList->numericString; } public function getUniqId() { return (string)$this->xml->customerProfileId; } public function processValidated() { } } class Am_Paysystem_Transaction_Securepaycomau_Payment extends Am_Paysystem_Transaction_Securepaycomau { protected function createXml() { $user = $this->invoice->getUser(); $z = date('Z')/60; if($z>=0)$z='+'.$z; $z = date('YdmHis')."000000$z"; $vars = array( 'SecurePayMessage'=>array( 'MessageInfo' => array( 'messageID' => substr(md5($this->invoice->public_id.time()),2), 'messageTimestamp' => $z, 'timeoutValue' => '60', 'apiVersion' => 'spxml-3.0' ), 'MerchantInfo'=>array( 'merchantID'=>$this->getPlugin()->getConfig('merchant_id'), 'password'=>$this->getPlugin()->getConfig('password') ), 'RequestType' => 'Periodic', 'Periodic' => array( 'PeriodicList count="1"' => array( 'PeriodicItem ID="1"' => array( 'actionType' => 'trigger', 'clientID' => $user->data()->get(Am_Paysystem_Securepaycomau::CLIENT_ID), 'amount' => intval(($this->doFirst ? $this->invoice->first_total : $this->invoice->second_total)*100), ) ) ) )); return $this->getPlugin()->get_xml($vars); } public function validate() { if ($this->response['STATUSCODE'] == '000' && in_array(intval($this->response['RESPONSECODE']),array(0,8,11,16,77))){ $this->result->setSuccess(); return true; } elseif(!@in_array(intval($this->response['RESPONSECODE']),array(0,8,11,16,77))) { $this->result->setFailed((string)$this->response['RESPONSETEXT'].' , ERROR CODE - '.intval($this->response['STATUSCODE'])); return; } else { $this->result->setFailed((string)$this->response['STATUSDESCRIPTION'].' , ERROR CODE - '.intval($this->response['STATUSCODE'])); return; } $this->result->setSuccess($this); return true; } public function getUniqId() { return $this->response['TXNID']; } public function processValidated() { $this->invoice->addPayment($this); } } plugins/dibs-recurring.php000064400000026345152101601040011655 0ustar00 '208', 'EUR' => '978', 'USD' => '840', 'GBP' => '826', 'SEK' => '752', 'AUD' => '036', 'CAD' => '124', 'ISK' => '352', 'JPY' => '392', 'NZD' => '554', 'NOK' => '578', 'CHF' => '756', 'TRY' => '949'); protected $error_codes = array( '0' => 'Rejected by acquirer', '1' => 'Communication problems', '2' => 'Error in the parameters sent to the DIBS server. An additional parameter called "message" is returned, with a value that may help indentifying error', '3' => 'Error at the acquirer', '4' => 'Credit card expired', '5' => 'Your shop does not support this credit card type, the credit card type could not be identified, or the credit card number was not modulus correct', '6' => 'Instant capture failed', '7' => 'The order number (orderid) is not unique', '8' => 'There number of amount parameters does not correspond to the number given in the split parameter', '9' => 'Control numbers (cvc) are missing', '10' => 'The credit card does not comply with the credit card type', '11' => 'Declined by DIBS Defender', '20' => 'Cancelled by user at 3D Secure authentication step' ); public function getRecurringType() { return self::REPORTS_CRONREBILL; } function storesCcInfo(){ return false; } function _process(Invoice $invoice, Am_Mvc_Request $request, Am_Paysystem_Result $result) { $a = new Am_Paysystem_Action_Redirect(self::FORM_URL); $order_id = $invoice->public_id."-".sprintf("%03d", $invoice->getPaymentsCount()); $currency = $this->getCurrencyCode($invoice); $a->merchant = $this->getConfig('merchant'); $a->amount = intval($invoice->first_total*100); $a->currency = $currency; $a->orderid = $order_id; $a->lang = $this->getConfig('lang'); $a->accepturl = $this->getReturnUrl($request); $a->cancelurl = $this->getCancelUrl($request); $a->continueurl = $this->getReturnUrl(); $a->callbackurl = $this->getPluginUrl('ipn'); $a->preauth = 'true'; if($this->getConfig('testing')) $a->test = 'yes'; $a->md5key = $this->getOutgoingMd5($a); $result->setAction($a); } function getOutgoingMd5(Am_Paysystem_Action_Redirect $a) { return md5($s2 = $this->getConfig('key2').md5($s1 = $this->getConfig('key1')."merchant=".$this->getConfig('merchant'). "&orderid=".$a->orderid."¤cy=".$a->currency."&amount=".$a->amount)); } function getIncomingMd5(Am_Mvc_Request $r, Invoice $invoice) { $currency = $this->getCurrencyCode($invoice); $key = md5($s2 = $this->getConfig('key2').md5($s1 = $this->getConfig('key1')."transact=".$r->get('transact'). "&preauth=true¤cy=".$currency)); return $key; } public function getSupportedCurrencies() { return array_keys($this->currency_codes); } function getCurrencyCode($invoice) { return $this->currency_codes[strtoupper($invoice->currency)]; } function getDibsRecurringError($reason) { return $this->error_codes[$reason]; } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc=null, Am_Paysystem_Result $result) { $request = new Am_HttpRequest(self::TICKET_AUTH_URL, Am_HttpRequest::METHOD_POST); $order_id = $invoice->public_id."-".sprintf("%03d",$invoice->getPaymentsCount() + 1); $currency = $this->getCurrencyCode($invoice); $amount = ($doFirst ? $invoice->first_total : $invoice->second_total) * 100; $post_params = new stdclass; $post_params->merchant = $this->getConfig('merchant'); $post_params->amount = $amount; $post_params->orderId = $order_id; $post_params->ticket = $invoice->data()->get(self::TICKET); $post_params->textreply = 'true'; $post_params->currency = $currency; $post_params->capturenow = 'true'; $post_params->md5key = md5($s2 = $this->getConfig('key2').md5($s1 = $this->getConfig('key1')."merchant=".$this->getConfig('merchant'). "&orderid=".$order_id."&ticket=".$invoice->data()->get(self::TICKET)."¤cy=".$currency."&amount=".$amount)); if($this->getConfig('testing')) $post_params->test = 'yes'; $request->addPostParameter((array)$post_params); $transaction = new Am_Paysystem_Transaction_DibsRecurringSale($this, $invoice, $request, $doFirst); $transaction->run($result); } function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $request = new Am_HttpRequest("https://".$this->getConfig('login').":".$this->getConfig('password')."@payment.architrade.com/cgi-adm/refund.cgi"); $invoice = $payment->getInvoice(); $currency = $this->getCurrencyCode($invoice); $post_params = new stdclass; $post_params->merchant = $this->getConfig('merchant'); $post_params->amount = $amount*100; $count = $this->getDi()->db->selectCol("SELECT COUNT(*) FROM ?_invoice_payment WHERE invoice_id=?d AND dattm < ? ", $payment->invoice_id, $payment->dattm); $post_params->orderId = $invoice->public_id."-".sprintf("%03d", array_shift($count)); $post_params->transact = $invoice->data()->get(self::TICKET); $post_params->textreply = 'true'; $post_params->currency = $currency; $post_params->md5key = md5($s2 = $this->getConfig('key2').md5($s1 = $this->getConfig('key1')."merchant=".$this->getConfig('merchant'). "&orderid=".$invoice->public_id."&transact=".$invoice->data()->get(self::TICKET)."&amount=".$amount)); $request->addPostParameter((array)$post_params); $response = $request->send(); $response = $this->parseResponse($response->getBody()); if ($response['result'] === 0) { $trans = new Am_Paysystem_Transaction_Manual($this); $trans->setAmount($amount); $trans->setReceiptId($payment->receipt_id.'-dibs-refund'); $result->setSuccess($trans); } else { $result->setFailed(array('Error Processing Refund!')); } } public function _initSetupForm(Am_Form_Setup $form) { $form->addText('merchant', array('size' => 20, 'maxlength' => 16)) ->setLabel("Dibs Merchant ID") ->addRule('required'); $form->addText('key1', array('size' => 20, 'maxlength' => 32)) ->setLabel("Dibs Secret Key1") ->addRule('required'); $form->addText('key2', array('size' => 20, 'maxlength' => 32)) ->setLabel("Dibs Secret Key2") ->addRule('required'); $form->addText("login") ->setLabel("Dibs login\n" . "It's needs for performing the refund") ->addRule('required'); $form->addText("password") ->setLabel("Dibs password\n" . "It's needs for performing the refund") ->addRule('required'); $form->addSelect('lang', array(), array('options' => array( 'da' => 'Danish', 'sv' => 'Swedish', 'no' => 'Norwegian', 'en' => 'English', 'nl' => 'Dutch', 'de' => 'German', 'fr' => 'French', 'fi' => 'Finnish', 'es' => 'Spanish', 'it' => 'Italian', 'fo' => 'Faroese', 'pl' => 'Polish' )))->setLabel('The payment window language'); $form->addAdvCheckbox("testing")->setLabel("Test Mode Enabled"); } function getReadme() { return <<DIBS Payment Plugin Configuration 1. Login to DIBS Administration and then go to "integration" -> Return Values. 2. Please check "orderid" parameter. CUT; } public function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Transaction_DibsRecurring($this, $request, $response, $invokeArgs); } public function parseResponse($body) { $result = ''; $params = explode("&", $body); foreach($params as $param) { list($k, $v) = explode("=",$param); $result[$k] = $v; } return $result; } } class Am_Paysystem_Transaction_DibsRecurring extends Am_Paysystem_Transaction_Incoming { public function getUniqId() { return $this->request->get('transact'); } public function findInvoiceId() { return $this->request->get('orderid'); } public function validateStatus() { if($this->request->get('statuscode') != NULL) return $this->request->get('statuscode') == '2'; else return true; } public function validateTerms() { return true; } public function validateSource() { //Am_Di::getInstance()->invoiceTable->loadIds($this->request->get('orderid')); $orderid = explode("-",$this->request->get('orderid')); $invoice = Am_Di::getInstance()->invoiceTable->findFirstByPublicId($orderid[0]); if(is_null($invoice)) return; if($this->getPlugin()->getIncomingMd5($this->request, $invoice) != $this->request->get('authkey')) return false; if($this->request->get('transact')){ $invoice->data()->set(Am_Paysystem_DibsRecurring::TICKET, $this->request->get('transact'))->update(); } $result = new Am_Paysystem_Result(); $this->getPlugin()->_doBill($invoice, true, null, $result); return true; } public function processValidated() { } } class Am_Paysystem_Transaction_DibsRecurringSale extends Am_Paysystem_Transaction_CreditCard { public function validate() { if($this->vars['status'] != 'ACCEPTED') { $this->result->setFailed("Payment failed : ".$this->plugin->getDibsRecurringError($this->vars['reason'])); } else { $this->result->setSuccess($this); } } public function parseResponse(){ $body = $this->response->getBody(); return $this->vars = $this->plugin->parseResponse($body); } public function getUniqId() { return $this->vars['transact']; } } plugins/uniteller.php000064400000043170152101601040010734 0ustar00 'https://wpay.uniteller.ru/pay/', 'result' => 'https://wpay.uniteller.ru/results/', 'refund' => 'https://wpay.uniteller.ru/unblock/', 'rebill' => 'https://wpay.uniteller.ru/recurrent/', ); public static $urlsTest = array( 'pay' => 'https://test.wpay.uniteller.ru/pay/', 'result' => 'https://test.wpay.uniteller.ru/results/', ); private static $meanType = array( 0 => 'Any', 1 => 'VISA', 2 => 'MasterCard', 3 => 'Diners Club', 4 => 'JCB', 5 => 'AMEX (not support now)', ); private static $eMoneyType = array( 0 => 'Any', 1 => 'Яндекс.Деньги', 2 => 'RBK Money', 3 => 'MoneyMail', 4 => 'WebCreds', 5 => 'EasyPay', 6 => 'Platezh.ru', 7 => 'Деньги@Mail.Ru', 8 => 'Мобильный платёж Мегафон', 9 => 'Мобильный платёж МТС', 10 => 'Мобильный платёж Билайн', 11 => 'PayPal', 12 => 'ВКонтакте', 13 => 'Евросеть', 14 => 'Yota.money', 15 => 'QIWI Кошелек', 16 => 'ПлатФон', 17 => 'Moneybookers', 29 => 'WebMoney WMR', ); public function getRecurringType() { return self::REPORTS_CRONREBILL; } public function getFormOptions() { $ret = parent::getFormOptions(); $ret[] = self::CC_PHONE; return $ret; } protected function _initSetupForm(Am_Form_Setup $form) { $form->addText('shopId') ->setLabel(/*"Shop ID"*/"Uniteller Point ID") ->addRule('required'); $form->addText('login') ->setLabel("Login\n" . "from your acount at Uniteller serice") ->addRule('required'); $form->addText('password', array('size' => 80)) ->setLabel("Password\n" . "from your acount at Uniteller serice") ->addRule('required'); $form->addSelect('language') ->setLabel("Interface Language") ->loadOptions(array( 'ru' => 'Russain', 'en' => 'English' )); $form->addSelect('meanType') ->setLabel("Credit Card Payment System\n" . "not use in test mode") ->loadOptions(self::$meanType); $form->addSelect('eMoneyType') ->setLabel("Type of e-Currency\n" . "not use in test mode") ->loadOptions(self::$eMoneyType); $form->addAdvCheckbox("testMode") ->setLabel("Test Mode Enabled"); $form->addAdvCheckbox("debugLog") ->setLabel("Debug Log Enabled\n" . "write all requests/responses to log"); } public function isConfigured() { return $this->getConfig('shopId') && $this->getConfig('login') && $this->getConfig('password'); } public function getUrl($action = 'pay') { if ($this->getConfig('testMode')) { if (isset(self::$urlsTest[$action])) return self::$urlsTest[$action]; $mode = 'TEST'; } else { if (isset(self::$urlsLive[$action])) return self::$urlsLive[$action]; $mode = 'LIVE'; } throw new Am_Exception_InternalError("Unknown action at $mode mode: [$action]"); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc = null, Am_Paysystem_Result $result) { if (!$doFirst) { $tr = new Am_Paysystem_Transaction_CreditCard_UnitellerRebill($this, $invoice, $doFirst); $tr->run($result); } } public function loadCreditCard(Invoice $invoice) { return $this->getDi()->CcRecordTable->createRecord(); // return fake record for rebill } public function _process(Invoice $invoice, Am_Mvc_Request $request, Am_Paysystem_Result $result) { $user = $invoice->getUser(); $post = array( 'Shop_IDP' => $this->getConfig('shopId'), 'Order_IDP' => $invoice->public_id, 'Subtotal_P' => $invoice->first_total, 'URL_RETURN' => $this->getRootUrl(), 'URL_RETURN_OK' => $this->getReturnUrl(), 'URL_RETURN_NO' => $this->getCancelUrl(), 'Language' => $this->getConfig('language'), 'Comment' => $invoice->getLineDescription(), 'FirstName' => $user->name_f, 'LastName' => $user->name_l, 'Email' => $user->email, 'Phone' => $user->phone, 'Address' => $user->street, 'Country' => $this->getDi()->countryTable->getTitleByCode($user->country), //not use now 'State' => $user->state, 'City' => $user->city, 'Zip' => $user->zip, ); if (!$this->getConfig('testMode')) { $post['MeanType'] = $this->getConfig('meanType'); $post['EMoneyType'] = $this->getConfig('eMoneyType'); $post['Signature'] = strtoupper(md5( md5($this->getConfig('shopId')) . "&" . md5($invoice->public_id) . "&" . md5($invoice->first_total) . "&" . md5($this->getConfig('meanType')) . "&" . md5($this->getConfig('eMoneyType')) . "&" . md5('') . "&" . //Lifetime md5('') . "&" . //Customer_ID md5('') . "&" . //Card_ID md5('') . "&" . //IData md5('') . "&" . //PT_Code md5($this->getConfig('password')) )); } else { // for test payment $post['Signature'] = strtoupper(md5( md5($this->getConfig('shopId')) . "&" . md5($invoice->public_id) . "&" . md5($invoice->first_total) . "&" . md5('') . "&" . //meanType md5('') . "&" . //eMoneyType md5('') . "&" . //Lifetime md5('') . "&" . //Customer_ID md5('') . "&" . //Card_ID md5('') . "&" . //IData md5('') . "&" . //PT_Code md5($this->getConfig('password')) )); } if ($this->getConfig('debugLog')) $this->getDi()->errorLogTable->log("UNITELLER [directAction-cc] request: " . json_encode($post)); $a = new Am_Paysystem_Action_Form($this->getUrl()); foreach($post as $k => $v) $a->addParam ($k, $v); $result->setAction($a); } public function directAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { if($request->getActionName() == self::ACTION_IPN) { $log = $this->logRequest($request); if ($this->getConfig('debugLog')) $this->getDi()->errorLogTable->log("UNITELLER [directAction-ipn] request: " . json_encode($request->getParams())); $expSign = strtoupper(md5($request->getParam('Order_ID') . $request->getParam('Status') . $this->getConfig('password'))); $getSign = $request->getParam('Signature'); if ($expSign != $getSign) throw new Am_Exception_InputError("Plugin Uniteller: Bad signature, expected [$expSign], getted [$getSign]"); $invoice = $this->getDi()->invoiceTable->findFirstByPublicId($request->getParam('Order_ID')); if (!$invoice) return; $result = new Am_Paysystem_Result(); $tr = new Am_Paysystem_Transaction_CreditCard_UnitellerResult($this, $invoice, true); $tr->run($result); } else { return parent::directAction($request, $response, $invokeArgs); } } public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $trans = new Am_Paysystem_Transaction_CreditCard_UnitellerRefund($this, $payment->getInvoice(), $payment->receipt_id, $amount); $trans->run($result); } public function getReadme() { return << contracts -> shop settings and enter: shop URL: {$this->getRootUrl()} notification URL: {$this->getPluginUrl('ipn')} - click "Save" ########################################################## Настройка платежного плагина Uniteller Этот плагин позволяет Вам использвать сервис Uniteller для приема платежей. Для настройки плагина: - зарегистрируйте аккаунт на сайте uniteller.ru - заполните необходимые поля на странице настроек плагина Uniteller (эта страница) если используется тестовый аккаунт в поле "Shop ID" внести значение из личного кабинета -> Договоры -> Shop_ID иначе - из личного кабинета -> Точки продаж -> Uniteller Point ID данные для полей "Login" и "Password" взять из личного кабинет -> Параметры Авторизации - сохраните настройки - в личном кабинете Uniteller "Договоры -> Настройки" заполните поля: URL-адрес магазина: {$this->getRootUrl()} URL для уведомление сервера интернет-магазина об изменившемся статусе счёта/оплаты: {$this->getPluginUrl('ipn')} - сохраните настройки CUT; } } class Am_Paysystem_Transaction_CreditCard_Uniteller extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse = array(); protected $act; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst) { $request = new Am_HttpRequest(); parent::__construct($plugin, $invoice, $request, $doFirst); $this->request->setUrl($this->plugin->getUrl($this->act)); $this->request->setMethod(Am_HttpRequest::METHOD_POST); $this->addRequestParams(); } public function getAmount() { return $this->doFirst ? $this->invoice->first_total : $this->invoice->second_total; } protected function addRequestParams() { } public function getUniqId() { return $this->parsedResponse->orders->order->billnumber; } public function getOrderId() { return $this->parsedResponse->orders->order->ordernumber; } public function parseResponse() { $this->parsedResponse = simplexml_load_string($this->response->getBody()); if ($this->plugin->getConfig('debugLog')) Am_Di::getInstance()->errorLogTable->log("UNITELLER [{$this->act}] response: " . json_encode((array)$this->parsedResponse)); } } class Am_Paysystem_Transaction_CreditCard_UnitellerResult extends Am_Paysystem_Transaction_CreditCard_Uniteller { protected $act = 'result'; protected function addRequestParams() { $this->request->addPostParameter('Shop_ID', $this->plugin->getConfig('shopId')); $this->request->addPostParameter('Login', $this->plugin->getConfig('login')); $this->request->addPostParameter('Password', $this->plugin->getConfig('password')); $this->request->addPostParameter('ShopOrderNumber', $this->invoice->public_id); $this->request->addPostParameter('Format', 4); // 1-CSV, 4-XML if ($this->plugin->getConfig('debugLog')) Am_Di::getInstance()->errorLogTable->log("UNITELLER [result] request: " . json_encode($this->request->getPostParams())); } public function validate() { switch ($this->parsedResponse->orders->order->status) { case 'Authorized': case 'Paid': break; default: $err = "Error: {$this->parsedResponse->orders->order->status} - {$this->parsedResponse->orders->order->error_comment} [#{$this->parsedResponse->orders->order->error_code}]."; break; } if (!empty($err)) { return $this->result->setFailed(array($err)); } $this->result->setSuccess($this); } } class Am_Paysystem_Transaction_CreditCard_UnitellerRefund extends Am_Paysystem_Transaction_CreditCard_Uniteller { protected $act = 'refund'; protected $amount; protected $orig_id; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $billNumber, $amount) { $this->amount = $amount; parent::__construct($plugin, $invoice, true); $this->orig_id = $billNumber; $this->request->addPostParameter('Shop_ID', $this->plugin->getConfig('shopId')); $this->request->addPostParameter('Login', $this->plugin->getConfig('login')); $this->request->addPostParameter('Password', $this->plugin->getConfig('password')); $this->request->addPostParameter('Billnumber', $billNumber); $this->request->addPostParameter('Subtotal_P', $this->getAmount()); $this->request->addPostParameter('Format', 3); // 1-CSV, 3-XML if ($this->plugin->getConfig('debugLog')) Am_Di::getInstance()->errorLogTable->log("UNITELLER [refund] request: " . json_encode($this->request->getPostParams())); } public function getAmount() { return $this->amount; } public function validate() { if((string)$this->parsedResponse->attributes()->firstcode) { return $this->result->setFailed(array("Error: ".(string)$this->parsedResponse->attributes()->secondcode." [#".(string)$this->parsedResponse->attributes()->firstcode."]")); } $this->result->setSuccess($this); } function processValidated() { $this->invoice->addRefund($this, $this->orig_id); } } class Am_Paysystem_Transaction_CreditCard_UnitellerRebill extends Am_Paysystem_Transaction_CreditCard_Uniteller { protected $act = 'rebill'; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst) { parent::__construct($plugin, $invoice, $doFirst); //$this->request->addPostParameter('Billnumber', $billNumber); $this->request->addPostParameter('Subtotal_P', $this->getAmount()); $this->request->addPostParameter('Format', 3); // 1-CSV, 3-XML if ($this->plugin->getConfig('debugLog')) Am_Di::getInstance()->errorLogTable->log("UNITELLER [rebill] request: " . json_encode($this->request->getPostParams())); } protected function addRequestParams() { $Order_IDP = $this->invoice->public_id . '-' . Am_Di::getInstance()->security->randomString(4); $this->request->addPostParameter('Shop_IDP', $this->plugin->getConfig('shopId')); $this->request->addPostParameter('Order_IDP', $Order_IDP); $this->request->addPostParameter('Subtotal_P', $this->getAmount()); $this->request->addPostParameter('Parent_Order_IDP', $this->invoice->public_id); $sign = strtoupper(md5( md5($this->plugin->getConfig('shopId')) . "&" . md5($Order_IDP) . "&" . md5($this->getAmount()) . "&" . md5($this->invoice->public_id) . "&" . md5($this->plugin->getConfig('password')) )); $this->request->addPostParameter('Signature', $sign); } public function parseResponse() { list($keys,$values) = explode(PHP_EOL,$this->response->getBody()); $keys = str_getcsv($keys, ';'); $values = str_getcsv($values, ';'); $this->parsedResponse = new stdClass(); foreach($keys as $k => $v) if(!empty($v)) $this->parsedResponse->$v = $values[$k]; if ($this->plugin->getConfig('debugLog')) Am_Di::getInstance()->errorLogTable->log("UNITELLER [rebill] response: " . json_encode($this->parsedResponse)); } public function validate() { if(isset($this->parsedResponse->ErrorCode)) { return $this->result->setFailed(array("Error: {$this->parsedResponse->ErrorMessage} [#{$this->parsedResponse->ErrorCode}].")); } elseif($this->parsedResponse->Status != 'Authorized' && $this->parsedResponse->Status != 'Paid') { return $this->result->setFailed(array("Error: {$this->parsedResponse->Recommendation}. Status: {$this->parsedResponse->Status}.")); } $this->result->setSuccess($this); } public function getUniqId() { return $this->parsedResponse->BillNumber; } function processValidated() { $this->invoice->addPayment($this); } }plugins/stripe.php000064400000074505152101601040010245 0ustar00getUser()->data()->get(self::TOKEN); if (!$token) return $result->setErrorMessages(array(___('Payment failed'))); if ($doFirst && (doubleval($invoice->first_total) <= 0)) { // free trial $tr = new Am_Paysystem_Transaction_Free($this); $tr->setInvoice($invoice); $tr->process(); $result->setSuccess($tr); } else { $tr = new Am_Paysystem_Transaction_Stripe($this, $invoice, $doFirst); $tr->run($result); } } public function getUpdateCcLink($user) { if ($user->data()->get(self::TOKEN)) return $this->getPluginUrl('update'); } public function storeCreditCard(CcRecord $cc, Am_Paysystem_Result $result) { } public function loadCreditCard(Invoice $invoice) { if ($invoice->getUser()->data()->get(self::TOKEN)) return $this->getDi()->CcRecordTable->createRecord(); // return fake record for rebill } protected function createController(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { $invokeArgs['hosted'] = $this->getConfig('hosted'); return new Am_Mvc_Controller_CreditCard_Stripe($request, $response, $invokeArgs); } protected function _initSetupForm(Am_Form_Setup $form) { $form->addText('secret_key', array('class'=>'el-wide'))->setLabel('Secret Key')->addRule('required'); $form->addText('public_key', array('class'=>'el-wide'))->setLabel('Publishable Key')->addRule('required'); $label = "Use Hosted Version (recommended)\n". "this option allows you to display credit card input right on your website\n". "(as a popup) and in the same time it does not require PCI DSS compliance"; if ('https' != substr(ROOT_SURL,0,5)) $label .= "\n" . 'This option requires https on your site'; $form->addAdvCheckbox('hosted', array('id'=>'hosted-version'))->setLabel($label); $form->addText('image_url', array('class'=>'el-wide', 'rel'=>'hosted-version')) ->setLabel("Image URL\nA relative or absolute URL to a square image of your brand or product. Recommended minimum size is 128x128px. Supported image types are: .gif, .jpeg, and .png."); $form->addAdvCheckbox('zip_validate', array('rel'=>'hosted-version')) ->setLabel("Validate Postal Code?\nSpecify whether Checkout should validate the billing postal code"); $form->addScript() ->setScript(<<getInvoice(), $payment->receipt_id, $amount); $tr->run($result); } public function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Transaction_Stripe_Webhook($this, $request, $response, $invokeArgs); } public function getReadme() { $url = $this->getPluginUrl('ipn'); return <<charge.refunded $url CUT; } } class Am_Mvc_Controller_CreditCard_Stripe extends Am_Mvc_Controller { /** @var Invoice*/ protected $invoice; /** @var Am_Paysystem_Stripe */ protected $plugin; public function setInvoice(Invoice $invoice) { $this->invoice = $invoice; } public function setPlugin($plugin) { $this->plugin = $plugin; } public function createForm($label, $cc_mask = null) { if($this->getInvokeArg('hosted')) return $this->createFormHosted($label, $cc_mask); else return $this->createFormRegular($label, $cc_mask); } public function createFormHosted($label, $cc_mask = null) { $form = new Am_Form('cc-stripe'); $key = $this->plugin->getConfig('public_key'); $amount = $this->invoice->first_total*100; $currency = $this->invoice->currency; $email = $this->invoice->getEmail(); $name = $this->getDi()->config->get('site_title'); $description = $this->invoice->getLineDescription(); $lang = $this->getDi()->locale->getLanguage(); $plabel = $label; $image = $this->plugin->getConfig('image_url'); $zipvalidate = $this->plugin->getConfig('zip_validate') ? 'true' : 'false'; $form->addHidden('id')->setValue($this->_request->get('id')); $form->addStatic()->setContent(<< CUT ); $form->addScript()->setScript(<<setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($this->getDefaultValues($this->invoice->getUser())) )); return $form; } public function createFormRegular($label, $cc_mask = null) { $form = new Am_Form('cc-stripe'); $name = $form->addGroup() ->setLabel(___("Cardholder Name\n" . 'cardholder first and last name, exactly as on the card')); $name->addRule('required', ___('Please enter credit card holder name')); $name->setSeparator(' '); $name_f = $name->addText('cc_name_f', array('size'=>15, 'id' => 'cc_name_f')); $name_f->addRule('required', ___('Please enter credit card holder first name'))->addRule('regex', ___('Please enter credit card holder first name'), '|^[a-zA-Z_\' -]+$|'); $name_l = $name->addText('cc_name_l', array('size'=>15, 'id' => 'cc_name_l')); $name_l->addRule('required', ___('Please enter credit card holder last name'))->addRule('regex', ___('Please enter credit card holder last name'), '|^[a-zA-Z_\' -]+$|'); $cc = $form->addText('', array('autocomplete'=>'off', 'size'=>22, 'maxlength'=>22, 'id' => 'cc_number')) ->setLabel(___('Credit Card Number')); if ($cc_mask) $cc->setAttribute('placeholder', $cc_mask); $cc->addRule('required', ___('Please enter Credit Card Number')) ->addRule('regex', ___('Invalid Credit Card Number'), '/^[0-9 -]+$/'); class_exists('Am_Form_CreditCard', true); // preload element $expire = $form->addElement(new Am_Form_Element_CreditCardExpire('cc_expire')) ->setLabel(___("Card Expire\n" . 'Select card expiration date - month and year')); $expire->addRule('required', ___('Please enter Credit Card expiration date')); $code = $form->addPassword('', array('autocomplete'=>'off', 'size'=>4, 'maxlength'=>4, 'id' => 'cc_code')) ->setLabel(___("Credit Card Code\n". 'The "Card Code" is a three- or four-digit security code that is printed on the back of credit cards in the card\'s signature panel (or on the front for American Express cards).')); $code->addRule('required', ___('Please enter Credit Card Code')) ->addRule('regex', ___('Please enter Credit Card Code'), '/^\s*\d{3,4}\s*$/'); $fieldSet = $form->addFieldset(___('Address Info')) ->setLabel(___("Address Info\n" . '(must match your credit card statement delivery address)')); $street = $fieldSet->addText('cc_street')->setLabel(___('Street Address')) ->addRule('required', ___('Please enter Street Address')); $city = $fieldSet->addText('cc_city')->setLabel(___('City ')) ->addRule('required', ___('Please enter City')); $zip = $fieldSet->addText('cc_zip')->setLabel(___('ZIP')) ->addRule('required', ___('Please enter ZIP code')); $country = $fieldSet->addSelect('cc_country')->setLabel(___('Country')) ->setId('f_cc_country') ->loadOptions(Am_Di::getInstance()->countryTable->getOptions(true)); $country->addRule('required', ___('Please enter Country')); $group = $fieldSet->addGroup()->setLabel(___('State')); $group->addRule('required', ___('Please enter State')); /** @todo load correct states */ $stateSelect = $group->addSelect('cc_state') ->setId('f_cc_state') ->loadOptions($stateOptions = Am_Di::getInstance()->stateTable->getOptions(@$_REQUEST['cc_country'], true)); $stateText = $group->addText('cc_state')->setId('t_cc_state'); $disableObj = $stateOptions ? $stateText : $stateSelect; $disableObj->setAttribute('disabled', 'disabled')->setAttribute('style', 'display: none'); $form->addSubmit('', array('value' => $label)); $form->addHidden('id')->setValue($this->_request->get('id')); $form->addHidden('stripe_info', 'id=stripe_info')->addRule('required'); $key = json_encode($this->plugin->getConfig('public_key')); $form->addScript()->setScript(file_get_contents(AM_APPLICATION_PATH . '/default/views/public/js/json2.min.js')); $form->addScript()->setScript(<< '') return true; // submit the form! event.stopPropagation(); Stripe.setPublishableKey($key); Stripe.createToken({ number: frm.find("#cc_number").val(), cvc: frm.find("#cc_code").val(), exp_month: frm.find("[name='cc_expire[m]']").val(), exp_year: frm.find("[name='cc_expire[y]']").val(), name: frm.find("#cc_name_f").val() + " " + frm.find("#cc_name_l").val(), address_zip : frm.find("[name=cc_zip]").val(), address_line1 : frm.find("[name=cc_street]").val(), address_country : frm.find("[name=cc_country]").val(), address_city : frm.find("[name=cc_city]").val(), address_state : frm.find("[name=cc_state]").val() }, function(status, response){ // handle response if (status == '200') { frm.find("input[name=stripe_info]").val(JSON.stringify(response)); frm.submit(); } else { frm.find("input[type=submit]").prop('disabled', null); var msg; if (response.error.type == 'card_error') msg = response.error.message; else msg = 'Payment failure, please try again later'; var el = frm.find("#cc_number"); var cnt = el.closest(".element"); cnt.addClass("error"); cnt.find("span.error").remove(); el.after("
"+msg+"
"); } }); frm.find("input[type=submit]").prop('disabled', 'disabled'); return false; }); }); CUT ); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($this->getDefaultValues($this->invoice->getUser())) )); return $form; } public function getDefaultValues(User $user){ return array( 'cc_name_f' => $user->name_f, 'cc_name_l' => $user->name_l, 'cc_street' => $user->street, 'cc_street2' => $user->street2, 'cc_city' => $user->city, 'cc_state' => $user->state, 'cc_country' => $user->country, 'cc_zip' => $user->zip, 'cc_phone' => $user->phone, ); } public function updateAction() { $user = $this->getDi()->user; $token = $user->data()->get(Am_Paysystem_Stripe::TOKEN); if (!$token) throw new Am_Exception_Paysystem("No credit card stored, nothing to update"); $this->invoice = $this->getDi()->invoiceTable->findFirstBy( array('user_id'=>$user->pk(), 'paysys_id'=>$this->plugin->getId()), 'invoice_id DESC'); if (!$this->invoice) throw new Am_Exception_Paysystem("No invoices found for user and paysystem"); $tr = new Am_Paysystem_Transaction_Stripe_GetCustomer($this->plugin, $this->invoice, $token); $tr->run(new Am_Paysystem_Result()); $info = $tr->getInfo(); if (empty($info['id'])) // cannot load profile { // todo delete old profile, and display cc form again! throw new Am_Exception_Paysystem("Could not load customer profile"); } if(!isset($info['active_card'])) { foreach($info['sources']['data'] as $c) if($c['id'] == $info['default_source']) $info['active_card'] = $c; } $this->form = $this->createForm(___('Update Credit Card Info'), 'XXXX XXXX XXXX ' . $info['active_card']['last4']); $n = preg_split('/\s+/', $info['active_card']['name'], 2); $this->form->addDataSource(new HTML_QuickForm2_DataSource_Array(array( 'cc_street' => $info['active_card']['address_line1'], 'cc_name_f' => $n[0], 'cc_name_l' => $n[1], 'cc_zip' => $info['active_card']['address_zip'], 'cc_expire' => sprintf('%02d%02d', $info['active_card']['exp_month'], $info['active_card']['exp_year']-2000), ))); $result = $this->ccFormAndSaveCustomer(); if ($result->isSuccess()) $this->_redirect($this->getDi()->url('member',null,false,true)); if(!$this->getInvokeArg('hosted')) { $this->form->getElementById('stripe_info')->setValue(''); $this->view->headScript()->appendFile('https://js.stripe.com/v1/'); } $this->view->title = ___('Payment info'); $this->view->display_receipt = false; $this->view->form = $this->form; $this->view->display('cc/info.phtml'); } protected function ccFormAndSaveCustomer() { $vars = $this->form->getValue(); $result = new Am_Paysystem_Result(); if(!$this->getInvokeArg('hosted')) { if (!empty($vars['stripe_info'])) { $stripe_info = json_decode($vars['stripe_info'], true); if (!$stripe_info['id']) throw new Am_Exception_Paysystem("No expected token id received"); $tr = new Am_Paysystem_Transaction_Stripe_CreateCustomer($this->plugin, $this->invoice, $stripe_info['id']); $tr->run($result); if ($result->isSuccess()) { $this->invoice->getUser()->data() ->set(Am_Paysystem_Stripe::TOKEN, $tr->getUniqId()) ->set(Am_Paysystem_Stripe::CC_EXPIRES, sprintf('%02d%02d', $stripe_info['source']['exp_month'], $stripe_info['source']['exp_year']-2000)) ->set(Am_Paysystem_Stripe::CC_MASKED, 'XXXX' . $stripe_info['source']['last4']) ->update(); // setup session to do not reask payment info within 30 minutes $s = new Zend_Session_Namespace($this->plugin->getId()); $s->setExpirationSeconds(60*30); // after 30 minutes we will reset the session $s->ccConfirmed = true; } else { $this->view->error = $result->getErrorMessages(); } } } else { if ($stripe_info = $this->_request->get('stripeToken')) { $tr = new Am_Paysystem_Transaction_Stripe_CreateCustomer($this->plugin, $this->invoice, $stripe_info); $tr->run($result); if ($result->isSuccess()) { $card = $tr->getCard(); $this->invoice->getUser()->data() ->set(Am_Paysystem_Stripe::TOKEN, $tr->getUniqId()) ->set(Am_Paysystem_Stripe::CC_EXPIRES, sprintf('%02d%02d', $card[0]['exp_month'], $card[0]['exp_year']-2000)) ->set(Am_Paysystem_Stripe::CC_MASKED, 'XXXX' . $card[0]['last4']) ->update(); // setup session to do not reask payment info within 30 minutes $s = new Zend_Session_Namespace($this->plugin->getId()); $s->setExpirationSeconds(60*30); // after 30 minutes we will reset the session $s->ccConfirmed = true; } else { $this->view->error = $result->getErrorMessages(); } } } return $result; } protected function displayReuse() { $result = new Am_Paysystem_Result; $tr = new Am_Paysystem_Transaction_Stripe_GetCustomer($this->plugin, $this->invoice, $this->invoice->getUser()->data()->get(Am_Paysystem_Stripe::TOKEN)); $tr->run($result); if (!$result->isSuccess()) throw new Am_Exception_Paysystem("Stored customer profile not found"); $card = $tr->getInfo(); $last4 = 'XXXX'; foreach($card['sources']['data'] as $c) if($c['id'] == $card['default_source']) $last4 = $c['last4']; $card = 'XXXX XXXX XXXX ' . $last4; $text = ___('Click "Continue" to pay this order using stored credit card %s', $card); $continue = ___('Continue'); $cancel = ___('Cancel'); $action = $this->plugin->getPluginUrl('cc'); $id = Am_Html::escape($this->_request->get('id')); $action = Am_Html::escape($action); $receipt = $this->view->partial('_receipt.phtml', array('invoice' => $this->invoice, 'di'=>$this->getDi())); $this->view->layoutNoMenu = true; $this->view->content .= <<

$text

   
CUT; $this->view->display('layout.phtml'); } public function ccAction() { $this->view->title = ___('Payment info'); $this->view->display_receipt = true; $this->view->layoutNoMenu = true; $this->view->invoice = $this->invoice; // if we have credit card on file, we will try to use it but we // have to display confirmation first if ($this->invoice->getUser()->data()->get(Am_Paysystem_Stripe::TOKEN)) { $s = new Zend_Session_Namespace($this->plugin->getId()); $s->setExpirationSeconds(60*30); // after 30 minutes we will reset the session //$s->ccConfirmed = !empty($s->ccConfirmed); if ($this->_request->get('reuse_ok')) { if(@$s->ccConfirmed === true) { $result = $this->plugin->doBill($this->invoice, true, $this->getDi()->CcRecordTable->createRecord()); if ($result->isSuccess()) { return $this->_redirect($this->plugin->getReturnUrl()); } else { $this->invoice->getUser()->data() ->set(Am_Paysystem_Stripe::TOKEN, null) ->set(Am_Paysystem_Stripe::CC_EXPIRES, null) ->set(Am_Paysystem_Stripe::CC_MASKED, null) ->update(); $this->view->error = $result->getErrorMessages(); $s->ccConfirmed = false; // failed } } } elseif ($this->_request->get('reuse_cancel') || (@$s->ccConfirmed === false)) { $s->ccConfirmed = false; } elseif (@$s->ccConfirmed === true) { try{ return $this->displayReuse(); }catch(Exception $e){ // Ignore it. } } } $this->form = $this->createForm(___('Subscribe And Pay')); $result = $this->ccFormAndSaveCustomer(); if ($result->isSuccess()) { $result = $this->plugin->doBill($this->invoice, true, $this->getDi()->CcRecordTable->createRecord()); if ($result->isSuccess()) { return $this->_redirect($this->plugin->getReturnUrl()); } else { $this->invoice->getUser()->data() ->set(Am_Paysystem_Stripe::TOKEN, null) ->set(Am_Paysystem_Stripe::CC_EXPIRES, null) ->set(Am_Paysystem_Stripe::CC_MASKED, null) ->update(); $this->view->error = $result->getErrorMessages(); } } if(!$this->getInvokeArg('hosted')) { $this->form->getElementById('stripe_info')->setValue(''); $this->view->headScript()->appendFile('https://js.stripe.com/v1/'); } $this->view->form = $this->form; $this->view->display('cc/info.phtml'); } } class Am_Paysystem_Transaction_Stripe extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse = array(); public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst) { $request = new Am_HttpRequest('https://api.stripe.com/v1/charges', 'POST'); $amount = $doFirst ? $invoice->first_total : $invoice->second_total; $request->setAuth($plugin->getConfig('secret_key'), '') ->addPostParameter('amount', sprintf('%.02f', $amount)*100) ->addPostParameter('currency', $invoice->currency) ->addPostParameter('customer', $invoice->getUser()->data()->get(Am_Paysystem_Stripe::TOKEN)) ->addPostParameter('metadata[invoice]', $invoice->public_id) ->addPostParameter('description', 'Invoice #'.$invoice->public_id.': '.$invoice->getLineDescription()); parent::__construct($plugin, $invoice, $request, $doFirst); } public function getUniqId() { return $this->parsedResponse['id']; } public function parseResponse() { $this->parsedResponse = json_decode($this->response->getBody(), true); } public function validate() { if (@$this->parsedResponse['paid'] != 'true') { if ($this->parsedResponse['error']['type'] == 'card_error') $this->result->setErrorMessages(array($this->parsedResponse['error']['message'])); else $this->result->setErrorMessages(array(___('Payment failed'))); return false; } $this->result->setSuccess($this); return true; } } /** * Convert temporary credit card profile to customer record */ class Am_Paysystem_Transaction_Stripe_CreateCustomer extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse = array(); public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $token) { $request = new Am_HttpRequest('https://api.stripe.com/v1/customers', 'POST'); $request->setAuth($plugin->getConfig('secret_key'), '') ->addPostParameter('card', $token) ->addPostParameter('email', $invoice->getEmail()) ->addPostParameter('description', 'Username:' . $invoice->getUser()->login); parent::__construct($plugin, $invoice, $request, true); } public function getUniqId() { return $this->parsedResponse['id']; } public function getCard() { return $this->parsedResponse['cards']['data']; } public function parseResponse() { $this->parsedResponse = json_decode($this->response->getBody(), true); } public function validate() { if (!@$this->parsedResponse['id']) { $code = @$this->parsedResponse['error']['code']; $message = @$this->parsedResponse['error']['message']; $error = "Error storing customer profile"; if ($code) $error .= " [".$code."]"; if ($message) $error .= " (".$message.")"; $this->result->setErrorMessages(array($error)); return false; } $this->result->setSuccess($this); return true; } public function processValidated() { } } class Am_Paysystem_Transaction_Stripe_GetCustomer extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse = array(); public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $token) { $request = new Am_HttpRequest('https://api.stripe.com/v1/customers/' . $token, 'GET'); $request->setAuth($plugin->getConfig('secret_key'), ''); parent::__construct($plugin, $invoice, $request, true); } public function getUniqId() { return $this->parsedResponse['id']; } public function parseResponse() { $this->parsedResponse = json_decode($this->response->getBody(), true); } public function validate() { if (!@$this->parsedResponse['id']) $this->result->setErrorMessages(array('Unable to fetch payment profile')); $this->result->setSuccess($this); } public function getInfo() { return $this->parsedResponse; } public function processValidated() { } } class Am_Paysystem_Transaction_Stripe_Refund extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse = array(); protected $charge_id; protected $amount; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $charge_id, $amount = null) { $this->charge_id = $charge_id; $this->amount = $amount > 0 ? $amount : null; $request = new Am_HttpRequest('https://api.stripe.com/v1/charges/' . $this->charge_id . '/refund', 'POST'); $request->setAuth($plugin->getConfig('secret_key'), ''); if ($this->amount > 0) $request->addPostParameter('amount', sprintf('%.2f', $this->amount)*100); parent::__construct($plugin, $invoice, $request, true); } public function getUniqId() { $r = null; foreach ($this->parsedResponse['refunds']['data'] as $refund) { if (is_null($r) || $refund['created'] > $r['created']) { $r = $refund; } } return $r['id']; } public function parseResponse() { $this->parsedResponse = json_decode($this->response->getBody(), true); } public function validate() { if (!@$this->parsedResponse['id']) $this->result->setErrorMessages(array('Unable to fetch payment profile')); $this->result->setSuccess(); } public function processValidated() { $this->invoice->addRefund($this, $this->charge_id, $this->amount); } } class Am_Paysystem_Transaction_Stripe_Webhook extends Am_Paysystem_Transaction_Incoming { public function process() { $this->event = json_decode($this->request->getRawBody(), true); switch ($this->event['type']) { case "charge.refunded" : $r = null; $refundsList = (isset($this->event['data']['object']['refunds']['data']) ? $this->event['data']['object']['refunds']['data'] : $this->event['data']['object']['refunds']); foreach ($refundsList as $refund) { if (is_null($r) || $refund['created'] > $r['created']) { $r = $refund; } } $this->refund = $r; break; } return parent::process(); } public function validateSource() { return (bool)$this->event; } public function validateTerms() { return true; } public function validateStatus() { return true; } public function getUniqId() { switch ($this->event['type']) { case "charge.refunded" : return $this->refund['id']; default : return $this->event['id']; } } public function findInvoiceId() { return $this->event['data']['object']['metadata']['invoice']; } public function processValidated() { switch ($this->event['type']) { case "charge.refunded" : try { $this->invoice->addRefund($this, $this->event['data']['object']["id"], $this->refund['amount']/100); } catch (Am_Exception_Db_NotUnique $e) { //nop, refund is added from aMemeber admin interface } break; } } }plugins/paymill-dd.php000064400000042054152101601040010765 0ustar00addText('private_key', 'class=el-wide') ->setLabel('Private Key')->addRule('required'); $form->addText('public_key', 'class=el-wide') ->setLabel('Public Key')->addRule('required'); $form->addAdvCheckbox("testing")->setLabel('Test Mode'); } public function storeEcheck(EcheckRecord $echeck, Am_Paysystem_Result $result) { //nop } public function _doBill(Invoice $invoice, $doFirst, EcheckRecord $echeck, Am_Paysystem_Result $result) { if (!is_null($echeck)) { $user = $this->getDi()->userTable->load($echeck->user_id); $tr = new Am_Paysystem_Transaction_PaymillDd_Payment($this, $user, $echeck->paymill_token); $tr->run($result); } $pay = $invoice->getUser()->data()->get(self::PAYMENT_ID); if (!$pay) { return $result->setFailed(array(___('Payment failed'))); } if ($doFirst && (doubleval($invoice->first_total) <= 0)) { // free trial $tr = new Am_Paysystem_Transaction_Free($this); $tr->setInvoice($invoice); $tr->process(); $result->setSuccess($tr); } else { $tr = new Am_Paysystem_Transaction_PaymillDd_Transaction($this, $invoice, $doFirst); $tr->run($result); } } public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $tr = new Am_Paysystem_Transaction_PaymillDd_Refund($this, $payment, $amount); $tr->run($result); } public function createForm($actionName) { return new Am_Form_EcheckPaymill($this); } public function getReadme() { return << Settings -> API keys) CUT; } } class Am_Form_EcheckPaymill extends Am_Form { public function __construct(Am_Paysystem_Echeck $plugin) { $this->plugin = $plugin; parent::__construct('echeck'); } function init() { $name = $this->addGroup() ->setLabel(___("Account Holder\n" . 'first and last name')); $name->addText('', array('size' => 15, 'id' => 'echeck_name_f')); $name->addText('', array('size' => 15, 'id' => 'echeck_name_l')); $this->addText('', array('autocomplete' => 'off', 'id' => 'echeck_number')) ->setLabel(___("Account Number / IBAN")); $this->addText('', array('autocomplete' => 'off', 'id' => 'echeck_bank')) ->setLabel(___("Bank code / BIC")); $this->addSaveButton(___('Subscribe And Pay')); $this->addHidden('paymill_token', 'id=paymill_token')->addRule('required'); $this->addProlog(''); $key = $this->plugin->getConfig('public_key'); if ($this->plugin->getConfig('testing')) { $this->addScript()->setScript("var PAYMILL_TEST_MODE = true;"); } $this->addScript()->setScript("var PAYMILL_PUBLIC_KEY = '$key';"); $this->addScript()->setScript(<< '') return true; // submit the form! event.stopPropagation(); if (isSepa()) { params = { iban: jQuery("#echeck_number").val().replace(/\s+/g, ""), bic: frm.find("#echeck_bank").val().replace(/\s+/g, ""), accountholder: frm.find("#echeck_name_f").val() + ' ' + frm.find("#echeck_name_l").val() } } else { params = { number: frm.find("#echeck_number").val().replace(/\s+/g, ""), bank: frm.find("#echeck_bank").val().replace(/\s+/g, ""), accountholder: frm.find("#echeck_name_f").val() + ' ' + frm.find("#echeck_name_l").val() } } paymill.createToken(params, function(error, result){ // handle response if (error) { frm.find("input[type=submit]").prop('disabled', null); var el = frm.find("#echeck_number"); var cnt = el.closest(".element"); cnt.addClass("error"); cnt.find("span.error").remove(); var errorMessage = ''; switch (error.apierror) { case 'internal_server_error': errorMessage = 'Communication with PSP failed'; break; case 'invalid_public_key': errorMessage = 'Invalid Public Key'; break; case 'unknown_error': errorMessage = 'Unknown Error'; break; case '3ds_cancelled': errorMessage = 'Password Entry of 3-D Secure password was cancelled by the user'; break; case 'field_invalid_card_number': errorMessage = 'Missing or invalid creditcard number'; break; case 'field_invalid_card_exp_year': errorMessage = 'Missing or invalid expiry year'; break; case 'field_invalid_card_exp_month': errorMessage = 'Missing or invalid expiry month'; break; case 'field_invalid_card_exp': errorMessage = 'Card is no longer valid or has expired'; break; case 'field_invalid_card_cvc': errorMessage = 'Invalid checking number'; break; case 'field_invalid_card_holder': errorMessage = 'Invalid cardholder'; break; case 'field_invalid_amount_int': errorMessage = 'Invalid or missing amount for 3-D Secure'; break; case 'field_invalid_amount': errorMessage = 'Invalid or missing amount for 3-D Secure deprecated, see blog post'; break; case 'field_invalid_currency': errorMessage = 'Invalid or missing currency code for 3-D Secure'; break; case 'field_invalid_account_number': errorMessage = 'Missing or invalid bank account number'; break; case 'field_invalid_account_holder': errorMessage = 'Missing or invalid bank account holder'; break; case 'field_invalid_bank_code': errorMessage = 'Missing or invalid bank code'; break; case 'field_invalid_iban': errorMessage = 'Missing or invalid IBAN'; break; case 'field_invalid_bic': errorMessage = 'Missing or invalid BIC'; break; case 'field_invalid_country': errorMessage = 'Missing or unsupported country (with IBAN)'; break; case 'field_invalid_bank_data': errorMessage = 'Missing or invalid bank data combination'; break; default: errorMessage = error.apierror; } el.after("
"+errorMessage+"
"); } else { frm.find("input[name=paymill_token]").val(result.token); frm.submit(); } }); frm.find("input[type=submit]").prop('disabled', 'disabled'); return false; }); }); CUT ); } public function getDefaultValues(User $user) { return array( 'echeck_name_f' => $user->name_f, 'echeck_name_l' => $user->name_l ); } public function toEcheckRecord(EcheckRecord $echeck) { $values = $this->getValue(); unset($values['a']); unset($values['id']); unset($values['action']); $echeck->setForInsert($values); } } abstract class Am_Paysystem_Transaction_PaymillDd_Abstract { protected $log; protected $user; /** @var Invoice */ protected $invoice; protected $plugin; protected $request; protected $params = array(); public function run(Am_Paysystem_Result $result) { $log = $this->getInvoiceLog(); $log->add($this->request); $response = $this->request->send(); $log->add($response); $this->validateResponseStatus($response, $result); if ($result->isFailure()) return; try { $this->parseResponse($response); $this->validateResponse($result); if ($result->isSuccess()) $this->processValidated(); } catch (Exception $e) { $log->add($e); } } protected function validateResponseStatus(HTTP_Request2_Response $response, Am_Paysystem_Result $result) { if ($response->getStatus() != 200) { $result->setFailed(array("Received invalid response from payment server: " . $this->response->getStatus())); } } protected function parseResponse(HTTP_Request2_Response $response) { $this->params = json_decode($response->getBody(), true); } protected function validateResponse(Am_Paysystem_Result $result) { if (!$this->plugin->getConfig('testing') && $this->params['mode'] == 'test') { $result->setFailed(array( 'Attempt to run test trunsaction in live mode' )); return; } $this->validate($result); } abstract protected function validate(Am_Paysystem_Result $result); abstract protected function processValidated(); /** @return InvoiceLog */ protected function getInvoiceLog() { if (!$this->log) { $this->log = $this->getPlugin()->getDi()->invoiceLogRecord; if ($this->invoice) { $this->log->invoice_id = $this->invoice->invoice_id; $this->log->user_id = $this->invoice->user_id; } elseif ($this->user) { $this->log->user_id = $this->user->user_id; } $this->log->paysys_id = $this->getPlugin()->getId(); $this->log->remote_addr = $_SERVER['REMOTE_ADDR']; $this->log->toggleMask(false); } return $this->log; } protected function getPlugin() { return $this->plugin; } } class Am_Paysystem_Transaction_PaymillDd_Payment extends Am_Paysystem_Transaction_PaymillDd_Abstract { public function __construct(Am_Paysystem_Abstract $plugin, User $user, $token) { $this->plugin = $plugin; $this->user = $user; $this->request = new Am_HttpRequest(Am_Paysystem_PaymillDd::API_ENDPOINT . 'payments', Am_HttpRequest::METHOD_POST); $this->request->setAuth($this->plugin->getConfig('private_key'), ''); $this->request->addPostParameter('token', $token); } protected function validate(Am_Paysystem_Result $result) { $result->setSuccess(); } protected function processValidated() { $this->user->data() ->set(Am_Paysystem_PaymillDd::PAYMENT_ID, $this->params['data']['id']) ->update(); } } class Am_Paysystem_Transaction_PaymillDd_PaymentDelete extends Am_Paysystem_Transaction_PaymillDd_Abstract { public function __construct(Am_Paysystem_Abstract $plugin, User $user) { $this->plugin = $plugin; $this->user = $user; $pay = $user->data()->get(Am_Paysystem_PaymillDd::PAYMENT_ID); $this->request = new Am_HttpRequest(Am_Paysystem_PaymillDd::API_ENDPOINT . 'payments/' . $pay, Am_HttpRequest::METHOD_DELETE); $this->request->setAuth($this->plugin->getConfig('private_key'), ''); } protected function validate(Am_Paysystem_Result $result) { $result->setSuccess(); } protected function processValidated() { $this->user->data() ->set(Am_Paysystem_PaymillDd::PAYMENT_ID, null) ->update(); } } class Am_Paysystem_Transaction_PaymillDd_Transaction extends Am_Paysystem_Transaction_PaymillDd_Abstract implements Am_Paysystem_Transaction_Interface { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst) { $this->plugin = $plugin; $this->user = $invoice->getUser(); $this->invoice = $invoice; $pay = $this->user->data()->get(Am_Paysystem_PaymillDd::PAYMENT_ID); $this->request = new Am_HttpRequest(Am_Paysystem_PaymillDd::API_ENDPOINT . 'transactions', Am_HttpRequest::METHOD_POST); $this->request->setAuth($this->plugin->getConfig('private_key'), ''); $this->request->addPostParameter(array( 'amount' => ($doFirst ? $invoice->first_total : $invoice->second_total) * 100, 'currency' => $invoice->currency, 'payment' => $pay, 'description' => $invoice->getLineDescription() )); } protected function validate(Am_Paysystem_Result $result) { if ($this->params['data']['response_code'] != 20000) { $result->setFailed(array(___('Payment Failed'))); return; } if ($this->params['data']['status'] == 'failed') { $result->setFailed(array(___('Payment Failed'))); return; } $result->setSuccess(); } protected function processValidated() { $this->invoice->addPayment($this); } function getTime() { return $this->getPlugin()->getDi()->dateTime; } function getRecurringType() { return $this->getPlugin()->getRecurringType(); } function getUniqId() { return $this->params['data']['id']; } function getPaysysId() { return $this->getPlugin()->getId(); } function getReceiptId() { return $this->params['data']['id']; } function getAmount() { return $this->params['data']['origin_amount'] / 100; } } class Am_Paysystem_Transaction_PaymillDd_Refund extends Am_Paysystem_Transaction_PaymillDd_Abstract implements Am_Paysystem_Transaction_Interface { public function __construct(Am_Paysystem_Abstract $plugin, InvoicePayment $payment, $amount) { $this->plugin = $plugin; $this->user = $payment->getUser(); $this->invoice = $payment->getInvoice(); $this->request = new Am_HttpRequest(Am_Paysystem_PaymillDd::API_ENDPOINT . 'refunds/' . $payment->receipt_id, Am_HttpRequest::METHOD_POST); $this->request->setAuth($this->plugin->getConfig('private_key'), ''); $this->request->addPostParameter(array( 'amount' => ($amount ? $amount : $payment->amount) * 100, )); } protected function validate(Am_Paysystem_Result $result) { if ($this->params['data']['response_code'] != 20000) { $result->setFailed(array(___('Refund Failed'))); return; } $result->setSuccess(); } protected function processValidated() { $this->invoice->addRefund($this, $this->params['data']['transaction']['id']); } function getTime() { return $this->getPlugin()->getDi()->dateTime; } function getRecurringType() { return $this->getPlugin()->getRecurringType(); } function getUniqId() { return $this->params['data']['id']; } function getPaysysId() { return $this->getPlugin()->getId(); } function getReceiptId() { return $this->params['data']['id']; } function getAmount() { return $this->params['data']['amount'] / 100; } }plugins/payflow.php000064400000055270152101601040010416 0ustar00getConfig('user') && $this->getConfig('pass'); } public function getSupportedCurrencies() { return array('USD', 'CAD', 'GBP'); } public function allowPartialRefunds() { return true; } protected function createController(\Am_Mvc_Request $request, \Am_Mvc_Response $response, array $invokeArgs) { if (!$this->getConfig('advanced')) return parent::createController($request, $response, $invokeArgs); else return new Am_Mvc_Controller_CreditCard_Payflow($request, $response, $invokeArgs); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { $addCc = true; // if it is a first charge, or user have valid CC info in file, we should use cc_info instead of reference transaction. // This is necessary when data was imported from amember v3 for example if ($doFirst || (!empty($cc->cc_number) && $cc->cc_number != '0000000000000000')) { if ($doFirst && (doubleval($invoice->first_total) == 0) ) // free trial { $tr = new Am_Paysystem_Payflow_Transaction_Authorization($this, $invoice, $doFirst, $cc); } else { $tr = new Am_Paysystem_Payflow_Transaction_Sale($this, $invoice, $doFirst, $cc); } } else { $user = $invoice->getUser(); $profileId = $user->data()->get(self::USER_PROFILE_KEY); if (!$profileId) return $result->setFailed(array("No saved reference transaction for customer")); $tr = new Am_Paysystem_Payflow_Transaction_Sale($this, $invoice, $doFirst, null, $profileId); } $tr->run($result); } public function storeCreditCard(CcRecord $cc, Am_Paysystem_Result $result) { $user = $this->getDi()->userTable->load($cc->user_id); $profileId = $user->data()->get(self::USER_PROFILE_KEY); 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 ($profileId && ($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(self::USER_PROFILE_KEY, null) ->update(); $profileId = null; } } if (!$profileId) { try { $tr = new Am_Paysystem_Payflow_Transaction_Upload($this, $invoice, $cc); $tr->run($result); if (!$result->isSuccess()) return; $user->data()->set(Am_Paysystem_Payflow::USER_PROFILE_KEY, $tr->getProfileId())->update(); } catch (Am_Exception_Paysystem $e) { $result->setFailed($e->getPublicError()); return false; } } /// $cc->cc = $cc->maskCc(@$cc->cc_number); $cc->cc_number = '0000000000000000'; if ($cc->pk()) $cc->update(); else $cc->replace(); $result->setSuccess(); } protected function _initSetupForm(Am_Form_Setup $form) { $form->addText('vendor')->setLabel('Merchant Vendor Id (main username)'); $form->addText('user')->setLabel('Merchant User Id (of the API user, or the same as Vendor Id)'); $form->addPassword('pass')->setLabel('Merchant Password'); $form->addText('partner')->setLabel('Partner'); $form->setDefault('partner', 'PayPal'); $form->addAdvCheckbox('advanced')->setLabel("Use PayPal Advanced\ncredit card info will be asked in iframe on your website"); $form->addAdvCheckbox('testing')->setLabel('Test Mode'); } public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $tr = new Am_Paysystem_Payflow_Transaction_Refund($this, $payment->getInvoice(), $payment->receipt_id, $amount); $tr->run($result); } public function getReadme() { $root = $this->getRootUrl(); return << Hosted Checkout Pages -> Set Up PayPal email address -> set to your paypal e-mail address Return URL -> set to $root/payment/payflow/thanks Enable Secure Token -> yes Click [Save Changes] button Click [Customize] -> Layout C and click [Save and Publish] You now are ready to go with PayPal Advanced. IMPORTANT: As a security measure, reference transactions are disallowed by default. Only your account administrator can enable reference transactions for your account. If you attempt to perform a reference transaction in an account for which reference transactions are disallowed, RESULT value 117 is returned. See PayPal Manager online help for instructions on setting reference transactions and other security features. CUT; } public function createThanksTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Payflow_Transaction_Thanks($this, $request, $response, $invokeArgs); } public function thanksAction(\Am_Mvc_Request $request, \Am_Mvc_Response $response, array $invokeArgs) { $ret = parent::thanksAction($request, $response, $invokeArgs); foreach ($response->getHeaders() as $h) if ($h['name'] == 'Location') $redirect = $h['value']; if ($response->isRedirect()) { $response->clearAllHeaders()->clearBody(); $url = Am_Html::escape($redirect); $response->setBody( " "); } return $ret; } public function cancelPaymentAction(\Am_Mvc_Request $request, \Am_Mvc_Response $response, array $invokeArgs) { $ret = parent::cancelPaymentAction($request, $response, $invokeArgs); foreach ($response->getHeaders() as $h) if ($h['name'] == 'Location') $redirect = $h['value']; if ($response->isRedirect()) { $response->clearAllHeaders()->clearBody(); $url = Am_Html::escape($redirect); $response->setBody( " "); } return $ret; } } class Am_Mvc_Controller_CreditCard_Payflow extends Am_Mvc_Controller_CreditCard { public function ccAction() { // invoice must be set to this point by the plugin if (!$this->invoice) throw new Am_Exception_InternalError('Empty invoice - internal error!'); $this->form = $this->createForm(); $this->getDi()->hook->call(Bootstrap_Cc::EVENT_CC_FORM, array('form' => $this->form)); $trans = new Am_Paysystem_Payflow_Transaction_CreateSecureToken($this->plugin, $this->invoice, true); $res = new Am_Paysystem_Result(); $trans->run($res); //var_dump($res);exit(); //if (!$res->isSuccess()) // throw new Am_Exception_Paysystem("Internal error - cannot get secure token from PayPal API"); $token = $trans->getToken(); $frm = new Am_Form(); $params = array( 'SECURETOKENID' => $trans->getTokenId(), 'SECURETOKEN' => $trans->getToken(), 'CANCELURL' => $this->plugin->getCancelUrl(), 'DISABLERECEIPT' => true, // Determines if the payment confirmation / order receipt page is a PayPal hosted page or a page on the merchant site. 'EMAILCUSTOMER' => true, //'ERRORURL' => $this->plugin->getCancelUrl(), //'RETURNURL' => $this->plugin->getReturnUrl(), 'INVNUM' => $this->invoice->public_id, 'TEMPLATE' => 'MINLAYOUT', // or MOBILE for mobile iframe or TEMPLATEA TEMPLATEB 'SHOWAMOUNT' => (double)$this->invoice->first_total <= 0 ? false : true, ); if ($this->plugin->getConfig('testing')) $params['MODE'] = 'TEST'; $params = http_build_query($params); $html = ''; $frm->addHtml()->setHtml($html)->addClass('no-label'); $this->view->form = $frm; $this->view->invoice = $this->invoice; $this->view->display_receipt = true; $this->view->layoutNoMenu = true; $this->view->display('cc/info.phtml'); } } class Am_Paysystem_Payflow_Transaction_Thanks extends Am_Paysystem_Transaction_Incoming_Thanks { /* @var $request Am_Mvc_Request */ protected $request; public function __construct(\Am_Paysystem_Abstract $plugin, Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { $this->request = $request; parent::__construct($plugin, $request, $response, $invokeArgs); } public function findInvoiceId() { return $this->request->getFiltered('INVNUM'); } public function getUniqId() { return $this->request->get('PNREF'); } public function validateStatus() { return $this->request->get('RESULT') === '0'; } public function validateTerms() { return true; // checked with securetoken } public function validateSource() { // check if secure token is registered in session $tok = $this->request->get('SECURETOKEN', rand(10,88888)); $k = 'payflow_securetoken_' . $tok; return (bool)Am_Di::getInstance()->session->$k; } public function processValidated() { parent::processValidated(); // TODO if there was a recurring invoice start it // $tr = new Am_Paysystem_Payflow_Transaction_CreateProfile($this->plugin, $this->invoice, $this->getReceiptId()); // $res = new Am_Paysystem_Result(); // $tr->run(res); // if (!$res->isSuccess()) // throw new Am_Exception_Paysystem("Could not start recurring billing for invoice " . $this->invoice->public_id ); } } class Am_Paysystem_Payflow_Transaction extends Am_Paysystem_Transaction_CreditCard { protected $parsedResponse = array(); public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst) { $request = new Am_HttpRequest($plugin->getConfig('testing') ? Am_Paysystem_Payflow::TEST_URL : Am_Paysystem_Payflow::LIVE_URL, Am_HttpRequest::METHOD_POST); parent::__construct($plugin, $invoice, $request, $doFirst); $this->addRequestParams(); } public function run(Am_Paysystem_Result $result) { $reqId = sha1(serialize($this->request->getPostParams())); // unique id of request $this->request->setHeader('X-VPS-REQUEST-ID', $reqId); $this->request->setHeader('X-VPS-CLIENT-TIMEOUT', 60); $this->request->setHeader('X-VPS-VIT-INTEGRATION-PRODUCT', 'aMember Pro'); // $this->request->setHeader('Content-Type', 'text/namevalue'); $this->request->addPostParameter('VERBOSITY', 'HIGH'); return parent::run($result); } protected function addRequestParams() { $this->request->addPostParameter('VENDOR', $this->plugin->getConfig('vendor')); $this->request->addPostParameter('USER', $this->plugin->getConfig('user')); $this->request->addPostParameter('PWD', $this->plugin->getConfig('pass')); $this->request->addPostParameter('PARTNER', $this->plugin->getConfig('partner')); $this->request->addPostParameter('BUTTONSOURCE', 'CgiCentral.aMemberPro'); } public function getUniqId() { return strlen(@$this->parsedResponse->PPREF) ? $this->parsedResponse->PPREF : $this->parsedResponse->PNREF; } public function getReceiptId() { return $this->parsedResponse->PNREF; } public function getAmount() { return $this->doFirst ? $this->invoice->first_total : $this->invoice->second_total; } public function parseResponse() { parse_str($this->response->getBody(), $this->parsedResponse); $this->parsedResponse = (object)$this->parsedResponse; if (!strlen(@$this->parsedResponse->RESULT)) $this->parsedResponse->RESULT = -1; // wrong response received } public function validate() { if ($this->parsedResponse->RESULT != '0') return $this->result->setFailed(array("Transaction declined, please check credit card information")); $this->result->setSuccess($this); } function setCcRecord(CcRecord $cc) { $this->request->addPostParameter(array( 'ACCT' => $cc->cc_number, 'EXPDATE' => $cc->cc_expire, 'BILLTOFIRSTNAME' => $cc->cc_name_f, 'BILLTOLASTNAME' => $cc->cc_name_l, 'BILLTOSTREET' => $cc->cc_street, 'BILLTOCITY' => $cc->cc_city, 'BILLTOSTATE' => $cc->cc_state, 'BILLTOZIP' => $cc->cc_zip, 'BILLTOCOUNTRY' => $cc->cc_country, 'CVV2' => $cc->getCvv(), )); } } class Am_Paysystem_Payflow_Transaction_CreateProfile extends Am_Paysystem_Payflow_Transaction { protected $pnref; public function __construct(\Am_Paysystem_Abstract $plugin, \Invoice $invoice, $pnref) { $this->pnref = $pnref; parent::__construct($plugin, $invoice, false); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter(array( 'TRXTYPE' => 'R', 'ACTION' => 'A', 'TENDER' => 'C', 'PROFILEREFERENCE' => $this->invoice->public_id, 'PROFILENAME' => $this->invoice->getLineDescription(), 'START' => '09182014', 'TERM' => '0', 'PAYPERIOD' => 'MONT', 'AMT' => $this->invoice->second_total, 'ORIGID' => $this->pnref, )); /// [13]=XXXXXX&USER[6]=XXXXX&PWD[8]=XXXXX&TRXTYPE=R&ACTION=A&TENDER=C&PROFILEREFERENCE=XXXX&PROFILENAME[38]=XAXXXXXAXXX&START=09182014&TERM=0&PAYPERIOD=MONT&AMT[4]=1.07&ORIGID=ESJPC2894AFC } } class Am_Paysystem_Payflow_Transaction_Upload extends Am_Paysystem_Payflow_Transaction { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, CcRecord $cc) { parent::__construct($plugin, $invoice, true); $this->setCcRecord($cc); } protected function addRequestParams() { parent::addRequestParams(); //https://cms.paypal.com/cms_content/en_US/files/developer/PP_PayflowPro_Guide.pdf $this->request->addPostParameter(array( 'TRXTYPE' => 'A', 'TENDER' => 'C', 'COMMENT' => 'UPDATE CC: ' . $this->invoice->getLineDescription(), 'CUSTIP' => $this->doFirst ? $_SERVER['REMOTE_ADDR'] : $this->invoice->getUser()->get('remote_addr'), 'AMT' => 0 )); } public function getProfileId() { return $this->parsedResponse->PNREF; } public function processValidated() { } } class Am_Paysystem_Payflow_Transaction_Sale extends Am_Paysystem_Payflow_Transaction { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst, CcRecord $cc = null, $referenceId = null) { parent::__construct($plugin, $invoice, $doFirst, $referenceId); if ($cc) $this->setCcRecord($cc); elseif ($referenceId) $this->request->addPostParameter('ORIGID', $referenceId); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter(array( 'TRXTYPE' => 'S', 'TENDER' => 'C', 'AMT' => $this->doFirst ? $this->invoice->first_total : $this->invoice->second_total, 'CURRENCY' => $this->invoice->currency, 'COMMENT' => $this->invoice->getLineDescription(), 'CUSTIP' => $this->doFirst ? $_SERVER['REMOTE_ADDR'] : $this->invoice->getUser()->get('remote_addr'), 'INVNUM' => $this->invoice->public_id . '-' . substr(md5(rand()),1,6), )); if (!$this->doFirst) $this->request->addPostParameter ('RECURRING', 'Y'); } public function processValidated() { if(!$this->doFirst) $this->invoice->getUser()->data()->set(Am_Paysystem_Payflow::USER_PROFILE_KEY, $this->parsedResponse->PNREF)->update(); parent::processValidated(); } } class Am_Paysystem_Payflow_Transaction_Authorization extends Am_Paysystem_Payflow_Transaction { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst, CcRecord $cc) { parent::__construct($plugin, $invoice, $doFirst); $this->setCcRecord($cc); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter(array( 'TRXTYPE' => 'A', 'TENDER' => 'C', 'AMT' => 0, 'COMMENT' => $this->invoice->getLineDescription(), 'CUSTIP' => $this->doFirst ? $_SERVER['REMOTE_ADDR'] : $this->invoice->getUser()->get('remote_addr'), 'INVNUM' => $this->invoice->public_id, )); } public function processValidated() { $this->invoice->addAccessPeriod($this); } } class Am_Paysystem_Payflow_Transaction_Refund extends Am_Paysystem_Payflow_Transaction { protected $origId; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $origId, $amount) { parent::__construct($plugin, $invoice, true); $this->request->addPostParameter('ORIGID', $origId); $this->amount = $amount; $this->request->addPostParameter('AMT', $amount); $this->origId = $origId; } public function getAmount() { return $this->amount; } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter(array( 'TRXTYPE' => 'C', 'TENDER' => 'C', )); } public function processValidated() { $this->result->setSuccess(); $this->invoice->addRefund($this, $this->origId); } } class Am_Paysystem_Payflow_Transaction_CreateSecureToken extends Am_Paysystem_Payflow_Transaction { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst, CcRecord $cc = null, $referenceId = null) { parent::__construct($plugin, $invoice, $doFirst, $referenceId); } protected function addRequestParams() { parent::addRequestParams(); $this->request->setNvpRequest(true); // send without encoding, else paypal handles encoded urls incorrectly $this->request->addPostParameter(array( 'TRXTYPE' => 'S', 'TENDER' => 'C', 'AMT' => $this->doFirst ? $this->invoice->first_total : $this->invoice->second_total, 'CURRENCY' => $this->invoice->currency, 'CREATESECURETOKEN' => 'Y', 'SECURETOKENID' => uniqid('PFP'), 'CANCELURL' => $this->plugin->getRootUrl() . '/payment/payflow/cancel-payment?id=' . $this->invoice->public_id, 'DISABLERECEIPT' => true, // Determines if the payment confirmation / order receipt page is a PayPal hosted page or a page on the merchant site. 'EMAILCUSTOMER' => true, 'ERRORURL' => $this->plugin->getRootUrl() . '/payment/payflow/cancel-payment?id=' . $this->invoice->public_id, 'INVNUM' => $this->invoice->public_id, 'RETURNURL' => $this->plugin->getRootUrl() . '/payment/payflow/thanks', 'TEMPLATE' => 'MINLAYOUT', // or MOBILE for mobile iframe or TEMPLATEA TEMPLATEB 'SHOWAMOUNT' => (double)$this->invoice->first_total <= 0 ? false : true, )); } public function getToken() { return $this->parsedResponse->SECURETOKEN; } public function getTokenId() { return $this->parsedResponse->SECURETOKENID; } public function validate() { $tok = $this->parsedResponse->SECURETOKEN; if ($tok == '') return $this->result->setFailed(array("Transaction declined, cannot get secure token id")); $this->result->setSuccess($this); $k = 'payflow_securetoken_' . $tok; Am_Di::getInstance()->session->$k = 1; } public function getReceiptId() { } public function getUniqId() { } } plugins/wepay-onsite.php000064400000041233152101601040011353 0ustar00getConfig('client_id')) && strlen($this->getConfig('secret')) && strlen($this->getConfig('token')) && strlen($this->getConfig('account_id')); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { if (!($preapproval_id = $invoice->getUser()->data()->get(self::WEPAY_PREAPPROVAL_ID))) throw new Am_Exception_Paysystem("Stored wepay preapproval id not found"); $tr = new Am_Paysystem_Transaction_WepayOnsite_Checkout_Charge($this, $invoice, $doFirst, $preapproval_id); $tr->run($result); } public function _initSetupForm(Am_Form_Setup $form) { $form->addInteger('client_id', array('size' => 20)) ->setLabel('Your Client ID#'); $form->addText('secret', array('size' => 20)) ->setLabel('Your Client Secret'); $form->addText('token', array('size' => 40)) ->setLabel('Your Access Token'); $form->addInteger('account_id', array('size' => 20)) ->setLabel('Your Account ID#'); $form->addSelect('fee_payer')->setLabel(___('Who is paying the fee')) ->loadOptions(array( 'payee' => 'the person receiving the money', 'payer' => 'the person paying', /* 'payee_from_app' => 'if payee is paying for app fee and app is paying for WePay fees' 'payer_from_app' => 'if payer is paying for app fee and the app is paying WePay fees', */ )); $form->addAdvCheckbox("testing")->setLabel("Test Mode Enabled" . "\n" . "The Test Mode requires a separate developer test account, which can be set up by filling out the following form: https://stage.wepay.com/developer/register"); } public function storeCreditCard(CcRecord $cc, Am_Paysystem_Result $result) { } function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $trans = new Am_Paysystem_Transaction_WepayOnsite_Checkout_Refund($this, $payment->getInvoice(), $payment->receipt_id, $amount); $trans->run($result); } function getIframeUri() { $user = $this->getDi()->userTable->load($this->invoice->user_id); $invoice = $this->invoice; $tr = new Am_Paysystem_Transaction_WepayOnsite_GetCheckoutUri($this, $invoice); $result = new Am_Paysystem_Result(); $tr->run($result); if (!$tr->getUniqId()) throw new Am_Exception_Paysystem("Could not get iframe from wepay.com - [".$tr->getErrorDescription()."]"); return $tr->getUniqId(); } // use custom controller protected function createController(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Mvc_Controller_CreditCard_WepayOnsite($request, $response, $invokeArgs); } public function directAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { if ($request->getActionName() == 'thanks') return $this->thanksAction($request, $response, $invokeArgs); parent::directAction($request, $response, $invokeArgs); } public function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { if ($request->get('checkout_id')) return new Am_Paysystem_Transaction_WepayOnsite_Checkout($this, $request, $response, $invokeArgs); else return new Am_Paysystem_Transaction_WepayOnsite_Preapproval($this, $request, $response, $invokeArgs); } public function getUpdateCcLink($user) { /* if ($user->data()->get(self::WEPAY_PREAPPROVAL_ID)) { $inv = $this->getDi()->invoiceTable->findFirstBy(array('user_id' => $user->pk(), 'paysys_id' => $this->getId()), 0, 1); if ($inv) return $this->getPluginUrl('update'); } */ } public function getReadme() { return <<createHttpRequest(), $doFirst); $this->request->setHeader("Content-Type", "application/json"); $this->request->setHeader("Authorization", "Bearer " . $this->plugin->getConfig('token')); $this->request->setBody(json_encode((array) $this->createParams())); $this->request->setMethod(Am_HttpRequest::METHOD_POST); $this->request->setUrl(!$this->plugin->getConfig('testing') ? Am_Paysystem_WepayOnsite::LIVE_URL : Am_Paysystem_WepayOnsite::SANDBOX_URL); } protected function createParams() { $params = new stdclass; $params->account_id = $this->plugin->getConfig('account_id'); $params->fee_payer = $this->plugin->getConfig('fee_payer'); return $params; } public function parseResponse() { $this->res = json_decode($this->response->getBody(), true); } public function validate() { if ($this->response->getStatus() != 200) { $this->result->setFailed(___('Payment failed')); return; } if (!empty($this->res['error_description'])) { $this->result->setFailed(___('Payment failed') . '(' . $this->res['error_description'] . ')'); return; } $this->result->setSuccess($this); return true; } public function getErrorDescription() { return @$this->res['error_description']; } } class Am_Paysystem_Transaction_WepayOnsite_GetCheckoutUri extends Am_Paysystem_Transaction_WepayOnsite { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst = true) { parent::__construct($plugin, $invoice, $doFirst); //recurring if (!is_null($invoice->second_period)) $this->request->setUrl($this->request->getUrl() . "preapproval/create"); else $this->request->setUrl($this->request->getUrl() . "checkout/create"); } protected function createParams() { $params = parent::createParams(); $params->short_description = $this->invoice->getLineDescription(); $params->reference_id = $this->invoice->public_id; $params->mode = 'iframe'; $params->redirect_uri = $this->plugin->getReturnUrl(); $params->callback_uri = $this->plugin->getPluginUrl('ipn'); //recurring if (!is_null($this->invoice->second_period)) { $params->amount = $this->invoice->second_total; $params->auto_recur = 'false'; $params->period = 'daily'; $this->uri = 'preapproval_uri'; } //not recurring else { $params->amount = $this->invoice->first_total; $params->type = 'GOODS'; $this->uri = 'checkout_uri'; } return $params; } public function getUniqId() { return $this->res[$this->uri]; } public function processValidated() { if (!is_null($this->invoice->second_period)) $this->invoice->data()->set(Am_Paysystem_WepayOnsite::WEPAY_PREAPPROVAL_ID, $this->res['preapproval_id'])->update(); else $this->invoice->data()->set(Am_Paysystem_WepayOnsite::WEPAY_PREAPPROVAL_ID, $this->res['checkout_id'])->update(); } } class Am_Mvc_Controller_CreditCard_WepayOnsite extends Am_Mvc_Controller { /** @var Am_Paysystem_WepayOnsite */ protected $plugin; /** @var Invoice */ protected $invoice; public function setPlugin($plugin) { $this->plugin = $plugin; } public function setInvoice($invoice) { $this->invoice = $invoice; } protected function ccError($msg) { $this->view->content .= "" . $msg . ""; $url = $this->_request->getRequestUri(); $url .= (strchr($url, '?') ? '&' : '?') . 'id=' . $this->_request->get('id'); $url = Am_Html::escape($url); $this->view->content .= " " . ___('Return and try again') . ""; $this->view->display('layout.phtml'); exit; } public function ccAction() { $this->view->title = ___('Payment Info'); $this->view->invoice = $this->invoice; $this->view->content = $this->view->render('_receipt.phtml'); return $this->displayHostedPage($this->plugin->getCancelUrl()); } protected function displayHostedPage($cancelUrl) { $uri = $this->plugin->getIframeUri(); $popupTitle = json_encode(___('Credit Card Info')); $this->view->content .= << CUT; $this->_response->setBody($this->view->render('layout.phtml')); } } class Am_Paysystem_Transaction_WepayOnsite_Checkout extends Am_Paysystem_Transaction_Incoming { public function __construct(Am_Paysystem_Abstract $plugin, Am_Mvc_Request $request, Am_Mvc_Response $response, $invokeArgs) { parent::__construct($plugin, $request, $response, $invokeArgs); $this->invoice = Am_Di::getInstance()->invoiceTable->findFirstBy(array('public_id' => $request->get('reference_id'))); $this->tr = new Am_Paysystem_Transaction_WepayOnsite_Checkout_Get($plugin, $this->invoice, true, $request); $result = new Am_Paysystem_Result(); $this->tr->run($result); if (!$this->tr->getReferenceId()) throw new Am_Exception_Paysystem(___('Error happened during payment process. ')); } public function findInvoiceId() { return $this->tr->getReferenceId(); } public function getUniqId() { return $this->tr->getUniqId(); } public function validateSource() { return $this->request->get('checkout_id') == $this->invoice->data()->get(Am_Paysystem_WepayOnsite::WEPAY_PREAPPROVAL_ID); } public function validateStatus() { return in_array($this->tr->getState(), array('captured', 'approved', 'authorized')); } public function validateTerms() { return true; } } class Am_Paysystem_Transaction_WepayOnsite_Checkout_Get extends Am_Paysystem_Transaction_WepayOnsite { /** @var Am_Mvc_Request */ protected $getrequest; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst = true, Am_Mvc_Request $getrequest) { $this->getrequest = $getrequest; parent::__construct($plugin, $invoice, $doFirst); $this->request->setUrl($this->request->getUrl() . "checkout"); } protected function createParams() { $params = parent::createParams(); unset($params->fee_payer); unset($params->account_id); $params->checkout_id = $this->getrequest->get('checkout_id'); return $params; } public function getUniqId() { return $this->res['checkout_id']; } public function getReferenceId() { return $this->res['reference_id']; } public function getState() { return $this->res['state']; } public function processValidated() { } } class Am_Paysystem_Transaction_WepayOnsite_Checkout_Charge extends Am_Paysystem_Transaction_WepayOnsite { /** @var Am_Mvc_Request */ protected $preapproval_id; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst = true, $preapproval_id) { $this->preapproval_id = $preapproval_id; parent::__construct($plugin, $invoice, $doFirst); $this->request->setUrl($this->request->getUrl() . "checkout/create"); } protected function createParams() { $params = parent::createParams(); $params->preapproval_id = $this->preapproval_id; $params->amount = $this->invoice->isPaid() ? $this->invoice->first_total : $this->invoice->second_total; $params->type = 'GOODS'; $params->short_description = $this->invoice->getLineDescription(); return $params; } public function getUniqId() { return $this->res['checkout_id']; } public function getState() { return $this->res['state']; } public function processValidated() { $user = $this->invoice->getUser(); $user->data()->set(Am_Paysystem_WepayOnsite::WEPAY_PREAPPROVAL_ID, $this->preapproval_id)->update(); $this->invoice->addPayment($this); } } class Am_Paysystem_Transaction_WepayOnsite_Preapproval extends Am_Paysystem_Transaction_Incoming { public function __construct(Am_Paysystem_Abstract $plugin, Am_Mvc_Request $request, Am_Mvc_Response $response, $invokeArgs) { parent::__construct($plugin, $request, $response, $invokeArgs); $this->invoice = Am_Di::getInstance()->invoiceTable->findFirstBy(array('public_id' => $request->get('reference_id'))); } public function findInvoiceId() { return $this->invoice->public_id; } public function getUniqId() { return $this->tr->getUniqId(); } public function validateSource() { if ($this->request->get('preapproval_id') != $this->invoice->data()->get(Am_Paysystem_WepayOnsite::WEPAY_PREAPPROVAL_ID)) return false; $this->tr = new Am_Paysystem_Transaction_WepayOnsite_Checkout_Charge($this->getPlugin(), $this->invoice, true, $this->request->get('preapproval_id')); $result = new Am_Paysystem_Result(); $this->tr->run($result); if (!$this->tr->getUniqId()) throw new Am_Exception_Paysystem(___('Error happened during payment process. ')); return true; } public function validateStatus() { return in_array($this->tr->getState(), array('captured', 'approved', 'authorized')); } public function validateTerms() { return true; } public function processValidated() { } } class Am_Paysystem_Transaction_WepayOnsite_Checkout_Refund extends Am_Paysystem_Transaction_WepayOnsite { protected $checkout_id; protected $amount; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $checkout_id, $amount) { $this->checkout_id = $checkout_id; $this->amount = $amount; parent::__construct($plugin, $invoice, $doFirst); $this->request->setUrl($this->request->getUrl() . "checkout/refund"); } protected function createParams() { $params = parent::createParams(); unset($params->fee_payer); unset($params->account_id); $params->checkout_id = $this->checkout_id; $params->amount = $this->amount; $params->refund_reason = 'Refund issued by admin'; return $params; } public function getUniqId() { return $this->res['checkout_id'] . '-refund'; } public function processValidated() { $this->invoice->addRefund($this, $this->checkout_id, $this->amount); } } plugins/paddle/paddle.php000064400000021457152101601040011417 0ustar00getProducts()) > 1) { return array( ___("This paysystem " . "can not work with multiple products in card")); } else { return parent::isNotAcceptableForInvoice($invoice); } } function _initSetupForm(\Am_Form_Setup $form) { $form->addText('vendor_id', array('size' => 10)) ->setLabel("Vendor ID") ->addRule('required'); $form->addTextarea( 'public_key', array( 'class' => 'el-wide textlocal', 'id' => 'comment', 'maxlength' => 900), array('label' => ___("Public Key\n" . "Please include -----BEGIN PUBLIC KEY-----"))) ->addRule('required'); } function createForm($actionName) { $url = $this->getReturnUrl(); return new Am_Form_CreditCard_Paddle($this, $url, $this->invoice); } function createTransaction( Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Paddle_Transaction($this, $request, $response, $invokeArgs); } function init() { $prod_id = new Am_CustomFieldText_Paddle( 'paddle_prod_id', ___('Product ID'), ___("Product ID at paddle.com"), 'number', array('size' => 8)); $key = new Am_CustomFieldText_SecretKey( 'checkout_key', ___('Secret Key'), ___("Checkout Secret Key at paddle.com"), null, array('size' => 8)); $this->getDi()->productTable->customFields()->add($prod_id); $this->getDi()->productTable->customFields()->add($key); } function isConfigured() { return $this->getConfig('vendor_id') && $this->getConfig('public_key'); } function getReadme() { return <<request->getPost('passthrough'); //return '8LRVY'; } function getUniqId() { return $this->request->getPost('p_order_id'); } function validateSource() { $fields = $this->request->getParams(); //$this->plugin->logRequest($params); // Your Paddle 'Public Key' $public_key = $this->plugin->getConfig('public_key'); // Get the p_signature parameter & base64 decode it. $signature = base64_decode($this->request->getPost('p_signature')); //$this->plugin->logRequest($this->request->getPost('p_signature')); //$this->plugin->logRequest($signature); // Get the fields sent in the request, and remove the p_signature parameter //$fields = $this->request->getParams(); unset($fields['p_signature']); $this->filterFields($fields); // ksort() and serialize the fields ksort($fields); foreach($fields as $k => $v) { if(!in_array(gettype($v), array('object', 'array'))) { $fields[$k] = "$v"; } } $data = serialize($fields); $this->plugin->logRequest($data); // Veirfy the signature return openssl_verify( $data, $signature, $public_key, OPENSSL_ALGO_SHA1); } private function filterFields(&$array) { $fields = array( 'p_product_id', 'p_price', 'p_country', 'p_currency', 'p_sale_gross', 'p_tax_amount', 'p_paddle_fee', 'p_coupon_savings', 'p_earnings', 'p_order_id', 'p_coupon', 'p_used_price_override', 'passthrough', 'p_quantity', 'quantity' ); foreach($array as $key => $value) { if (!in_array($key, $fields)) { unset($array[$key]); } } } function validateStatus() { return true; } function validateTerms() { return true; } } class Am_CustomFieldText_Paddle extends Am_CustomFieldText { function addToQF2( HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { $el = $container->addElement($this->qfType, $this->name, $attr, $data) ->setLabel( !empty($this->description) ? ___($this->title) . "\n" . ___($this->description) : ___($this->title)); if (!empty($this->size)) $el->setAttribute('size', $this->size); if (!empty($this->default)) $el->setValue($this->default); $this->addValidateFunction($el, $runAt); return $el; } } class Am_CustomFieldText_SecretKey extends Am_CustomFieldText_Paddle { function addToQF2( HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { $el = parent::addToQF2($container, $attr, $data, $runAt); $r = $el->addRule( 'regex', ___("5-10 lowercase alphanumeriv characters"), '/^[a-z0-9]{5,10}$/', $runAt); return $el; } } class Am_Mvc_Controller_CreditCard_Paddle extends Am_Mvc_Controller_CreditCard { function ccAction() { // invoice must be set to this point by the plugin if (!$this->invoice) throw new Am_Exception_InternalError('Empty invoice - internal error!'); $this->form = $this->createForm(); $this->getDi()->hook->call(Bootstrap_Cc::EVENT_CC_FORM, array('form' => $this->form)); if ($this->form->isSubmitted() && $this->form->validate()) { if ($this->processCc()) return; } $this->view->form = $this->form; $this->view->invoice = $this->invoice; $this->view->display_receipt = true; $this->view->layoutNoMenu = true; $helper = $this->view->headScript(); $helper->appendFile( "https://cdn.paddle.com/paddle/paddle.js"); $helper->appendFile( $this->getDi()->url("application/cc/plugins/paddle/scripts.js",null,false,true)); $this->view->display('cc/info.phtml'); } } class Am_Form_CreditCard_Paddle extends Am_Form_CreditCard { /** * @var Invoice */ private $invoice; private $url; public function __construct( Am_Paysystem_CreditCard $plugin, $url, Invoice $invoice) { $this->invoice = $invoice; $this->url = $url; parent::__construct($plugin); } public function init() { Am_Form::init(); $invoice = $this->invoice; $user = $invoice->getUser(); $this->addStatic('payment-errors') ->setContent(<< CUT ); $products = $this->invoice->getProducts(); /* @var $product Product */ $product = $products[0]; $product_id = $product->data()->get('paddle_prod_id'); if (!$product_id) { throw new Am_Exception( "No paddle_prod_id product " . "is specified for {$product->title}"); } $checkout_key = $product->data()->get('checkout_key'); if (!$checkout_key) { throw new Am_Exception( "No checkout_key " . "is specified for {$product->title}"); } $price = $invoice->first_total; $priceAuthentication = md5($price.$checkout_key); $buttons = $this->addGroup(); $buttons->setSeparator(' '); $buttons->addInputButton('input_button', array( 'value'=> $this->payButtons[ $this->formType ] )) ->addClass('paddle_button') ->setAttribute('data-product', $product_id) ->setAttribute('data-email', $user->email) ->setAttribute('data-passthrough', $invoice->public_id) ->setAttribute('data-url', $this->url) ->setAttribute('data-price', $price) ->setAttribute('data-auth', $priceAuthentication); $vedor_id = $this->plugin->getConfig('vendor_id'); $debug = Am_Paysystem_Paddle::DEBUG; $this->addScript() ->setScript(<<setAction($this->url); $this->plugin->onFormInit($this); } }plugins/paddle/scripts.js000064400000001352152101601040011472 0ustar00jQuery(function($) { Paddle.Setup({ vendor: vendor, debug: debug }); /*jQuery('input[name="input_button"]').click(function() { var prod_id = $(this).data('product'); var email = $(this).data('email'); var invoice = $(this).data('invoice'); var url = $(this).data('url'); var price = $(this).data('price'); var auth = $(this).data('auth'); $('#row-payment-errors-0').hide(); Paddle.Checkout.open({ product: prod_id, email: email, passthrough: invoice, success: url, price: price, auth: auth, closeCallback: function(data) { console.log(data); $('#payment-errors').html("Payment failed..."); $('#row-payment-errors-0').show(); $('#payment-errors').show(); } }); });*/ }); plugins/payforit/scripts/payment-payforit-iframe.phtml000064400000000252152101601040017355 0ustar00setLayout('layout.phtml'); ?> plugins/payforit/payforit.php000064400000026413152101601040012424 0ustar00_template = $template; $this->_path = $path; } public function process(Am_Mvc_Controller $action = null) { $action->view->addBasePath($this->_path); $action->view->assign($this->getVars()); $action->renderScript($this->_template); throw new Am_Exception_Redirect; } } class Am_Paysystem_Payforit extends Am_Paysystem_CreditCard { const PLUGIN_STATUS = self::STATUS_BETA; const PLUGIN_DATE = '$Date$'; const PLUGIN_REVISION = '5.0.6'; const URL = 'https://payforit.txtnation.com/api/'; const TRANSACTION_ID = 'payforit-reference-transaction'; public static $serverIPs = array( '67.23.27.65', '72.32.41.114', '72.32.41.115', '74.54.223.228', '74.54.223.230', '166.78.164.15', '174.143.237.218', '174.143.239.166', ); protected $defaultTitle = "Payforit: Pay with your Credit Card"; protected $defaultDescription = "accepts all major credit cards"; public function _initSetupForm(Am_Form_Setup $form) { $form->addText("company") ->setLabel("Your company name in payforit platform") ->addRule('required'); $form->addText("password", array('size' => 40)) ->setLabel("Your password in payforit platform") ->addRule('required'); $form->addSelect("window") ->setLabel("Type of Payforit Window\n" . "to render for the end user") ->loadOptions(array( 'small' => 'small', 'embed_small ' => 'embed_small ', 'large' => 'large', 'embed_large' => 'embed_large', )); $form->addAdvCheckbox("is_frame") ->setLabel("Use Iframe"); $form->addAdvCheckbox("debugLog") ->setLabel("Debug Log Enabled\n" . "write all requests/responses to log"); // hide reattempt $form->addScript()->setScript('jQuery(function(){jQuery("[id^=\'row-reattempt-\']").remove()});'); } public function init() { parent::init(); if($this->getConfig('is_frame')) { $script = <<getDi()->view->headScript()->appendScript($script); } } public function getRecurringType() { return self::REPORTS_REBILL; } public function storesCcInfo() { return false; } public function getSupportedCurrencies() { return array('GBP'); } public function isConfigured() { return strlen($this->getConfig('company')) && strlen($this->getConfig('password')); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { } private function parsePeriod($period) { preg_match('/(\d+)(\w)/', $period, $matches); @list($_, $num, $per) = $matches; switch ($per) { case 'd': $per = 'days'; break; case 'm': $per = 'months'; break; case 'y': $num *= 12; $per = 'months'; break; default: throw new Am_Exception_InternalError("Unknown period [$period]"); break; } return array( 'period' => $num, 'period_units' => $per, ); } public function _process(Invoice $invoice, Am_Mvc_Request $request, Am_Paysystem_Result $result) { $this->invoice = $invoice; $post = array( 'currency' => $this->invoice->currency, //'currency' => 'GBP', 'company' => $this->getConfig('company'), 'password' => $this->getConfig('password'), 'value' => $this->invoice->first_total, //'value' => 0.25, 'name' => $this->invoice->getLineDescription(), 'description' => $this->invoice->getLineDescription(), 'id' => $this->invoice->public_id, 'window' => $this->getConfig('window'), 'marketing' => 0, 'confirmation' => 0, 'callback_url' => $this->getPluginUrl('ipn'), 'success_url' => $this->getReturnUrl(), 'cancel_url' => $this->getCancelUrl(), ); if ($this->invoice->second_total > 0) // subscription charges { if ($this->invoice->first_total > 0 && $this->invoice->first_total != $this->invoice->second_total) throw new Am_Exception_InternalError('If product has no free trial first price must be the same second price'); if ($this->invoice->first_total > 0 && $this->invoice->first_period != $this->invoice->second_period) throw new Am_Exception_InternalError('If product has no free trial first period must be the same second period'); $post['sub_repeat'] = $this->invoice->rebill_times == IProduct::RECURRING_REBILLS ? 0 : $this->invoice->rebill_times; $period = $this->parsePeriod($this->invoice->second_period); $post['sub_period'] = $period['period']; $post['sub_period_units'] = $period['period_units']; if (!(float)$this->invoice->first_total) { $post['value'] = $this->invoice->second_total; $period = $this->parsePeriod($this->invoice->first_period); $post['sub_free_period'] = $period['period']; $post['sub_free_period_units'] = $period['period_units']; } } if ($this->getConfig('debugLog')) $this->getDi()->errorLogTable->log('Payforit. Request[cc]: ' . json_encode($post)); $req = new Am_HttpRequest(self::URL, Am_HttpRequest::METHOD_POST); $req->addPostParameter($post); $res = $req->send(); if ($res->getStatus() != '200') throw new Am_Exception_InternalError("Payforit API Error: bad status of server response [{$res->getStatus()}]"); if (!$res->getBody()) throw new Am_Exception_InternalError("Payforit API Error: server return null"); $this->logResponse($res->getBody()); if ($this->getConfig('debugLog')) $this->getDi()->errorLogTable->log('Payforit. Response[cc]: ' . $res->getBody()); $response = explode('|', $res->getBody()); if($response[0] != 'OK') { throw new Am_Exception_InternalError("Payforit API Error: {$response[1]}"); } $this->invoice->data()->set(self::TRANSACTION_ID, $response[1])->update(); if (!$this->getConfig('is_frame')) { header('Location: ' . $response[2]); return; } $a = new Am_Paysystem_Action_HtmlTemplate_Payforit($this->getDir(), 'payment-payforit-iframe.phtml'); $a->src = $response[2]; $result->setAction($a); } public function directAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { $log = $this->logRequest($request); switch ($request->getActionName()) { case 'ipn': // first and rebill payments or subscription cancelled if ($this->getConfig('debugLog')) Am_Di::getInstance()->errorLogTable->log('Payforit. Request[ipn]: ' . json_encode($request->getParams())); if ($request->getInt('stop')) // subscription cancelled { if (!in_array($request->getClientIp(), Am_Paysystem_Payforit::$serverIPs)) throw new Am_Exception_Paysystem_TransactionInvalid("Bad server IP [{$request->getClientIp()}]"); $invoice = Am_Di::getInstance()->invoiceTable->findFirstByPublicId($request->getFiltered('key')); $invoice->setCancelled(true); return; } if ($request->getFiltered('status') == 'EXPIRED') // user navigated away from the PFI window without cancelling { return header("HTTP/1.0 200 OK"); } $transaction = new Am_Paysystem_Transaction_Payforit($this, $request, $response, $invokeArgs); try { $transaction->process(); } catch (Exception $e) { $this->getDi()->errorLogTable->logException($e); return header("HTTP/1.0 400 Bad request"); } $this->invoice = $transaction->getInvoice(); $log->setInvoice($this->invoice)->update(); $response->setRedirect($this->getReturnUrl()); break; default: if ($this->getConfig('debugLog')) Am_Di::getInstance()->errorLogTable->log('Payforit. Request[default]: ' . json_encode($request->getParams())); return parent::directAction($request, $response, $invokeArgs); break; } } public function getReadme() { return <<NOTE 1: Refunds are not possible via plugin, payforit service can only process them manually. NOTE 2: Cancel subscription is a feature payforit service plans to add to the API. NOTE 3: If product has no free trial first price must be the same second price. NOTE 4: If product has no free trial first period must be the same second period. CUT; } } class Am_Paysystem_Transaction_Payforit extends Am_Paysystem_Transaction_Incoming_Thanks { public function findInvoiceId() { return $this->request->getFiltered('key'); } public function getUniqId() { return $this->request->getInt('transactionId') . '-' . Am_Di::getInstance()->security->randomString(4); } public function validateStatus() { return $this->request->getFiltered('billed') == 1 && $this->request->getFiltered('status') == 'OK'; } public function validateTerms() { $savesTrId = Am_Di::getInstance()->invoiceTable->findFirstByPublicId($this->findInvoiceId())->data()->get(Am_Paysystem_Payforit::TRANSACTION_ID); $gettedTrId = $this->request->getInt('transactionId'); if ($savesTrId != $gettedTrId) throw new Am_Exception_Paysystem_TransactionInvalid("Getted transactionId [$gettedTrId] does not match saved [$savesTrId]"); return true; } public function validateSource() { if (!in_array($this->request->getClientIp(), Am_Paysystem_Payforit::$serverIPs)) throw new Am_Exception_Paysystem_TransactionInvalid("Bad server IP [{$this->request->getClientIp()}]"); return true; } }plugins/quickpay.php000064400000021575152101601040010564 0ustar00 'Approved.', '001' => 'Rejected by acquirer. See field \'chstat\' and \'chstatmsg\' for further explanation.', '002' => 'Communication error.', '003' => 'Card expired.', '004' => 'Transaction is not allowed for transaction current state.', '005' => 'Authorization is expired.', '006' => 'Error reported by acquirer.', '007' => 'Error reported by QuickPay.', '008' => 'Error in request data.', '009' => 'Payment aborted by shopper.' ); //const WINDOW_URL = 'https://secure.quickpay.dk/form/'; const API_URL = 'https://secure.quickpay.dk/api/'; const SUBSCRIBE = 'subscribe_transaction_number'; public function getFormOptions() { $ret = parent::getFormOptions(); $ret = array_diff($ret,array(self::CC_ADDRESS)); return $ret; } function storesCcInfo(){ return false; } public function getSupportedCurrencies() { return array('DKK', 'EUR', 'SEK', 'NOK', 'USD'); } function isRefundable(InvoicePayment $payment) { return true; } function getErrorMessage($code) { $message = $this->errorMessages[$code]; return $message; } public function _initSetupForm(Am_Form_Setup $form) { $form->addText('merchant', array('size' => 20, 'maxlength' => 16)) ->setLabel("The QuickPayId") ->addRule('required'); $form->addText('secret', array('size' => 66, 'maxlength' => 80)) ->setLabel("Secret code") ->addRule('required'); $fl = $form->addFieldset()->setLabel(___('Quickpay parameters')); $fl->addSelect('lang', array(), array('options' => array( 'da' => 'Danish', 'de' => 'German', 'en' => 'English', 'es' => 'Spanish', 'fo' => 'Faeroese', 'fi' => 'Finnish', 'fr' => 'French', 'kl' => 'Greenlandish', 'it' => 'Italian', 'no' => 'Norwegian', 'nl' => 'Dutch', 'pl' => 'Polish', 'ru' => 'Russian', 'sv' => 'Swedish' )))->setLabel("The payment window language"); $form->addAdvCheckbox("testing")->setLabel("Test Mode Enabled"); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc=null, Am_Paysystem_Result $result) { if($doFirst) { //1 Step subscription from api $parsedResponse = $this->processSubscribe($invoice, $cc); if(!$this->checkResponse($parsedResponse, $result)) return; $invoice->data()->set(self::SUBSCRIBE, (string)$parsedResponse->transaction)->update(); //2 Step recurring from api $request = $this->processRecurring($invoice, $doFirst); $transaction = new Am_Paysystem_Transaction_QuickpayCapture($this, $invoice, $request, $doFirst); $transaction->run($result); } else { //2 Step recurring from api $request = $this->processRecurring($invoice, $doFirst); $transaction = new Am_Paysystem_Transaction_QuickpayCapture($this, $invoice, $request, $doFirst); $transaction->run($result); } } function checkResponse($parsedResponse, Am_Paysystem_Result $result) { if((string)$parsedResponse->qpstat != '000') { $result->setFailed("Payment failed: Description - " .$this->getErrorMessage((string)$parsedResponse->qpstat) ." QuickPay message - " .$parsedResponse->qpstatmsg); return false; } return true; } function processSubscribe(Invoice $invoice, CcRecord $cc = null) { $request = new Am_HttpRequest(self::API_URL, Am_HttpRequest::METHOD_POST); $post_params = new stdclass; $post_params->protocol = '6'; $post_params->msgtype = 'subscribe'; $post_params->merchant = $this->getConfig('merchant'); $post_params->ordernumber = $invoice->public_id."-".sprintf("%03d", $invoice->getPaymentsCount()); $post_params->cardnumber = $cc->cc_number; $post_params->expirationdate = $cc->cc_expire; $post_params->cvd = $cc->getCvv(); $post_params->description = "Subscribe from aMember"; if($this->getConfig('testing')) $post_params->testmode = $this->getConfig('testing'); foreach((array)$post_params as $k => $v) { $cstr .= $v; } $cstr .= $this->getConfig('secret'); $post_params->md5check = md5($cstr); $request->addPostParameter((array)$post_params); $response = $request->send(); $parsedResponse = simplexml_load_string($response->getBody()); return $parsedResponse; } function processRecurring(Invoice $invoice, $doFirst = true) { $request = new Am_HttpRequest(self::API_URL, Am_HttpRequest::METHOD_POST); $post_params = new stdclass; $post_params->protocol = '6'; $post_params->msgtype = 'recurring'; $post_params->merchant = $this->getConfig('merchant'); $post_params->ordernumber = $invoice->public_id."-".sprintf("%03d", $invoice->getPaymentsCount())."-R"; $post_params->amount = ($doFirst ? $invoice->first_total : $invoice->second_total) * 100; $post_params->currency = $invoice->currency; $post_params->autocapture = '1'; $post_params->transaction = $invoice->data()->get(self::SUBSCRIBE); foreach((array)$post_params as $k => $v) { $cstr .= $v; } $cstr .= $this->getConfig('secret'); $post_params->md5check = md5($cstr); $request->addPostParameter((array)$post_params); return $request; } function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $request = new Am_HttpRequest(self::API_URL, Am_HttpRequest::METHOD_POST); $post_params = new stdclass; $post_params->protocol = '6'; $post_params->msgtype = 'refund'; $post_params->merchant = $this->getConfig('merchant'); $post_params->amount = intval($amount * 100); $post_params->transaction = $payment->receipt_id; foreach((array)$post_params as $k => $v) { $cstr .= $v; } $cstr .= $this->getConfig('secret'); $post_params->md5check = md5($cstr); $request->addPostParameter((array)$post_params); $response = $request->send(); $parsedResponse = simplexml_load_string($response->getBody()); if((string)$parsedResponse->qpstat != '000') { $result->setFailed("Payment failed: Description - " .$this->getErrorMessage((string)$parsedResponse->qpstat) ." QuickPay message - " .$parsedResponse->qpstatmsg); } else { $trans = new Am_Paysystem_Transaction_Manual($this); $trans->setAmount($amount); $trans->setReceiptId($payment->receipt_id.'-quickpay-refund'); $result->setSuccess($trans); } } function getReadme() { return <<Quickpay configuration: CUT; } } class Am_Paysystem_Transaction_QuickpayCapture extends Am_Paysystem_Transaction_CreditCard { public function validate() { if((string)$this->parsedResponse->qpstat != '000') { $this->result->setFailed("Payment failed: Description - " .$this->getPlugin()->getErrorMessage((string)$this->parsedResponse->qpstat) ." QuickPay message - " .$this->parsedResponse->qpstatmsg); } else { $this->result->setSuccess($this); } } public function parseResponse() { $this->parsedResponse = simplexml_load_string($this->response->getBody()); } public function getUniqId() { return (string)$this->parsedResponse->transaction; } } plugins/epay.php000064400000027337152101601040007676 0ustar00language = $this->getConfig('language', 2); // English default for testing. $a->merchantnumber = $this->getConfig('id'); $a->orderid = $invoice->public_id; $a->currency = Am_Currency::getNumericCode($invoice->currency); $a->amount = $invoice->first_total * 100; $a->accepturl = $this->getReturnUrl(); $a->declineurl = $this->getCancelUrl(); $a->callbackurl = $this->getPluginUrl('ipn'); $a->instantcallback = 1; // Call callback before user returned to accept_url $a->instantcapture = 1; $a->ordertext = $invoice->getLineDescription(); $a->windowstate = 2; if ($invoice->rebill_times) { $a->subscription = 1; $a->subscriptionname = sprintf('Invoice %s, User %s', $invoice->public_id, $invoice->getLogin()); } $a->md5key = $this->getOutgoingMd5($a); $result->setAction($a); } function getSupportedCurrencies() { return array('EUR', 'USD', 'GBP', 'DKK', 'NOK', 'SEK'); } function renameElement(Am_Form $form, $from, $to) { foreach ($form->getElementsByName($from) as $el) $el->setName($to); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc=null, Am_Paysystem_Result $result) { $transaction = new Am_Paysystem_Transaction_EpaySale($this, $invoice, null, $doFirst); $transaction->run($result); } public function _initSetupForm(Am_Form_Setup $form) { $form->addText('id')->setLabel('MerchantNumber'); $form->addText('key')->setLabel('Security Key'); $form->addSelect('language', array(), array('options' => array( 2 => 'English', 1 => 'Danish', 3 => 'Swedish', 4 => 'Norwegian', 5 => 'Greenland', 6 => 'Iceland', 7 => 'German', 8 => 'Finnish', 9 => 'Spanish' )))->setLabel('Language'); } public function isConfigured() { return strlen($this->getConfig('id')) && strlen($this->getConfig('key')); } function getEpayError($epay_code, $pbs_code){ $result = $this->APIRequest("subscription", "getEpayError", array( 'merchantnumber' => $this->getConfig('id'), 'language' => $this->getConfig('language'), 'epayresponsecode' => $epay_code )); $result1 = $this->APIRequest("subscription", "getPbsError", array( 'merchantnumber' => $this->getConfig('id'), 'language' => $this->getConfig('language'), 'pbsResponseCode' => $pbs_code )); $xml = $this->getResponseXML($result); $xml1 = $this->getResponseXML($result1); return $xml->getEpayErrorResponse->epayResponseString."
".$xml1->getPbsErrorResponse->pbsResponseString; } /** * * @param String $response * @return SimpleXmlElement */ public function getResponseXML($response){ if(!$response) throw new Am_Exception_InternalError("Can't cancel subscription. Empty result received from epay server!"); // We do this to not deal with namespaces. $response = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response); $xml = simplexml_load_string($response); if($xml === false) throw new Am_Exception_InternalError("Can't parse XML!. Got response: $response"); return $xml->soapBody; } public function cancelInvoice(Invoice $invoice) { $subscriptionid = $invoice->data()->get(self::SUBSCRIPTIONID); if(!$subscriptionid) throw new Am_Exception_InternalError('Subscriptionid is empty in invoice! Nothing to cancel. '); $result = $this->APIRequest("subscription", "deletesubscription", array( 'merchantnumber' => $this->getConfig('id'), 'subscriptionid'=>$subscriptionid )); $xml = $this->getResponseXML($result); if($xml->deletesubscriptionResponse->deletesubscriptionResult != 'true') { throw new Am_Exception_InternalError("Subscription was not cancelled! Got: ".$xml->deletesubscriptionResponse->epayresponse); } // Cancelled; return ; } function createXML($type, $method, $vars){ $request = << CUT; $x = new SimpleXMLElement($request); $ns = $x->getNamespaces(); $m = $x->children($ns['soap'])->addChild($method,"", 'https://ssl.ditonlinebetalingssystem.dk/remote/'.$type); foreach($vars as $k=>$v){ $m->addChild($k, $v); } $xml = $x->asXML(); return $xml; } function APIRequest($type='subscription', $function='',$vars=array()){ try{ $client = new Am_HttpRequest(sprintf("https://ssl.ditonlinebetalingssystem.dk/remote/%s.asmx?op=%s", $type, $function), Am_HttpRequest::METHOD_POST); $client->setHeader('Content-type', 'text/xml'); $client->setHeader('SOAPAction', sprintf("https://ssl.ditonlinebetalingssystem.dk/remote/%s/%s", $type, $function)); $client->setBody($xml = $this->createXML($type, $function, $vars)); $response = $client->send(); }catch(Exception $e){ $this->getDi()->errorLogTable->logException($e); throw new Am_Exception_InputError("Unable to contact webservice. Got error: ".$e->getMessage()); } if(!$response->getBody()) throw new Am_Exception_InputError("Empty response received from API"); return $response->getBody(); } function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Transaction_Epay($this, $request, $response, $invokeArgs); } function getOutgoingMd5(Am_Paysystem_Action $a) { return md5($a->currency.$a->amount.$a->orderid.$this->getConfig('key')); } function getIncomingMd5(Am_Mvc_Request $r) { $key = md5($s = $r->get('amount').$r->get('orderid').$r->get('tid').$this->getConfig('key')); return $key; } function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $result = $this->APIRequest("payment", "credit", array( 'merchantnumber' => $this->getConfig('id'), 'transactionid'=> $payment->receipt_id, 'amount' => $amount*100 )); $xml = $this->getResponseXML($result); if($xml->creditResponse->creditResult == 'true'){ $trans = new Am_Paysystem_Transaction_Manual($this); $trans->setAmount($amount); $trans->setReceiptId($payment->receipt_id.'-epay-refund'); $result->setSuccess($trans); }else{ $result->setFailed(array('Error Processing Refund!')); } } function getReadme(){ return << "Payment system" -> "Go to settings for the "Payment system" Here you have 3 options for MD5 Security check 1: Don't want to use MD5 Disable the MD5 security feature 2: On accepturl only Only your accepturl will be using MD5 security check 3: On accepturl and by authorization Your accepturl and the authorization will be using MD5 security check Select option 2 or 3 then enter a MD5 security key in the "key" field CUT; } } class Am_Paysystem_Transaction_Epay extends Am_Paysystem_Transaction_Incoming{ public function getUniqId() { return $this->request->get('tid'); } public function findInvoiceId() { return $this->request->get('orderid'); } public function validateSource() { return $this->getPlugin()->getIncomingMd5($this->request) == $this->request->get('eKey'); } public function validateStatus() { return true; } public function validateTerms() { return $amount == ($this->first_total *100); } public function processValidated() { $this->invoice->addPayment($this); if($this->request->get('subscriptionid')){ $this->invoice->data()->set(Am_Paysystem_Epay::SUBSCRIPTIONID, $this->request->get('subscriptionid'))->update(); } } } class Am_Paysystem_Transaction_EpaySale extends Am_Paysystem_Transaction_CreditCard { protected $ret; public function run(Am_Paysystem_Result $result) { $subscriptionid = $this->invoice->data()->get(Am_Paysystem_Epay::SUBSCRIPTIONID); $req = $this->plugin->APIRequest('subscription', 'authorize', $vars= array( 'merchantnumber' => $this->plugin->getConfig('id'), 'subscriptionid' => $subscriptionid, 'orderid' => $this->invoice->public_id."-".$this->invoice->getPaymentsCount(), 'amount' => $this->invoice->second_total*100, 'currency' => Am_Currency::getNumericCode($this->invoice->currency), 'instantcapture' => 1, 'description' => 'Recurring payment for invoice '.$this->invoice->public_id, 'email' => $this->invoice->getEmail(), 'ipaddress' => $this->invoice->getUser()->remote_addr )); $log = $this->getInvoiceLog(); $log->add(print_r($vars, true)); $this->ret = $this->plugin->getResponseXML($req); $log->add(print_r($this->ret, true)); if($this->ret->authorizeResponse->authorizeResult != 'true') { $result->setFailed(___("Payment failed"). ":" . $this->plugin->getEpayError($this->ret->authorizeResponse->epayresponse)); } else { $result->setSuccess($this); $this->processValidated(); } } public function getUniqId() { return $this->ret->authorizeResponse->transactionid; } public function parseResponse() { } public function validate() { $this->result->setSuccess($this); } public function processValidated() { $this->invoice->addPayment($this); } }plugins/eway.php000064400000007402152101601040007674 0ustar00getConfig('customer_id')); } public function _initSetupForm(Am_Form_Setup $form) { $form->addText("customer_id")->setLabel("eWAY customer ID\n" . 'Your unique eWAY customer ID assigned to you when you join eWAY. eg 87654321'); $form->addAdvCheckbox("testing")->setLabel("Test Mode Enabled"); } public function getGateway() { return $this->getConfig('testing') ? self::GATEWAY_URL_TEST : self::GATEWAY_URL; } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { if ($doFirst && !(float)$invoice->first_total) { // free trial $tr = new Am_Paysystem_Transaction_Free($this); $tr->setInvoice($invoice); $tr->process(); $result->setSuccess($tr); } else { $xml = new SimpleXMLElement(''); $xml->ewayCustomerID = $this->getConfig('customer_id'); $xml->ewayTotalAmount = $doFirst ? ($invoice->first_total * 100) : ($invoice->second_total * 100); $xml->ewayCustomerFirstName = $cc->cc_name_f; $xml->ewayCustomerLastName = $cc->cc_name_l; $xml->ewayCustomerEmail = $invoice->getUser()->email; $xml->ewayCustomerAddress = $cc->cc_street; $xml->ewayCustomerPostcode = $cc->cc_zip; $xml->ewayCustomerInvoiceDescription = $invoice->getLineDescription(); $xml->ewayCustomerInvoiceRef = $invoice->public_id; $xml->ewayCardHoldersName = sprintf('%s %s', $cc->cc_name_f, $cc->cc_name_l); $xml->ewayCardNumber = $cc->cc_number; $xml->ewayCardExpiryMonth = $cc->getExpire('%1$02d'); $xml->ewayCardExpiryYear = $cc->getExpire('%2$02d'); $xml->ewayTrxnNumber = $invoice->public_id; $xml->ewayOption1 = ''; $xml->ewayOption2 = ''; $xml->ewayOption3 = ''; $xml->ewayCVN = $cc->getCvv(); $request = new Am_HttpRequest($this->getGateway(), Am_HttpRequest::METHOD_POST); $request->setBody($xml->asXML()); $request->setHeader('Content-type', 'text/xml'); $tr = new Am_Paysystem_Transaction_CreditCard_Eway($this, $invoice, $request, $doFirst); $tr->run($result); } } } class Am_Paysystem_Transaction_CreditCard_Eway extends Am_Paysystem_Transaction_CreditCard { public function validate() { $xml = $this->vars; if ($xml->ewayTrxnStatus != 'True') { $this->result->setFailed($xml->ewayTrxnError); return; } $this->result->setSuccess(); } public function parseResponse() { $this->vars = new SimpleXMLElement($this->response->getBody()); return $this->vars; } public function getUniqId() { return $this->vars->ewayTrxnNumber; } }plugins/micropayment-dbt/micropayment-dbt.php000064400000036622152101601040015471 0ustar00getConfig('key')); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { $user = $invoice->getUser(); if ($cc->user_id != $user->pk()) throw new Am_Exception_Paysystem("Assertion failed: cc.user_id != user.user_id"); // will be stored only if cc# or expiration changed $this->storeCreditCard($cc, $result); if (!$result->isSuccess()) return; $user->refresh(); // we have both profile id and payment id, run the necessary transaction now if amount > 0 $result->reset(); if ($doFirst && (doubleval($invoice->first_total) <= 0)) { // free trial $tr = new Am_Paysystem_Transaction_Free($this); $tr->setInvoice($invoice); $tr->process(); $result->setSuccess($tr); } else { $request = array( $this->getConfig('key'), $this->getConfig('testing'), $user->data()->get(self::CUSTOMER_VAULT_ID), null, $this->getConfig('project'), '', '', '', ($doFirst ? intval($invoice->first_total*100) : intval($invoice->second_total*100)), $invoice->currency, $invoice->getLineDescription(), $invoice->getLineDescription(), $user->remote_addr ? $user->remote_addr : $_SERVER['REMOTE_ADDR'] ); $tr = new Am_Paysystem_Transaction_MicropaymentDbtSale($this, $invoice, $request, $doFirst); $tr->run($result); } } public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $transaction = new Am_Paysystem_Transaction_MicropaymentDbtRefund($this, $invoice, $request, $payment->transaction_id); $transaction->run($result); } public function _initSetupForm(Am_Form_Setup $form) { $form->addText("key")->setLabel("Access Key\n" . 'You\'ll find your AccessKey in ' . 'ControlCenter --> My Configuration'); $form->addText("project")->setLabel('Project Identifier'); $form->addAdvCheckbox("testing")->setLabel("Test Mode Enabled"); } public function storeCreditCard(CcRecord $cc, Am_Paysystem_Result $result) { $user = $this->getDi()->userTable->load($cc->user_id); $profileId = $user->data()->get(self::CUSTOMER_VAULT_ID); if ($this->invoice) { // to link log records with current invoice $invoice = $this->invoice; } else { // updating debit 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 ($profileId) { if($cc->cc_number != '0000000000000000') { $storedCc = $this->getDi()->ccRecordTable->findFirstByUserId($user->pk()); if ($storedCc && (($storedCc->cc != $cc->maskCc($cc->cc_number)))) $update = true; } else { $result->setSuccess(); return; } } if (!$profileId) { try { $res = self::$dispatcher->customerCreate( $this->getConfig('key'), $this->getConfig('testing'), null, null); if($res) $profileId = $res['customerId']; else return; $user->data()->set(self::CUSTOMER_VAULT_ID, $profileId)->update(); } catch (Exception $e) { $result->setFailed($e->getMessage()); return false; } } try { $res = self::$dispatcher->addressSet( $this->getConfig('key'), $this->getConfig('testing'), $profileId, $cc->cc_name_f, $cc->cc_name_l, $cc->cc_street, $cc->cc_zip, $cc->cc_city, $cc->cc_country); $res = self::$dispatcher->bankaccountSet( $this->getConfig('key'), $this->getConfig('testing'), $profileId, $cc->cc_country,// $cc->cc_company, $cc->cc_number, $cc->cc_name_f.' '.$cc->cc_name_l); } catch(Exception $e) { $result->setFailed($e->getMessage()); return false; } /// $cc->cc = $cc->maskCc(@$cc->cc_number); $cc->cc_number = '0000000000000000'; if ($cc->pk()) $cc->update(); else $cc->replace(); $result->setSuccess(); } public function getDispatcher() { return self::$dispatcher; } public function getReadme() { return <<data()->get(self::CUSTOMER_VAULT_ID)) return $this->getPluginUrl('update'); } } class Am_Paysystem_Transaction_MicropaymentDbtSale extends Am_Paysystem_Transaction_CreditCard { protected $response; protected $sessionData; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $request, $doFirst = true) { parent::__construct($plugin, $invoice, $request, $doFirst); } public function run(Am_Paysystem_Result $result) { $this->result = $result; $log = $this->getInvoiceLog(); $log->add($this->request); try { $this->sessionData = call_user_func_array(array($this->getPlugin()->getDispatcher(),'sessionCreate'),$this->request); $this->response = $this->getPlugin()->getDispatcher()->sessionApprove( $this->getPlugin()->getConfig('key'), $this->getPlugin()->getConfig('testing'), $this->sessionData['sessionId']); $log->add($this->response); if ($this->response['status'] == 'APPROVED'){ $this->processValidated(); $result->setSuccess(); } } catch (Exception $e) { $result->setFailed($e->getMessage()); } } public function getUniqId() { return $this->sessionData['sessionId']; } public function parseResponse() { } } class Am_Mvc_Controller_CreditCard_MicropaymentDbt extends Am_Mvc_Controller_CreditCard { public function createUpdateForm() { $form = new Am_Form_MicropaymentDbt($this->plugin, Am_Form_CreditCard::USER_UPDATE); $user = $this->getDi()->auth->getUser(true); if (!$user) throw new Am_Exception_InputError("You are not logged-in"); $cc = $this->getDi()->ccRecordTable->findFirstByUserId($user->user_id); if (!$cc) $this->getDi()->ccRecordRecord; $arr = $cc->toArray(); unset($arr['cc_number']); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($arr) )); return $form; } } class Am_Form_MicropaymentDbt extends Am_Form { const PAYFORM = 'payform'; const USER_UPDATE = 'user-update'; const ADMIN_UPDATE = 'admin-update'; const ADMIN_INSERT = 'admin-insert'; protected $payButtons = array(); /** @var Am_Paysystem_CreditCard */ protected $plugin; protected $formType = self::PAYFORM; public function __construct(Am_Paysystem_CreditCard $plugin, $formType = self::PAYFORM) { $this->plugin = $plugin; $this->formType = $formType; $this->payButtons = array( self::PAYFORM => ___('Subscribe And Pay'), self::ADMIN_UPDATE => ___('Update Debit Card Info'), self::USER_UPDATE => ___('Update Debit Card Info'), self::ADMIN_INSERT => ___('Update Debit Card Info'), ); parent::__construct('cc'); } public function init() { parent::init(); $name = $this->addGroup() ->setLabel(___("Cardholder Name\n" . 'cardholder first and last name, exactly as on the card')); $name->addRule('required', ___('Please enter debit card holder name')); $name_f = $name->addText('cc_name_f', array('size'=>15)); $name_f->addRule('required', ___('Please enter debit card holder first name'))->addRule('regex', ___('Please enter debit card holder first name'), '|^[a-zA-Z_\' -]+$|'); $name_l = $name->addText('cc_name_l', array('size'=>15)); $name_l->addRule('required', ___('Please enter debit card holder last name'))->addRule('regex', ___('Please enter debit card holder last name'), '|^[a-zA-Z_\' -]+$|'); $options = $this->plugin->getFormOptions(); $company = $this->addText('cc_company') ->setLabel(___('Bank Code')); if ($this->formType == self::ADMIN_UPDATE) { $group = $this->addGroup()->setLabel(___('Debit Card Number'), ___('for example: 1111-2222-3333-4444')); $group->addStatic('cc'); $cc = $group->addText('cc_number', array('autocomplete'=>'off', 'size'=>22, 'maxlength'=>22, 'style'=>'display:none')); $cc->addRule('regex', ___('Invalid Debit Card Number'), '/^[0-9 -]+$/'); $group->addScript("")->setScript(<<addText('cc_number', array('autocomplete'=>'off', 'size'=>22, 'maxlength'=>22)) ->setLabel(___('Debit Card Number'), ___('for example: 1111-2222-3333-4444')); $cc->addRule('required', ___('Please enter Debit Card Number')) ->addRule('regex', ___('Invalid Debit Card Number'), '/^[0-9 -]+$/'); } $fieldSet = $this->addFieldset(___('Address Info')) ->setLabel(___("Address Info\n" . '(must match your debit card statement delivery address)')); $street = $fieldSet->addText('cc_street')->setLabel(___('Street Address')) ->addRule('required', ___('Please enter Street Address')); $city = $fieldSet->addText('cc_city')->setLabel(___('City')) ->addRule('required', ___('Please enter City')); $zip = $fieldSet->addText('cc_zip')->setLabel(___('ZIP')) ->addRule('required', ___('Please enter ZIP code')); $country = $fieldSet->addSelect('cc_country')->setLabel(___('Country')) ->setId('f_cc_country') ->loadOptions(Am_Di::getInstance()->countryTable->getOptions(true)); $country->addRule('required', ___('Please enter Country')); $group = $fieldSet->addGroup()->setLabel(___('State')); $group->addRule('required', ___('Please enter State')); /** @todo load correct states */ $stateSelect = $group->addSelect('cc_state') ->setId('f_cc_state') ->loadOptions($stateOptions = Am_Di::getInstance()->stateTable->getOptions(@$_REQUEST['cc_country'], true)); $stateText = $group->addText('cc_state')->setId('t_cc_state'); $disableObj = $stateOptions ? $stateText : $stateSelect; $disableObj->setAttribute('disabled', 'disabled')->setAttribute('style', 'display: none'); // if free trial set _TPL_CC_INFO_SUBMIT_BUT2 $buttons = $this->addGroup(); $buttons->addSubmit('_cc_', array('value'=> ' ' . $this->payButtons[ $this->formType ] .' ')); if ($this->formType == self::USER_UPDATE) { $buttons->addInputButton('_cc_', array('value'=> ' ' . ___("Back") .' ', 'onclick'=>'goBackToMember()')); $this->addScript("")->setScript("function goBackToMember(){ window.location = amUrl('/member'); }"); } //$this->plugin->onFormInit($this); } /** * Return array of default values based on $user record * @param User $user */ public function getDefaultValues(User $user){ return array( 'cc_name_f' => $user->name_f, 'cc_name_l' => $user->name_l, 'cc_street' => $user->street, 'cc_street2' => $user->street2, 'cc_city' => $user->city, 'cc_state' => $user->state, 'cc_country' => $user->country, 'cc_zip' => $user->zip, 'cc_phone' => $user->phone, ); } public function validate() { return parent::validate();// && $this->plugin->onFormValidate($this); } public function getValue() { $ret = parent::getValue(); array_walk_recursive($ret, function(&$v, $k) {$v=trim($v);}); if (!empty($ret['cc_number'])) $ret['cc_number'] = preg_replace('/\D/', '', $ret['cc_number']); return $ret; } public function toCcRecord(CcRecord $cc){ $values = $this->getValue(); foreach ($values as $k=>$v) if (is_array($v) && !empty($v['m'])) $values[$k] = sprintf('%02d%02d', $v['m'], substr($v['y'], -2)); unset($values['_cc_bin_name']); unset($values['_cc_bin_phone']); unset($values['a']); unset($values['id']); if( !empty($values['cc_code'])) $cc->setCvv($values['cc_code']); unset($values['cc_code']); unset($values['action']); $cc->setForInsert($values); } } plugins/micropayment-dbt/lib/clients/TSimpleHttpClientException.txt000064400000000466152101601040021702 0ustar00streamopen_failed = An Error occured during connect to "{2}:{3}" #{0} "{1}" streamopen_ssl_not_supported = SSL currently not supported streamwrite_failed = An Error occured during writing data to stream "{1}:{2}{3}" {0} streamread_failed = An Error occured during reading data from stream "{1}:{2}{3}"plugins/micropayment-dbt/lib/clients/TSimpleHttpClient.php000064400000024351152101601040017772 0ustar00 _headers; } /** * @param array $value */ public function setHeaders($value) { if( is_array($value) ) { $this -> _headers = $value; } } /** * @param string $name * @param string $value * @param boolean $replace */ public function addHeader($name, $value, $replace=true) { if($replace OR !isset($this -> _headers[$name])) $this -> _headers[$name] = $value; } /** * @param string $name */ public function removeHeader($name) { if(isset($this -> _headers[$name])) unset($this -> _headers[$name]); } /** * @param string $name * @return boolean */ public function isHeader($name) { array_key_exists($name, $this -> _headers); } /** * @param string $name * @return string */ public function getHeader($name) { if(isset($this -> _headers[$name])) return $this -> _headers[$name]; } /** * @param string $value */ public function setBody($value) { $this -> _body = (string)$value; } /** * @return string $value */ public function getBody() { return $this -> _body; } } class TSimpleHttpResponse extends TSimpleHttpBase { /** * @var integer */ private $_statusCode = 0; /** * @var string */ private $_statusMessage = ''; /** * @param integer $code * @param string $message * @param string $body * @param array $headers */ public function __construct($code=0, $message='', $body='', $headers=null) { $this -> setStatusCode($code); $this -> setStatusMessage($message); $this -> setBody($body); $this -> setHeaders($headers); } /** * @param integer $value */ protected function setStatusCode($value) { $this -> _statusCode = (integer)$value; } /** * @return integer */ public function getStatusCode() { return $this -> _statusCode; } /** * @param string $value */ protected function setStatusMessage($value) { $this -> _statusMessage = (string)$value; } /** * @return string */ public function getStatusMessage() { return $this -> _statusMessage; } public function __toString() { return $this -> getBody(); } /** * @param string $data * @return TSimpleHttpResponse */ public static function parse(&$data) { $tmp = preg_split('(\r\n\r\n|\r\r|\n\n)', $data); $code = 0; $message = ''; $body = $data; $headers = array(); $tmpHeader = trim($tmp[0]); if( preg_match('/^HTTP\/\d.\d/i', $tmpHeader) ) { $body = preg_replace('/' . preg_quote($tmpHeader, '/') . '/', '', $body); $body = preg_replace('/^(\r\n\r\n|\r\r|\n\n)/', '', $body); } else { $tmpHeader = ''; } if($tmpHeader) { $proto = false; $tmp = explode("\n", $tmpHeader); foreach($tmp as $row) { if(!$proto) { if( $proto = preg_match('/^HTTP\/\d.\d (\d{3,}) (.*)/i', $row, $matches) ) { $code = $matches[1]; $message = $matches[2]; continue; } } list($k, $v) = preg_split('(: )', $row); $headers[trim($k)] = trim($v); } } // UTF-8 Hack laut Guido if(substr($body, 0, 3) === '') { $body = substr($body, 3); } return new TSimpleHttpResponse($code, $message, $body, $headers); } } class TSimpleHttpClient extends TSimpleHttpBase { const SSL_PORT = 443; const POST = 'POST'; const GET = 'GET'; const AUTHORIZATION_BASIC = 'Basic'; private $_requestMethod = self::POST; /** * @var string */ private $_host = ''; /** * @var integer */ private $_port = 80; /** * @var string */ private $_path = '/'; /** * @var string */ private $_query = ''; /** * @var string */ private $_user = ''; /** * @var string */ private $_pass = ''; /** * @var string */ private $_auth = self::AUTHORIZATION_BASIC; /** * @var resource */ private $fp = null; /** * @param string $method * @param string $uri */ public function __construct($method=TSimpleHttpClient::POST, $uri='') { $this -> setRequestMethod($method); $this -> parseUri($uri); } /** * @return TSimpleHttpResponse * @throws TSimpleHttpClientException */ public function request() { if( $this -> getBody() ) $this -> setRequestMethod(self::POST); $result = ''; switch($this -> getAuthorization() ) { case self::AUTHORIZATION_BASIC: if( $this -> getUser() )$this -> addHeader('Authorization', 'Basic ' . base64_encode($this -> getUser() . ':' . $this -> getPass()) ); break; } // modifications for GET requests do { if($this -> isPost()) break; if($this -> isHeader('Content-type')) $this -> removeHeader('Content-type'); if($this -> isHeader('Content-length')) $this -> removeHeader('Content-length'); } while(0); // modifications for POST requests do { if(!$this -> isPost()) break; if( !$this -> isHeader('Content-length') ) $this -> addHeader('Content-length', (integer)strlen($this -> getBody()) ); } while(0); $query = $this -> getQuery(); if($query) { $path = $this -> getPath(); $path .= '?' .$query; $this -> setPath($path); } $this -> streamOpen(); $this -> streamWrite( sPrintF("%s %s HTTP/1.0\n", $this -> getRequestMethod(), $this -> getPath()) ); $this -> streamWrite( sPrintF("Host: %s \n", $this -> getHost()) ); foreach( $this -> getHeaders() as $hName => $hValue) $this -> streamWrite( sPrintF("%s: %s\n", $hName, $hValue) ); $this -> streamWrite("Connection: close\n\n"); if($this -> isPost() ) $this -> streamWrite( $this -> getBody() . "\n"); while(!$this -> streamEOF() ) { $result .= $this -> streamRead(512); } $this -> streamClose(); return TSimpleHttpResponse::parse($result); } private function streamOpen() { $errno = 0; $errstr = ''; $host = $this -> isSSL() ? 'ssl://' : ''; $host .= $this -> getHost(); $this -> fp = @fSockOpen($host, $this -> getPort(), $errno, $errstr, 30); if(!$this -> fp) throw new TSimpleHttpClientException('streamopen_failed', $errno, $errstr, $this -> getHost(), $this -> getPort()); } private function streamWrite($data) { $result = fWrite($this -> fp, $data); if($result === false) throw new TSimpleHttpClientException('streamwrite_failed', $data, $this -> getHost(), $this -> getPort(), $this -> getPath()); return $result; } private function streamRead($length) { $result = fRead($this -> fp, $length); if($result === false) throw new TSimpleHttpClientException('streamread_failed', $this -> getHost(), $this -> getPort(), $this -> getPath()); return $result; } private function streamEOF() { return fEoF($this -> fp); } private function streamClose() { if($this -> fp) @fClose($this -> fp); } /** * @param string $uri * @return boolean */ public function parseUri($uri) { if( empty($uri) ) return false; $uri = parse_url( $uri); if( isset($uri['port'])) $this -> setPort($uri['port']); if( isset($uri['scheme']) AND $uri['scheme'] == 'https') $this -> setPort(self::SSL_PORT); if( isset($uri['host'])) $this -> setHost($uri['host']); if( isset($uri['path'])) $this -> setPath($uri['path']); if( isset($uri['query'])) $this -> setQuery($uri['query']); if( isset($uri['user'])) $this -> setUser($uri['user']); if( isset($uri['pass'])) $this -> setPass($uri['pass']); if( isset($uri['user']) ) $this -> setAuthorization(self::AUTHORIZATION_BASIC); } /** * @param string $value */ public function setAuthorization($value) { $this -> _auth = $value; } /** * @return string */ public function getAuthorization() { return $this -> _auth; } /** * @param string $value */ public function setRequestMethod($value) { $this -> _requestMethod = $value; } /** * @return string */ public function getRequestMethod() { return $this -> _requestMethod; } /** * @param string $value */ public function setHost($value) { $this -> _host = (string)$value; } /** * @return string */ public function getHost() { return $this -> _host; } /** * @param integer $value */ public function setPort($value) { $this -> _port = (integer)$value; } /** * @return integer */ public function getPort() { return $this -> _port; } /** * @param string $value */ public function setPath($value) { $this -> _path = (string)$value; } /** * @return string */ public function getPath() { return $this -> _path; } /** * @param string $value */ public function setUser($value) { $this -> _user = (string)$value; $this -> setAuthorization(self::AUTHORIZATION_BASIC); } /** * @return string */ public function getUser() { return $this -> _user; } /** * @param string $value */ public function setPass($value) { $this -> _pass = (string)$value; } /** * @return string */ public function getPass() { return $this -> _pass; } /** * @param mixed $value * @param string only if $value is array or object */ public function setQuery($value, $argSeparator='&') { if( is_scalar($value) ) { $this -> _query = (string)$value; } else { $this -> _query = http_build_query($value, '', $argSeparator); } } /** * @param boolean $map * @return mixed */ public function getQuery($map=false) { $result = $this -> _query; if($map) parse_str($this -> _query, $result); return $result; } /** * * @return boolean */ public function isSSL() { return ($this -> _port == self::SSL_PORT); } /** * @return boolean */ public function isPost() { return ($this -> getRequestMethod() == self::POST); } } ?>plugins/micropayment-dbt/lib/common/messages.txt000064400000000000152101601040016065 0ustar00plugins/micropayment-dbt/lib/common/TException.php000064400000011507152101601040016326 0ustar00 _errorCode = $errorCode; $errorMessage = $this -> translateErrorMessage($errorCode); $args = func_get_args(); array_shift($args); parent::__construct( $this -> replaceMessageToken($errorMessage, $args), (is_numeric($errorCode) ? $errorCode : null) ); } /** * @param string $message * @param array $args * @return string */ protected function replaceMessageToken($message, $args) { $n = count($args); $tokens = array(); for($i=0; $i<$n; ++$i) $tokens['{' . $i . '}'] = (string)$args[$i]; return strTr($message, $tokens); } /** * Translates an error code into an error message. * @param string $key error code that is passed in the exception constructor. * @return string the translated error message */ protected function translateErrorMessage($key) { $msgFile = $this -> getErrorMessageFile(); if( ($entries = @file($msgFile) ) === false ) return $key; else { foreach($entries as $entry) { $tmp = explode('=', $entry, 2); $code = isset($tmp[0]) ? $tmp[0] : ''; $message = isset($tmp[1]) ? $tmp[1] : ''; if( trim($code) == $key ) return trim($message); } return $key; } } /** * @return string path to the error message file */ protected function getErrorMessageFile() { return dirname(__FILE__) . '/messages.txt'; } /** * @return string error code */ public function getErrorCode() { return $this -> _errorCode; } /** * @param string $code error code */ public function setErrorCode($code) { $this -> _errorCode = $code; } /** * @return string error message */ public function getErrorMessage() { return $this -> getMessage(); } /** * @param string $message error message */ protected function setErrorMessage($message) { $this -> message = $message; } } class TSystemException extends TException {} class TPhpErrorException extends TSystemException { /** * Constructor. * @param integer $errno error number * @param string $errstr error string * @param string $errfile error file * @param integer $errline error line number */ public function __construct($errno, $errstr, $errfile, $errline) { static $errorTypes = array( E_ERROR => 'Error', E_WARNING => 'Warning', E_PARSE => 'Parsing Error', E_NOTICE => 'Notice', E_CORE_ERROR => 'Core Error', E_CORE_WARNING => 'Core Warning', E_COMPILE_ERROR => 'Compile Error', E_COMPILE_WARNING => 'Compile Warning', E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', E_STRICT => 'Runtime Notice' ); $errorType = isset($errorTypes[$errno]) ? $errorTypes[$errno] : 'Unknown Error'; parent::__construct( sPrintF('[%s] %s (@line %d in file %s)', $errorType, $errstr, $errline, $errfile) ); } } /** * THttpException class * * THttpException represents an exception that is caused by invalid operations * of end-users. The {@link getStatusCode StatusCode} gives the type of HTTP error. */ class THttpException extends TSystemException { private $_statusCode; /** * Constructor. * @param integer $statusCode HTTP status code, such as 404, 500, etc. * @param string $errorCode error message. This can be a string that is listed * in the message file. If so, the message in the preferred language * will be used as the error message. Any rest parameters will be used * to replace placeholders ({0}, {1}, {2}, etc.) in the message. */ public function __construct($statusCode, $errorCode) { $this -> setStatusCode($statusCode); $this -> setErrorCode($errorCode); $errorMessage = $this -> translateErrorMessage($errorCode); $args = func_get_args(); array_shift($args); array_shift($args); $this -> setErrorMessage( $this -> replaceMessageToken($errorMessage, $args) ); } /** * @return integer HTTP status code, such as 404, 500, etc. */ public function getStatusCode() { return $this -> _statusCode; } public function setStatusCode($value) { $this -> _statusCode = (integer)$value; $this -> code = (integer)$value; } } ?>plugins/micropayment-dbt/lib/common/TServiceProtocol.php000064400000001532152101601040017507 0ustar00plugins/micropayment-dbt/lib/common/ServiceReflection.php000064400000033000152101601040017647 0ustar00 _className = $className; $this -> reflectService($options); } /** * @return ClassServiceReflection */ public function getServiceClassReflection() { return $this -> _serviceClassReflection; } /** * @return string */ public function getClass() { return $this -> _className; } /** * @param integer $options */ protected function reflectService($options=0) { $this -> _serviceClassReflection = ClassServiceReflection::createReflection(new ReflectionClass( $this -> getClass() ) ); } } abstract class BaseServiceReflection { /** * @var string */ private $_type = ''; /** * @var string */ private $_description = ''; /** * @param string $type * @param string $description */ public function __construct($type='', $description='') { $this -> setType($type); $this -> setDescription($description); } /** * @param string $value */ public function setType($value) { $this -> _type = (string)$value; } /** * @return string */ public function getType() { return $this -> _type; } /** * @param string $value */ public function setDescription($value) { $this -> _description = (string)$value; } /** * @return string */ public function getDescription() { return $this -> _description; } protected static function removeCommentElements($value) { $value = preg_replace('/(^[\\s]*\\/\\*\\*)|(^[\\s]\\*\\/)|(^[\\s]*\\*?\\s)|(^[\\s]*)|(^[\\t]*)/ixm', '', $value); $value = preg_replace('/(\*\/)$/i', '', $value); $value = trim($value); return $value; } protected static function prepareDocComment($value) { $value = self::removeCommentElements($value); $value = str_replace("\r", '', $value); $value = preg_replace("/([\\t])+/", "\t", $value); return explode("\n", $value); } /** * @param string $value * @return string */ protected static function getDescriptionBlock($value) { $commentLines = self::prepareDocComment($value); $result = ''; foreach($commentLines as $line) { if($line == '') continue; if($line{0} == '@') continue; $result .= $line; $result .= "\n"; } $result = trim($result); return $result; } } class NamedServiceReflectionBase extends BaseServiceReflection { /** * @var string */ private $_name = null; /** * @param string $name * @param string $type * @param string $description */ public function __construct($name='', $type='', $description='') { parent::__construct($type, $description); $this -> setName($name); } /** * @param string $value */ public function setName($value) { $this -> _name = (string)$value; } /** * @return string */ public function getName() { return $this -> _name; } } class ClassServiceReflection extends BaseServiceReflection { /** * @var MethodServiceReflection[] */ private $_methods = array(); /** * @param MethodServiceReflection[] $value */ public function setMethods($value) { if( !is_array($value) ) return; } /** * @return MethodServiceReflection[] */ public function getMethods() { return $this -> _methods; } /** * @param string $name * @return MethodServiceReflection */ public function getMethod($name) { if( !$this -> hasMethod($name) ) return null; return $this -> _methods[$name]; } /** * @param string $name * @return boolean */ public function hasMethod($name) { return isset($this -> _methods[$name]); } /** * @param MethodServiceReflection $value */ public function addMethod(MethodServiceReflection $value) { $this -> _methods[$value -> getName()] = $value; } static public function createReflection(ReflectionClass $reflection) { $result = new ClassServiceReflection($reflection -> getName(), self::getDescriptionBlock($reflection -> getDocComment())); $methods = $reflection -> getMethods(); foreach($methods as $method) { if( !$method -> isPublic()) continue; $result -> addMethod( MethodServiceReflection::createReflection($method) ); } return $result; } public function getTypes() { $result = array(); foreach($this -> getMethods() as $item) { $result = array_merge($result, $item -> getTypes()); } return $result; } } class MethodServiceReflection extends NamedServiceReflectionBase { /** * @var ParameterServiceReflection[] */ private $_parameters = array(); /** * @var ReturnServiceReflection */ private $_return = null; /** * @param string $name * @param string $type * @param string $description * @param ParameterServiceReflection[] $parameters * @param ReturnServiceReflection $return */ public function __construct($name='', $type='', $description='', $parameters=null, $return=null) { parent::__construct($name, $type, $description); $this -> setParameters($parameters); $this -> setReturn($return); } /** * @param ParameterServiceReflection[] $value */ public function setParameters($value) { if( !is_array($value) ) return; $_parameters = array(); foreach($value as $param) { $this -> addParamter($param); } } /** * @return ParameterServiceReflection[] */ public function getParameters() { return $this -> _parameters; } /** * @return integer */ public function getNumberOfParameters() { return count($this -> _parameters); } /** * @param ParameterServiceReflection $value */ public function addParamter(ParameterServiceReflection $value) { $this -> _parameters[$value -> getName()] = $value; } /** * @param string $name * @return ParameterServiceReflection */ public function getParamter($name) { if( !isset($this -> _parameters[$name]) ) return null; return $this -> _parameters[$name]; } /** * @param ReturnServiceReflection $value */ public function setReturn($value) { if( !$value instanceof ReturnServiceReflection) return; $this -> _return = $value; } /** * @return ReturnServiceReflection */ public function getReturn() { return $this -> _return; } static public function createReflection(ReflectionMethod $reflection) { $comment = $reflection -> getDocComment(); $result = new MethodServiceReflection($reflection -> getName(), '', self::getDescriptionBlock($comment)); $commentLines = self::prepareDocComment($comment); $tmpParams = array(); $return = new ReturnServiceReflection('void', ''); $params = $reflection -> getParameters(); foreach($params as $param) { $name = $param -> getName(); $tmpParams[$name] = new ParameterServiceReflection($name, 'mixed'); /* QUICKFIX: for Bug#62715 in PHP 5.3.16 * @link https://bugs.php.net/bug.php?id=62715 * Fatal error: Uncaught exception 'ReflectionException' with message 'Parameter is not optional' */ if(version_compare(str_replace(PHP_EXTRA_VERSION, '', PHP_VERSION) , '5.3.16', 'eq')) continue; if( $param -> isDefaultValueAvailable() ) { $tmpParams[$name] -> setHasDefault(true); $tmpParams[$name] -> setRequired(false); $tmpParams[$name] -> setDefault( $param -> getDefaultValue(), false); } } foreach($commentLines as $line) { if ($line == '') continue; if ($line{0} != '@') continue; if( preg_match('/^@param\s+([\w\[\]()]+)\s+\$([\w()]+)\s*(.*)/i', $line, $match) ) { $type = $match[1]; $name = $match[2]; $desc = $match[3]; $param = new ParameterServiceReflection($name, $type, $desc); if( preg_match('/\(default=(.*)\)/i', $desc, $regs) ) { $param -> setHasDefault(true); $param -> setRequired(false); $default = $regs[1]; if($default == "''") { $param -> setDefault('', false); } elseif($default{0} == "'") { $default = substr($default, 1, strlen($default) -2); $param -> setDefault($default); } else { $param -> setDefault($default); } $desc = preg_replace('/' . preg_quote('(default=' . $regs[1] . ')', '/') . '/i', '', $desc); $param -> setDescription($desc); } $tmpParams[$name] = $param; } if( preg_match('/^@return\s+([\w\[\]()]+)\s*(.*)/i', $line, $match) ) { $return -> setType($match[1]); $return -> setDescription($match[2]); } if( preg_match('/^@result\s+([\w\[\]()]+)\s+\$([\w()]+)\s*(.*)/i', $line, $match) ) { $name = $match[2]; $type = $match[1]; $desc = $match[3]; if( preg_match('/\(default=(.*)\)/i', $desc, $regs) ) { $resultParam = new ParameterServiceReflection($name, $type, $desc); $resultParam -> setHasDefault(true); $resultParam -> setRequired(false); $default = $regs[1]; if($default == "''") { $resultParam -> setDefault('', false); } elseif($default{0} == "'") { $default = substr($default, 1, strlen($default) -2); $resultParam -> setDefault($default); } else { $resultParam -> setDefault($default); } $desc = preg_replace('/' . preg_quote('(default=' . $regs[1] . ')', '/') . '/i', '', $desc); $resultParam -> setDescription($desc); $return -> addResult( $resultParam ); } else { $return -> addResult( new NamedServiceReflectionBase($name, $type, $desc) ); } } } $result -> setParameters($tmpParams); $result -> setReturn($return); return $result; } public function getTypes() { $result = $this -> getReturn() -> getTypes(); foreach($this -> getParameters() as $item) { $result[$item -> getType()] = $item -> getType(); } return $result; } } class ParameterServiceReflection extends NamedServiceReflectionBase { /** * @var boolean */ private $_hasDefault = false; /** * @var boolean */ private $_required = false; /** * @var mixed */ private $_default = null; /** * @param string $name * @param string $type * @param string $description * @param boolean $required * @param mixed $default */ public function __construct($name='', $type='', $description='', $hasDefault = false, $required=true, $default=null) { parent::__construct($name, $type, $description); $this -> setDefault($default); $this -> setHasDefault($hasDefault); $this -> setRequired($required); } /** * @param mixed $value * @param boolean $typeCast */ public function setDefault($value, $typeCast=true) { if($typeCast) { $type = gettype($value); switch( strToLower($type) ) { case 'boolean': case 'bool': $this -> _default = $value ? true : false; break; case 'double': case 'float': $this -> _default = (float)$value; break; case 'integer': $this -> _default = (integer)$value; break; case 'null': $this -> _default = null; break; case 'string': if($value == 'null') $this -> _default = null; elseif($value == 'true') $this -> _default = true; elseif($value == 'false') $this -> _default = false; else $this -> _default = $value; break; default: $this -> _default = $value; break; } } else { $this -> _default = $value; } $this -> setHasDefault(true); } /** * @return string */ public function getDefault() { return $this -> _default; } public function getDefaultAsText() { $result = $this -> _default; if($result === null) { $result = 'null'; } elseif( is_bool($result) ) { $result = $result ? 'true' : 'false'; } elseif( is_string($result) ) { if( is_numeric($result) OR $result === '' ) { } else { $result = '\'' . $result . '\''; } } return $result; } /** * @param boolean $value */ public function setRequired($value) { $this -> _required = (boolean)$value; } /** * @return boolean */ public function getRequired() { return $this -> _required; } /** * @param boolean $value */ public function setHasDefault($value) { $this -> _hasDefault = (boolean)$value; } /** * @return boolean */ public function getHasDefault() { return $this -> _hasDefault; } } class ReturnServiceReflection extends BaseServiceReflection { /** * @var NamedServiceReflectionBase[] */ protected $_results = array(); /** * @return integer */ public function getNumberOfResults() { return count($this -> _results); } /** * @param NamedServiceReflectionBase[] $value */ public function setResults($value) { if(!is_array($value)) return; } /** * @return NamedServiceReflectionBase[] */ public function getResults() { return $this -> _results; } /** * @param NamedServiceReflectionBase $value */ public function addResult(NamedServiceReflectionBase $value) { $this -> _results[$value -> getName()] = $value; } public function getTypes() { $result = array(); $result[$this -> getType()] = $this -> getType(); foreach($this -> getResults() as $item) { $result[$item -> getType()] = $item -> getType(); } return $result; } } ?>plugins/micropayment-dbt/lib/serializer/TBaseSerializer.php000064400000001052152101601040020147 0ustar00 $v) { if( !is_numeric($k)) { $result = true; break; } } } return $result; } } ?>plugins/micropayment-dbt/lib/serializer/TNvpSerializer.php000064400000007134152101601040020047 0ustar00 0) $result .= self::serializeNVP('@indices', implode(',', array_keys($data)), '#' . $prefix); foreach($data as $key => $value) { $idx = ($deep > 0 ? '[' . $key . ']' : $key); if(is_scalar($value)) { $result .= self::serializeNVP($idx, $value, $prefix); } else { $result .= self::serializeRecursive($value, $prefix . $idx, $deep); } } } elseif( is_array($data) ) { $cnt = count($data); //$result .= self::serializeNVP('@count', $cnt, '#' . $prefix); for($i=0; $i<$cnt; $i++) { $idx = '[' . $i . ']'; if(is_scalar($data[$i])) { $result .= self::serializeNVP($idx, $data[$i], $prefix); } else { $result .= self::serializeRecursive($data[$i], $prefix . $idx, $deep); } } } elseif( is_object($data)) { if($deep > 0) { $reflectionObject = new ReflectionObject($data); $reflectionProperties = $reflectionObject -> getProperties(); $properties = array(); foreach($reflectionProperties as $property) { if( !$property -> isPublic() ) continue; $properties[] = $property -> getName(); } //$result .= self::serializeNVP('@properties', implode(',', $properties), '#' . $prefix); } foreach($data as $key => $value) { $idx = ($deep > 0 ? '.' . $key : $key); if(is_scalar($value)) { $result .= self::serializeNVP($idx, $value, $prefix); } else { $result .= self::serializeRecursive($value, $prefix . $idx, $deep); } } } return $result; } } ?>plugins/micropayment-dbt/lib/serializer/ISerializer.php000064400000001006152101601040017340 0ustar00plugins/micropayment-dbt/lib/services/IMcpDebitService_v1_4.php000064400000042034152101601040020550 0ustar00plugins/micropayment-dbt/lib/init.php000064400000001571152101601040013717 0ustar00plugins/micropayment-dbt/lib/dispatcher/TServiceDispatcher.php000064400000012401152101601040020627 0ustar00 setInterface($interface); $this -> setRequestAdapter( TDispatchRequestAdapter::createByServiceProtocol($this, $requestProtocol, $requestAdapterOptions) ); $this -> setResponseAdapter( TDispatchResponseAdapter::createByServiceProtocol($this, $responseProtocol, $responseAdapterOptions) ); $this -> oRequestAdapter -> setServiceUri($serviceUri); } /** * @return ClassServiceReflection */ public function getInterfaceReflection() { return ClassServiceReflection::createReflection( new ReflectionClass($this -> getInterface())); } /** * @param string $name * @param array $params * @return mixed * @todo params mit reflection aufbereiten * @throws TNotificationDispatcherException */ public function send($name, $params=null) { $result = null; $args = array(); $iReflection = $this -> getInterfaceReflection(); if( !$iReflection -> hasMethod($name) ) throw new TServiceDispatcherException('servicemethod_unkown', $name); $nReflection = $iReflection -> getMethod($name); do { if( $nReflection -> getNumberOfParameters() == 0 ) break; $aNpReflection = $nReflection -> getParameters(); $c = -1; foreach($aNpReflection as $npReflection) { ++$c; $paramName = $npReflection -> getName(); $args[$paramName] = $paramValue = null; if( $params AND is_array($params) AND array_key_exists($paramName, $params) ) { $args[$paramName] = $paramValue = $params[$paramName]; continue; } if( $params AND is_array($params) AND array_key_exists($c, $params) ) { $args[$paramName] = $paramValue = $params[$c]; continue; } if( $npReflection -> getHasDefault() ) { $args[$paramName] = $npReflection -> getDefault(); continue; } if( $npReflection -> getRequired() ) throw new TServiceDispatcherException('servicemethod_param_missing', $name, $paramName); } } while(0); $this -> oRequestAdapter -> setMethodName($name); $this -> oRequestAdapter -> setMethodParameters($args); $data = $this -> oRequestAdapter -> call(); return $this -> oResponseAdapter -> unserialize($data, $nReflection); } /** * @param string $name * @param mixed $params */ public function __call($name, $params) { return $this -> send($name, $params); } /** * @param string $value */ public function setInterface($value) { $this -> _interface = $value; } /** * @return string */ public function getInterface() { return $this -> _interface; } /** * @return string */ public function getServiceUri() { return $this -> oRequestAdapter -> getServiceUri(); } /** * @param string $value */ public function setServiceUri($value) { $this -> oRequestAdapter -> setServiceUri($value); } /** * @return string * @see TServiceProtocol */ public function getRequestProtocol() { return $this -> oRequestAdapter -> getServiceProtocol(); } /** * @return string * @see TServiceProtocol */ public function getResponseProtocol() { return $this -> oResponseAdapter -> getServiceProtocol(); } /** * assign response-adapter * * @param IDispatchResponseAdapter $adapder */ public function setResponseAdapter(IDispatchResponseAdapter $adapder) { $this -> oResponseAdapter = $adapder; } /** * return current response-adapter * * @return IDispatchResponseAdapter */ public function getResponseAdapter() { return $this -> oResponseAdapter; } /** * assign server adapter * * @param IDispatchRequestAdapter $adapder */ public function setRequestAdapter(IDispatchRequestAdapter $adapder) { $this -> oRequestAdapter = $adapder; } /** * return current request-adapter * * @return IDispatchRequestAdapter */ public function getRequestAdapter() { return $this -> oRequestAdapter; } } ?>plugins/micropayment-dbt/lib/dispatcher/TServiceDispatcherException.txt000064400000000217152101601040022540 0ustar00servicemethod_unkown = service method named "{0}" do not exists servicemethod_param_missing = service method named "{0}" requires param "{1}"plugins/micropayment-dbt/lib/dispatcher/adapter/TDispatchResponseAdapter.php000064400000010222152101601040023416 0ustar00 setManager($manager); $result -> setServiceProtocol($protocol); $result -> setOptions($options); return $result; } /** * @return mixed $value */ public function getData() { return $this -> _data; } /** * @param mixed $value */ protected function setData($value) { $this -> _data = $value; } /** * @return MethodServiceReflection */ public function getReflection() { return $this -> _reflection; } /** * @param MethodServiceReflection $value */ protected function setReflection(MethodServiceReflection $value) { $this -> _reflection = $value; } /** * @param mixed $data * @param MethodServiceReflection $interfaceNotificationReflection * @return mixed */ public function unserialize($data, MethodServiceReflection $interfaceNotificationReflection) { $this -> setData($data); $this -> setReflection($interfaceNotificationReflection); return $this -> formatDataWithReflection( $this -> unserializeData() ); } /** * @param mixed $data */ protected function formatDataWithReflection($data) { if( is_scalar($data)) return $data; $result = array(); $reflection = $this -> getReflection() -> getReturn(); $aResultReflection = $reflection -> getResults(); if(count($aResultReflection) == 0) return $data; foreach($aResultReflection as $resultReflection) { $name = $resultReflection -> getName(); $result[$name] = $value = null; $required = true; $hasValue = false; do { if( !$resultReflection instanceof ParameterServiceReflection ) break; $required = $resultReflection -> getRequired(); if( !$resultReflection -> getHasDefault() ) break; $value = $resultReflection -> getDefault(); $hasValue = true; } while(0); if( array_key_exists($name, $data) ) { $value = $data[$name]; $hasValue = true; switch($value) { case 'null': $value = null; break; case 'true': $value = true; break; case 'false': $value = false; break; } } if( !$hasValue AND $required) throw new TDispatchAdapterException('servicemethod_result_missing', $this -> getReflection() -> getName(), $name); $result[$name] = $value; } return $result; } /** * @return mixed */ abstract protected function unserializeData(); } class TDummyDispatchResponseAdapter extends TDispatchResponseAdapter { protected function unserializeData() { return $this -> getData(); } } ?>plugins/micropayment-dbt/lib/dispatcher/adapter/IDispatchRequestAdapter.php000064400000001542152101601040023242 0ustar00plugins/micropayment-dbt/lib/dispatcher/adapter/IDispatchAdapter.php000064400000002107152101601040021667 0ustar00plugins/micropayment-dbt/lib/dispatcher/adapter/THttpParamsDispatchRequestAdapter.php000064400000007172152101601040025266 0ustar00 setStatusCode($statusCode); $this -> _response = $reponse; $this -> setErrorCode($errorCode); $errorMessage = $this -> translateErrorMessage($errorCode); $args = func_get_args(); array_shift($args); array_shift($args); array_shift($args); $this -> setErrorMessage( $this -> replaceMessageToken($errorMessage, $args) ); } /** * @return TSimpleHttpResponse */ public function getResponse() { return $this -> _response; } } class THttpParamsDispatchRequestAdapter extends TDispatchRequestAdapter { /** * * @return TSimpleHttpResponse * @throws SimpleHttpClientException * @throws THttpException */ public function call() { $option = $this -> getOptions(); if(!is_array($option)) $option = array(); if(!isset($option['http-params-servicemethod']) OR $option['http-params-servicemethod'] == '') $option['http-params-servicemethod'] = 'action'; $servicemethodParamName = $option['http-params-servicemethod']; $result = ''; switch( $this -> getServiceProtocol() ) { case TServiceProtocol::HTTP_PARAMS_GET: $client = new TSimpleHttpClient(TSimpleHttpClient::GET, $this -> getServiceUri()); break; case TServiceProtocol::HTTP_PARAMS: case TServiceProtocol::HTTP_PARAMS_POST: default: $client = new TSimpleHttpClient(TSimpleHttpClient::POST, $this -> getServiceUri()); break; } $client -> addHeader('User-Agent', 'MCP-SimpleHttpClient/1.0 API-ServiceDispatcher via HttpParamsDispatchRequestAdapter'); $client -> addHeader('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,*/*;q=0.5'); $client -> addHeader('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7'); $client -> addHeader('X-MCP-API-RequestServiceProtocol', $this -> getServiceProtocol()); $client -> addHeader('X-MCP-API-ResponseServiceProtocol', $this -> getManager() -> getResponseProtocol()); $methodParameters = http_build_query($this -> getMethodParameters(), null, '&'); parse_str($methodParameters, $methodParameters); $aData = array(); if( !$client -> isPost() ) $aData = $client -> getQuery(true); $aData = array_merge($aData, $methodParameters); $aData[$servicemethodParamName] = $this -> getMethodName(); if( $client -> isPost() ) { $client -> addHeader('Content-type', 'application/x-www-form-urlencoded'); $client -> setBody( http_build_query($aData, null, '&') ); } else { $client -> setQuery($aData); } $result = $client -> request(); switch( $result -> getStatusCode() ) { case 200: // OK break; case 301: // Moved permanently case 302: // Found case 303: // See other case 305: // Use proxy case 307: // Moved temporarily // ::ToDo:: // ggf. Location Header auswerten // und Request umschreiben case 304: // Not modified default: throw new THttpParamsDispatchRequestAdapterHttpException($result -> getStatusCode(), $result -> getStatusMessage(), $result); break; } return $result; } } ?>plugins/micropayment-dbt/lib/dispatcher/adapter/TDispatchAdapter.php000064400000002744152101601040021711 0ustar00 _manager = $manager; } /** * @return IServiceDispatcher */ public function getManager() { return $this -> _manager; } /** * @param string $value * @see TServiceProtocol */ public function setServiceProtocol($value) { $this -> _serviceProtocol = $value; } /** * @return string $value * @see TServiceProtocol */ public function getServiceProtocol() { return $this -> _serviceProtocol; } /** * @param mixed $value */ public function setOptions($value=null) { $this -> _options = $value; } /** * @return mixed $value */ public function getOptions() { return $this -> _options; } } ?>plugins/micropayment-dbt/lib/dispatcher/adapter/TDispatchRequestAdapter.php000064400000005430152101601040023255 0ustar00 setManager($manager); $result -> setServiceProtocol($protocol); $result -> setOptions($options); return $result; } /** * @return string */ public function getServiceUri() { return $this -> _serviceUri; } /** * @param string $value */ public function setServiceUri($value) { $this -> _serviceUri = $value; } /** * @return string */ public function getMethodName() { return $this -> _methodName; } /** * @param string $value */ public function setMethodName($value) { $this -> _methodName= $value; } /** * @param array $value */ public function setMethodParameters($value) { $this -> _methodParameters = $value; } /** * @return array */ public function getMethodParameters() { return $this -> _methodParameters; } } class TDummyDispatchRequestAdapter extends TDispatchRequestAdapter { public function call() { return null; } } ?>plugins/micropayment-dbt/lib/dispatcher/adapter/TDispatchAdapterException.txt000064400000000612152101601040023610 0ustar00protocol_not_supported = service protocol "{1}" not supported by "{0}" yet protocol_unknown = adapter "{0}" don't know service protocol "{1}" adapter_datatype_not_supported = Response data type "{0}" not supported adapter_protocol_not_supported = {0} do not support service protocol "{1}" use another Adapter servicemethod_result_missing = service method named "{0}" requires result "{1}"plugins/micropayment-dbt/lib/dispatcher/adapter/TNvpDispatchResponseAdapter.php000064400000004112152101601040024103 0ustar00 getData(); $result = null; $dataType = gettype($data); switch($dataType) { case 'object': $dataType = get_class($data); case 'TSimpleHttpResponse': $format = $this -> getServiceProtocol(); if( $data -> isHeader('X-MCP-API-ResonseServiceProtocol') ) $format = $data -> getHeader('X-MCP-API-ResonseServiceProtocol'); if( !in_array($format, array($this -> getServiceProtocol(), TServiceProtocol::NONE, TServiceProtocol::UNKNOWN )) ) throw new TDispatchAdapterException('adapter_protocol_not_supported', __CLASS__, $format); $result = TNvpSerializer::unserialize( $data -> getBody() ); break; case 'boolean': case 'integer': case 'boolean': case 'array': case 'null': case 'NULL': $result = TNvpSerializer::unserialize( TNvpSerializer::serialize($data) ); break; case 'string': $result = TNvpSerializer::unserialize($data); break; case 'resource': case 'unknown type': default: throw new TDispatchAdapterException('adapter_datatype_not_supported', $dataType); break; } do { if( !isset($result['error']) ) break; if($result['error'] == 0) break; $code = $result['error']; $msg = isset($result['errorMessage']) ? stripSlashes($result['errorMessage']) : ''; throw new Exception($msg, $code); } while(0); do { if( !isset($result['result']) ) break; if( count($result) > 2) break; $result = $result['result']; } while(0); return $result; } } ?>plugins/micropayment-dbt/lib/dispatcher/adapter/IDispatchResponseAdapter.php000064400000001315152101601040023406 0ustar00plugins/micropayment-dbt/lib/dispatcher/TNvpServiceDispatcher.php000064400000001067152101601040021321 0ustar00plugins/micropayment-dbt/lib/dispatcher/IServiceDispatcher.php000064400000001710152101601040020615 0ustar00plugins/comenpay.php000064400000023776152101601040010556 0ustar00addText('paccount_id', array('size' => 5)) ->setLabel('Mid identification number provided by Comen Pay support') ->addRule('required'); $form->addPassword('apiKey', array('class' => 'el-wide')) ->setLabel('API Key') ->addRule('required'); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { $client = new SoapClient(self::WSDL); $user = $invoice->getUser(); if ($cc) { $data = array( 'customer_firstname' => $user->name_f ? $user->name_f : $cc->cc_name_f, 'customer_lastname' => $user->name_l ? $user->name_l : $cc->cc_name_l, 'customer_email' => $user->email, 'holder_firstname' => $cc->cc_name_f, 'holder_lastname' => $cc->cc_name_l, 'pan' => $cc->cc_number, 'digit' => $cc->getCvv(), 'exp' => $cc->getExpire('%02d-20%02d') ); $data = base64_encode(serialize($data)); $param = array( $this->getConfig('apiKey'), $data ); $request = new SoapRequestWrapperComenpay($client, 'AddCustomerData', $param); $t = new Am_Paysystem_Transaction_CreditCard_Comenpay_AddCustomerData($this, $invoice, $request, $user); $r = new Am_Paysystem_Result; $t->run($r); if ($r->isFailure()) { $result->setFailed($r->getErrorMessages()); return; } } if (!$user->data()->get(self::COMENPAY_CARD_TOKEN)) { $result->setFailed('Can not process payment: customer has not associated CC'); return; } if ($doFirst && !(float) $invoice->first_total) { //free trial $t = new Am_Paysystem_Transaction_Free($this); $t->setInvoice($invoice); $t->process(); $result->setSuccess(); } else { $payment = null; @list($payment) = $invoice->getPaymentRecords(); $data = array( 'paccount_id' => $this->getConfig('paccount_id'), 'type' => $payment ? 'REBILL' : 'BILL', 'transaction_ip' => $user->last_ip, 'amount_cnts' => 100 * ($doFirst ? $invoice->first_total : $invoice->second_total), 'client_reference' => $invoice->public_id, 'client_customer_id' => $user->pk(), 'affiliate_id' => 0, 'site_url' => $this->getDi()->config->get('site_url'), 'member_login' => $user->login, 'support_url' => $this->getDi()->config->get('site_url'), 'support_tel' => 'N/A', 'support_email' => $this->getDi()->config->get('admin_email'), 'customer_lang' => 'en', 'customer_useragent' => $user->last_user_agent, 'billing_invoicing_id' => 0, 'billing_description' => $invoice->getLineDescription(), 'billing_preauth_duration' => 0, 'billing_rebill_period' => 0, 'billing_rebill_duration' => 0, 'billing_rebill_price_cnts' => 100 * $invoice->second_total, ); if ($payment) { $data['billing_initial_transaction_id'] = $payment->receipt_id; } $param = array( $this->getConfig('apiKey'), $user->data()->get(self::COMENPAY_CARD_TOKEN), $user->data()->get(self::COMENPAY_CARD_KEY), $data ); $request = new SoapRequestWrapperComenpay($client, 'Transaction', $param); $t = new Am_Paysystem_Transaction_CreditCard_Comenpay_Transaction($this, $invoice, $request, $doFirst); $t->run($result); } } public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $client = new SoapClient(self::WSDL); $invoice = $payment->getInvoice(); $user = $invoice->getUser(); $data = array( 'paccount_id' => $this->getConfig('paccount_id'), 'type' => 'REFUND', 'transaction_ip' => $user->last_ip, 'amount_cnts' => 100 * $amount, 'client_reference' => $invoice->public_id, 'client_customer_id' => $user->pk(), 'affiliate_id' => 0, 'site_url' => $this->getDi()->config->get('site_url'), 'member_login' => $user->login, 'support_url' => $this->getDi()->config->get('site_url'), 'support_tel' => 'N/A', 'support_email' => $this->getDi()->config->get('admin_email'), 'customer_lang' => 'en', 'customer_useragent' => $user->last_user_agent, 'billing_invoicing_id' => 0, 'billing_description' => $invoice->getLineDescription(), 'billing_preauth_duration' => 0, 'billing_rebill_period' => 0, 'billing_rebill_duration' => 0, 'billing_rebill_price_cnts' => 100 * $invoice->second_total, 'billing_initial_transaction_id' => $payment->receipt_id ); $param = array( $this->getConfig('apiKey'), $user->data()->get(self::COMENPAY_CARD_TOKEN), $user->data()->get(self::COMENPAY_CARD_KEY), $data ); $request = new SoapRequestWrapperComenpay($client, 'Transaction', $param); $t = new Am_Paysystem_Transaction_CreditCard_Comenpay_Transaction_Refund($this, $invoice, $request, $payment->receipt_id, $amount); $t->run($result); } } class SoapRequestWrapperComenpay { protected $client; protected $method; protected $data; public function __construct(SoapClient $client, $method, $data) { $this->client = $client; $this->method = $method; $this->data = $data; } public function send() { $res = call_user_func_array(array($this->client, $this->method), $this->data); return new SoapResponseWrapperComenpay($res); } public function toArray() { $v = end($this->data); return is_array($v) ? $this->data : unserialize(base64_decode($v)); } } class SoapResponseWrapperComenpay { protected $response; public function __construct($response) { $this->response = $response; } public function toArray() { return $this->objectToArray($this->response); } public function getStatus() { return 200; } protected function objectToArray($d) { if (is_object($d)) { $d = get_object_vars($d); } if (is_array($d)) { return array_map(array($this, 'objectToArray'), $d); } else { return $d; } } } class Am_Paysystem_Transaction_CreditCard_Comenpay_AddCustomerData extends Am_Paysystem_Transaction_CreditCard { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $request, $user) { $this->setInvoice($invoice); $this->plugin = $plugin; $this->request = $request; $this->user = $user; } public function parseResponse() { $this->vars = $this->response->toArray(); } public function getUniqId() { return null; } public function validate() { if ($this->vars['status'] == 'OK') { $this->result->setSuccess(); } else { $this->result->setFailed(___('Payment Failed')); } } public function processValidated() { $this->user->data()->set(Am_Paysystem_Comenpay::COMENPAY_CARD_TOKEN, $this->vars['token']); $this->user->data()->set(Am_Paysystem_Comenpay::COMENPAY_CARD_KEY, $this->vars['otpKey']); $this->user->save(); } } class Am_Paysystem_Transaction_CreditCard_Comenpay_Transaction extends Am_Paysystem_Transaction_CreditCard { public function parseResponse() { $this->vars = $this->response->toArray(); } public function getUniqId() { return $this->vars['transaction_id']; } public function validate() { if ($this->vars['status'] == 'OK') { $this->result->setSuccess(); } else { $this->result->setFailed($this->vars['status_reason']); } } public function processValidated() { $this->invoice->addPayment($this); } } class Am_Paysystem_Transaction_CreditCard_Comenpay_Transaction_Refund extends Am_Paysystem_Transaction_CreditCard_Comenpay_Transaction { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $request, $receipt, $amount) { $this->setInvoice($invoice); $this->plugin = $plugin; $this->request = $request; $this->receipt = $receipt; $this->amount = $amount; } public function getAmount() { return $this->amount; } public function processValidated() { $this->invoice->addRefund($this, $this->receipt); } } plugins/authorize-echeck.php000064400000026472152101601040012171 0ustar00 'Personal Checking', self::TYPE_BUSINESSCHECKING => 'Business Checking', self::TYPE_SAVINGS => 'Personal Savings', ); public function getRecurringType() { return self::REPORTS_CRONREBILL; } public function getFormOptions() { $ret = parent::getFormOptions(); $ret[] = self::ECHECK_TYPE_OPTIONS; $ret[] = self::ECHECK_BANK_NAME; $ret[] = self::ECHECK_ACCOUNT_NAME; return $ret; } protected function _afterInitSetupForm(Am_Form_Setup $form) { parent::_afterInitSetupForm($form); $form->setTitle(___("Authorize.NET eCheck")); } public function _initSetupForm(Am_Form_Setup $form) { $form->addText("login")->setLabel("API Login ID\n" . 'can be obtained from the same page as Transaction Key (see below)'); $form->addText("tkey")->setLabel("Transaction Key" . "

The transaction key is generated by the system and can be obtained from Merchant Interface. To obtain the transaction key from the Merchant Interface

  1. Log into the Merchant Interface
  2. Select Settings from the Main Menu
  3. Click on Obtain Transaction Key in the Security section
  4. Type in the answer to the secret question configured on setup
  5. Click Submit
"); $form->addAdvCheckbox("test_mode") ->setLabel("Test Mode Enabled"); } public function getEcheckTypeOptions() { return $this->acctTypeOptions; } public function _doBill(Invoice $invoice, $doFirst, EcheckRecord $echeck, Am_Paysystem_Result $result) { if ($doFirst && !(float)$invoice->first_total) { // free trial $tr = new Am_Paysystem_Transaction_Free($this); $tr->setInvoice($invoice); $tr->process(); $result->setSuccess($tr); } else { $user = $invoice->getUser(); $ps = new stdclass; $ps->x_Invoice_Num = $invoice->public_id; $ps->x_Cust_ID = $invoice->user_id; $ps->x_Description = $invoice->getLineDescription(); $ps->x_First_Name = $echeck->cc_name_f; $ps->x_Last_Name = $echeck->cc_name_l; $ps->x_Address = $echeck->cc_street; $ps->x_City = $echeck->cc_city; $ps->x_State = $echeck->cc_state; $ps->x_Country = $echeck->cc_country; $ps->x_Zip = $echeck->cc_zip; $ps->x_Tax = $doFirst ? $invoice->first_tax : $invoice->second_tax; $ps->x_Email = $user->email; $ps->x_Phone = $echeck->cc_phone; $ps->x_Amount = $doFirst ? $invoice->first_total : $invoice->second_total; $ps->x_Currency_Code = $invoice->currency; $ps->x_Type = 'AUTH_CAPTURE'; $ps->x_Customer_IP = $user->remote_addr ? $user->remote_addr : $_SERVER['REMOTE_ADDR']; $ps->x_Relay_Response = 'FALSE'; $ps->x_Delim_Data = 'TRUE'; $ps->x_bank_acct_num = $echeck->echeck_ban; $ps->x_bank_aba_code = $echeck->echeck_aba; $ps->x_bank_acct_type = $echeck->echeck_type; $ps->x_bank_name = $echeck->check_bank_name; $ps->x_bank_acct_name = $echeck->echeck_account_name; $ps->x_echeck_type = 'WEB'; $ps->x_recurring_billing = ($invoice->rebill_times) ? 'TRUE' : 'FALSE'; $request = $this->_sendRequest($this->createHttpRequest()); $request->addPostParameter((array) $ps); $transaction = new Am_Paysystem_Transaction_AuthorizeEcheck_Payment($this, $invoice, $request, $doFirst); $transaction->run($result); } } /** @return HTTP_Request2_Response */ public function _sendRequest(Am_HttpRequest $request) { $request->addPostParameter('x_login', $this->getConfig('login')); $request->addPostParameter('x_tran_key', $this->getConfig('tkey')); $request->addPostParameter('x_Delim_Data', "True"); $request->addPostParameter('x_Delim_Char', "|"); $request->addPostParameter('x_Version', "3.1"); $request->addPostParameter('x_method', "ECHECK"); if ($this->getConfig('test_mode')) { $request->addPostParameter("x_test_request", "TRUE"); $request->setUrl(self::SANDBOX_URL); } else { $request->setUrl(self::LIVE_URL); } $request->setMethod(Am_HttpRequest::METHOD_POST); return $request; } public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $request = $this->_sendRequest($this->createHttpRequest()); $request->addPostParameter('x_Type', 'CREDIT'); $request->addPostParameter('x_Trans_Id', $this->getConfig('test_mode') ? 0 : $payment->transaction_id); $request->addPostParameter('x_Amount', $amount); $echeck = $this->loadEcheck($payment->getInvoice()); $request->addPostParameter('x_bank_acct_num', $echeck->echeck_ban); $request->addPostParameter('x_bank_aba_code', $echeck->echeck_aba); $request->addPostParameter('x_bank_acct_type', $echeck->echeck_type); $request->addPostParameter('x_bank_name', $echeck->check_bank_name); $request->addPostParameter('x_bank_acct_name', $echeck->echeck_account_name); $request->addPostParameter('x_echeck_type', $echeck->echeck_type == self::TYPE_BUSINESSCHECKING ? 'CCD' : 'PPD'); $transaction = new Am_Paysystem_Transaction_AuthorizeEcheck_Refund($this, $payment->getInvoice(), $request, $payment->transaction_id, $amount); $transaction->run($result); } } class Am_Paysystem_Transaction_AuthorizeEcheck extends Am_Paysystem_Transaction_Echeck //Am_Paysystem_Transaction_CreditCard { const APPROVED = 1; const DECLINED = 2; const ERROR = 3; const HELD = 4; protected $response; protected $res; // Parsed response; public function parseResponse() { $response = $this->response->getBody(); $this->response->approved = false; $this->response->error = true; $vars = explode('|', $response); if ($vars) { if (count($vars) < 10) { $this->response->error_message = "Unrecognized response from AuthorizeNet: $response"; return; } // Set all fields $this->response->response_code = $vars[0]; $this->response->response_subcode = $vars[1]; $this->response->response_reason_code = $vars[2]; $this->response->response_reason_text = $vars[3]; $this->response->authorization_code = $vars[4]; $this->response->avs_response = $vars[5]; $this->response->transaction_id = $vars[6]; $this->response->invoice_number = $vars[7]; $this->response->description = $vars[8]; $this->response->amount = $vars[9]; $this->response->method = $vars[10]; $this->response->transaction_type = $vars[11]; $this->response->customer_id = $vars[12]; $this->response->first_name = $vars[13]; $this->response->last_name = $vars[14]; $this->response->company = $vars[15]; $this->response->address = $vars[16]; $this->response->city = $vars[17]; $this->response->state = $vars[18]; $this->response->zip_code = $vars[19]; $this->response->country = $vars[20]; $this->response->phone = $vars[21]; $this->response->fax = $vars[22]; $this->response->email_address = $vars[23]; $this->response->ship_to_first_name = $vars[24]; $this->response->ship_to_last_name = $vars[25]; $this->response->ship_to_company = $vars[26]; $this->response->ship_to_address = $vars[27]; $this->response->ship_to_city = $vars[28]; $this->response->ship_to_state = $vars[29]; $this->response->ship_to_zip_code = $vars[30]; $this->response->ship_to_country = $vars[31]; $this->response->tax = $vars[32]; $this->response->duty = $vars[33]; $this->response->freight = $vars[34]; $this->response->tax_exempt = $vars[35]; $this->response->purchase_order_number = $vars[36]; $this->response->md5_hash = $vars[37]; $this->response->card_code_response = $vars[38]; $this->response->cavv_response = $vars[39]; $this->response->account_number = $vars[40]; $this->response->card_type = $vars[51]; $this->response->split_tender_id = $vars[52]; $this->response->requested_amount = $vars[53]; $this->response->balance_on_card = $vars[54]; $this->response->approved = ($this->response->response_code == self::APPROVED); $this->response->declined = ($this->response->response_code == self::DECLINED); $this->response->error = ($this->response->response_code == self::ERROR); $this->response->held = ($this->response->response_code == self::HELD); } else { $this->response->error_message = "Error connecting to AuthorizeNet"; } } public function getUniqId() { return ($this->plugin->getConfig('test_mode')) ? $this->response->transaction_id . "-test_mode-" . time() : $this->response->transaction_id; } public function getAmount() { return $this->response->amount; } public function validate() { if ($this->response->approved) { $this->result->setSuccess($this); } else { $this->result->setFailed($this->getErrorMessage()); } } public function getErrorMessage() { return!empty($this->response->error_message) ? $this->response->error_message : $this->response->response_reason_text; } } class Am_Paysystem_Transaction_AuthorizeEcheck_Payment extends Am_Paysystem_Transaction_AuthorizeEcheck { function processValidated() { $this->invoice->addPayment($this); } } class Am_Paysystem_Transaction_AuthorizeEcheck_Refund extends Am_Paysystem_Transaction_AuthorizeEcheck { protected $orig_id; protected $amount; function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $request, $orig_id, $amount) { parent::__construct($plugin, $invoice, $request, true); $this->orig_id = $orig_id; $this->amount = $amount; } function processValidated() { $this->invoice->addRefund($this, $this->orig_id); } function getAmount() { return $this->amount; } } plugins/paymentsystemsworldwide.php000064400000030605152101601040013756 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 _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 { $trAddFree = new Am_Paysystem_Paymentsystemsworldwide_Transaction_Authorization($this, $invoice, $cc); $trAddFree->run($result); $transactionId = $trAddFree->getUniqId(); $customerVaultId = $trAddFree->getCustomerVaultId(); if (!$transactionId || !$customerVaultId) { return $result->setFailed(array("PSWW Plugin: Bad auth response.")); } $trVoid = new Am_Paysystem_Paymentsystemsworldwide_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 { $trAddPay = new Am_Paysystem_Paymentsystemsworldwide_Transaction_AddCustomer ($this, $invoice, $cc); $trAddPay->run($result); $customerVaultId = $trAddPay->getCustomerVaultId(); if (!$customerVaultId) { return $result->setFailed(array("PSWW Plugin: Bad add response.")); } $trSale = new Am_Paysystem_Paymentsystemsworldwide_Transaction_Sale($this, $invoice, $doFirst, $customerVaultId); $trSale->run($result); } $user->data()->set(self::CUSTOMER_VAULT_ID, $customerVaultId)->update(); } else { $customerVaultId = $user->data()->get(self::CUSTOMER_VAULT_ID); if (!$customerVaultId) { return $result->setFailed(array("No saved reference transaction for customer")); } $trSale = new Am_Paysystem_Paymentsystemsworldwide_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(self::CUSTOMER_VAULT_ID); 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(self::CUSTOMER_VAULT_ID, null)->update(); $customerVaultId = null; } } if (!$customerVaultId) { $trAdd = new Am_Paysystem_Paymentsystemsworldwide_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(self::CUSTOMER_VAULT_ID, $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(self::CUSTOMER_VAULT_ID); $tr = new Am_Paysystem_Paymentsystemsworldwide_Transaction_Refund($this, $payment->getInvoice(), $payment->receipt_id, $amount, $customerVaultId); $tr->run($result); } public function getReadme() { return <<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; } } class Am_Paysystem_Paymentsystemsworldwide_Transaction_Sale extends Am_Paysystem_Paymentsystemsworldwide_Transaction { 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 Am_Paysystem_Paymentsystemsworldwide_Transaction_Refund extends Am_Paysystem_Paymentsystemsworldwide_Transaction { // public $amount2 = 0; 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 Am_Paysystem_Paymentsystemsworldwide_Transaction_Authorization extends Am_Paysystem_Paymentsystemsworldwide_Transaction { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, CcRecord $cc) { parent::__construct($plugin, $invoice, true); $this->setCcRecord($cc); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('type', 'auth'); $this->request->addPostParameter('amount', 1.00); $this->request->addPostParameter('customer_vault', 'add_customer'); } public function processValidated(){} // no process payment } class Am_Paysystem_Paymentsystemsworldwide_Transaction_Void extends Am_Paysystem_Paymentsystemsworldwide_Transaction { 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 Am_Paysystem_Paymentsystemsworldwide_Transaction_AddCustomer extends Am_Paysystem_Paymentsystemsworldwide_Transaction { public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, CcRecord $cc) { parent::__construct($plugin, $invoice, true); $this->setCcRecord($cc); } protected function addRequestParams() { parent::addRequestParams(); $this->request->addPostParameter('customer_vault', 'add_customer'); } public function processValidated(){} // no process payment }plugins/authorize-dpm.php000064400000027067152101601040011530 0ustar00getId(); $form->addText("login")->setLabel("API Login ID\n" . 'can be obtained from the same page as Transaction Key (see below)'); $form->addText("tkey")->setLabel("Transaction Key\n" . '

The transaction key is generated by the system and can be obtained from Merchant Interface. To obtain the transaction key from the Merchant Interface

  1. Log into the Merchant Interface
  2. Select Settings from the Main Menu
  3. Click on Obtain Transaction Key in the Security section
  4. Type in the answer to the secret question configured on setup
  5. Click Submit
'); $form->addText('secret') ->setLabel("Secret Word\n" . "From authorize.net MD5 Hash menu\n" . "You have to create secret word") ->addRule('required'); $form->addAdvCheckbox("testing")->setLabel("Test Mode Enabled"); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { //nop } protected function createController(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Mvc_Controller_AuthorizeDpm($request, $response, $invokeArgs); } function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Transaction_AuthorizeDpm($this, $request, $response, $invokeArgs); } public function createForm($actionName) { class_exists('Am_Form_CreditCard', true); return new Am_Form_AuthorizeDpm($this); } public function getFingerprint($amount, $currency, $fp_sequence, $fp_timestamp) { return hash_hmac("md5", implode("^", array( $this->getConfig('login'), $fp_sequence, $fp_timestamp, $amount, $currency )), $this->getConfig("tkey")); } public function onInitForm(Am_Form_AuthorizeDpm $form) { $i = $this->invoice; $p = array( 'x_type' => 'AUTH_CAPTURE', 'x_amount' => $i->first_total, 'x_currency_code' => $i->currency, 'x_fp_sequence' => $i->pk(), 'x_fp_timestamp' => time(), 'x_relay_response' => "TRUE", 'x_relay_url' => $this->getPluginUrl('ipn'), 'x_login' => $this->getConfig('login'), 'x_invoice_num' => $i->public_id, 'x_customer_ip' => $_SERVER['REMOTE_ADDR'], 'x_test_request' => $this->getConfig('testing') ? 'TRUE' : 'FALSE' ); $p['x_fp_hash'] = $this->getFingerprint($i->first_total, $i->currency, $p['x_fp_sequence'], $p['x_fp_timestamp']); foreach ($p as $k => $v) { $form->addHidden($k, array('value' => $v)); } } } class Am_Paysystem_Transaction_AuthorizeDpm extends Am_Paysystem_Transaction_Incoming { const APPROVED = 1; public function getUniqId() { return $this->request->getParam('x_trans_id'); } public function findInvoiceId() { return $this->request->getParam('x_invoice_num'); } public function validateSource() { return $this->request->getParam('x_MD5_Hash') == strtoupper( md5( $this->plugin->getConfig('secret') . $this->plugin->getConfig('login') . $this->request->getParam('x_trans_id') . $this->request->getParam('x_amount') ) ); } public function validateStatus() { return true; } public function validateTerms() { $this->assertAmount($this->invoice->first_total, $this->request->getParam('x_amount')); return true; } public function processValidated() { switch ($this->request->getParam('x_response_code')) { case self::APPROVED: $this->invoice->addPayment($this); $redirect_url = $this->plugin->getRootUrl() . "/thanks?id=" . $this->invoice->getSecureId("THANKS"); break; default: $redirect_url = $this->plugin->getRootUrl() . "/cancel?id=" . $this->invoice->getSecureId('CANCEL'); } echo << CUT; exit; } } class Am_Mvc_Controller_AuthorizeDpm extends Am_Mvc_Controller { /** @var Invoice */ public $invoice; /** @var Am_Paysystem_CreditCard */ public $plugin; /** @var Am_Form_CreditCard */ public $form; public function setPlugin(Am_Paysystem_CreditCard $plugin) { $this->plugin = $plugin; } public function setInvoice(Invoice $invoice) { $this->invoice = $invoice; } /** * we only show form, authorize.net process it */ public function ccAction() { // invoice must be set to this point by the plugin if (!$this->invoice) throw new Am_Exception_InternalError('Empty invoice - internal error!'); $this->form = $this->createForm(); $this->getDi()->hook->call(Bootstrap_Cc::EVENT_CC_FORM, array('form' => $this->form)); $this->view->form = $this->form; $this->view->invoice = $this->invoice; $this->view->display_receipt = true; $this->view->display('cc/info.phtml'); } public function createForm() { $form = $this->plugin->createForm($this->_request->getActionName(), $this->invoice); $form->setDataSources(array( $this->_request, new HTML_QuickForm2_DataSource_Array($form->getDefaultValues($this->invoice->getUser())) )); return $form; } } class Am_Form_AuthorizeDpm extends Am_Form { protected $plugin = null; public function __construct(Am_Paysystem_CreditCard $plugin) { $this->plugin = $plugin; return parent::__construct('cc'); } public function init() { parent::init(); $this->setAction($this->plugin->getConfig('testing') ? Am_Paysystem_AuthorizeDpm::SANDBOX_URL : Am_Paysystem_AuthorizeDpm::LIVE_URL); $name = $this->addGroup() ->setLabel(___("Cardholder Name\n" . 'cardholder first and last name, exactly as on the card')); $name->setSeparator(' '); $name->addRule('required', ___('Please enter credit card holder name')); $name_f = $name->addText('x_first_name', array('size' => 15)); $name_f->addRule('required', ___('Please enter credit card holder first name'))->addRule('regex', ___('Please enter credit card holder first name'), '/^[^=:<>{}()"]+$/D'); $name_l = $name->addText('x_last_name', array('size' => 15)); $name_l->addRule('required', ___('Please enter credit card holder last name'))->addRule('regex', ___('Please enter credit card holder last name'), '/^[^=:<>{}()"]+$/D'); $cc = $this->addText('x_card_num', array('autocomplete' => 'off', 'size' => 22, 'maxlength' => 22)) ->setLabel(___("Credit Card Number\n" . 'for example: 1111-2222-3333-4444')); $cc->addRule('required', ___('Please enter Credit Card Number')) ->addRule('regex', ___('Invalid Credit Card Number'), '/^[0-9 -]+$/'); $expire = $this->addElement(new Am_Form_Element_CreditCardExpire('cc_expire')) ->setLabel(___("Card Expire\n" . 'Select card expiration date - month and year')) ->addRule('required'); $this->addHidden('x_exp_date'); $this->addScript()->setScript(<<addPassword('x_card_code', array('autocomplete' => 'off', 'size' => 4, 'maxlength' => 4)) ->setLabel(___("Credit Card Code\n" . 'The "Card Code" is a three- or four-digit security code ' . 'that is printed on the back of credit cards in the card\'s ' . 'signature panel (or on the front for American Express cards)')); $code->addRule('required', ___('Please enter Credit Card Code')) ->addRule('regex', ___('Please enter Credit Card Code'), '/^\s*\d{3,4}\s*$/'); $fieldSet = $this->addFieldset(___('Address Info')) ->setLabel(___("Address Info\n" . '(must match your credit card statement delivery address)')); $street = $fieldSet->addText('x_address')->setLabel(___('Street Address')) ->addRule('required', ___('Please enter Street Address')); $city = $fieldSet->addText('x_city')->setLabel(___('City')) ->addRule('required', ___('Please enter City')); $zip = $fieldSet->addText('x_zip')->setLabel(___('ZIP')) ->addRule('required', ___('Please enter ZIP code')); $country = $fieldSet->addSelect('x_country')->setLabel(___('Country')) ->setId('f_cc_country') ->loadOptions(Am_Di::getInstance()->countryTable->getOptions(true)); $country->addRule('required', ___('Please enter Country')); $group = $fieldSet->addGroup()->setLabel(___('State')); $group->addRule('required', ___('Please enter State')); $stateSelect = $group->addSelect('x_state') ->setId('f_cc_state') ->loadOptions($stateOptions = Am_Di::getInstance()->stateTable->getOptions(@$_REQUEST['x_country'], true)); $stateText = $group->addText('x_state')->setId('t_cc_state'); $disableObj = $stateOptions ? $stateText : $stateSelect; $disableObj->setAttribute('disabled', 'disabled')->setAttribute('style', 'display: none'); $buttons = $this->addGroup(); $buttons->addSubmit('_cc_', array('value' => ___('Subscribe And Pay'))); $this->plugin->onInitForm($this); } public function getDefaultValues(User $user) { return array( 'x_first_name' => $user->name_f, 'x_last_name' => $user->name_l, 'x_address' => $user->street, 'x_city' => $user->city, 'x_state' => $user->state, 'x_country' => $user->country, 'x_zip' => $user->zip ); } }plugins/altcharge.php000064400000037522152101601040010667 0ustar00addText('api_key')->setLabel('API Key'); $form->addText('mid')->setLabel("Merchant MID\n" . 'Leave empty if you do not have mylpitle MIDs'); } function createForm($actionName) { return new Am_Form_CreditCard_AltchargeCheck($this); } function storesCcInfo() { return false; } public function getCycle(Invoice $invoice) { $p = new Am_Period(); $p->fromString($invoice->second_period); switch ($p->getUnit()) { case Am_Period::DAY : return $p->getCount(); case Am_Period::MONTH : return $p->getCount() * 30; case Am_Period::YEAR : return $p->getCount() * 356; default: throw new Am_Exception_InputError('Incorrect product second period: ' . $p->getUnit()); } } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { list($account_number, $routing_number) = explode('-', $cc->cc_number); $r = new Am_HttpRequest(Am_Paysystem_Altcharge::URL, Am_HttpRequest::METHOD_POST); $r->addPostParameter('userkey', $this->getConfig('api_key')); $r->addPostParameter('type', $invoice->rebill_times ? self::RECURRING : self::SINGLE); $r->addPostParameter('version', '2.6'); $r->addPostParameter('email', $invoice->getEmail()); $r->addPostParameter('firstName', $cc->cc_name_f); $r->addPostParameter('lastName', $cc->cc_name_l); $r->addPostParameter('address1', $cc->cc_street); $r->addPostParameter('city', $cc->cc_city); $r->addPostParameter('state', $cc->cc_state); $r->addPostParameter('zip', $cc->cc_zip); $r->addPostParameter('country', $cc->cc_country); $r->addPostParameter('phone', $cc->cc_phone); $r->addPostParameter('ipaddress', $this->getDi()->request->getClientIp()); $r->addPostParameter('accountNumber', $account_number); $r->addPostParameter('routingNumber', $routing_number); $r->addPostParameter('merchantMID', $this->getConfig('mid', 1)); $r->addPostParameter('currency', $invoice->currency ? $invoice->currency : 'USD'); $r->addPostParameter('misc1', $invoice->public_id); $r->addPostParameter('amount', $invoice->first_total); if ($invoice->rebill_times) { $r->addPostParameter('cycle', $this->getCycle($invoice)); $r->addPostParameter('desc', $invoice->getLineDescription()); } $r->addPostParameter('signature', $this->getDi()->request->get('signature')); $transaction = new Am_Paysystem_Transaction_Altcharge_Sale($this, $invoice, $r, $doFirst); $transaction->run($result); } public function isConfigured() { return strlen($this->getConfig('api_key')); } public function getSupportedCurrencies() { return array( 'AUD', 'CAD', 'CHF', 'CLP', 'CYP', 'DKK', 'DOP', 'EGP', 'EUR', 'HKD', 'HNL', 'HRK', 'ISK', 'JMD', 'JPY', 'MXN', 'SEK', 'SGD', 'THB', 'USD' ); } public function isNotAcceptableForInvoice(Invoice $invoice) { if ($invoice->rebill_times && ($invoice->rebill_times != '99999')) return 'Incorrect Rebill Times setting!'; if (($invoice->second_total > 0) && ($invoice->second_total != $invoice->first_total)) return 'Firtst & Second price must be the same in invoice!'; if (($invoice->second_period > 0) && ($invoice->second_period != $invoice->first_period)) return 'Firtst & Second period must be the same in invoice!'; } function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $r = new Am_HttpRequest(self::URL, Am_HttpRequest::METHOD_POST); $r->addPostParameter('userkey', $this->getConfig('api_key')); $r->addPostParameter('type', self::REFUND); $r->addPostParameter('version', '2.6'); $r->addPostParameter('transid', $payment->transaction_id); $r->addPostParameter('merchantMID', $this->getPlugin()->getConfig('mid', 1)); $tr = new Am_Paysystem_Transaction_Altcharge_Refund($this, $payment->getInvoice(), $r, $doFirst, $payment->transaction_id); $tr->run($result); } function cancelInvoice(Invoice $invoice) { $r = new Am_HttpRequest(self::URL, Am_HttpRequest::METHOD_POST); $r->addPostParameter('userkey', $this->getConfig('api_key')); $r->addPostParameter('type', self::CANCEL); $r->addPostParameter('version', '2.6'); $r->addPostParameter('transid', $invoice->data()->get(self::INITIAL_TRANSACTION_ID)); $r->addPostParameter('merchantMID', $this->getPlugin()->getConfig('mid', 1)); $tr = new Am_Paysystem_Transaction_Altcharge($this, $payment->getInvoice(), $r, $doFirst); $tr->run(new Am_Paysystem_Result()); } function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Transaction_Altcharge_Incoming($this, $request, $response, $invokeArgs); } function getReadme() { return <<response; } public function parseResponse() { $this->response = trim($this->response->getBody()); } public function validate() { if (preg_match('/^\d+$/', $this->response)) $this->result->setSuccess($this); else $this->result->setFailed($this->response); } public function processValidated() { // } } class Am_Paysystem_Transaction_Altcharge_Refund extends Am_Paysystem_Transaction_Altcharge { protected $origID; function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $request, $doFirst, $origID) { parent::__construct($plugin, $invoice, $request, $doFirst); $this->origID = $origID; } public function processValidated() { $this->invoice->addRefund($this, $this->origID); } } class Am_Paysystem_Transaction_Altcharge_Sale extends Am_Paysystem_Transaction_Altcharge { function processValidated() { $this->invoice->addPayment($this); $this->invoice->data()->set(Am_Paysystem_Altcharge::INITIAL_TRANSACTION_ID, $this->getUniqId())->update(); } } class Am_Paysystem_Transaction_Altcharge_Incoming extends Am_Paysystem_Transaction_Incoming { public function findInvoiceId() { return $this->request->getFiltered('misc1'); } public function getUniqId() { return $this->request->get('tid'); } public function validateSource() { $this->_checkIp('208.72.244.208-208.72.244.223'); return true; } public function validateStatus() { return ($this->request->get('status') == 'APPROVED'); } public function validateTerms() { return true; } } class Am_Form_CreditCard_AltchargeCheck extends Am_Form_CreditCard { public function init() { $name = $this->addGroup() ->setLabel(___("Your Name\n" . 'your first and last name')); $name->addRule('required', ___('Please enter your name')); $name_f = $name->addText('cc_name_f', array('size' => 15)); $name_f->addRule('required', ___('Please enter first name'))->addRule('regex', ___('Please enter first name'), '|^[a-zA-Z_\' -]+$|'); $name_l = $name->addText('cc_name_l', array('size' => 15)); $name_l->addRule('required', ___('Please enter your last name'))->addRule('regex', ___('Please enter your last name'), '|^[a-zA-Z_\' -]+$|'); $options = $this->plugin->getFormOptions(); if ($this->formType == self::ADMIN_UPDATE) { $group = $this->addGroup()->setLabel(___('Credit Card Number'), ___('for example: 1111-2222-3333-4444')); $group->addStatic('cc'); $cc = $group->addText('cc_number', array('autocomplete' => 'off', 'size' => 22, 'maxlength' => 22, 'style' => 'display:none')); $cc->addRule('regex', ___('Invalid Credit Card Number'), '/^[0-9 -]+$/') ->addRule('callback2', 'Invalid CC#', array($this->plugin, 'validateCreditCardNumber')); $group->addScript("")->setScript(<<addText('account_number', array('autocomplete' => 'off', 'size' => 22, 'maxlength' => 22)) ->setLabel(___('Your Bank Account Number')) ->addRule('required', ___('Please enter Account Number')) ->addRule('regex', ___('Invalid Account Number'), '/^[0-9]+$/'); $this->addText('routing_number', array('autocomplete' => 'off', 'size' => 9, 'maxlength' => 9)) ->setLabel(___('Your 9 digit ABA Routing Number')) ->addRule('required', ___('Please enter Routing Number')) ->addRule('regex', ___('Invalid Routing Number'), '/^[0-9]+$/'); } $fieldSet = $this->addFieldset(___('Address Info'))->setLabel(___('Address Info')); $street = $fieldSet->addText('cc_street')->setLabel(___('Street Address')) ->addRule('required', ___('Please enter Street Address')); if (in_array(Am_Paysystem_CreditCard::CC_HOUSENUMBER, $options)) { $house = $fieldSet->addText('cc_housenumber', array('size' => 15))->setLabel(___('Housenumber')) ->addRule('required', ___('Please enter housenumber')); } $city = $fieldSet->addText('cc_city')->setLabel(___('City')) ->addRule('required', ___('Please enter City')); if (in_array(Am_Paysystem_CreditCard::CC_PROVINCE_OUTSIDE_OF_US, $options)) { $province = $fieldSet->addText('cc_province', array('size' => 15)) ->setLabel(___("Billing International Province\n" . 'for international provinces outside of US & Canada include the province name here')) ->addRule('required', ___('Please choose state')); } $zip = $fieldSet->addText('cc_zip')->setLabel(___('ZIP')) ->addRule('required', ___('Please enter ZIP code')); $country = $fieldSet->addSelect('cc_country')->setLabel(___('Country')) ->setId('f_cc_country') ->loadOptions(Am_Di::getInstance()->countryTable->getOptions(true)); $country->addRule('required', ___('Please enter Country')); $group = $fieldSet->addGroup()->setLabel(___('State')); $group->addRule('required', ___('Please enter State')); /** @todo load correct states */ $stateSelect = $group->addSelect('cc_state') ->setId('f_cc_state') ->loadOptions($stateOptions = Am_Di::getInstance()->stateTable->getOptions(@$_REQUEST['cc_country'], true)); $stateText = $group->addText('cc_state')->setId('t_cc_state'); $disableObj = $stateOptions ? $stateText : $stateSelect; $disableObj->setAttribute('disabled', 'disabled')->setAttribute('style', 'display: none'); if (in_array(Am_Paysystem_CreditCard::CC_PHONE, $options)) { $phone = $fieldSet->addText('cc_phone', array('size' => 14))->setLabel(___('Phone')) ->addRule('required', ___('Please enter phone number')) ->addRule('regex', ___('Please enter phone number'), '|^[\d() +-]+$|'); } $sig = $this->addFieldset(___('Signature'))->setLabel(___('Signature')); $sig->addElement('html', 'signature')->setHtml(<<
Please press and hold left mouse button to sign
EOSIG ); // if free trial set _TPL_CC_INFO_SUBMIT_BUT2 $buttons = $this->addGroup(); $buttons->addSubmit('_cc_', array('value' => ' ' . $this->payButtons[$this->formType] . ' ')); if ($this->formType == self::USER_UPDATE) { $buttons->addInputButton('_cc_', array('value' => ' ' . ___("Back") . ' ', 'onclick' => 'goBackToMember()')); $this->addScript("")->setScript("function goBackToMember(){ window.location = amUrl('/member'); }"); } $this->addProlog(<< EOL ); $this->addEpilog(<< EOL2 ); $this->plugin->onFormInit($this); } /** * Return array of default values based on $user record * @param User $user */ public function getDefaultValues(User $user) { return array( 'cc_name_f' => $user->name_f, 'cc_name_l' => $user->name_l, 'cc_street' => $user->street, 'cc_city' => $user->city, 'cc_state' => $user->state, 'cc_country' => $user->country, 'cc_zip' => $user->zip, 'cc_phone' => $user->phone, ); } public function validate() { return parent::validate() && $this->plugin->onFormValidate($this); } public function getValue() { $ret = parent::getValue(); array_walk_recursive($ret, function(&$v, $k) {$v=trim($v);}); return $ret; } public function toCcRecord(CcRecord $cc) { $values = $this->getValue(); $values['cc_expire'] = '1299'; unset($values['_cc_bin_name']); unset($values['_cc_bin_phone']); unset($values['a']); unset($values['id']); unset($values['cc_code']); unset($values['action']); $values['cc_number'] = $values['account_number'] . '-' . $values['routing_number']; unset($values['account_number']); unset($values['routing_number']); $cc->setForInsert($values); } }plugins/cybersource.php000064400000025204152101601040011254 0ustar00getConfig('merchant_id')) && strlen($this->getConfig('transaction_key')); } public function _initSetupForm(Am_Form_Setup $form) { $form->addText("merchant_id") ->setLabel('CyberSource Merchant ID') ->addRule('required'); $form->addText("transaction_key", array('class' => 'el-wide')) ->setLabel('CyberSource SOAP API Security Transaction Key') ->addRule('required'); $form->addAdvCheckbox("test_mode") ->setLabel("Test Mode Enabled"); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { $user = $invoice->getUser(); $soapData = new stdClass(); $soapData->merchantID = $this->getConfig('merchant_id'); $soapData->merchantReferenceCode = $invoice->public_id . '/' . $invoice->getLineDescription(); $soapData->clientLibrary = 'aMember'; $soapData->clientLibraryVersion = '4.x'; $soapData->clientEnvironment = php_uname(); $soapData->customerID = $user->pk(); $soapData->customerFirstName = $user->name_f; $soapData->customerLastName = $user->name_l; $billTo = new stdClass(); $billTo->firstName = $cc->cc_name_f ? $cc->cc_name_f : $user->name_f; $billTo->lastName = $cc->cc_name_l ? $cc->cc_name_l : $user->name_l; $billTo->street1 = $cc->cc_street ? $cc->cc_street : $user->street; $billTo->city = $cc->cc_city ? $cc->cc_city : $user->city; $billTo->state = $cc->cc_state ? $cc->cc_state : $user->state; $billTo->postalCode = $cc->cc_zip ? $cc->cc_zip : $user->zip; $billTo->country = $cc->cc_country ? $cc->cc_country : $user->country; $billTo->email = $user->email; $billTo->ipAddress = $this->getDi()->request->getClientIp(); $soapData->billTo = $billTo; $card = new stdClass(); $card->accountNumber = $cc->cc_number; $card->expirationMonth = substr($cc->cc_expire, 0, 2); $card->expirationYear = 2000 + substr($cc->cc_expire, 2, 2); $card->cvNumber = $cc->getCvv(); $soapData->card = $card; $purchaseTotals = new stdClass(); $purchaseTotals->currency = $invoice->currency; $soapData->purchaseTotals = $purchaseTotals; $soapData->item = array(); $id = 0; foreach ($invoice->getItems() as $item) { $itm = new stdClass(); $itm->unitPrice = $doFirst ? ( (float)$invoice->first_total ? $item->first_total : 1.00 ) : $item->second_total; $itm->quantity = $item->qty; $itm->id = $id++; $soapData->item[] = $itm; } if ($doFirst && !(float)$invoice->first_total) // first & free { $ccAuthService = new stdClass(); $ccAuthService->run = "true"; $soapData->ccAuthService = $ccAuthService; $trAuth = new Am_Paysystem_Transaction_CreditCard_Cybersource_Auth($this, $invoice, $doFirst, $soapData); $trAuth->run($result); $requestId = $trAuth->getRequestId(); $requestToken = $trAuth->getToken(); if (!$requestId || !$requestToken) { return $result->setFailed(array("CyberSource Plugin: Bad auth response.")); } $soapData->ccAuthService = null; $ccAuthReversalService = new stdClass(); $ccAuthReversalService->authRequestID = $requestId; $ccAuthReversalService->authRequestToken = $requestToken; $ccAuthReversalService->run = "true"; $soapData->ccAuthReversalService = $ccAuthReversalService; $trVoid = new Am_Paysystem_Transaction_CreditCard_Cybersource_Auth($this, $invoice, $doFirst, $soapData); $trVoid->run($result); $trFree = new Am_Paysystem_Transaction_Free($this); $trFree->setInvoice($invoice); $trFree->process(); $result->setSuccess($trFree); } else { $ccCreditService = new stdClass(); $ccCreditService->run = "true"; $soapData->ccCreditService = $ccCreditService; $tr = new Am_Paysystem_Transaction_CreditCard_Cybersource($this, $invoice, $doFirst, $soapData); $tr->run($result); } } public function getReadme() { return <<http://www.cybersource.com/register - generate SOAP API Security Keys by this insctruction http://www.cybersource.com/support_center/implementation/downloads/soap_api/SOAP_toolkits.pdf, paragraph 'Transaction Key' - copy all text of just created key and paste it at 'CyberSource SOAP API Security Transaction Key' plugin option (this page) - fill 'CyberSource Merchant ID' option (this page) - click "Save" CyberSource plugin requires at least these php-extensions:SOAP, OpenSSL and libxml. For configuring your web-server read instruction http://www.cybersource.com/support_center/implementation/downloads/soap_api/SOAP_toolkits.pdf, paragraph 'Preparing your PHP Installation'. ATTENTION: SOAP API Security Keys for test and live account are diffrent always. CUT; } } class Am_Paysystem_Transaction_CreditCard_Cybersource extends Am_Paysystem_Transaction_CreditCard { protected $soapData; function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst, $soapData) { $this->soapData = $soapData; parent::__construct($plugin, $invoice, new Am_Mvc_Request(), $doFirst); } public function validate() { if ($this->vars->reasonCode != 100) { $this->result->setFailed($this->vars->reasonCode . ' - ' . $this->vars->decision); return; } $this->result->setSuccess(); } public function parseResponse() { $this->vars = json_decode(json_encode($this->response));; return $this->vars; } public function getUniqId() { return $this->vars->ccCreditReply->reconciliationID; } public function getRequestId() { return $this->vars->requestID; } public function getToken() { return $this->vars->requestToken; } public function validateResponseStatus(Am_Paysystem_Result $result) { if (empty($this->response)) { $result->setFailed(array("Received empty response from payment server")); } } public function run(Am_Paysystem_Result $result) { $this->result = $result; $log = $this->getInvoiceLog(); try { $soapClient = new SoapClient_Cybersource($this->getPlugin()); } catch (Exception $ex) { throw new Am_Exception_InternalError("Cannot create soapclient object: " . $ex->getMessage()); } $soapResult = $soapClient->runTransaction($this->soapData); $this->response = json_decode(json_encode($soapResult), true); $log->add($this->response); $this->validateResponseStatus($this->result); if ($this->result->isFailure()) return; try { $this->parseResponse(); $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); } } } class Am_Paysystem_Transaction_CreditCard_Cybersource_Auth extends Am_Paysystem_Transaction_CreditCard_Cybersource { public function processValidated(){} // no process payment } class SoapClient_Cybersource extends SoapClient { protected $merchantId; protected $transactionKey; function __construct($plugin) { $this->merchantId = $plugin->getConfig('merchant_id'); $this->transactionKey = $plugin->getConfig('transaction_key'); parent::__construct(($plugin->getConfig('test_mode')) ? Am_Paysystem_Cybersource::URL_TEST : Am_Paysystem_Cybersource::URL_LIVE); } function __doRequest($request, $location, $action, $version, $one_way = 0) { $soapHeader = "{$this->merchantId}{$this->transactionKey}"; $requestDOM = new DOMDocument('1.0'); $soapHeaderDOM = new DOMDocument('1.0'); try { $requestDOM->loadXML($request); $soapHeaderDOM->loadXML($soapHeader); $node = $requestDOM->importNode($soapHeaderDOM->firstChild, true); $requestDOM->firstChild->insertBefore($node, $requestDOM->firstChild->firstChild); $request = $requestDOM->saveXML(); } catch (DOMException $e) { throw new Am_Exception_InternalError("Error adding UsernameToken: " . $e->getMessage()); } return parent::__doRequest($request, $location, $action, $version, $one_way); } } plugins/beanstream-remote.php000064400000021542152101601040012342 0ustar00addText("merchant_id")->setLabel('BeanStream Merchant ID'); $form->addText("passcode", array('class' => 'el-wide')) ->setLabel("Recurring billing passcode\n" . "note that this is not the same passcode\nused for Username/Passcode validation\nin the Process Transaction API"); } public function isConfigured() { return strlen($this->getConfig('merchant_id')) && strlen($this->getConfig('passcode')); } public function getSupportedCurrencies() { return array('USD', 'EUR', 'CAD'); } public function onSetupForms(Am_Event_SetupForms $event) { parent::onSetupForms($event); $event->getForm($this->getId())->removeElementByName('payment.'.$this->getId().'.reattempt'); } public function getRecurringType() { return self::REPORTS_REBILL; } public function storesCcInfo() { return false; } public function getFormOptions() { $ret = parent::getFormOptions(); $ret[] = self::CC_PHONE; return $ret; } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { if(!$doFirst) throw new Am_Exception_InternalError('Cannot to run rebill from aMember'); $post = array( 'approvedPage' => $this->getReturnUrl(), 'declinedPage' => $this->getCancelUrl(), 'errorPage' => $this->getCancelUrl(), 'merchant_id' => $this->getConfig('merchant_id'), 'trnOrderNumber' => $invoice->public_id, 'trnAmount' => $invoice->first_total, 'ordEmailAddress' => $invoice->getUser()->email, 'ordName' => $invoice->getUser()->getName(), 'trnComments' => $invoice->getLineDescription(), ); if ($invoice->second_total > 0) // subscription charges { if ($invoice->first_total != $invoice->second_total) throw new Am_Exception_InternalError('First price must be the same second price'); if ($invoice->first_period != $invoice->second_period) throw new Am_Exception_InternalError('First period must be the same second period'); list($period, $period_unit) = self::parsePeriod($invoice->first_period); $post['trnRecurring'] = 1; $post['rbBillingPeriod'] = $period_unit; $post['rbBillingIncrement'] = $period; } $post['trnCardOwner'] = $cc->cc_name_f . " " . $cc->cc_name_l; $post['trnCardNumber'] = $cc->cc_number; $post['trnExpMonth'] = substr($cc->cc_expire,0,2); $post['trnExpYear'] = substr($cc->cc_expire,2); $post['ordAddress1'] = $cc->cc_street; $post['ordCity'] = $cc->cc_city; $post['ordCountry'] = $cc->cc_country; $post['ordProvince'] = $cc->cc_state; $post['ordPostalCode'] = $cc->cc_zip; $post['ordPhoneNumber'] = $cc->cc_phone; $action = new Am_Paysystem_Action_Form(self::URL_PT); foreach ($post as $k => $v) { $action->$k = $v; } $result->setAction($action); } public function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { return new Am_Paysystem_Transaction_Incoming_Beanstream($this, $request, $response, $invokeArgs); } public function cancelAction(Invoice $invoice, $actionName, Am_Paysystem_Result $result) { $this->cancelInvoice($invoice); parent::cancelAction($invoice, $actionName, $result); } public function cancelInvoice(Invoice $invoice) { if(!($rbAccId = $invoice->data()->get(self::RB_ACCOUNT_ID_KEY))) { throw new Am_Exception_InputError("Subscription can not be cancelled"); } $request = new Am_HttpRequest(self::URL_RB, Am_HttpRequest::METHOD_POST); $request->addPostParameter(array( 'serviceVersion' => '1.0', 'operationType' => 'C', 'merchantId' => $this->getConfig('merchant_id'), 'passCode' => $this->getConfig('passcode'), 'rbAccountId' => $rbAccId, )); $response = $request->send()->getBody(); $xml = simplexml_load_string($response); if((string)$xml->code != 1) { throw new Am_Exception_InternalError("Cancel subscription[{$invoice->pk()}/{$invoice->public_id}] ERROR: " . (string)$xml->message); } $invoice->data()->set(self::RB_ACCOUNT_ID_KEY, null)->update(); return true; } static function parsePeriod($period) { preg_match('/(\d+)(\w)/', $period, $matches); list(, $num, $per) = $matches; $per = strtoupper($per); switch ($per) { case 'Y': if (($num < 1) || ($num > 5)) throw new Am_Exception_InternalError("Period must be in interval 1-5 years"); break; case 'M': if (($num < 1) || ($num > 24)) throw new Am_Exception_InternalError("Period must be in interval 1-24 months"); break; case 'D': if (($num < 1) || ($num > 90)) throw new Am_Exception_InternalError("Period must be in interval 1-90 days"); break; default: throw new Am_Exception_InternalError( "Unknown period unit: $per"); } return array($num, $per); } public function getReadme() { $ipn = $this->getPluginUrl('ipn'); $testUrl = 'http://developer.beanstream.com/quick-start/'; return <<BeanstreamRemote plugin installation 1. Copy 'beanstream-remote.php' file to 'amember_main_directory/application/cc/plugins/' 2. Enable 'beanstream-remote' plugin at 'aMember CP -> Setup/Configuration -> Plugins -> Payment Plugins', select 'beanstream-remote' 3. Configure plugin at 'aMember CP -> Setup/Configuration -> BeanstreamRemote': 'BeanStream Merchant ID' you can find at 'Beanstream Account -> administration -> company info' 'Recurring billing passcode' you can find at 'Beanstream Account -> administration -> account settings -> order settings', then 'Recurring Billing' fieldset and 'API access passcode' option (if it's empty - click 'Generate New Code' button) NOTE:Before using this plugin at live mode you may test it using developer account (registration here - $testUrl) and these test credit crads: TYPE CARD NUMBER | RESPONSE -------------------------------------------- VISA | 4030 0000 1000 1234 | Approved VISA | 4003 0505 0004 0005 | Declined MasterCard | 5100 0000 1000 1004 | Approved MasterCard | 5100 0000 2000 2000 | Declined AMEX | 3711 0000 1000 131 | Approved AMEX | 3424 0000 1000 180 | Declined 4. Go to 'Beanstream Account -> administration -> account settings -> order settings', find 'Response Notification' fieldset and fields 'Payment Gateway' & 'Recurring billing'. Insert to these both field this url: $ipn CUT; } } class Am_Paysystem_Transaction_Incoming_Beanstream extends Am_Paysystem_Transaction_Incoming { public function findInvoiceId() { if($this->request->getFiltered('billingId')) return $this->request->getFiltered('orderNumber'); return $this->request->getFiltered('trnOrderNumber'); } public function getUniqId() { return $this->request->getFiltered('trnId'); } public function validateSource() { return true; } public function validateStatus() { return $this->request->getFiltered('trnApproved') == 1; } public function validateTerms() { return true; } public function processValidated() { if($rbAccId = $this->request->get('rbAccountId')) $this->invoice->data()->set(Am_Paysystem_BeanstreamRemote::RB_ACCOUNT_ID_KEY, $rbAccId)->update(); parent::processValidated(); } } plugins/networkmerchants-ach.php000064400000001644152101601040013060 0ustar00getConfig('hosted') || $this->getConfig('acceptjs')) return false; else return true; } function allowPartialRefunds() { return true; } public function getRecurringType() { return self::REPORTS_CRONREBILL; } public function getFormOptions() { $ret = parent::getFormOptions(); $ret[] = self::CC_PHONE; return $ret; } public function supportsCancelPage() { return $this->getConfig('hosted'); } public function getSupportedCurrencies() { return array('USD', 'EUR', 'GBP', 'CAD'); } public function isConfigured() { return strlen($this->getConfig('login')) && strlen($this->getConfig('tkey')); } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { if (!$this->getConfig('hosted') && !$this->getConfig('acceptjs')) { $user = $invoice->getUser(); if ($cc->user_id != $user->pk()) throw new Am_Exception_Paysystem("Assertion failed: cc.user_id != user.user_id"); // will be stored only if cc# or expiration changed $this->storeCreditCard($cc, $result); if (!$result->isSuccess()) return; $user->refresh(); // we have both profile id and payment id, run the necessary transaction now if amount > 0 $result->reset(); } //moved from AIM to CIM elseif(!$invoice->getUser()->data()->get(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY)) { $cc = $this->getDi()->ccRecordTable->findFirstByUserId($invoice->user_id); if (!$cc) throw new Am_Exception_Paysystem("No credit card saved, cannot rebill"); $user = $invoice->getUser(); if ($cc->user_id != $user->pk()) throw new Am_Exception_Paysystem("Assertion failed: cc.user_id != user.user_id"); // will be stored only if cc# or expiration changed $this->storeCreditCard($cc, $result); if (!$result->isSuccess()) return; $user->refresh(); // we have both profile id and payment id, run the necessary transaction now if amount > 0 $result->reset(); } $this->_doTheBill($invoice, $doFirst, $cc, $result); } /* process invoice after creating payment profile */ public function _doTheBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { if ($doFirst && (doubleval($invoice->first_total) <= 0)) { // free trial $tr = new Am_Paysystem_Transaction_Free($this); $tr->setInvoice($invoice); $tr->process(); $result->setSuccess($tr); } else { //fix for previously not saved payment profile if(!$invoice->getUser()->data()->get(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY)) $this->loadLastProfile($invoice); $tr = new Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerProfileTransaction($this, $invoice, $doFirst); $tr->run($result); } } /** * Attempt to laod payment profile from Authorize.Net * if profile exists, it is set in user's record. * * Return Am_Paysystem_result * @param Invoice $invoice * @return Am_Paysystem_Result $result; */ public function loadLastProfile(Invoice $invoice) { // fetch list of payment profiles from Auth.Net and save latest to customer $result = new Am_Paysystem_Result(); $tr = new Am_Paysystem_Transaction_AuthorizeCim_GetCustomerProfile($this, $invoice, $invoice->getUser()->data()->get(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY)); $tr->run($result); if ($result->isSuccess()) { $invoice->getUser()->data()->set(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY, $tr->getUniqId())->update(); } return $result; } public function _initSetupForm(Am_Form_Setup $form) { $label = "Use Hosted Version (recommended)\n". "this option allows you to display credit card input right on your website\n". "(as a popup) and in the same time it does not require PCI DSS compliance\n". "Maxmind verification will not work if enable this option"; if ('https' != substr(ROOT_SURL,0,5)) $label .= "\n" . 'This option requires https on your site'; $form->addAdvCheckbox('hosted')->setLabel($label); $form->addAdvCheckbox('acceptjs')->setLabel( "Use Accept.JS library to capture CC info\n" . "Using this method, CC info will be submited to Authorize.net servers directly. " . "So it doesn't require PCI DSS compliance" ); $form->addText('public_key', array('class'=>'el-wide', 'rel'=>'acceptjs-version')) ->setLabel("Public key for the merchant\n" . "It can be generated in the Authorize.Net Merchant interface\n" . "at Account > Settings > Security Settings > General Security Settings > Manage Public Client Key."); $form->addAdvCheckbox('zip_validate', array()) ->setLabel("Validate Postal Code?\nSpecify whether Checkout should validate the billing postal code"); $form->addText("login")->setLabel("API Login ID\n" . 'can be obtained from the same page as Transaction Key (see below)'); $form->addText("tkey")->setLabel("Transaction Key\n" . "The transaction key is generated by the system and can be obtained from Merchant Interface. To obtain the transaction key from the Merchant Interface: * Log into the Merchant Interface * Select [Settings] from the Main Menu * Click on [API Login ID and Transaction Key] in the Security section * Type in the answer to the secret question configured on setup * Click Submit"); $form->addSelect("validationMode") ->setLabel("Validation mode for creating customer profile" . "\n" . "Validation mode allows you to generate a test transaction at the time you create a customer payment profile. In Test Mode, only field validation is performed. In Live Mode, a transaction is generated and submitted to the processor with the amount of $0.01. If successful, the transaction is immediately voided. When a value of \"none\" is submitted, no additional validation is performed. ") ->loadOptions(array( 'liveMode' => 'liveMode', 'testMode' => 'testMode', 'none' => 'none' )); $form->addAdvCheckbox("testing")->setLabel("Test Mode Enabled" . "\n" . "The Test Mode requires a separate developer test account, which can be set up by filling out the following form: http://developer.authorize.net/testaccount"); $form->addScript()->setScript(<<pk(); return $userOrId . '-' . substr($this->getDi()->security->siteHash('cim'), 0, 5); } public function storeCreditCard(CcRecord $cc, Am_Paysystem_Result $result) { $user = $this->getDi()->userTable->load($cc->user_id); $profileId = $user->data()->get(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY); 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 ($profileId && ($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))) { $tr = new Am_Paysystem_Transaction_AuthorizeCim_UpdateCustomerPaymentProfile($this, $invoice, $cc); $tr->run($result); if($result->isFailure()){ // Try to delete all profiles and create new one. $result->reset(); $user->data() ->set(self::USER_PROFILE_KEY, null) ->set(self::PAYMENT_PROFILE_KEY, null) ->update(); $deleteTr = new Am_Paysystem_Transaction_AuthorizeCim_DeleteCustomerProfile($this, $invoice, $profileId); $deleteTr->run($res = new Am_Paysystem_Result); $profileId = null; } } } if (!$profileId) { try { $tr = new Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerProfile($this, $invoice, $cc); $tr->run($result); if (!$result->isSuccess()) { if($tr->getErrorCode() == 'E00039') { $error = $result->getLastError(); if(preg_match('/A duplicate record with ID (\d+) already exists/', $error, $regs) && $regs[1]) { $user->data()->set(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY, $regs[1])->update(); $result->reset(); $result->setSuccess(); } else { return; } } else if($tr->getErrorCode() == 'E00027') { // Authorize.Net has 2 minutes timeframe before each provide validation attempt in live mode. // If interval between two createCustomerProfile requests is less then 2 minutes Duplicate error will be returned. // We need to inform customer about such delay. $error = $result->getLastError(); if(preg_match('/A duplicate transaction has been submitted/', $error)) { $result->setFailed(___("A duplicate transaction has been submitted. Please wait 2 minutes before next attempt")); } else { return; } } else { return; } }else{ $user->data()->set(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY, $tr->getProfileId())->update(); $user->data()->set(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY, $tr->getPaymentId())->update(); } } catch (Am_Exception_Paysystem $e) { $result->setFailed($e->getPublicError()); return false; } } $paymentProfileId = $user->data()->get(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY); if (!$paymentProfileId) { try { $tr = new Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerPaymentProfile($this, $invoice, $cc); $tr->run($result); if($tr->getErrorCode() == 'E00039') { $error = strtolower($result->getLastError()); if(preg_match('/a duplicate customer payment profile already exists/', $error, $regs)) { $user->data()->set(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY, $tr->getProfileId())->update(); $result->reset(); $result->setSuccess(); } else { return; } } elseif (!$result->isSuccess()) return; $user->data()->set(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY, $tr->getProfileId())->update(); } catch (Am_Exception_Paysystem $e) { $result->setFailed($e->getPublicError()); return false; } } /// $cc->cc = $cc->maskCc(@$cc->cc_number); $cc->cc_number = '0000000000000000'; if ($cc->pk()) $cc->update(); else $cc->replace(); $result->setSuccess(); } public function getReadme() { return <<You need to enable CIM service in your authorize.net account to use this plugin. (Tools -> Customer Information Manager -> Sign Up Now) This is a paid service. 1. Enable and configure plugin in aMember CP -> Setup -> Plugins 2. You NEED to use external cron with this plugins (See aMember CP -> Configuration -> Setup/Configuration -> Advanced) CUT; } function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount) { $trans = new Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerProfileTransactionRefund($this, $payment->getInvoice(), $payment, $amount); $trans->run($result); } function createCustomerProfile(Invoice $invoice){ $tr = new Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerProfile($this, $invoice); $result = new Am_Paysystem_Result(); $tr->run($result); if (!$result->isSuccess()) { if($tr->getErrorCode() == 'E00039') { $error = $result->getLastError(); if(preg_match('/A duplicate record with ID (\d+) already exists/', $error, $regs) && $regs[1]) { $invoice->getUser()->data()->set(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY, $regs[1])->update(); return; } else { throw new Am_Exception_Paysystem("Failed Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerProfile " . $result->getLastError()); } } else if($tr->getErrorCode() == 'E00027') { // Authorize.Net has 2 minutes timeframe before each provide validation attempt in live mode. // If interval between two createCustomerProfile requests is less then 2 minutes Duplicate error will be returned. // We need to inform customer about such delay. $error = $result->getLastError(); if(preg_match('/A duplicate transaction has been submitted/', $error)) { throw new Am_Exception_Paysystem(___("A duplicate transaction has been submitted. Please wait 2 minutes before next attempt")); } else { throw new Am_Exception_Paysystem("Failed Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerProfile " . $result->getLastError()); } } throw new Am_Exception_Paysystem("Failed Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerProfile " . $result->getLastError()); } $invoice->getUser()->data()->set(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY, $tr->getProfileId())->update(); return $tr->getProfileId(); } function getHostedProfilePageToken() { $user = $this->getDi()->userTable->load($this->invoice->user_id); $profileId = $user->data()->get(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY); $invoice = $this->invoice; if (!$profileId) $profileId = $this->createCustomerProfile ($invoice); $tr = new Am_Paysystem_Transaction_AuthorizeCim_GetHostedProfilePageRequest($this, $invoice, $profileId); $result = new Am_Paysystem_Result(); $tr->run($result); if(!$tr->getUniqId() && ($tr->getErrorCode() == 'E00040')){ // Profile doesn't exists on Authorize.NET $profileId = $this->createCustomerProfile($invoice); $tr = new Am_Paysystem_Transaction_AuthorizeCim_GetHostedProfilePageRequest($this, $invoice, $profileId); $result = new Am_Paysystem_Result(); $tr->run($result); } if (!$tr->getUniqId()) throw new Am_Exception_Paysystem("Could not get hosted-profile-page-token from authorize.net - connection problem"); return $tr->getUniqId(); } protected function createController(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs) { if ($this->getConfig('hosted')) return new Am_Mvc_Controller_CreditCard_AuthorizeNet($request, $response, $invokeArgs); elseif ($this->getConfig('acceptjs')) return new Am_Mvc_Controller_CreditCard_AuthorizeNetAcceptJs($request, $response, $invokeArgs); else return parent::createController($request, $response, $invokeArgs); } public function directAction( $request, $response, $invokeArgs) { if ($request->getActionName() == 'iframe') { $p = $this->createController($request, $response, $invokeArgs); $p->setPlugin($this); $p->run(); return; } parent::directAction($request, $response, $invokeArgs); } public function getUpdateCcLink($user) { $inv = $this->getDi()->invoiceTable->findFirstBy(array('user_id' => $user->pk(), 'paysys_id' => $this->getId()), 0, 1); if ($inv) return $this->getPluginUrl('update'); } public function canUseMaxmind() { return true; } } abstract class Am_Paysystem_Transaction_AuthorizeCim extends Am_Paysystem_Transaction_CreditCard { protected $apiName = null; protected $aimResponse = null; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $doFirst = true) { parent::__construct($plugin, $invoice, $plugin->createHttpRequest(), $doFirst); $this->request->setHeader('Content-type', 'text/xml'); $this->request->setBody($this->createXml($this->apiName)->asXml()); $this->request->setMethod(Am_HttpRequest::METHOD_POST); $this->request->setUrl(!$this->plugin->getConfig('testing') ? Am_Paysystem_AuthorizeCim::LIVE_URL : Am_Paysystem_AuthorizeCim::SANDBOX_URL); } /** @return SimpleXmlElement */ protected function createXml($name) { $xml = new SimpleXmlElement('<'.$name.'/>'); $xml['xmlns'] = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'; $xml->merchantAuthentication->name = $this->plugin->getConfig('login'); $xml->merchantAuthentication->transactionKey = $this->plugin->getConfig('tkey'); return $xml; } public function parseResponse() { $body = trim($this->response->getBody()); $body = str_replace('xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd"', '', $body); $this->xml = new SimpleXMLElement($body); } public function parseAimString($string) { $vars = explode(',', $string); $this->aimResponse = new stdclass; if (count($vars) < 10) { $this->aimResponse->error_message = "Unrecognized response from Authorize.Net: " . $string; return false; } // Set all fields $this->aimResponse->aimResponse_code = $vars[0]; $this->aimResponse->aimResponse_subcode = $vars[1]; $this->aimResponse->aimResponse_reason_code = $vars[2]; $this->aimResponse->aimResponse_reason_text = $vars[3]; $this->aimResponse->authorization_code = $vars[4]; $this->aimResponse->avs_response = $vars[5]; $this->aimResponse->transaction_id = $vars[6]; $this->aimResponse->invoice_number = $vars[7]; $this->aimResponse->description = $vars[8]; $this->aimResponse->amount = $vars[9]; $this->aimResponse->method = $vars[10]; $this->aimResponse->transaction_type = $vars[11]; $this->aimResponse->customer_id = $vars[12]; $this->aimResponse->first_name = $vars[13]; $this->aimResponse->last_name = $vars[14]; $this->aimResponse->company = $vars[15]; $this->aimResponse->address = $vars[16]; $this->aimResponse->city = $vars[17]; $this->aimResponse->state = $vars[18]; $this->aimResponse->zip_code = $vars[19]; $this->aimResponse->country = $vars[20]; $this->aimResponse->phone = $vars[21]; $this->aimResponse->fax = $vars[22]; $this->aimResponse->email_address = $vars[23]; $this->aimResponse->ship_to_first_name = $vars[24]; $this->aimResponse->ship_to_last_name = $vars[25]; $this->aimResponse->ship_to_company = $vars[26]; $this->aimResponse->ship_to_address = $vars[27]; $this->aimResponse->ship_to_city = $vars[28]; $this->aimResponse->ship_to_state = $vars[29]; $this->aimResponse->ship_to_zip_code = $vars[30]; $this->aimResponse->ship_to_country = $vars[31]; $this->aimResponse->tax = $vars[32]; $this->aimResponse->duty = $vars[33]; $this->aimResponse->freight = $vars[34]; $this->aimResponse->tax_exempt = $vars[35]; $this->aimResponse->purchase_order_number= $vars[36]; $this->aimResponse->md5_hash = $vars[37]; $this->aimResponse->card_code_response = $vars[38]; $this->aimResponse->cavv_response = $vars[39]; $this->aimResponse->account_number = $vars[40]; $this->aimResponse->card_type = $vars[51]; $this->aimResponse->split_tender_id = $vars[52]; $this->aimResponse->requested_amount = $vars[53]; $this->aimResponse->balance_on_card = $vars[54]; return true; } } class Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerProfile extends Am_Paysystem_Transaction_AuthorizeCim { protected $apiName = 'createCustomerProfileRequest'; /** @var CcRecord */ protected $cc; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, CcRecord $cc = null) { $this->cc = $cc; parent::__construct($plugin, $invoice); } protected function createXml($name) { $xml = parent::createXml($name); $user = $this->invoice->getUser(); $xml->profile->merchantCustomerId = $this->plugin->getMerchantCustomerId($this->cc ? $this->cc->user_id : $this->invoice->user_id); $xml->profile->description = "Username: $user->login"; $xml->profile->email = $user->email; if ($this->cc) { $xml->profile->paymentProfiles->billTo->firstName = $this->cc->cc_name_f; $xml->profile->paymentProfiles->billTo->lastName = $this->cc->cc_name_l; $xml->profile->paymentProfiles->billTo->address = $this->cc->cc_street; $xml->profile->paymentProfiles->billTo->city = $this->cc->cc_city; $xml->profile->paymentProfiles->billTo->state = $this->cc->cc_state; $xml->profile->paymentProfiles->billTo->zip = $this->cc->cc_zip; $xml->profile->paymentProfiles->billTo->country = $this->cc->cc_country; $xml->profile->paymentProfiles->billTo->phoneNumber = $this->cc->cc_phone; $xml->profile->paymentProfiles->payment->creditCard->cardNumber = $this->cc->cc_number; $xml->profile->paymentProfiles->payment->creditCard->expirationDate = $this->cc->getExpire('20%2$02d-%1$02d'); if (strlen($this->cc->getCvv())) $xml->profile->paymentProfiles->payment->creditCard->cardCode = $this->cc->getCvv(); $xml->validationMode = $this->getPlugin()->getConfig('validationMode', 'liveMode'); } if($user->data()->get(Am_Paysystem_AuthorizeCim::OPAQUE_DATA_VALUE)){ $xml->profile->paymentProfiles->billTo->firstName = $this->invoice->getFirstName(); $xml->profile->paymentProfiles->billTo->lastName = $this->invoice->getLastName(); $xml->profile->paymentProfiles->payment->opaqueData->dataDescriptor = $user->data()->get(Am_Paysystem_AuthorizeCim::OPAQUE_DATA_DESCRIPTOR); $xml->profile->paymentProfiles->payment->opaqueData->dataValue = $user->data()->get(Am_Paysystem_AuthorizeCim::OPAQUE_DATA_VALUE); } return $xml; } public function validate() { if ($this->xml->getName() != 'createCustomerProfileResponse') { $this->result->setFailed(___('Payment failed')); return; } if ((string)$this->xml->messages->message->code != 'I00001') { $this->result->setFailed((string)$this->xml->messages->message->text); return; } $this->result->setSuccess(); return true; } function getProfileId() { return (string)$this->xml->customerProfileId; } function getPaymentId() { return (string)$this->xml->customerPaymentProfileIdList->numericString; } public function getUniqId() { return (string)$this->xml->customerProfileId; } public function processValidated() { } public function getErrorCode(){ return (string)$this->xml->messages->message->code; } } class Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerPaymentProfile extends Am_Paysystem_Transaction_AuthorizeCim { protected $apiName = 'createCustomerPaymentProfileRequest'; /** @var CcRecord */ protected $cc; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, CcRecord $cc = null) { $this->cc = $cc; parent::__construct($plugin, $invoice); } protected function createXml($name) { $xml = parent::createXml($name); $user = $this->invoice->getUser(); $xml->customerProfileId = $user->data()->get(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY); if ($this->cc) { $xml->paymentProfile->billTo->firstName = $this->cc->cc_name_f; $xml->paymentProfile->billTo->lastName = $this->cc->cc_name_l; $xml->paymentProfile->billTo->address = $this->cc->cc_street; $xml->paymentProfile->billTo->city = $this->cc->cc_city; $xml->paymentProfile->billTo->state = $this->cc->cc_state; $xml->paymentProfile->billTo->zip = $this->cc->cc_zip; $xml->paymentProfile->billTo->country = $this->cc->cc_country; $xml->paymentProfile->billTo->phoneNumber = $this->cc->cc_phone; $xml->paymentProfile->payment->creditCard->cardNumber = $this->cc->cc_number; $xml->paymentProfile->payment->creditCard->expirationDate = $this->cc->getExpire('20%2$02d-%1$02d'); if (strlen($this->cc->getCvv())) $xml->paymentProfile->payment->creditCard->cardCode = $this->cc->getCvv(); $xml->validationMode = $this->getPlugin()->getConfig('validationMode', 'liveMode'); } if($user->data()->get(Am_Paysystem_AuthorizeCim::OPAQUE_DATA_VALUE)){ $xml->paymentProfile->billTo->firstName = $this->invoice->getFirstName(); $xml->paymentProfile->billTo->lastName = $this->invoice->getLastName(); $xml->paymentProfile->payment->opaqueData->dataDescriptor = $user->data()->get(Am_Paysystem_AuthorizeCim::OPAQUE_DATA_DESCRIPTOR); $xml->paymentProfile->payment->opaqueData->dataValue = $user->data()->get(Am_Paysystem_AuthorizeCim::OPAQUE_DATA_VALUE); } return $xml; } public function validate() { if ($this->xml->getName() != 'createCustomerPaymentProfileResponse') { $this->result->setFailed(___('Payment failed')); return; } if ((string)$this->xml->messages->message->code != 'I00001') { $this->result->setFailed((string)$this->xml->messages->message->text); return; } $this->result->setSuccess(); return true; } function getProfileId() { return (string)$this->xml->customerPaymentProfileId; } public function getUniqId() { return (string)$this->xml->customerPaymentProfileId; } public function processValidated() { } public function getErrorCode(){ return (string)$this->xml->messages->message->code; } } class Am_Paysystem_Transaction_AuthorizeCim_UpdateCustomerPaymentProfile extends Am_Paysystem_Transaction_AuthorizeCim { protected $apiName = 'updateCustomerPaymentProfileRequest'; /** @var CcRecord */ protected $cc; protected $profileId; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, CcRecord $cc = null) { $this->cc = $cc; parent::__construct($plugin, $invoice); } protected function createXml($name) { $xml = parent::createXml($name); $user = $this->invoice->getUser(); $xml->customerProfileId = $user->data()->get(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY); if ($this->cc) { $xml->paymentProfile->billTo->firstName = $this->cc->cc_name_f; $xml->paymentProfile->billTo->lastName = $this->cc->cc_name_l; $xml->paymentProfile->billTo->address = $this->cc->cc_street; $xml->paymentProfile->billTo->city = $this->cc->cc_city; $xml->paymentProfile->billTo->state = $this->cc->cc_state; $xml->paymentProfile->billTo->zip = $this->cc->cc_zip; $xml->paymentProfile->billTo->country = $this->cc->cc_country; $xml->paymentProfile->billTo->phoneNumber = $this->cc->cc_phone; $xml->paymentProfile->payment->creditCard->cardNumber = $this->cc->cc_number; $xml->paymentProfile->payment->creditCard->expirationDate = $this->cc->getExpire('20%2$02d-%1$02d'); if (strlen($this->cc->getCvv())) $xml->paymentProfile->payment->creditCard->cardCode = $this->cc->getCvv(); $xml->validationMode = $this->getPlugin()->getConfig('validationMode', 'liveMode'); $xml->paymentProfile->customerPaymentProfileId = $user->data()->get(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY); } if($user->data()->get(Am_Paysystem_AuthorizeCim::OPAQUE_DATA_VALUE)){ $xml->paymentProfile->billTo->firstName = $this->invoice->getFirstName(); $xml->paymentProfile->billTo->lastName = $this->invoice->getLastName(); $xml->paymentProfile->payment->opaqueData->dataDescriptor = $user->data()->get(Am_Paysystem_AuthorizeCim::OPAQUE_DATA_DESCRIPTOR); $xml->paymentProfile->payment->opaqueData->dataValue = $user->data()->get(Am_Paysystem_AuthorizeCim::OPAQUE_DATA_VALUE); } return $xml; } public function validate() { if ($this->xml->getName() != 'updateCustomerPaymentProfileResponse') { $this->result->setFailed(___('Payment failed')); return; } if ((string)$this->xml->messages->message->code != 'I00001') { $this->result->setFailed((string)$this->xml->messages->message->text); return; } $this->result->setSuccess(); return true; } public function getUniqId() { return uniqid(); } public function processValidated() { } } class Am_Paysystem_Transaction_AuthorizeCim_DeleteCustomerProfile extends Am_Paysystem_Transaction_AuthorizeCim { protected $apiName = 'deleteCustomerProfileRequest'; protected $profileId; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $profileId) { $this->profileId = $profileId; parent::__construct($plugin, $invoice, true); } protected function createXml($name) { $xml = parent::createXml($name); $xml->customerProfileId = $this->profileId; return $xml; } public function validate() { if ($this->xml->getName() != 'deleteCustomerProfileResponse') { $this->result->setFailed(___('Profile update transaction failed')); return; } if ((string)$this->xml->messages->message->code != 'I00001') { $this->result->setFailed((string)$this->xml->messages->message->text); return; } $this->result->setSuccess(); return true; } public function getUniqId() { return uniqid(); } public function processValidated() { } } class Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerProfileTransaction extends Am_Paysystem_Transaction_AuthorizeCim { protected $apiName = 'createCustomerProfileTransactionRequest'; /** @var object */ protected $aimResponse; protected function createXml($name) { $xml = parent::createXml($name); $xml->transaction->profileTransAuthCapture->amount = $this->doFirst ? $this->invoice->first_total : $this->invoice->second_total; $xml->transaction->profileTransAuthCapture->tax->amount = $this->doFirst ? $this->invoice->first_tax : $this->invoice->second_tax; $xml->transaction->profileTransAuthCapture->shipping->amount = $this->doFirst ? $this->invoice->first_shipping : $this->invoice->second_shipping; foreach ($this->invoice->getItems() as $item) { /* @var $item InvoiceItem */ $line = $xml->transaction->profileTransAuthCapture->addChild('lineItems'); $line->itemId = $item->item_id; $line->name = substr($item->item_title, 0, 30); $line->quantity = $item->qty; $price = $this->doFirst ? $item->first_price : $item->second_price; if ($price) $line->unitPrice = $price; if ($this->doFirst ? $item->first_tax : $item->second_tax) $line->taxable = 'true'; else $line->taxable = 'false'; } $user = $this->invoice->getUser(); $taxAmount = $this->doFirst ? $this->invoice->first_tax : $this->invoice->second_tax; if ($taxAmount) $xml->transaction->profileTransAuthCapture->tax->amount = $taxAmount; $xml->transaction->profileTransAuthCapture->customerProfileId = $user->data()->get(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY); $xml->transaction->profileTransAuthCapture->customerPaymentProfileId = $user->data()->get(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY); $xml->transaction->profileTransAuthCapture->order->description = $this->invoice->getLineDescription(); $xml->transaction->profileTransAuthCapture->order->purchaseOrderNumber = $this->invoice->public_id . '-' . $this->invoice->getPaymentsCount(); $xml->transaction->profileTransAuthCapture->recurringBilling = $this->doFirst ? 'true' : 'false'; $xml->addChild('extraOptions', 'x_Customer_IP=' . ($user->remote_addr ? $user->remote_addr : $_SERVER['REMOTE_ADDR'])); return $xml; } public function validate() { if ($this->xml->getName() != 'createCustomerProfileTransactionResponse') { $this->result->setFailed(___('Payment failed')); return; } if (!$this->parseAimString($this->xml->directResponse)) { $this->result->setFailed(___('Payment failed')); return; } if ((string)$this->xml->messages->message->code != 'I00001') { $this->result->setFailed(___('Payment failed')); return; } $this->result->setSuccess($this); return true; } public function getUniqId() { return $this->aimResponse->transaction_id; } public function processValidated() { $this->invoice->addPayment($this); } } class Am_Paysystem_Transaction_AuthorizeCim_CreateCustomerProfileTransactionRefund extends Am_Paysystem_Transaction_AuthorizeCim { protected $apiName = 'createCustomerProfileTransactionRequest'; /** @var object */ protected $aimResponse; public $amount; public $transId; function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, InvoicePayment $payment, $amount) { $this->transId = $payment->receipt_id; $this->amount = $amount; parent::__construct($plugin, $invoice); } protected function createXml($name) { $xml = parent::createXml($name); $xml->transaction->profileTransRefund->amount = $this->amount; $user = $this->invoice->getUser(); $xml->transaction->profileTransRefund->customerProfileId = $user->data()->get(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY); $xml->transaction->profileTransRefund->customerPaymentProfileId = $user->data()->get(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY); $xml->transaction->profileTransRefund->order->description = "Refund for payment: ".$this->transId." amount: ".$this->amount; $xml->transaction->profileTransRefund->order->purchaseOrderNumber = $this->invoice->public_id . '-RFND-' . $this->transId; $xml->transaction->profileTransRefund->transId = $this->transId; return $xml; } public function validate() { if ($this->xml->getName() != 'createCustomerProfileTransactionResponse') { $this->result->setFailed(___('Payment failed')); return; } if (!$this->parseAimString($this->xml->directResponse)) { $this->result->setFailed(___('Payment failed')); return; } if ((string)$this->xml->messages->message->code != 'I00001') { $this->result->setFailed(___('Payment failed')); return; } $this->result->setSuccess(); return true; } public function getUniqId() { return $this->aimResponse->transaction_id; } public function processValidated() { $this->invoice->addRefund($this, $this->transId, $this->amount); } } class Am_Paysystem_Transaction_AuthorizeCim_GetHostedProfilePageRequest extends Am_Paysystem_Transaction_AuthorizeCim { protected $apiName = 'getHostedProfilePageRequest'; /** @var object */ protected $aimResponse; protected $profileId; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $profileId) { $this->profileId = $profileId; parent::__construct($plugin, $invoice, true); } protected function createXml($name) { $xml = parent::createXml($name); $xml->customerProfileId = $this->profileId; /* $xml->hostedProfileSettings->setting[0]->settingName = 'hostedProfileReturnUrl'; $xml->hostedProfileSettings->setting[0]->settingValue = $this->plugin->getReturnUrl(); $xml->hostedProfileSettings->setting[1]->settingName = 'hostedProfileReturnUrlText'; $xml->hostedProfileSettings->setting[1]->settingValue = 'Return'; $xml->hostedProfileSettings->setting[2]->settingName = 'hostedProfileHeadingBgColor'; $xml->hostedProfileSettings->setting[2]->settingValue = ''; */ $xml->hostedProfileSettings->setting[0]->settingName = 'hostedProfilePageBorderVisible'; $xml->hostedProfileSettings->setting[0]->settingValue = 'false'; $xml->hostedProfileSettings->setting[1]->settingName = 'hostedProfileIFrameCommunicatorUrl'; $xml->hostedProfileSettings->setting[1]->settingValue = $this->getPlugin()->getPluginUrl('iframe'); $xml->hostedProfileSettings->setting[2]->settingName = 'hostedProfileValidationMode'; $xml->hostedProfileSettings->setting[2]->settingValue = $this->getPlugin()->getConfig('validationMode', 'liveMode'); return $xml; } public function validate() { if ($this->xml->getName() != 'getHostedProfilePageResponse') { $this->result->setFailed(___('Payment failed')); return; } if ((string)$this->xml->messages->message->code != 'I00001') { $this->result->setFailed(___('Payment failed')); return; } $this->result->setSuccess($this); return true; } public function getUniqId() { return (string)$this->xml->token;; } public function getErrorCode(){ return (string) $this->xml->messages->message->code; } public function processValidated() { } } class Am_Paysystem_Transaction_AuthorizeCim_GetCustomerProfile extends Am_Paysystem_Transaction_AuthorizeCim { protected $apiName = 'getCustomerProfileRequest'; /** @var object */ protected $aimResponse; protected $profileId; public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $profileId) { $this->profileId = $profileId; parent::__construct($plugin, $invoice, true); } protected function createXml($name) { $xml = parent::createXml($name); $xml->customerProfileId = $this->profileId; return $xml; } public function validate() { if ($this->xml->getName() != 'getCustomerProfileResponse') { $this->result->setFailed(___('Payment failed')); return; } if (!$this->getUniqId()) { $this->result->setFailed(___('Payment failed')); return; } $this->result->setSuccess($this); return true; } public function getUniqId() { // fetch LAST payment profile id from customer profile $ret = ''; foreach ($this->xml->profile->paymentProfiles as $x) $ret = (string)$x->customerPaymentProfileId; return $ret; } public function processValidated() { } } class Am_Mvc_Controller_CreditCard_AuthorizeNet extends Am_Mvc_Controller { /** @var Am_Paysystem_AuthorizeCim */ protected $plugin; /** @var Invoice */ protected $invoice; public function setPlugin($plugin) { $this->plugin = $plugin; } public function setInvoice($invoice) { $this->invoice = $invoice; } protected function ccError($msg) { $this->view->content .= "".$msg.""; $url = $this->_request->getRequestUri(); $url .= (strchr($url, '?') ? '&' : '?') . 'id=' . $this->_request->get('id'); $url = Am_Html::escape($url); $this->view->content .= " ".___('Return and try again').""; $this->view->display('layout.phtml'); exit; } /** * Process a transaction using saved customer payment profile * and do either success redirect, or display error * @return Am_Paysystem_Result */ protected function ccActionProcessAfterProfileExists() { $result = new Am_Paysystem_Result(); $this->plugin->_doTheBill($this->invoice, true, $this->getDi()->CcRecordTable->createRecord(), $result); if (@$result->errorCode == 'E00040') // Customer Profile ID or Customer Payment Profile ID not found { // cleanup customer payment profile $user = $this->invoice->getUser(); $user->data()->set(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY, null); $tr = new Am_Paysystem_Transaction_AuthorizeCim_DeleteCustomerProfile($this->plugin, $this->invoice, $user->data()->get(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY)); $tr->run(new Am_Paysystem_Result); $user->data()->set(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY, null); $user->data()->update(); } return $result; } public function ccAction() { $this->view->title = ___('Payment Info'); $this->view->invoice = $this->invoice; $this->view->content = $this->view->render('_receipt.phtml'); if ($this->_request->get('result') == 'success') { if (!$this->invoice->getUser()->data()->get(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY)) { $ret = $this->plugin->loadLastProfile($this->invoice); if(!$ret->isSuccess()){ $this->ccError($ret->getLastError()); } // check status here? } $result = $this->ccActionProcessAfterProfileExists(); if ($result->isSuccess()) { $s = new Zend_Session_Namespace($this->plugin->getId()); $s->setExpirationSeconds(60*30); // after 30 minutes we will reset the session $s->ccConfirmed = true; $this->_response->setRedirect($this->plugin->getReturnUrl()); } else { $this->ccError($result->getLastError()); } } else { if ($this->invoice->getUser()->data()->get(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY)) { // if we have credit card on file, we will try to use it but we // have to display confirmation first $s = new Zend_Session_Namespace($this->plugin->getId()); $s->setExpirationSeconds(60*30); // after 30 minutes we will reset the session $s->ccConfirmed = !empty($s->ccConfirmed); switch ($this->_request->get('result')) { case 'confirm': $s->ccConfirmed = true; break; case 'new' : break; default: if (!$s->ccConfirmed) return $this->displayReuse(); } if ($s->ccConfirmed) { $result = $this->ccActionProcessAfterProfileExists(); if ($result->isSuccess()) { return $this->_response->setRedirect($this->plugin->getReturnUrl()); } } } else { //try to load previously not saved payment profile if($this->plugin->loadLastProfile($this->invoice)->isSuccess()) { return $this->displayReuse(); } } return $this->displayHostedPage($this->plugin->getCancelUrl()); } } protected function displayReuse() { $result = new Am_Paysystem_Result; $tr = new Am_Paysystem_Transaction_AuthorizeCim_GetCustomerProfile($this->plugin, $this->invoice, $this->invoice->getUser()->data()->get(Am_Paysystem_AuthorizeCim::USER_PROFILE_KEY)); $tr->run($result); if (!$result->isSuccess()) throw new Am_Exception_Paysystem("Stored customer profile not found"); $payprofileid = $this->invoice->getUser()->data()->get(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY); foreach ($tr->xml->profile->paymentProfiles as $pp) { if ($payprofileid == (string)$pp->customerPaymentProfileId) { $card = (string)$pp->payment->creditCard->cardNumber; } } if (empty($card)) throw new Am_Exception_Paysystem("Store payment profile not found"); $text = ___('Click "Continue" to pay this order using stored credit card %s', $card); $continue = ___('Continue'); $use_new_card = ___("Use another card"); $url = $this->_request->assembleUrl(false,true); $action = $this->plugin->getPluginUrl('cc'); $id = Am_Html::escape($this->_request->get('id')); $action = Am_Html::escape($action); $this->view->content .= << $text
   $use_new_card
CUT; $this->view->display('layout.phtml'); } public function updateAction() { $user = $this->getDi()->auth->getUser(true); // dirty hack - load last user invoice to be used in process $this->invoice = $this->getDi()->invoiceTable->findFirstBy(array('user_id' => $user->pk(), 'paysys_id' => $this->plugin->getId()), 0, 1, "invoice_id DESC"); if (!$this->invoice) throw new Am_Exception_InternalError("No active invoices found, but update cc info request received?"); $this->plugin->_setInvoice($this->invoice); $this->view->title = ___('Payment Info'); $this->view->invoice = null; $this->view->content = ""; if ($this->_request->get('result') == 'success') { if(!$user->data()->get(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY)) { $this->plugin->loadLastProfile($this->invoice); } $this->_redirect($this->getDi()->url('member',null,false,true)); } return $this->displayHostedPage($this->getDi()->url('member',null,false,true)); } protected function displayHostedPage($cancelUrl) { $id = $this->invoice->getUser()->data()->get(Am_Paysystem_AuthorizeCim::PAYMENT_PROFILE_KEY); if ($id) { $method = 'editPayment'; $id = filterId($id); } else { $method = 'addPayment'; $id = null; } $token = $this->plugin->getHostedProfilePageToken(); $domain = $this->plugin->getConfig('testing') ? 'test' : 'secure'; $cancelUrl = json_encode($cancelUrl); $popupTitle = json_encode(___('Credit Card Info')); $plzwt = ___('Please wait while we process your order...'); $plzwt2 = ___('Click here if you do not want to wait any longer (or if your browser does not automatically forward you).'); $this->view->content .= <<