Automatic installer.php lang files by installer_builder (20070726)
[moodle-linuxchix.git] / enrol / authorize / authorizenetlib.php
blob71fdcdc234d5b355c5efd60b6561f9e691a5f9c5
1 <?php // $Id$
3 if (!defined('MOODLE_INTERNAL')) {
4 die('Direct access to this script is forbidden.');
7 define('AN_DELIM', '|');
8 define('AN_ENCAP', '"');
10 define('AN_REASON_NOCCTYPE', 17);
11 define('AN_REASON_NOCCTYPE2', 28);
12 define('AN_REASON_NOACH', 18);
13 define('AN_REASON_ACHONLY', 56);
14 define('AN_REASON_NOACHTYPE', 245);
15 define('AN_REASON_NOACHTYPE2', 246);
17 require_once($CFG->dirroot.'/enrol/authorize/const.php');
18 require_once($CFG->dirroot.'/enrol/authorize/localfuncs.php');
20 /**
21 * Gets settlement date and time
23 * @param int $time Time processed, usually now.
24 * @return int Settlement date and time
26 function authorize_getsettletime($time)
28 global $CFG;
30 $cutoff = intval($CFG->an_cutoff);
31 $mins = $cutoff % 60;
32 $hrs = ($cutoff - $mins) / 60;
33 $cutofftime = strtotime("$hrs:$mins", $time);
34 if ($cutofftime < $time) {
35 $cutofftime = strtotime("$hrs:$mins", $time + (24 * 3600));
37 return $cutofftime;
40 /**
41 * Is order settled? Status must be auth_captured or credited.
43 * @param object $order Order details
44 * @return bool true, if settled, false otherwise.
46 function authorize_settled($order)
48 return (($order->status == AN_STATUS_AUTHCAPTURE || $order->status == AN_STATUS_CREDIT) &&
49 ($order->settletime > 0) && ($order->settletime < time()));
52 /**
53 * Is order expired? 'Authorized/Pending Capture' transactions are expired after 30 days.
55 * @param object &$order Order details.
56 * @return bool true, transaction is expired, false otherwise.
58 function authorize_expired(&$order)
60 static $timediff30;
62 if ($order->status == AN_STATUS_EXPIRE) {
63 return true;
65 elseif ($order->status != AN_STATUS_AUTH) {
66 return false;
69 if (empty($timediff30)) {
70 $timediff30 = authorize_getsettletime(time()) - (30 * 24 * 3600);
73 $isexpired = (authorize_getsettletime($order->timecreated) < $timediff30);
74 if ($isexpired) {
75 $order->status = AN_STATUS_EXPIRE;
76 update_record('enrol_authorize', $order);
78 return $isexpired;
81 /**
82 * Performs an action on authorize.net and updates/inserts records. If record update fails,
83 * sends email to admin.
85 * @param object &$order Which transaction data will be sent. See enrol_authorize table.
86 * @param string &$message Information about error message.
87 * @param object &$extra Extra data that used for refunding and credit card information.
88 * @param int $action Which action will be performed. See AN_ACTION_*
89 * @param string $cctype Used internally to configure credit types automatically.
90 * @return int AN_APPROVED Transaction was successful, AN_RETURNZERO otherwise. Use $message for reason.
91 * @author Ethem Evlice <ethem a.t evlice d.o.t com>
92 * @uses $CFG
94 function authorize_action(&$order, &$message, &$extra, $action=AN_ACTION_NONE, $cctype=NULL)
96 global $CFG;
97 static $conststring;
99 if (!isset($conststring)) {
100 $mconfig = get_config('enrol/authorize');
101 $constdata = array(
102 'x_version' => '3.1',
103 'x_delim_data' => 'True',
104 'x_delim_char' => AN_DELIM,
105 'x_encap_char' => AN_ENCAP,
106 'x_relay_response' => 'FALSE',
107 'x_login' => rc4decrypt($mconfig->an_login)
109 $str = '';
110 foreach($constdata as $ky => $vl) {
111 $str .= $ky . '=' . urlencode($vl) . '&';
113 $str .= (!empty($mconfig->an_tran_key)) ?
114 'x_tran_key=' . urlencode(rc4decrypt($mconfig->an_tran_key)):
115 'x_password=' . urlencode(rc4decrypt($mconfig->an_password));
117 $conststring = $str;
118 $str = '';
121 if (empty($order) or empty($order->id)) {
122 $message = "Check order->id!";
123 return AN_RETURNZERO;
126 $method = $order->paymentmethod;
127 if (empty($method)) {
128 $method = AN_METHOD_CC;
130 elseif ($method != AN_METHOD_CC && $method != AN_METHOD_ECHECK) {
131 $message = "Invalid method: $method";
132 return AN_RETURNZERO;
135 $action = intval($action);
136 if ($method == AN_METHOD_ECHECK) {
137 if ($action != AN_ACTION_AUTH_CAPTURE && $action != AN_ACTION_CREDIT) {
138 $message = "Please perform AUTH_CAPTURE or CREDIT for echecks";
139 return AN_RETURNZERO;
143 $poststring = $conststring;
144 $poststring .= '&x_method=' . $method;
146 $test = !empty($CFG->an_test);
147 $poststring .= '&x_test_request=' . ($test ? 'TRUE' : 'FALSE');
149 switch ($action) {
150 case AN_ACTION_AUTH_ONLY:
151 case AN_ACTION_CAPTURE_ONLY:
152 case AN_ACTION_AUTH_CAPTURE:
154 if ($order->status != AN_STATUS_NONE) {
155 $message = "Order status must be AN_STATUS_NONE(0)!";
156 return AN_RETURNZERO;
158 elseif (empty($extra)) {
159 $message = "Need extra fields!";
160 return AN_RETURNZERO;
162 elseif (($action == AN_ACTION_CAPTURE_ONLY) and empty($extra->x_auth_code)) {
163 $message = "x_auth_code is required for capture only transactions!";
164 return AN_RETURNZERO;
167 $ext = (array)$extra;
168 $poststring .= '&x_type=' . (($action==AN_ACTION_AUTH_ONLY)
169 ? 'AUTH_ONLY' :( ($action==AN_ACTION_CAPTURE_ONLY)
170 ? 'CAPTURE_ONLY' : 'AUTH_CAPTURE'));
171 foreach($ext as $k => $v) {
172 $poststring .= '&' . $k . '=' . urlencode($v);
174 break;
177 case AN_ACTION_PRIOR_AUTH_CAPTURE:
179 if ($order->status != AN_STATUS_AUTH) {
180 $message = "Order status must be authorized!";
181 return AN_RETURNZERO;
183 if (authorize_expired($order)) {
184 $message = "Transaction must be captured within 30 days. EXPIRED!";
185 return AN_RETURNZERO;
187 $poststring .= '&x_type=PRIOR_AUTH_CAPTURE&x_trans_id=' . urlencode($order->transid);
188 break;
191 case AN_ACTION_CREDIT:
193 if ($order->status != AN_STATUS_AUTHCAPTURE) {
194 $message = "Order status must be authorized/captured!";
195 return AN_RETURNZERO;
197 if (!authorize_settled($order)) {
198 $message = "Order must be settled. Try VOID, check Cut-Off time if it fails!";
199 return AN_RETURNZERO;
201 $timenowsettle = authorize_getsettletime(time());
202 $timediff = $timenowsettle - (120 * 3600 * 24);
203 if ($order->settletime < $timediff) {
204 $message = "Order must be credited within 120 days!";
205 return AN_RETURNZERO;
207 if (empty($extra)) {
208 $message = "Need extra fields to REFUND!";
209 return AN_RETURNZERO;
211 $total = floatval($extra->sum) + floatval($extra->amount);
212 if (($extra->amount == 0) || ($total > $order->amount)) {
213 $message = "Can be credited up to original amount.";
214 return AN_RETURNZERO;
216 $poststring .= '&x_type=CREDIT&x_trans_id=' . urlencode($order->transid);
217 $poststring .= '&x_currency_code=' . urlencode($order->currency);
218 $poststring .= '&x_invoice_num=' . urlencode($extra->orderid);
219 $poststring .= '&x_amount=' . urlencode($extra->amount);
220 if ($method == AN_METHOD_CC) {
221 $poststring .= '&x_card_num=' . sprintf("%04d", intval($order->refundinfo));
223 elseif ($method == AN_METHOD_ECHECK && empty($order->refundinfo)) {
224 $message = "Business checkings can be refunded only.";
225 return AN_RETURNZERO;
227 break;
230 case AN_ACTION_VOID:
232 if (authorize_expired($order) || authorize_settled($order)) {
233 $message = "The transaction cannot be voided due to the fact that it is expired or settled.";
234 return AN_RETURNZERO;
236 $poststring .= '&x_type=VOID&x_trans_id=' . urlencode($order->transid);
237 break;
240 default: {
241 $message = "Invalid action: $action";
242 return AN_RETURNZERO;
246 $referer = '';
247 if (! (empty($CFG->an_referer) || $CFG->an_referer == "http://")) {
248 $referer = "Referer: $CFG->an_referer\r\n";
251 $errno = 0; $errstr = '';
252 $host = $test ? 'certification.authorize.net' : 'secure.authorize.net';
253 $fp = fsockopen("ssl://$host", 443, $errno, $errstr, 60);
254 if (!$fp) {
255 $message = "no connection: $errstr ($errno)";
256 return AN_RETURNZERO;
259 // critical section
260 @ignore_user_abort(true);
261 if (intval(ini_get('max_execution_time')) > 0) {
262 @set_time_limit(300);
265 fwrite($fp, "POST /gateway/transact.dll HTTP/1.0\r\n" .
266 "Host: $host\r\n" . $referer .
267 "Content-type: application/x-www-form-urlencoded\r\n" .
268 "Connection: close\r\n" .
269 "Content-length: " . strlen($poststring) . "\r\n\r\n" .
270 $poststring . "\r\n"
273 $tmpstr = '';
274 while(!feof($fp) && !stristr($tmpstr, 'content-length')) {
275 $tmpstr = fgets($fp, 4096);
277 if (!stristr($tmpstr, 'content-length')) {
278 $message = "content-length error";
279 @fclose($fp);
280 return AN_RETURNZERO;
282 $length = trim(substr($tmpstr, strpos($tmpstr,'content-length')+15));
283 fgets($fp, 4096);
284 $data = fgets($fp, $length);
285 @fclose($fp);
286 $response = explode(AN_ENCAP.AN_DELIM.AN_ENCAP, $data);
287 if ($response === false) {
288 $message = "response error";
289 return AN_RETURNZERO;
291 $rcount = count($response) - 1;
292 if ($response[0]{0} == AN_ENCAP) {
293 $response[0] = substr($response[0], 1);
295 if (substr($response[$rcount], -1) == AN_ENCAP) {
296 $response[$rcount] = substr($response[$rcount], 0, -1);
299 $responsecode = intval($response[0]);
300 if ($responsecode == AN_APPROVED || $responsecode == AN_REVIEW)
302 $transid = intval($response[6]);
303 if ($test || $transid == 0) {
304 return $responsecode; // don't update original transaction in test mode.
306 switch ($action) {
307 case AN_ACTION_AUTH_ONLY:
308 case AN_ACTION_CAPTURE_ONLY:
309 case AN_ACTION_AUTH_CAPTURE:
310 case AN_ACTION_PRIOR_AUTH_CAPTURE:
312 $order->transid = $transid;
314 if ($method == AN_METHOD_CC) {
315 if ($action == AN_ACTION_AUTH_ONLY || $responsecode == AN_REVIEW) {
316 $order->status = AN_STATUS_AUTH;
317 } else {
318 $order->status = AN_STATUS_AUTHCAPTURE;
319 $order->settletime = authorize_getsettletime(time());
322 elseif ($method == AN_METHOD_ECHECK) {
323 $order->status = AN_STATUS_UNDERREVIEW;
326 if (! update_record('enrol_authorize', $order)) {
327 email_to_admin("Error while trying to update data " .
328 "in table enrol_authorize. Please edit manually this record: ID=$order->id.", $order);
330 break;
332 case AN_ACTION_CREDIT:
334 // Credit generates new transaction id.
335 // So, $extra must be updated, not $order.
336 $extra->status = AN_STATUS_CREDIT;
337 $extra->transid = $transid;
338 $extra->settletime = authorize_getsettletime(time());
339 unset($extra->sum); // this is not used in refunds table.
340 if (! $extra->id = insert_record('enrol_authorize_refunds', $extra)) {
341 unset($extra->id);
342 email_to_admin("Error while trying to insert data " .
343 "into table enrol_authorize_refunds. Please add manually this record:", $extra);
345 break;
347 case AN_ACTION_VOID:
349 $tableupdate = 'enrol_authorize';
350 if ($order->status == AN_STATUS_CREDIT) {
351 $tableupdate = 'enrol_authorize_refunds';
352 unset($order->paymentmethod);
354 $order->status = AN_STATUS_VOID;
355 if (! update_record($tableupdate, $order)) {
356 email_to_admin("Error while trying to update data " .
357 "in table $tableupdate. Please edit manually this record: ID=$order->id.", $order);
359 break;
363 else
365 $reasonno = $response[2];
366 $reasonstr = "reason" . $reasonno;
367 $message = get_string($reasonstr, "enrol_authorize");
368 if ($message == '[[' . $reasonstr . ']]') {
369 $message = isset($response[3]) ? $response[3] : 'unknown error';
371 if ($method == AN_METHOD_CC && !empty($CFG->an_avs) && $response[5] != "P") {
372 $avs = "avs" . strtolower($response[5]);
373 $stravs = get_string($avs, "enrol_authorize");
374 $message .= "<br />" . get_string("avsresult", "enrol_authorize", $stravs);
376 if (!$test) { // Autoconfigure :)
377 switch($reasonno) {
378 // Credit card type isn't accepted
379 case AN_REASON_NOCCTYPE:
380 case AN_REASON_NOCCTYPE2:
382 if (!empty($cctype)) {
383 $ccaccepts = get_list_of_creditcards();
384 unset($ccaccepts[$cctype]);
385 set_config('an_acceptccs', implode(',', array_keys($ccaccepts)));
386 email_to_admin("$message ($cctype)" .
387 "This is new config(an_acceptccs):", $ccaccepts);
389 break;
391 // Echecks only
392 case AN_REASON_ACHONLY:
394 set_config('an_acceptmethods', AN_METHOD_ECHECK);
395 email_to_admin("$message " .
396 "This is new config(an_acceptmethods):", array(AN_METHOD_ECHECK));
397 break;
399 // Echecks aren't accepted
400 case AN_REASON_NOACH:
402 set_config('an_acceptmethods', AN_METHOD_CC);
403 email_to_admin("$message " .
404 "This is new config(an_acceptmethods):", array(AN_METHOD_CC));
405 break;
407 // This echeck type isn't accepted
408 case AN_REASON_NOACHTYPE:
409 case AN_REASON_NOACHTYPE2:
411 if (!empty($extra->x_echeck_type)) {
412 switch ($extra->x_echeck_type) {
413 // CCD=BUSINESSCHECKING
414 case 'CCD':
416 set_config('an_acceptechecktypes', 'CHECKING,SAVINGS');
417 email_to_admin("$message " .
418 "This is new config(an_acceptechecktypes):", array('CHECKING','SAVINGS'));
420 break;
421 // WEB=CHECKING or SAVINGS
422 case 'WEB':
424 set_config('an_acceptechecktypes', 'BUSINESSCHECKING');
425 email_to_admin("$message " .
426 "This is new config(an_acceptechecktypes):", array('BUSINESSCHECKING'));
428 break;
431 break;
436 return $responsecode;