mirror of
https://github.com/moodle/moodle.git
synced 2025-02-24 12:03:12 +01:00
* New Feature (Authorize.net eCheck) Authorize.Net provides an exclusive, fully integrated electronic check payment method, eCheck.Net. Using eCheck.Net, merchants can accept and process payments from consumer and corporate bank accounts directly from their Web site or through the Authorize.Net Virtual Terminal. By accepting electronic checks, you expand the payment options available to new and existing customers, enhancing customer loyalty and potentially increasing sales. + Lower Fees - Lower rates than credit cards or PayPal. + More Efficient - eCheck.Net does everything online, eliminating the cost and inconvenience of manually processing paper checks and waiting for checks in the mail. + Fully Integrated Solution - No third-party integration required implementing eCheck.Net is easy for merchants already using the Authorize.Net Payment Gateway. + Integrated Reporting - Provides a combined view of all eCheck.Net and credit card payment transactions. Reconcile payment and billing activity using online reports and statements. + Ship Product Sooner - Improved up-front transaction validation that returns the status of transactions faster. + Security - Authorize.Net uses the latest 128-bit Secure Socket Layer (SSL) technology for secure Internet Protocol (IP) transactions. == TO DO == - Generate Echeck forms. - Show users a echeck option if admin enabled echeck method. - Allow admins/teachers to enrol a student using echeck method (FIX: role consept) ==========
375 lines
13 KiB
PHP
375 lines
13 KiB
PHP
<?php // $Id$
|
|
|
|
if (!defined('MOODLE_INTERNAL')) {
|
|
die('Direct access to this script is forbidden.');
|
|
}
|
|
|
|
define('AN_APPROVED', '1');
|
|
define('AN_DECLINED', '2');
|
|
define('AN_ERROR', '3');
|
|
define('AN_DELIM', '|');
|
|
define('AN_ENCAP', '"');
|
|
|
|
require_once($CFG->dirroot.'/enrol/authorize/const.php');
|
|
require_once($CFG->dirroot.'/enrol/authorize/enrol.php');
|
|
|
|
/**
|
|
* Gets settlement date and time
|
|
*
|
|
* @param int $time Time processed, usually now.
|
|
* @return int Settlement date and time
|
|
*/
|
|
function authorize_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.
|
|
*/
|
|
function authorize_settled($order)
|
|
{
|
|
return (($order->status == AN_STATUS_AUTHCAPTURE || $order->status == AN_STATUS_CREDIT) &&
|
|
($order->settletime > 0) && ($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.
|
|
*/
|
|
function authorize_expired(&$order)
|
|
{
|
|
static $timediff30;
|
|
|
|
if ($order->status == AN_STATUS_EXPIRE) {
|
|
return true;
|
|
}
|
|
elseif ($order->status != AN_STATUS_AUTH) {
|
|
return false;
|
|
}
|
|
|
|
if (empty($timediff30)) {
|
|
$timediff30 = authorize_getsettletime(time()) - (30 * 24 * 3600);
|
|
}
|
|
|
|
$isexpired = (authorize_getsettletime($order->timecreated) < $timediff30);
|
|
if ($isexpired) {
|
|
$order->status = AN_STATUS_EXPIRE;
|
|
update_record('enrol_authorize', $order);
|
|
}
|
|
return $isexpired;
|
|
}
|
|
|
|
/**
|
|
* 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 if this function returns false.
|
|
* @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 int $method Transaction method. AN_METHOD_CC or AN_METHOD_ECHECK
|
|
* @return bool true Transaction was successful, false otherwise. Use $message for reason.
|
|
* @author Ethem Evlice <ethem a.t evlice d.o.t com>
|
|
* @uses $CFG
|
|
*/
|
|
function authorize_action(&$order, &$message, &$extra, $action=AN_ACTION_NONE, $method=AN_METHOD_CC)
|
|
{
|
|
global $CFG;
|
|
static $conststring;
|
|
|
|
$test = !empty($CFG->an_test);
|
|
|
|
if (!isset($conststring)) {
|
|
$constdata = array(
|
|
'x_version' => '3.1',
|
|
'x_delim_data' => 'True',
|
|
'x_delim_char' => AN_DELIM,
|
|
'x_encap_char' => AN_ENCAP,
|
|
'x_relay_response' => 'FALSE',
|
|
'x_login' => $CFG->an_login
|
|
);
|
|
$str = '';
|
|
foreach($constdata as $ky => $vl) {
|
|
$str .= $ky . '=' . urlencode($vl) . '&';
|
|
}
|
|
$str .= (!empty($CFG->an_tran_key)) ?
|
|
'x_tran_key=' . urlencode($CFG->an_tran_key):
|
|
'x_password=' . urlencode($CFG->an_password);
|
|
|
|
$conststring = $str;
|
|
$str = '';
|
|
}
|
|
|
|
if (empty($order) or empty($order->id)) {
|
|
$message = "Check order->id!";
|
|
return false;
|
|
}
|
|
|
|
if (empty($method)) {
|
|
$method = AN_METHOD_CC;
|
|
}
|
|
elseif ($method != AN_METHOD_CC && $method != AN_METHOD_ECHECK) {
|
|
$message = "Missing method: $method";
|
|
return false;
|
|
}
|
|
|
|
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 false;
|
|
}
|
|
}
|
|
|
|
$action = intval($action);
|
|
if ($action <= AN_ACTION_NONE or $action > AN_ACTION_VOID) {
|
|
$message = "Invalid action!";
|
|
return false;
|
|
}
|
|
|
|
$poststring = $conststring;
|
|
$poststring .= '&x_method=' . ($method==AN_METHOD_CC ? 'CC' : 'ECHECK');
|
|
$poststring .= '&x_test_request=' . ($test ? 'TRUE' : 'FALSE');
|
|
$timenowsettle = getsettletime(time());
|
|
|
|
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 false;
|
|
}
|
|
elseif (empty($extra)) {
|
|
$message = "Need extra fields!";
|
|
return false;
|
|
}
|
|
elseif (($action == AN_ACTION_CAPTURE_ONLY) and empty($extra->x_auth_code)) {
|
|
$message = "x_auth_code is required for capture only transactions!";
|
|
return false;
|
|
}
|
|
|
|
$ext = (array)$extra;
|
|
$poststring .= '&x_type=' . (($action==AN_ACTION_AUTH_ONLY)
|
|
? 'AUTH_ONLY' :( ($action==AN_ACTION_CAPTURE_ONLY)
|
|
? 'CAPTURE_ONLY' : 'AUTH_CAPTURE'));
|
|
foreach($ext as $k => $v) {
|
|
$poststring .= '&' . $k . '=' . urlencode($v);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case AN_ACTION_PRIOR_AUTH_CAPTURE:
|
|
{
|
|
if ($order->status != AN_STATUS_AUTH) {
|
|
$message = "Order status must be authorized!";
|
|
return false;
|
|
}
|
|
if (authorize_expired($order)) {
|
|
$message = "Transaction must be captured within 30 days. EXPIRED!";
|
|
return false;
|
|
}
|
|
$poststring .= '&x_type=PRIOR_AUTH_CAPTURE&x_trans_id=' . urlencode($order->transid);
|
|
break;
|
|
}
|
|
|
|
case AN_ACTION_CREDIT:
|
|
{
|
|
if ($order->status != AN_STATUS_AUTHCAPTURE) {
|
|
$message = "Order status must be authorized/captured!";
|
|
return false;
|
|
}
|
|
if (!authorize_settled($order)) {
|
|
$message = "Order must be settled. Try VOID, check Cut-Off time if it fails!";
|
|
return false;
|
|
}
|
|
$timenowsettle = authorize_getsettletime(time());
|
|
$timediff = $timenowsettle - (120 * 3600 * 24);
|
|
if ($order->settletime < $timediff) {
|
|
$message = "Order must be credited within 120 days!";
|
|
return false;
|
|
}
|
|
if (empty($extra)) {
|
|
$message = "Need extra fields to REFUND!";
|
|
return false;
|
|
}
|
|
$total = floatval($extra->sum) + floatval($extra->amount);
|
|
if (($extra->amount == 0) || ($total > $order->amount)) {
|
|
$message = "Can be credited up to original amount.";
|
|
return false;
|
|
}
|
|
$poststring .= '&x_type=CREDIT&x_trans_id=' . urlencode($order->transid);
|
|
$poststring .= '&x_card_num=' . sprintf("%04d", intval($order->cclastfour));
|
|
$poststring .= '&x_currency_code=' . urlencode($order->currency);
|
|
$poststring .= '&x_amount=' . urlencode($extra->amount);
|
|
break;
|
|
}
|
|
|
|
case AN_ACTION_VOID:
|
|
{
|
|
if ($order->status == AN_STATUS_AUTH) {
|
|
if (authorize_expired($order)) {
|
|
$message = "Authorized transaction must be voided within 30 days. EXPIRED!";
|
|
return false;
|
|
}
|
|
}
|
|
elseif ($order->status == AN_STATUS_AUTHCAPTURE or $order->status == AN_STATUS_CREDIT) {
|
|
if (authorize_settled($order)) {
|
|
$message = "Settled transaction cannot be voided. Check Cut-Off time!";
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
$message = "Order status must be authorized/pending capture or captured-refunded/pending settlement!";
|
|
return false;
|
|
}
|
|
$poststring .= '&x_type=VOID&x_trans_id=' . urlencode($order->transid);
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
$message = "Invalid action: $action";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
$referer = '';
|
|
if (! (empty($CFG->an_referer) || $CFG->an_referer == "http://")) {
|
|
$referer = "Referer: $CFG->an_referer\r\n";
|
|
}
|
|
|
|
$host = $test ? 'certification.authorize.net' : 'secure.authorize.net';
|
|
$fp = fsockopen("ssl://$host", 443, $errno, $errstr, 60);
|
|
if (!$fp) {
|
|
$message = "no connection: $errstr ($errno)";
|
|
return false;
|
|
}
|
|
|
|
// critical section
|
|
@ignore_user_abort(true);
|
|
if (intval(ini_get('max_execution_time')) > 0) {
|
|
@set_time_limit(300);
|
|
}
|
|
|
|
fwrite($fp, "POST /gateway/transact.dll HTTP/1.0\r\n" .
|
|
"Host: $host\r\n" . $referer .
|
|
"Content-type: application/x-www-form-urlencoded\r\n" .
|
|
"Connection: close\r\n" .
|
|
"Content-length: " . strlen($poststring) . "\r\n\r\n" .
|
|
$poststring . "\r\n"
|
|
);
|
|
|
|
$tmpstr = '';
|
|
while(!feof($fp) && !stristr($tmpstr, 'content-length')) {
|
|
$tmpstr = fgets($fp, 4096);
|
|
}
|
|
if (!stristr($tmpstr, 'content-length')) {
|
|
$message = "content-length error";
|
|
@fclose($fp);
|
|
return false;
|
|
}
|
|
$length = trim(substr($tmpstr, strpos($tmpstr,'content-length')+15));
|
|
fgets($fp, 4096);
|
|
$data = fgets($fp, $length);
|
|
@fclose($fp);
|
|
$response = explode(AN_ENCAP.AN_DELIM.AN_ENCAP, $data);
|
|
if ($response === false) {
|
|
$message = "response error";
|
|
return false;
|
|
}
|
|
$rcount = count($response) - 1;
|
|
if ($response[0]{0} == AN_ENCAP) {
|
|
$response[0] = substr($response[0], 1);
|
|
}
|
|
if (substr($response[$rcount], -1) == AN_ENCAP) {
|
|
$response[$rcount] = substr($response[$rcount], 0, -1);
|
|
}
|
|
|
|
if ($response[0] == AN_APPROVED)
|
|
{
|
|
$transid = intval($response[6]);
|
|
if ($test || $transid == 0) {
|
|
return true; // 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 ($action == AN_ACTION_AUTH_ONLY) {
|
|
$order->status = AN_STATUS_AUTH;
|
|
// don't update order->settletime
|
|
} else {
|
|
$order->status = AN_STATUS_AUTHCAPTURE;
|
|
$order->settletime = authorize_getsettletime(time());
|
|
}
|
|
if (! update_record('enrol_authorize', $order)) {
|
|
enrolment_plugin_authorize::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 = authorize_getsettletime(time());
|
|
unset($extra->sum); // this is not used in refunds table.
|
|
if (! $extra->id = insert_record('enrol_authorize_refunds', $extra)) {
|
|
enrolment_plugin_authorize::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 = ($order->status == AN_STATUS_CREDIT) ? 'enrol_authorize_refunds' : 'enrol_authorize';
|
|
$order->status = AN_STATUS_VOID;
|
|
// don't update order->settletime
|
|
if (! update_record($tableupdate, $order)) {
|
|
enrolment_plugin_authorize::email_to_admin("Error while trying to update data " .
|
|
"in table $tableupdate. Please edit manually this record: ID=$order->id.", $order);
|
|
}
|
|
break;
|
|
}
|
|
default: return false;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
$reason = "reason" . $response[2];
|
|
$message = get_string($reason, "enrol_authorize");
|
|
if ($message == '[[' . $reason . ']]') {
|
|
$message = isset($response[3]) ? $response[3] : 'unknown error';
|
|
}
|
|
if (!empty($CFG->an_avs) and $method == AN_METHOD_CC) {
|
|
$avs = "avs" . strtolower($response[5]);
|
|
$stravs = get_string($avs, "enrol_authorize");
|
|
$message .= "<br />" . get_string("avsresult", "enrol_authorize", $stravs);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
?>
|