--- /dev/null
+<?php // $Id$
+
+if (!defined('MOODLE_INTERNAL')) {
+ die('Direct access to this script is forbidden.');
+}
+
+require_once($CFG->dirroot.'/enrol/authorize/const.php');
+require_once($CFG->dirroot.'/enrol/authorize/localfuncs.php');
+
+class AuthorizeNet
+{
+ const AN_DELIM = '|';
+ const AN_ENCAP = '"';
+
+ const AN_REASON_NOCCTYPE = 17;
+ const AN_REASON_NOCCTYPE2 = 28;
+ const AN_REASON_NOACH = 18;
+ const AN_REASON_ACHONLY = 56;
+ const AN_REASON_NOACHTYPE = 245;
+ const AN_REASON_NOACHTYPE2 = 246;
+
+ /**
+ * Gets settlement time
+ *
+ * @param int $time Time processed, usually now.
+ * @return int Settlement time
+ */
+ public static function getsettletime($time)
+ {
+ global $CFG;
+
+ $cutoff = intval($CFG->an_cutoff);
+ $mins = $cutoff % 60;
+ $hrs = ($cutoff - $mins) / 60;
+ $cutofftime = strtotime("$hrs:$mins", $time);
+ if ($cutofftime < $time) {
+ $cutofftime = strtotime("$hrs:$mins", $time + (24 * 3600));
+ }
+ return $cutofftime;
+ }
+
+ /**
+ * Is order settled? Status must be auth_captured or credited.
+ *
+ * @param object $order Order details
+ * @return bool true, if settled, false otherwise.
+ */
+ public static function settled($order)
+ {
+ return ((AN_STATUS_AUTHCAPTURE == $order->status || AN_STATUS_CREDIT == $order->status) and ($order->settletime > 0) and ($order->settletime < time()));
+ }
+
+ /**
+ * Is order expired? 'Authorized/Pending Capture' transactions are expired after 30 days.
+ *
+ * @param object &$order Order details.
+ * @return bool true, transaction is expired, false otherwise.
+ */
+ public static function expired(&$order)
+ {
+ global $DB;
+ static $timediff30;
+
+ if ($order->status == AN_STATUS_EXPIRE) {
+ return true;
+ }
+ elseif ($order->status != AN_STATUS_AUTH) {
+ return false;
+ }
+
+ if (empty($timediff30)) {
+ $timediff30 = self::getsettletime(time()) - (30 * 24 * 3600);
+ }
+
+ $expired = self::getsettletime($order->timecreated) < $timediff30;
+ if ($expired)
+ {
+ $order->status = AN_STATUS_EXPIRE;
+ $DB->update_record('enrol_authorize', $order);
+ }
+ return $expired;
+ }
+
+ /**
+ * Performs an action on authorize.net and updates/inserts records. If record update fails,
+ * sends email to admin.
+ *
+ * @param object &$order Which transaction data will be sent. See enrol_authorize table.
+ * @param string &$message Information about error message.
+ * @param object &$extra Extra data that used for refunding and credit card information.
+ * @param int $action Which action will be performed. See AN_ACTION_*
+ * @param string $cctype Used internally to configure credit types automatically.
+ * @return int AN_APPROVED Transaction was successful, AN_RETURNZERO otherwise. Use $message for reason.
+ */
+ public static function process(&$order, &$message, &$extra, $action=AN_ACTION_NONE, $cctype=NULL)
+ {
+ global $CFG, $DB;
+ static $constpd;
+ require_once($CFG->libdir.'/filelib.php');
+
+ if (!isset($constpd)) {
+ $mconfig = get_config('enrol/authorize');
+ $constpd = array(
+ 'x_version' => '3.1',
+ 'x_delim_data' => 'True',
+ 'x_delim_char' => self::AN_DELIM,
+ 'x_encap_char' => self::AN_ENCAP,
+ 'x_relay_response' => 'FALSE',
+ 'x_login' => rc4decrypt($mconfig->an_login)
+ );
+
+ if (!empty($mconfig->an_tran_key)) {
+ $constpd['x_tran_key'] = rc4decrypt($mconfig->an_tran_key);
+ }
+ else {
+ $constpd['x_password'] = rc4decrypt($mconfig->an_password);
+ }
+ }
+
+ if (empty($order) or empty($order->id)) {
+ $message = "Check order->id!";
+ return AN_RETURNZERO;
+ }
+
+ $method = $order->paymentmethod;
+ if (empty($method)) {
+ $method = AN_METHOD_CC;
+ }
+ elseif ($method != AN_METHOD_CC && $method != AN_METHOD_ECHECK) {
+ $message = "Invalid method: $method";
+ return AN_RETURNZERO;
+ }
+
+ $action = intval($action);
+ if ($method == AN_METHOD_ECHECK) {
+ if ($action != AN_ACTION_AUTH_CAPTURE && $action != AN_ACTION_CREDIT) {
+ $message = "Please perform AUTH_CAPTURE or CREDIT for echecks";
+ return AN_RETURNZERO;
+ }
+ }
+
+ $pd = $constpd;
+ $pd['x_method'] = $method;
+ $test = !empty($CFG->an_test);
+ $pd['x_test_request'] = ($test ? 'TRUE' : 'FALSE');
+
+ switch ($action) {
+ case AN_ACTION_AUTH_ONLY:
+ case AN_ACTION_CAPTURE_ONLY:
+ case AN_ACTION_AUTH_CAPTURE:
+ {
+ if ($order->status != AN_STATUS_NONE) {
+ $message = "Order status must be AN_STATUS_NONE(0)!";
+ return AN_RETURNZERO;
+ }
+ elseif (empty($extra)) {
+ $message = "Need extra fields!";
+ return AN_RETURNZERO;
+ }
+ elseif (($action == AN_ACTION_CAPTURE_ONLY) and empty($extra->x_auth_code)) {
+ $message = "x_auth_code is required for capture only transactions!";
+ return AN_RETURNZERO;
+ }
+
+ $ext = (array)$extra;
+ $pd['x_type'] = (($action==AN_ACTION_AUTH_ONLY)
+ ? 'AUTH_ONLY' :( ($action==AN_ACTION_CAPTURE_ONLY)
+ ? 'CAPTURE_ONLY' : 'AUTH_CAPTURE'));
+ foreach($ext as $k => $v) {
+ $pd[$k] = $v;
+ }
+ }
+ break;
+
+ case AN_ACTION_PRIOR_AUTH_CAPTURE:
+ {
+ if ($order->status != AN_STATUS_AUTH) {
+ $message = "Order status must be authorized!";
+ return AN_RETURNZERO;
+ }
+ if (self::expired($order)) {
+ $message = "Transaction must be captured within 30 days. EXPIRED!";
+ return AN_RETURNZERO;
+ }
+ $pd['x_type'] = 'PRIOR_AUTH_CAPTURE';
+ $pd['x_trans_id'] = $order->transid;
+ }
+ break;
+
+ case AN_ACTION_CREDIT:
+ {
+ if ($order->status != AN_STATUS_AUTHCAPTURE) {
+ $message = "Order status must be authorized/captured!";
+ return AN_RETURNZERO;
+ }
+ if (!self::settled($order)) {
+ $message = "Order must be settled. Try VOID, check Cut-Off time if it fails!";
+ return AN_RETURNZERO;
+ }
+ if (empty($extra->amount)) {
+ $message = "No valid amount!";
+ return AN_RETURNZERO;
+ }
+ $timenowsettle = self::getsettletime(time());
+ $timediff = $timenowsettle - (120 * 3600 * 24);
+ if ($order->settletime < $timediff) {
+ $message = "Order must be credited within 120 days!";
+ return AN_RETURNZERO;
+ }
+
+ $pd['x_type'] = 'CREDIT';
+ $pd['x_trans_id'] = $order->transid;
+ $pd['x_currency_code'] = $order->currency;
+ $pd['x_invoice_num'] = $extra->orderid;
+ $pd['x_amount'] = $extra->amount;
+ if ($method == AN_METHOD_CC) {
+ $pd['x_card_num'] = sprintf("%04d", intval($order->refundinfo));
+ }
+ elseif ($method == AN_METHOD_ECHECK && empty($order->refundinfo)) {
+ $message = "Business checkings can be refunded only.";
+ return AN_RETURNZERO;
+ }
+ }
+ break;
+
+ case AN_ACTION_VOID:
+ {
+ if (self::expired($order) || self::settled($order)) {
+ $message = "The transaction cannot be voided due to the fact that it is expired or settled.";
+ return AN_RETURNZERO;
+ }
+ $pd['x_type'] = 'VOID';
+ $pd['x_trans_id'] = $order->transid;
+ }
+ break;
+
+ default:
+ {
+ $message = "Invalid action: $action";
+ return AN_RETURNZERO;
+ }
+ }
+
+ $headers = array('Connection' => 'close');
+ if (! (empty($CFG->an_referer) || $CFG->an_referer == "http://")) {
+ $headers['Referer'] = $CFG->an_referer;
+ }
+
+ @ignore_user_abort(true);
+ if (intval(ini_get('max_execution_time')) > 0) {
+ @set_time_limit(300);
+ }
+
+ $host = $test ? 'certification.authorize.net' : 'secure.authorize.net';
+ $data = download_file_content("https://$host:443/gateway/transact.dll", $headers, $pd, false, 60);
+ if (!$data) {
+ $message = "No connection to https://$host:443";
+ return AN_RETURNZERO;
+ }
+ $response = explode(self::AN_ENCAP.self::AN_DELIM.self::AN_ENCAP, $data);
+ if ($response === false) {
+ $message = "response error";
+ return AN_RETURNZERO;
+ }
+ $rcount = count($response) - 1;
+ if ($response[0]{0} == self::AN_ENCAP) {
+ $response[0] = substr($response[0], 1);
+ }
+ if (substr($response[$rcount], -1) == self::AN_ENCAP) {
+ $response[$rcount] = substr($response[$rcount], 0, -1);
+ }
+
+ $responsecode = intval($response[0]);
+ if ($responsecode == AN_APPROVED || $responsecode == AN_REVIEW)
+ {
+ $transid = intval($response[6]);
+ if ($test || $transid == 0) {
+ return $responsecode; // don't update original transaction in test mode.
+ }
+ switch ($action) {
+ case AN_ACTION_AUTH_ONLY:
+ case AN_ACTION_CAPTURE_ONLY:
+ case AN_ACTION_AUTH_CAPTURE:
+ case AN_ACTION_PRIOR_AUTH_CAPTURE:
+ {
+ $order->transid = $transid;
+
+ if ($method == AN_METHOD_CC) {
+ if ($action == AN_ACTION_AUTH_ONLY || $responsecode == AN_REVIEW) {
+ $order->status = AN_STATUS_AUTH;
+ } else {
+ $order->status = AN_STATUS_AUTHCAPTURE;
+ $order->settletime = self::getsettletime(time());
+ }
+ }
+ elseif ($method == AN_METHOD_ECHECK) {
+ $order->status = AN_STATUS_UNDERREVIEW;
+ }
+
+ if (!$DB->update_record('enrol_authorize', $order)) {
+ email_to_admin("Error while trying to update data in table enrol_authorize. Please edit manually this record: ID=$order->id.", $order);
+ }
+ }
+ break;
+
+ case AN_ACTION_CREDIT:
+ {
+ // Credit generates new transaction id.
+ // So, $extra must be updated, not $order.
+ $extra->status = AN_STATUS_CREDIT;
+ $extra->transid = $transid;
+ $extra->settletime = self::getsettletime(time());
+ if (! $extra->id = $DB->insert_record('enrol_authorize_refunds', $extra)) {
+ unset($extra->id);
+ email_to_admin("Error while trying to insert data into table enrol_authorize_refunds. Please add manually this record:", $extra);
+ }
+ }
+ break;
+
+ case AN_ACTION_VOID:
+ {
+ $tableupdate = 'enrol_authorize';
+ if ($order->status == AN_STATUS_CREDIT) {
+ $tableupdate = 'enrol_authorize_refunds';
+ unset($order->paymentmethod);
+ }
+ $order->status = AN_STATUS_VOID;
+ if (! $DB->update_record($tableupdate, $order)) {
+ email_to_admin("Error while trying to update data in table $tableupdate. Please edit manually this record: ID=$order->id.", $order);
+ }
+ }
+ break;
+ }
+ }
+ else
+ {
+ $reasonno = $response[2];
+ $reasonstr = "reason" . $reasonno;
+ $message = get_string($reasonstr, "enrol_authorize");
+ if ($message == '[[' . $reasonstr . ']]') {
+ $message = isset($response[3]) ? $response[3] : 'unknown error';
+ }
+ if ($method == AN_METHOD_CC && !empty($CFG->an_avs) && $response[5] != "P") {
+ $avs = "avs" . strtolower($response[5]);
+ $stravs = get_string($avs, "enrol_authorize");
+ $message .= "<br />" . get_string("avsresult", "enrol_authorize", $stravs);
+ }
+ if (!$test) { // Autoconfigure :)
+ switch($reasonno) {
+ // Credit card type isn't accepted
+ case self::AN_REASON_NOCCTYPE:
+ case self::AN_REASON_NOCCTYPE2:
+ {
+ if (!empty($cctype)) {
+ $ccaccepts = get_list_of_creditcards();
+ unset($ccaccepts[$cctype]);
+ set_config('an_acceptccs', implode(',', array_keys($ccaccepts)));
+ email_to_admin("$message ($cctype) This is new config(an_acceptccs):", $ccaccepts);
+ }
+ break;
+ }
+ // Echecks only
+ case self::AN_REASON_ACHONLY:
+ {
+ set_config('an_acceptmethods', AN_METHOD_ECHECK);
+ email_to_admin("$message This is new config(an_acceptmethods):", array(AN_METHOD_ECHECK));
+ break;
+ }
+ // Echecks aren't accepted
+ case self::AN_REASON_NOACH:
+ {
+ set_config('an_acceptmethods', AN_METHOD_CC);
+ email_to_admin("$message This is new config(an_acceptmethods):", array(AN_METHOD_CC));
+ break;
+ }
+ // This echeck type isn't accepted
+ case self::AN_REASON_NOACHTYPE:
+ case self::AN_REASON_NOACHTYPE2:
+ {
+ if (!empty($extra->x_echeck_type)) {
+ switch ($extra->x_echeck_type) {
+ // CCD=BUSINESSCHECKING
+ case 'CCD':
+ {
+ set_config('an_acceptechecktypes', 'CHECKING,SAVINGS');
+ email_to_admin("$message This is new config(an_acceptechecktypes):", array('CHECKING','SAVINGS'));
+ }
+ break;
+ // WEB=CHECKING or SAVINGS
+ case 'WEB':
+ {
+ set_config('an_acceptechecktypes', 'BUSINESSCHECKING');
+ email_to_admin("$message This is new config(an_acceptechecktypes):", array('BUSINESSCHECKING'));
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ return $responsecode;
+ }
+}
+
+?>
*/
private function cc_submit($form, $course)
{
- global $CFG, $USER, $SESSION;
+ global $CFG, $USER, $SESSION, $DB;
prevent_double_paid($course);
$order->timecreated = $timenow;
$order->amount = $curcost['cost'];
$order->currency = $curcost['currency'];
- $order->id = insert_record("enrol_authorize", $order);
+ $order->id = $DB->insert_record("enrol_authorize", $order);
if (!$order->id) {
email_to_admin("Error while trying to insert new data", $order);
return "Insert record error. Admin has been notified!";
*/
private function echeck_submit($form, $course)
{
- global $CFG, $USER, $SESSION;
+ global $CFG, $USER, $SESSION, $DB;
prevent_double_paid($course);
$order->timecreated = $timenow;
$order->amount = $curcost['cost'];
$order->currency = $curcost['currency'];
- $order->id = insert_record("enrol_authorize", $order);
+ $order->id = $DB->insert_record("enrol_authorize", $order);
if (!$order->id) {
email_to_admin("Error while trying to insert new data", $order);
return "Insert record error. Admin has been notified!";
*/
public function config_form($frm)
{
- global $CFG;
+ global $CFG, $DB;
$mconfig = get_config('enrol/authorize');
if (!check_curl_available()) {
}
}
- if (($count = count_records('enrol_authorize', 'status', AN_STATUS_AUTH))) {
+ if (($count = $DB->count_records('enrol_authorize', array('status'=>AN_STATUS_AUTH)))) {
$a = new stdClass;
$a->count = $count;
$a->url = $CFG->wwwroot."/enrol/authorize/index.php?status=".AN_STATUS_AUTH;
*/
public function cron()
{
- global $CFG;
+ global $CFG, $DB;
$oneday = 86400;
$timenow = time();
}
$timediffcnf = $settlementtime - (intval($CFG->an_capture_day) * $oneday);
- $select = "(status = '" .AN_STATUS_AUTH. "') AND (timecreated < '$timediffcnf') AND (timecreated > '$timediff30')";
- if (!($ordercount = count_records_select('enrol_authorize', $select))) {
+ $select = "(status = ?) AND (timecreated < ?) AND (timecreated > ?)";
+ $params = array(AN_STATUS_AUTH, $timediffcnf, $timediff30);
+ if (!($ordercount = $DB->count_records_select('enrol_authorize', $select, $params))) {
mtrace("no pending orders");
return;
}
$this->log = "AUTHORIZE.NET AUTOCAPTURE CRON: " . userdate($timenow) . "\n";
$lastcourseid = 0;
- for ($rs = get_recordset_select('enrol_authorize', $select, 'courseid'); ($order = rs_fetch_next_record($rs)); )
+ $rs = $DB->get_recordset_select('enrol_authorize', $select, 'courseid', $params);
+ foreach ( $rs as $order)
{
$message = '';
$extra = NULL;
if (AN_APPROVED == AuthorizeNet::process($order, $message, $extra, AN_ACTION_PRIOR_AUTH_CAPTURE)) {
if ($lastcourseid != $order->courseid) {
$lastcourseid = $order->courseid;
- $course = get_record('course', 'id', $lastcourseid);
+ $course = $DB->get_record('course', array('id'=>$lastcourseid));
$role = get_default_course_role($course);
$context = get_context_instance(CONTEXT_COURSE, $lastcourseid);
}
$timestart = $timenow;
$timeend = $order->settletime + $course->enrolperiod;
}
- $user = get_record('user', 'id', $order->userid);
+ $user = $DB->get_record('user', array('id'=>$order->userid));
if (role_assign($role->id, $user->id, 0, $context->id, $timestart, $timeend, 0, 'authorize')) {
$this->log .= "User($user->id) has been enrolled to course($course->id).\n";
if (!empty($CFG->enrol_mailstudents)) {
$this->log .= "Error, Order# $order->id: " . $message . "\n";
}
}
- rs_close($rs);
+ $rs->close();
mtrace("processed");
$timenow = time();
*/
private function cron_daily()
{
- global $CFG, $SITE;
+ global $CFG, $SITE, $DB;
$oneday = 86400;
$timenow = time();
$settlementtime = AuthorizeNet::getsettletime($timenow);
$timediff30 = $settlementtime - (30 * $oneday);
- $select = "(status='".AN_STATUS_NONE."') AND (timecreated<'$timediff30')";
- if (delete_records_select('enrol_authorize', $select)) {
+ $select = "(status=?) AND (timecreated<?)";
+ $params = array(AN_STATUS_NONE, $timediff30);
+ if ($DB->delete_records_select('enrol_authorize', $select, $params)) {
mtrace(" orders no transaction made have deleted");
}
- $select = "(status='".AN_STATUS_AUTH."') AND (timecreated<'$timediff30')";
- if (execute_sql("UPDATE {$CFG->prefix}enrol_authorize SET status='".AN_STATUS_EXPIRE."' WHERE $select", false)) {
+ $select = "(status=?) AND (timecreated<?)";
+ $params = array(AN_STATUS_EXPIRE, AN_STATUS_AUTH, $timediff30);
+ if ($DB->execute("UPDATE {enrol_authorize SET status=? WHERE $select", $params)) {
mtrace(" pending orders to expire have updated");
}
$timediff60 = $settlementtime - (60 * $oneday);
- $select = "(status='".AN_STATUS_EXPIRE."') AND (timecreated<'$timediff60')";
- if (delete_records_select('enrol_authorize', $select)) {
+ $select = "(status=?) AND (timecreated<?)";
+ $params = array(AN_STATUS_EXPIRE, $timediff30);
+ if ($DB->delete_records_select('enrol_authorize', $select, $params)) {
mtrace(" orders expired older than 60 days have deleted");
}
$adminuser = get_admin();
- $select = "status IN(".AN_STATUS_UNDERREVIEW.",".AN_STATUS_APPROVEDREVIEW.") AND (timecreated<'$onepass') AND (timecreated>'$timediff60')";
- if (($count = count_records_select('enrol_authorize', $select)) &&
+ $select = "status IN(?,?) AND (timecreated<?) AND (timecreated>?)";
+ $params = array(AN_STATUS_UNDERREVIEW, AN_STATUS_APPROVEDREVIEW, $onepass, $timediff30);
+ if (($count = $DB->count_records_select('enrol_authorize', $select, $params)) &&
($csvusers = get_users_by_capability(get_context_instance(CONTEXT_SYSTEM), 'enrol/authorize:uploadcsv'))) {
$a = new stdClass;
$a->count = $count;
$timediffem = $settlementtime - ((30 - intval($CFG->an_emailexpired)) * $oneday);
- $select = "(status='". AN_STATUS_AUTH ."') AND (timecreated<'$timediffem') AND (timecreated>'$timediff30')";
- $count = count_records_select('enrol_authorize', $select);
+ $select = "(status=?) AND (timecreated<?) AND (timecreated>?)";
+ $params = array(AN_STATUS_AUTH, $timediffem, $timediff30);
+ $count = $DB->count_records_select('enrol_authorize', $select, $params);
if (!$count) {
mtrace("no orders prior to $CFG->an_emailexpired days");
return;
$sorttype = empty($CFG->an_sorttype) ? 'ttl' : $CFG->an_sorttype;
$sql = "SELECT e.courseid, e.currency, c.fullname, c.shortname,
COUNT(e.courseid) AS cnt, SUM(e.amount) as ttl
- FROM {$CFG->prefix}enrol_authorize e
- INNER JOIN {$CFG->prefix}course c ON c.id = e.courseid
- WHERE (e.status = ". AN_STATUS_AUTH .")
- AND (e.timecreated < $timediffem)
- AND (e.timecreated > $timediff30)
+ FROM {enrol_authorize} e
+ INNER JOIN {course} c ON c.id = e.courseid
+ WHERE (e.status = ?)
+ AND (e.timecreated < ?)
+ AND (e.timecreated > ?)
GROUP BY e.courseid
ORDER BY $sorttype DESC";
+ $params = array(AN_STATUS_AUTH, $timediffem, $timediff30);
- for ($rs = get_recordset_sql($sql); ($courseinfo = rs_fetch_next_record($rs)); )
+ $rs = $DB->get_recordset_sql($sql, $params);
+ foreach ($rs as $courseinfo)
{
$lastcourse = $courseinfo->courseid;
$context = get_context_instance(CONTEXT_COURSE, $lastcourse);
}
}
}
- rs_close($rs);
+ $rs->close();
}
}
?>