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 home/ajdemo/public_html/mempro/application/cc/plugins/skipjack.php000064400000051240152101633310021412 0ustar00 0, self::FREQUENCY_BI_WEEKLY => 1, self::FREQUENCY_TWICE_MONTHLY => 2, self::FREQUENCY_MONTHLY => 3, self::FREQUENCY_FOUR_WEEKS => 4, self::FREQUENCY_BI_MONTHLY => 5, self::FREQUENCY_QUARTERLY => 6, self::FREQUENCY_SEMI_ANNUALLY => 7, self::FREQUENCY_ANNUALLY => 8, self::FREQUENCY_ANNUALLY2 => 8, ); const RECURRING_PAYMENT_ID = 'recurring-payment-id'; const RECURRING_DATE = 'recurring-date'; protected $ccTypes = array( 'visa' => 'Visa', 'mastercard' => 'MasterCard', 'american_express' => 'American Express', 'jcb' => 'JCB', 'discover_novus' => 'Discover/Novus', 'diners_club' => 'Diners Club', 'carte_blanche' => 'Carte Blanche', 'australian_bankcard' => 'Australian BankCard', ); public function _initSetupForm(Am_Form_Setup $form) { $form->addText("serial_number", array('size' => 12)) ->setLabel('HTML Serial Number') ->addRule('required'); $form->addText("dev_serial_number", array('size' => 12)) ->setLabel('Developer Serial Number') ->addRule('required'); $form->addMagicSelect("cc_type") ->setLabel("Credit Cards Types\n" . 'empty - any type') ->loadOptions($this->ccTypes); $form->addAdvCheckbox('test_mode') ->setLabel('Test Mode'); } public function isConfigured() { return (bool)$this->getConfig('serial_number'); } public function onSetupForms(Am_Event_SetupForms $event) { parent::onSetupForms($event); $event->getForm($this->getId())->removeElementByName('payment.'.$this->getId().'.reattempt'); } public function storesCcInfo() { return false; } public function getRecurringType() { return self::REPORTS_REBILL; } public function getFormOptions() { $ret = parent::getFormOptions(); $ret[] = self::CC_PHONE; return $ret; } public function validateCreditCardNumber($cc) { if($ret = parent::validateCreditCardNumber($cc)) return $ret; $validator = new CreditCardValidationSolution; $validator->validateCreditCard($cc); $type = str_replace("/", "_", str_replace(" ", "_", strtolower($validator->CCVSType))); $cfg = $this->getConfig('cc_type', array()); if( !empty($cfg) && !in_array($type, $cfg) ) return sprintf("You cannot use %s credit card for this order. Please select another payment method or credit card", $this->ccTypes[$type]); return null; } public function getSupportedCurrencies() { return array('USD'); } public function isNotAcceptableForInvoice(Invoice $invoice) { if ($invoice->rebill_times) { if($invoice->first_total != $invoice->second_total) { $this->getDi()->errorLogTable->log("Skipjack: first and second price should be the same"); return "or contact webmaster"; } // first period = second period if($invoice->first_period != $invoice->second_period) { $this->getDi()->errorLogTable->log("Skipjack: first and second period should be the same"); return "or contact webmaster"; } // check period if(!isset($this->supportedFrequencies[$invoice->first_period])) { $this->getDi()->errorLogTable->log("Skipjack: doesn't support recurring period: {$invoice->first_period}"); return "or contact webmaster"; } } return parent::isNotAcceptableForInvoice($invoice); } public function cancelAction(Invoice $invoice, $actionName, Am_Paysystem_Result $result) { $this->cancelInvoice($invoice); parent::cancelAction($invoice, $actionName, $result); } public function cancelInvoice(Invoice $invoice) { if(!($paymentId = $invoice->data()->get(self::RECURRING_PAYMENT_ID))) throw new Am_Exception_InternalError("Skipjack: invoice #{$invoice->pk()} has no Recurring Payment ID"); $request = new Am_HttpRequest($this->getUrl('check'), Am_HttpRequest::METHOD_POST); $post = array( 'szSerialNumber' => $this->getConfig('serial_number'), 'szDeveloperSerialNumber' => $this->getConfig('dev_serial_number'), 'szPaymentId' => $paymentId, ); $request->addPostParameter($post); $response = $request->send(); $body = $response->getBody(); if($this->getConfig('test_mode')) { $this->getDi()->errorLogTable->log("Skipjack: cancel invoice #{$invoice->pk()} \r\n $body"); } $res = explode(preg_match("/\r\n/", $body) ? "\r\n" : "\r", $body); $first = explode('","', trim($res[0],'"')); if($first[1] != 0) { throw new Am_Exception_InternalError("Skipjack: cancel recurring error #{$first[1]}: {$res[1]} for invoice #{$invoice->pk()}"); } return true; } public function onHourly() { $invoiceTime = strtotime($this->getDi()->sqlDate); $rebillInvoices = $this->getDi()->invoiceTable->findByRebillDate($this->getDi()->sqlDate); foreach ($rebillInvoices as $invoice) { if(!($paymentId = $invoice->data()->get(self::RECURRING_PAYMENT_ID))) continue; $time = $this->getNextRebillByInvoice($invoice, false); if($invoiceTime >= $time) continue; $transaction = new Am_Paysystem_Transaction_Manual($this); $transaction->setAmount($invoice->first_total) ->setReceiptId($paymentId . "-" . $this->getDi()->time) ->setTime(new DateTime($this->getDi()->sqlDateTime)); $invoice->addPayment($transaction); $invoice->updateQuick('rebill_date', sqlDate($time)); } } public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result) { // one product at invoice only if(count($invoice->getItems()) > 1) throw new Am_Exception_InternalError("Skipjack: one product should be at invoice"); if(!$invoice->rebill_times) //first and one-paid { $request = new Am_HttpRequest($this->getUrl(), Am_HttpRequest::METHOD_POST); $post = array( 'SJName' => $cc->cc_name_f . " " . $cc->cc_name_l, 'Email' => $invoice->getUser()->email, 'StreetAddress' => $cc->cc_street, 'City' => $cc->cc_city, 'State' => $cc->cc_state, 'ZipCode' => $cc->cc_zip, 'Country' => $cc->cc_country, 'ShipToPhone' => $cc->cc_phone, 'OrderNumber' => $invoice->public_id, 'ItemNumber' => $invoice->getItem(0)->item_id, 'ItemDescription' => $invoice->getLineDescription(), 'ItemCost' => $invoice->first_total, 'Quantity' => $invoice->getItem(0)->qty, 'Taxable' => $invoice->first_tax > 0 ? 'Y' : 'N', 'TransactionAmount' => $invoice->first_total, 'AccountNumber' => $cc->cc_number, 'Month' => substr($cc->cc_expire, 0, 2), 'Year' => "20" . substr($cc->cc_expire, 2, 2), 'CVV2' => $cc->getCvv(), 'SerialNumber' => $this->getConfig('serial_number'), 'DeveloperSerialNumber' => $this->getConfig('dev_serial_number'), ); if($invoice->first_tax > 0) { $post['TaxAmount'] = $invoice->first_tax; } $post['OrderString'] = "{$post['ItemNumber']}~{$post['ItemDescription']}~{$post['ItemCost']}~{$post['Quantity']}~{$post['Taxable']}~||"; $request->addPostParameter($post); $tr = new Am_Paysystem_Transaction_SkipjackCharge($this, $invoice, $request, true); $tr->run($result); return; } // create recurring // first price = second price if($invoice->first_total != $invoice->second_total) throw new Am_Exception_InternalError("Skipjack: first and second price should be the same"); // first period = second period if($invoice->first_period != $invoice->second_period) throw new Am_Exception_InternalError("Skipjack: first and second period should be the same"); // check period if(!isset($this->supportedFrequencies[$invoice->first_period])) throw new Am_Exception_InternalError("Skipjack doesn't support recurring period: {$invoice->first_period}"); $request = new Am_HttpRequest($this->getUrl('recurring-add'), Am_HttpRequest::METHOD_POST); $post = array( 'rtName' => $cc->cc_name_f . " " . $cc->cc_name_l, 'rtEmail' => $invoice->getUser()->email, 'rtAddress1' => $cc->cc_street, 'rtCity' => $cc->cc_city, 'rtState' => $cc->cc_state, 'rtPostalCode' => $cc->cc_zip, 'rtCountry' => $cc->cc_country, 'rtPhone' => $cc->cc_phone, 'rtOrderNumber' => $invoice->public_id, 'rtItemNumber' => $invoice->getItem(0)->item_id, 'rtItemDescription' => $invoice->getLineDescription(), 'rtAmount' => $invoice->first_total, 'rtTotalTransactions' => $invoice->first_total, 'rtOrderNumber' => $invoice->public_id, 'rtItemNumber' => $invoice->getItem(0)->item_id, 'rtItemDescription' => $invoice->getLineDescription(), 'rtAmount' => $invoice->first_total, 'rtAccountNumber' => $cc->cc_number, 'rtExpMonth' => substr($cc->cc_expire, 0, 2), 'rtExpYear' => "20" . substr($cc->cc_expire, 2, 2), 'rtCVV2' => $cc->getCvv(), 'rtStartingDate' => $this->getStartDate(), 'rtFrequency' => $this->supportedFrequencies[$invoice->first_period], 'szSerialNumber' => $this->getConfig('serial_number'), 'szDeveloperSerialNumber' => $this->getConfig('dev_serial_number') ); $request->addPostParameter($post); $tr = new Am_Paysystem_Transaction_SkipjackRecurring($this, $invoice, $request, true); $tr->run($result); if($result->isSuccess() && ($time = $this->getNextRebillByInvoice($invoice))) { $invoice->updateQuick('rebill_date', sqlDate($time)); } } protected function getNextRebillByInvoice(Invoice $invoice, $isFirst = true) { if(!($paymentId = $invoice->data()->get(self::RECURRING_PAYMENT_ID))) throw new Am_Exception_InternalError("Skipjack: invoice #{$invoice->pk()} has no Recurring Payment ID"); $request = new Am_HttpRequest($this->getUrl('check'), Am_HttpRequest::METHOD_POST); $post = array( 'szSerialNumber' => $this->getConfig('serial_number'), 'szDeveloperSerialNumber' => $this->getConfig('dev_serial_number'), 'szPaymentId' => $paymentId, ); $request->addPostParameter($post); $response = $request->send(); $body = $response->getBody(); if($this->getConfig('test_mode')) { $this->getDi()->errorLogTable->log("Skipjack: check payments for invoice #{$invoice->pk()} \r\n $body"); } $res = explode(preg_match("/\r\n/", $body) ? "\r\n" : "\r", $body); if(!is_array($res)) return false; $first = array_shift($res); $d = explode('","', trim($first,'"')); if($d[1] != 0) { $this->getDi()->errorLogTable->log("Skipjack: payment status error #{$d[1]}: {$res[0]} for invoice #{$invoice->pk()}"); return false; } if($isFirst) { foreach($res as $r) { $data = explode('","', trim($r,'"')); if( $data[0] == $this->getConfig('serial_number') && $data[1] == $this->getConfig('dev_serial_number') && $data[2] == $paymentId && preg_match("/\d+\/\d+\/\d{2}/", $data[6]) && strtotime($data[6]) > strtotime($this->getDi()->sqlDate) ) return strtotime($data[6]); } return false; } $nextRebill = array_shift($res); $data = explode('","', trim($nextRebill,'"')); if( $data[0] == $this->getConfig('serial_number') && $data[1] == $this->getConfig('dev_serial_number') && $data[2] == $paymentId && preg_match("/\d+\/\d+\/\d{2}/", $data[6]) && strtotime($data[6]) > strtotime($this->getDi()->sqlDate) ) return strtotime($data[6]); return false; } protected function getUrl($mode = 'charge') { $start = $this->getConfig('test_mode') ? "https://developer." : "https://"; switch ($mode) { case 'charge': return $start . self::URL_CHARGE; case 'recurring-add': return $start . self::URL_RECURRING_ADD; case 'recurring-del': return $start . self::URL_RECURRING_DEL; case 'check': return $start . self::URL_CHECK; } } protected function getStartDate() { $todaysDayValue = date("j"); if ($todaysDayValue > 28) $todaysDayValue = '28'; $startDate = date("n") . "/" . $todaysDayValue . "/" . date("Y"); return $startDate; } public function getReadme() { return <<Skipjack plugin readme NOTE 1. Only one product at invoice. NOTE 2. For recurring subscriptions - first and second price of product should be the same. NOTE 3. For recurring subscriptions - first and second period of product should be the same. NOTE 4. For recurring subscriptions - if transaction date is 29/30/31 - start date of subscription will be 28. NOTE 5. For recurring subscriptions - supported recurring periods: - 7 Days - 14 Days - 28 Days - 1 Month - 2 Months - 3 Months - 6 Months - 12 Months - 1 Year Test INFO: HTML Serial Number: 000059994718 Developer Serial Number: 111122223333 Credit Card Number: 4445999922225 Credit Card Code: 999 Expiry Date: Any date in the future Maximum dollar amount to receive an approved payment response: $150.00. To receive a declined payment response, use any dollar amount greater than $150.00. CUT; } } class Am_Paysystem_Transaction_SkipjackCharge extends Am_Paysystem_Transaction_CreditCard { protected $ret; protected $errorCodes = array( "-35" => "Invalid credit card number", "-37" => "Error failed communication", "-39" => "Error length serial number", "-51" => "Invalid Billing Zip Code", "-52" => "Invalid Shipto zip code", "-53" => "Invalid expiration date", "-54" => "Error length account number date", "-55" => "Invalid Billing Street Address", "-56" => "Invalid Shipto Street Address", "-57" => "Error length transaction amount", "-58" => "Invalid Name", "-59" => "Error length location", "-60" => "Invalid Billing State", "-61" => "Invalid Shipto State", "-62" => "Error length order string", "-64" => "Invalid Phone Number", "-65" => "Empty name", "-66" => "Empty email", "-67" => "Empty street address", "-68" => "Empty city", "-69" => "Empty state", "-79" => "Error length customer name", "-80" => "Error length shipto customer name", "-81" => "Error length customer location", "-82" => "Error length customer state", "-83" => "Invalid Phone Number", "-84" => "Pos error duplicate ordernumber", "-91" => "Pos_error_CVV2", "-92" => "Pos_error_Error_Approval_Code", "-93" => "Pos_error_Blind_Credits_Not_Allowed", "-94" => "Pos_error_Blind_Credits_Failed", "-95" => "Pos_error_Voice_Authorizations_Not_Allowed" ); protected $publicErrorCodes = array("-35","-37","-51","-52","-53","-55","-56","-58","-60","-61","-64","-65","-67","-68","-69","-83"); public function parseResponse() { $response = explode(preg_match("/\r\n/", $this->response->getBody()) ? "\r\n" : "\r", $this->response->getBody()); $header = explode('","', $response[0]); $data = explode('","', $response[1]); foreach($header as $i => $array) { $this->ret[str_replace(array("\r",'"'), "", $array)] = str_replace(array("\r",'"'), "", $data[$i]); } } public function getUniqId() { return $this->ret['szTransactionFileName']; } public function validate() { if ($this->ret['szIsApproved'] == 1) { $this->result->setSuccess($this); return; } $this->result->setFailed($this->getErrorMessage()); $err = isset($this->errorCodes[$this->ret['szReturnCode']]) ? $this->errorCodes[$this->ret['szReturnCode']] : "#{$this->ret['szReturnCode']}"; throw new Am_Exception_Paysystem_TransactionInvalid($err); } protected function getErrorMessage() { if(!empty($this->ret['szAuthorizationDeclinedMessage'])) return $this->ret['szAuthorizationDeclinedMessage']; if(in_array($this->ret['szReturnCode'], $this->publicErrorCodes)) return $this->errorCodes[$this->ret['szReturnCode']]; return ___("Payment failed"); } } class Am_Paysystem_Transaction_SkipjackRecurring extends Am_Paysystem_Transaction_CreditCard { protected $ret; protected $errorCodes = array( "-1" => "Invalid command", "-2" => "Parameter missing", "-3" => "Failed retrieving message", "-4" => "Invalid Status", "-5" => "Failed reading security flags", "-6" => "Developer serial number not found", "-7" => "Invalid serial number", "-8" => "Expiration year is not 4 characters", "-9" => "Credit card has expired", "-10" => "Invalid starting date", "-11" => "Failed AddingRecurring Payment", "-12" => "Invalid Recurring Payment frequency", "-15" => "Failed", "-16" => "Invalid expiration month", ); protected $publicErrorCodes = array("-9","-16"); public function parseResponse() { $this->ret = explode('","', $this->response->getBody()); } public function getUniqId() { return $this->ret[3] . "-" . time(); } public function validate() { if ($this->ret[1] != 0) { $this->result->setFailed($this->getErrorMessage()); $err = isset($this->errorCodes[$this->ret[1]]) ? $this->errorCodes[$this->ret[1]] : "#{$this->ret[1]}"; throw new Am_Exception_Paysystem_TransactionInvalid($err); } $this->invoice->data()->set(Am_Paysystem_Skipjack::RECURRING_PAYMENT_ID, $this->ret[3])->update(); $this->result->setSuccess($this); } protected function getErrorMessage() { if(in_array($this->ret[1], $this->publicErrorCodes)) return $this->errorCodes[$this->ret[1]]; return ___("Payment failed"); } }