v7‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT PK\5 FĖ Paypal.phpnu[request->isPost()) $this->request = new Am_Mvc_Request($_POST, 'POST'); $this->txn_type = $this->request->getFiltered('txn_type'); } public function findTime() { $time = get_first( $this->request->get('subscr_date'), $this->request->get('payment_date')); if (!$time) return parent::findTime(); $d = new DateTime($time); $d->setTimezone(new DateTimeZone(date_default_timezone_get())); return $d; } public function getUniqId() { return $this->request->getFiltered('txn_id', $this->txn_type . '-' . $this->getTime()->format('YmdHis')); } function emailRegardingWrongBusiness($incoming, array $businesses){ $root_url = ROOT_URL; $businesses = implode(',', $businesses); $msg = << Configuration -> Setup/Configuration -> PayPal $businesses If it is really your transaction and your primary PayPal email address is $incoming, go to aMember CP -> Configuration -> Setup/Configuration -> PayPal and set PayPal email address as $incoming Once you have fixed the configuration, please visit Amember CP -> Utilities -> Logs -> Invoice, find this transaction (invoice #{$this->request->invoice}), and press "Retry Processing". -- Your aMember Pro script P.S. If you have any questions, resend this email to support@cgi-central.net with server access details. CUT; $mail = $this->getPlugin()->getDi()->mail; $mail->toAdmin(); $mail->setBodyText($msg); $mail->setSubject('*** PayPal plugin error in Amember ***'); $mail->send(); } public function validateSource() { // validate if that is genuine POST coming from PayPal if (!$this->plugin->getConfig('dont_verify')) { try { $req = $this->plugin->createHttpRequest(); $domain = $this->plugin->getConfig('testing') ? 'www.sandbox.paypal.com' : 'www.paypal.com'; $req->setConfig('ssl_verify_peer', false); $req->setConfig('ssl_verify_host', false); $req->setUrl('https://'.$domain.'/cgi-bin/webscr'); $req->addPostParameter('cmd','_notify-validate'); foreach ($this->request->getRequestOnlyParams() as $key => $value) $req->addPostParameter($key, $value); $req->setMethod(Am_HttpRequest::METHOD_POST); $resp = $req->send(); if ($resp->getStatus() != 200 || $resp->getBody()!=="VERIFIED") throw new Am_Exception_Paysystem("Wrong IPN received, paypal [_notify-validate] answers: ".$resp->getBody().'='.$resp->getStatus()); } catch(HTTP_Request2_ConnectionException $e) { Am_Di::getInstance()->errorLogTable->logException($e); header('HTTP/1.1 500 Internal Server Error'); die; } } /// validate business return $this->validateBusiness(); } public function validateBusiness(){ $businesses = array_merge(array($this->plugin->getConfig('business')), preg_split('|[\r\n]+|', $this->plugin->getConfig('alt_business'))); $businesses = array_filter(array_map('trim', $businesses), 'strlen'); $businesses = array_map("strtolower", $businesses); $incoming = strtolower($this->request->get('business',$this->request->get('receiver_email'))); if(!$incoming) return; // Nothing to validate foreach ($businesses as $e) if ($incoming === $e && $e) return true; // no match found $this->emailRegardingWrongBusiness($incoming, $businesses); throw new Am_Exception_Paysystem("IPN transaction comes for foreign business e-mail: " . htmlentities($incoming)); } public function processValidated() { switch ($this->txn_type) { // case self::TXN_SUBSCR_SIGNUP: // if ($this->invoice->first_total <= 0) // no payment will be reported // if ($this->invoice->status == Invoice::PENDING) // handle only once // $this->invoice->addAccessPeriod($this); // add first trial period // break; case self::TXN_SUBSCR_EOT: $this->invoice->stopAccess($this); break; case self::TXN_RECURRING_PAYMENT_PROFILE_CANCEL: case self::TXN_SUBSCR_CANCEL: $this->invoice->setCancelled(true); break; // case self::TXN_WEB_ACCEPT: case self::TXN_CART: case self::TXN_RECURRING_PAYMENT: switch ($this->request->payment_status) { case 'Completed': $this->invoice->addPayment($this); break; default: } break; } switch($this->request->payment_status){ case 'Refunded': case 'Chargeback': $this->invoice->addRefund($this, $this->request->parent_txn_id, $this->getAmount()); break; } } function getAmount() { return $this->request->get('mc_gross', $this->request->get('payment_gross')); } public function validateStatus() { return true; } public function validateTerms() { return true; } public function findInvoiceId() { $invoiceId = $this->request->getFiltered('invoice', $this->request->getFiltered('rp_invoice_id')); if ($invoiceId) return $invoiceId; // for paypal-pro/express if ($profileId = $this->request->get('recurring_payment_id')) { if ($invoice = Am_Di::getInstance()->invoiceTable->findFirstByData( 'paypal-profile-id', $profileId)) { return $invoice->public_id; } } } }PK\o|I Abstract.phpnu[ * $transaction = new Am_Paysystem_Transaction_Abstract($plugin, $request); * $transaction->process(); * * * or if we know exact invoice and it is valid * * $transaction = new Am_Paysystem_Transaction_Abstract($plugin, $request); * $transaction->setInvoice($invoice)->processValidated(); * * * @package Am_Paysystem */ abstract class Am_Paysystem_Transaction_Abstract implements Am_Paysystem_Transaction_Interface { /** @var Invoice */ public $invoice; /** @var Am_Paysystem_Abstract */ protected $plugin; /** @var DateTime @see findTime */ private $time = null; /** * @param array of variables coming from the paysystem */ function __construct(Am_Paysystem_Abstract $plugin){ $this->plugin = $plugin; $this->init(); } function init() {} function process() { $this->validate(); $this->processValidated(); } /** * Function must return receipt id of the payment - it is the payment reference# * as returned from payment system. By default it just calls @see getUniqId, * but this can be overriden * @return string */ function getReceiptId(){ return $this->getUniqId(); } /** * Return the related plugin * @return Am_Paysystem_Abstract */ public function getPlugin(){ return $this->plugin; } /** * Return date/time/zone object from the request, if that is impossible, * then returns current date/time * @see findTime() * @return DateTime */ public function getTime(){ if (!$this->time) $this->time = $this->findTime(); return $this->time; } public function setTime(DateTime $time) { $this->time = $time; return $this; } /** * Returns to timestamp of transaction * Be careful with timezones, etc. May be it is even better * to keep it as is * @return DateTime */ public function findTime(){ return $this->getPlugin()->getDi()->dateTime; } public function validate() { } /** * Return payment amount of the transaction * @throws Am_Exception_Paysystem if it is not a payment transaction * @return double|null number or null to use default value from invoice */ public function getAmount(){ return null; } /** * Once the process of IPN validation is completed, we can do * actions in this method. Use @link Invoice::startAccessNow(), * @link Invoice::stopAccessNow(), @link Invoice::addPaymentAndAccess() * @link Invoice::stopAccessAfterPaidPeriodOver() */ public function processValidated() { $this->invoice->addPayment($this); } public function setInvoice(Invoice $invoice) { $this->invoice = $invoice; return $this; } /** set invoice log record for futher update with found details */ public function setInvoiceLog(InvoiceLog $log) { $this->log = $log; } /** * Get Invoice associated with transacion. * @return Invoice $invoice; */ public function getInvoice(){ return $this->invoice; } public function getRecurringType() { return $this->getPlugin()->getRecurringType(); } public function getPaysysId() { return $this->getPlugin()->getId(); } }PK\%_̗ Interface.phpnu[config->get('auto_login_after_signup')) Am_Di::getInstance()->auth->setUser($this->invoice->getUser(), $this->request->getClientIp()); } }PK\4== Incoming.phpnu[ transactionField */ protected $_autoCreateMap = array(); /** * @param Am_Paysystem_Abstract $plugin * @param Am_Mvc_Request $request * @param Am_Mvc_Response $response * @param type $invokeArgs */ public function __construct(/*Am_Paysystem_Abstract*/ $plugin, /*Am_Mvc_Request*/ $request, /*Am_Mvc_Response*/ $response, $invokeArgs) { $this->request = $request; $this->response = $response; $this->invokeArgs = $invokeArgs; parent::__construct($plugin); } /** * By transaction : * - find or create user * - find or create invoice * - return created invoice * @return Invoice */ function autoCreateInvoice() { $invoiceExternalId = $this->generateInvoiceExternalId(); $invoice = Am_Di::getInstance()->invoiceTable->findFirstByData('external_id', $invoiceExternalId); $products = $this->autoCreateGetProducts(); if (!$invoice && !$products) return null; // If we are able to retrive invoice but doesn;t have products, // we should get products from invoice in order to handle situations when invoice was imported into amember; if($invoice && !$products) { $products = $invoice->getProducts(); } if (!is_array($products)) $products = array($products); $userTable = $this->getPlugin()->getDi()->userTable; $userInfo = $this->fetchUserInfo(); $externalId = $this->generateUserExternalId($userInfo); $user = null; if ($externalId) $user = $userTable->findFirstByData('external_id', $externalId); if (!$user) { $user = $userTable->findFirstByEmail($userInfo['email']); if ($user) $user->data()->set('external_id', $externalId)->update(); } if (!$user) { $user = $userTable->createRecord($userInfo); if(!$user->login) $user->generateLogin(); if(!$user->pass) $user->generatePassword(); else $user->setPass($user->pass); $user->data()->set('external_id', $externalId); $user->insert(); if ($this->getPlugin()->getDi()->config->get('registration_mail')) $user->sendRegistrationEmail(); if ($this->getPlugin()->getDi()->config->get('registration_mail_admin')) $user->sendRegistrationToAdminEmail(); } // if ($invoice) { if ($invoice->user_id != $user->user_id) { $invoice = null; // strange!!! } else { $invoice->_autoCreated = true; } } /// if (!$invoice) { $invoice = $this->getPlugin()->getDi()->invoiceRecord; $invoice->setUser($user); foreach ($products as $pr) $invoice->add($pr, $this->autoCreateGetProductQuantity($pr)); $invoice->calculate(); $invoice->data()->set('external_id', $invoiceExternalId); $invoice->paysys_id = $this->plugin->getId(); $invoice->insert(); $invoice->_autoCreated = true; } if ($invoice && $this->log) { $this->log->updateQuick(array( 'invoice_id' => $invoice->pk(), 'user_id' => $user->user_id, )); } return $invoice; } /** * find matching products according to request * @return array */ function autoCreateGetProducts() { throw new Am_Exception_NotImplemented("autoCreateGetProducts not implemented"); } function autoCreateGetProductQuantity(Product $pr){ return 1; } /** * Find out user info from transaction details * @see $_autoCreateMap * @return array */ function fetchUserInfo() { if (!$this->_autoCreateMap) throw new Am_Exception_NotImplemented("fetchUserInfo not implemented"); $ret = array(); foreach ($this->_autoCreateMap as $field => $valKey) switch ($field) { case 'user_external_id': case 'invoice_external_id': break; case 'name': @list($ret['name_f'], $ret['name_l']) = preg_split('/\s+/', $this->request->get($valKey), 2); break; default: $ret[$field] = $this->request->get($valKey); } return $ret; } function fillInUserFields(User $user) { $info = $this->fetchUserInfo(); if (!$info) return; $updated = 0; foreach ($info as $k => $v) { if (''==$user->get($k)) { $user->set($k, $v); $updated++; } } if ($updated) $user->update(); } /** * @see $_autoCreateMap * @return string unique id of user - so user will not be re-generated even if he changes email */ function generateUserExternalId(array $userInfo) { $field = @$this->_autoCreateMap['user_external_id']; if (!empty($field)) return $this->request->getFiltered($field); if (!empty($userInfo['email'])) return md5($userInfo['email']); throw new Am_Exception_Paysystem_TransactionInvalid("Could not generate externalId"); } /** * Must return the same value for single rebill sequence * @see $_autoCreateMap * @return string unique id of invoice - so rebills can be added to the same invoice */ function generateInvoiceExternalId() { $field = @$this->_autoCreateMap['invoice_external_id']; if (!empty($field)){ if(is_array($field)) { foreach($field as $v) if($res = $this->request->getFiltered($v)) return $res; } return $this->request->getFiltered($field); } throw new Am_Exception_Paysystem_NotImplemented("Not Implemented"); } function resendPostback() { if ($this->plugin->getConfig('resend_postback')) { $urls = $this->plugin->getConfig('resend_postback_urls'); $urls = explode("\n", $urls); $urls = array_filter(array_map('trim', $urls)); foreach ($urls as $url) try { $tm = microtime(true); $this->log->add("Resending postback to [$url]"); if ($url == $this->plugin->getPluginUrl('ipn')) { throw new Am_Exception_Configuration("DO NOT CONFIGURE RESENDING IPN TO ITSELF!"); } $req = new Am_HttpRequest($url); $req->setConfig('connect_timeout', 1000); $req->setConfig('timeout', 2000); $method = strtoupper($this->request->getMethod()); $req->setMethod($method); if ($method == 'POST') { foreach ($this->request->getPost() as $k => $v) $req->addPostParameter($k, $v); } else { $arr = $this->request->getQuery(); $req->setUrl($req->getUrl() . '?' . http_build_query($arr, '', '&')); } $req->send(); $tm = sprintf('%.3f', microtime(true) - $tm); $this->log->add("Postback resent successfully ($tm sec)"); } catch (Exception $e) { $tm = sprintf('%.3f', microtime(true) - $tm); $this->log->add("Cannot resend postback ($tm sec)"); $this->log->add($e); } } } function process() { // resend postback first as exception may be raised below $this->resendPostback(); parent::process(); // all went OK, with no exceptions, lets try to update customer info if (!empty($this->invoice->_autoCreated) && ($user = $this->invoice->getUser())) $this->fillInUserFields($user); } /** @return Invoice|null */ public function loadInvoice($invoiceId) { $invoiceId = preg_replace('/-[^-]*$/', '', $invoiceId); $invoice = Am_Di::getInstance()->invoiceTable->findFirstByPublicId($invoiceId); // update invoice_id in the log record if ($invoice && $this->log) { $this->log->updateQuick(array( 'invoice_id' => $invoice->pk(), 'user_id' => $invoice->user_id, )); } return $invoice; } protected function autoCreate() { try { $invoiceId = $this->findInvoiceId(); if ($invoiceId === null) throw new Am_Exception_Paysystem_TransactionEmpty("Looks like an invalid IPN post - no Invoice# passed"); $invoiceId = filterId($invoiceId); if (!strlen($invoiceId)) throw new Am_Exception_Paysystem_TransactionInvalid("Could not load Invoice related to this transaction, passed id is not a valid Invoice#[$invoiceId]"); if (!$this->invoice = $this->loadInvoice($invoiceId)) throw new Am_Exception_Paysystem_TransactionUnknown("Unknown transaction: related invoice not found #[$invoiceId]"); } catch (Am_Exception_Paysystem $e) { if (!$this->plugin->getConfig('auto_create')) throw $e; // try auto-create invoice $invoice = $this->autoCreateInvoice(); if ($invoice) $this->invoice = $invoice; else throw $e; } $this->setTime($this->findTime()); } /** * Validates if transaction comes from trusted source, * and if it matches found invoice * @throws Am_Exception_Paysystem * @return null must throw exception if it is not OK */ public function validate() { if (!$this->validateSource()) throw new Am_Exception_Paysystem_TransactionSource("IPN seems to be received from unknown source, not from the paysystem"); $this->autoCreate(); if($this->invoice->paysys_id != $this->getPaysysId()) throw new Am_Exception_Paysystem_TransactionInvalid("Invoice was created by another payment plugin."); if (empty($this->invoice->_autoCreated) && !$this->validateTerms()) throw new Am_Exception_Paysystem_TransactionInvalid("Subscriptions terms in the IPN does not match subscription terms in our Invoice"); if (!$this->validateStatus()) throw new Am_Exception_Paysystem_TransactionInvalid("Payment status is invalid, this IPN is not regarding a completed payment"); } /** * Make sure transaction is coming from the payment system and not from the fraudient source * @return true if OK, false if not */ abstract public function validateSource(); /** * Make sure the transaction have the same terms (at least paid amount as the @see $invoice ) * NOTE: if invoice is auto-created, its terms will NOT be validated * @return true if OK, false if not */ abstract public function validateTerms(); /** * Make sure this transaction is not regarding a pending or failed payment * @return true if OK, false if not */ abstract public function validateStatus(); /** * Find Invoice related with the current post as passed to the paysystem * must return null if no invoice_id present in the post * @return int|null id of invoice, null if not found */ public function findInvoiceId() { throw new Am_Exception_NotImplemented("findInvoiceId is not implemented"); } /** * Compare request IP with configured in plugin * and raise exception if that is wrong * @param mixed $ip string will be parsed using this format: $ip1_start [- $ip1_end][\n$ip2_start [- $ip2_end]] etc... * Array should have this format: array( array('start1', 'stop1'), single_ip, array('start2', 'stop2')) * also it may automatically check for hostname belonging to subdomain like * .worldpay.com */ public function _checkIp($ip) { $got = $this->request->getClientIp(false); if(!is_array($ip)) { $expected = array(); foreach(explode("\n", $ip) as $l){ if(strpos($l, "-")!== false){ list($k, $v) = explode("-", $l); $expected[] = array(trim($k),trim($v)); }else{ $expected[] = trim($l); } } }else $expected = $ip; $expected = array_filter($expected); if(empty($expected)) throw new Am_Exception_InputError("{$this->plugin->getId()} configuration error. Expected IP address array is empty!"); $found =false; $hostname = null; foreach ($expected as $v) { if (is_array($v)) { if(ip2long($got) >= ip2long($v[0]) && ip2long($got) <= ip2long($v[1])) { $found = true; break; } } else { if($got == $v) { $found = true; break; } if ($v[0] == '.') { if (!$hostname) $hostname = gethostbyaddr($got); if (preg_match($x='|'.preg_quote($v).'$|', $hostname)) { $found = true; break; } } } } if (!$found) throw new Am_Exception_Paysystem_TransactionSource("{$this->plugin->getId()} post comes from unknown IP [$got]"); } public function assertAmount($expected, $got, $whereMsg = "amount") { if ($e=moneyRound($expected) != $g=moneyRound($got)) throw new Am_Exception_Paysystem_TransactionInvalid("Transaction $whereMsg [$g] does not match expected [$e]"); } }PK\dy]Free.phpnu[invoice = $invoice; return $this; } public function processValidated() { // Update Rebill Date is not required here. It will be executed from addAccessPeriod; $this->invoice->addAccessPeriod($this); } public function getUniqId() { return $_SERVER['REMOTE_ADDR'] . '-' . $this->plugin->getDi()->time; } } PK\J Saved.phpnu[Amount = $transaction->getAmount(); $this->PaysysId = $transaction->getPaysysId(); $this->ReceiptId = $transaction->getReceiptId(); $this->RecurringType= $transaction->getRecurringType(); $this->Time = $transaction->getTime()->format('Y-m-d H:i:s'); $this->TimeZone = $transaction->getTime()->getTimezone()->getName(); $this->UniqId = $transaction->getUniqId(); } public function getAmount() { return $this->Amount; } public function getPaysysId() { return $this->PaysysId; } public function getReceiptId() { return $this->ReceiptId; } public function getRecurringType() { return $this->RecurringType; } public function getTime() { $t = new DateTime($this->Time); $t->setTimezone(new DateTimeZone($this->TimeZone)); return $t; } public function getUniqId() { return $this->UniqId; } }PK\.[@@ Manual.phpnu[plugin = $plugin; } function setAmount($amount) { $this->amount = moneyRound($amount); return $this; } function getAmount() { return $this->amount; } function getReceiptId() { return $this->receiptId; } function setReceiptId($receiptId) { $this->receiptId = (string)$receiptId; return $this; } public function getUniqId() { return $this->getReceiptId().'-m-'.time().rand(1000,9999); } }PK\5 FĖ Paypal.phpnu[PK\o|I Abstract.phpnu[PK\%_̗ *Interface.phpnu[PK\5.Incoming/Thanks.phpnu[PK\4== 2Incoming.phpnu[PK\dy]_oFree.phpnu[PK\J jrSaved.phpnu[PK\.[@@ txManual.phpnu[PK]{