Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
06b69b18 | 4 | | CiviCRM version 4.5 | |
6a488035 | 5 | +--------------------------------------------------------------------+ |
06b69b18 | 6 | | Copyright CiviCRM LLC (c) 2004-2014 | |
6a488035 TO |
7 | +--------------------------------------------------------------------+ |
8 | | This file is a part of CiviCRM. | | |
9 | | | | |
10 | | CiviCRM is free software; you can copy, modify, and distribute it | | |
11 | | under the terms of the Affero General Public License Version 1, | | |
12 | | March 2002. | | |
13 | | | | |
14 | | CiviCRM is distributed in the hope that it will be useful, but | | |
15 | | WITHOUT ANY WARRANTY; without even the implied warranty of | | |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | |
17 | | See the Affero General Public License for more details. | | |
18 | | | | |
19 | | You should have received a copy of the Affero General Public | | |
20 | | License and the CiviCRM Licensing Exception along | | |
21 | | with this program; if not, contact CiviCRM LLC | | |
22 | | at info[AT]civicrm[DOT]org. If you have questions about the | | |
23 | | Affero General Public License or the licensing of CiviCRM, | | |
24 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | | |
25 | +--------------------------------------------------------------------+ | |
26 | */ | |
27 | ||
28 | /** | |
29 | * | |
30 | * @package CRM | |
31 | * @author Alan Dixon | |
06b69b18 | 32 | * @copyright CiviCRM LLC (c) 2004-2014 |
6a488035 TO |
33 | * $Id$ |
34 | * | |
35 | */ | |
36 | class CRM_Core_Payment_IATS extends CRM_Core_Payment { | |
37 | # (not used, implicit in the API, might need to convert?) | |
38 | CONST CHARSET = 'UFT-8'; | |
39 | /* check IATS website for additional supported currencies */ | |
40 | CONST CURRENCIES = 'CAD,USD,AUD,GBP,EUR,NZD'; | |
41 | ||
42 | /** | |
43 | * We only need one instance of this object. So we use the singleton | |
44 | * pattern and cache the instance in this variable | |
45 | * | |
46 | * @var object | |
47 | * @static | |
48 | */ | |
49 | static private $_singleton = NULL; | |
50 | ||
51 | /** | |
52 | * Constructor | |
53 | * | |
54 | * @param string $mode the mode of operation: live or test | |
55 | * | |
77b97be7 EM |
56 | * @param $paymentProcessor |
57 | * | |
58 | * @return \CRM_Core_Payment_IATS | |
6a488035 TO |
59 | */ |
60 | function __construct($mode, &$paymentProcessor) { | |
61 | $this->_paymentProcessor = $paymentProcessor; | |
62 | $this->_processorName = ts('IATS'); | |
63 | ||
64 | // get merchant data from config | |
65 | $config = CRM_Core_Config::singleton(); | |
66 | // live or test | |
67 | $this->_profile['mode'] = $mode; | |
68 | $this->_profile['webserver'] = parse_url($this->_paymentProcessor['url_site'], PHP_URL_HOST); | |
69 | $currencyID = $config->defaultCurrency; | |
70 | ||
71 | if (!in_array($currencyID, explode(',', self::CURRENCIES))) { | |
72 | // Configuration error: default currency must be in CURRENCIES const | |
73 | return self::error('Invalid configuration:' . $currencyID . ', you must use one of ' . self::CURRENCIES . ' with IATS'); | |
74 | } | |
75 | } | |
76 | ||
6c786a9b EM |
77 | /** |
78 | * @param string $mode | |
79 | * @param array $paymentProcessor | |
80 | * | |
81 | * @return mixed | |
82 | */ | |
6a488035 TO |
83 | static function &singleton($mode, &$paymentProcessor) { |
84 | $processorName = $paymentProcessor['name']; | |
85 | if (self::$_singleton[$processorName] === NULL) { | |
86 | self::$_singleton[$processorName] = new CRM_Core_Payment_IATS($mode, $paymentProcessor); | |
87 | } | |
88 | return self::$_singleton[$processorName]; | |
89 | } | |
90 | ||
b5c2afd0 EM |
91 | /** |
92 | * This function collects all the information from a web/api form and invokes | |
93 | * the relevant payment processor specific functions to perform the transaction | |
94 | * | |
95 | * @param array $params assoc array of input parameters for this transaction | |
96 | * | |
97 | * @return array the result in an nice formatted array (or an error object) | |
98 | * @abstract | |
99 | */ | |
6a488035 TO |
100 | function doDirectPayment(&$params) { |
101 | // $result = ''; | |
102 | // foreach($params as $key => $value) { | |
103 | // $result .= "<strong>$key</strong>: $value<br />"; | |
104 | // } | |
105 | // return self::error($result); | |
106 | // make sure i've been called correctly ... | |
107 | ||
108 | if (!$this->_profile) { | |
109 | return self::error('Unexpected error, missing profile'); | |
110 | } | |
111 | if (!in_array($params['currencyID'], explode(',', self::CURRENCIES))) { | |
112 | return self::error('Invalid currency selection, must be one of ' . self::CURRENCIES); | |
113 | } | |
f065946a | 114 | $isRecur = CRM_Utils_Array::value('is_recur', $params, FALSE); |
6a488035 TO |
115 | // AgentCode = $this->_paymentProcessor['signature']; |
116 | // Password = $this->_paymentProcessor['password' ]; | |
117 | // beginning of modified sample code from IATS php api include IATS supplied api library | |
118 | ||
119 | if ($isRecur) { | |
120 | include_once ('Services/IATS/iats_reoccur.php'); | |
121 | $iatslink1 = new iatslinkReoccur; | |
122 | } | |
123 | else { | |
124 | include_once ('Services/IATS/iatslink.php'); | |
125 | $iatslink1 = new iatslink; | |
126 | } | |
127 | ||
128 | $iatslink1->setTestMode($this->_profile['mode'] != 'live'); | |
129 | $iatslink1->setWebServer($this->_profile['webserver']); | |
130 | ||
131 | // return self::error($this->_profile['webserver']); | |
132 | ||
133 | // Put your invoice here | |
134 | $iatslink1->setInvoiceNumber($params['invoiceID']); | |
135 | ||
136 | // $iatslink1->setCardType("VISA"); | |
137 | // If CardType is not set, iatslink will find the cardType | |
138 | // CardType not set because IATS uses different names! | |
139 | // $iatslink1->setCardType($params['credit_card_type']); | |
140 | ||
141 | $iatslink1->setCardNumber($params['credit_card_number']); | |
142 | $expiry_string = sprintf('%02d/%02d', $params['month'], ($params['year'] % 100)); | |
143 | $iatslink1->setCardExpiry($expiry_string); | |
144 | $amount = sprintf('%01.2f', $params['amount']); | |
145 | // sell | |
146 | $iatslink1->setDollarAmount($amount); | |
147 | // refund | |
148 | //$iatslink1->setDollarAmount(-1.15); | |
149 | ||
150 | $AgentCode = $this->_paymentProcessor['signature']; | |
151 | $Password = $this->_paymentProcessor['password']; | |
152 | $iatslink1->setAgentCode($AgentCode); | |
153 | $iatslink1->setPassword($Password); | |
154 | // send IATS my invoiceID to match things up later | |
155 | $iatslink1->setInvoiceNumber($params['invoiceID']); | |
156 | ||
157 | // Set billing fields | |
158 | $iatslink1->setFirstName($params['billing_first_name']); | |
159 | $iatslink1->setLastName($params['billing_last_name']); | |
160 | $iatslink1->setStreetAddress($params['street_address']); | |
161 | $iatslink1->setCity($params['city']); | |
162 | $iatslink1->setState($params['state_province']); | |
163 | $iatslink1->setZipCode($params['postal_code']); | |
164 | // and now go! ... uses curl to post and retrieve values | |
165 | // after various data integrity tests | |
166 | // simple version | |
167 | if (!$isRecur) { | |
168 | // cvv2 only seems to get set for this! | |
169 | $iatslink1->setCVV2($params['cvv2']); | |
170 | ||
171 | // Allow further manipulation of the arguments via custom hooks, | |
172 | // before initiating processCreditCard() | |
173 | CRM_Utils_Hook::alterPaymentProcessorParams($this, $params, $iatslink1); | |
174 | ||
175 | $iatslink1->processCreditCard(); | |
176 | // extra fields for recurring donations | |
177 | } | |
178 | else { | |
179 | // implicit - test?: 1 == $params['frequency_interval']; | |
180 | $scheduleType = NULL; | |
181 | if ($params['installments']) { | |
182 | $paymentsRecur = $params['installments'] - 1; | |
183 | } | |
184 | // handle unspecified installments by setting to 10 years, IATS doesn't allow indefinitely recurring contributions | |
185 | else { | |
186 | switch ($params['frequency_unit']) { | |
187 | case 'week': | |
188 | $paymentsRecur = 520; | |
189 | case 'month': | |
190 | $paymentsRecur = 120; | |
191 | } | |
192 | } | |
193 | // IATS requires end date, calculated here | |
194 | ||
195 | // to be converted to date format later | |
196 | $startTime = time(); | |
197 | $date = getdate($startTime); | |
198 | ||
199 | switch ($params['frequency_unit']) { | |
200 | case 'week': | |
201 | $scheduleType = 'WEEKLY'; | |
202 | $scheduleDate = $date['wday'] + 1; | |
203 | $endTime = $startTime + ($paymentsRecur * 7 * 24 * 60 * 60); | |
204 | break; | |
205 | ||
206 | case 'month': | |
207 | $scheduleType = 'MONTHLY'; | |
208 | $scheduleDate = $date['mday']; | |
209 | $date['mon'] += $paymentsRecur; | |
210 | while ($date['mon'] > 12) { | |
211 | $date['mon'] -= 12; | |
212 | $date['year'] += 1; | |
213 | } | |
214 | $endTime = mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year']); | |
215 | break; | |
216 | ||
217 | default: | |
218 | die('Invalid frequency unit!'); | |
219 | break; | |
220 | } | |
221 | $endDate = date('Y-m-d', $endTime); | |
222 | $startDate = date('Y-m-d', $startTime); | |
223 | $iatslink1->setReoccuringStatus("ON"); | |
224 | $iatslink1->setBeginDate($startDate); | |
225 | $iatslink1->setEndDate($endDate); | |
226 | $iatslink1->setScheduleType($scheduleType); | |
227 | $iatslink1->setScheduleDate($scheduleDate); | |
228 | ||
229 | // Allow further manipulation of the arguments via custom hooks, | |
230 | // before initiating the curl process | |
231 | CRM_Utils_Hook::alterPaymentProcessorParams($this, $params, $iatslink1); | |
232 | ||
233 | // this next line is the reoccc equiv of processCreditCard | |
234 | $iatslink1->createReoccCustomer(); | |
235 | } | |
236 | ||
237 | if ($iatslink1->getStatus() == 1) { | |
238 | // this just means we got some kind of answer, not necessarily approved | |
239 | $result = $iatslink1->getAuthorizationResult(); | |
240 | //return self::error($result); | |
241 | $result = explode(':', $result, 2); | |
242 | $trxn_result = trim($result[0]); | |
243 | $trxn_id = trim($result[1]); | |
244 | if ($trxn_result == 'OK') { | |
245 | $params['trxn_id'] = $trxn_id . ':' . time(); | |
246 | $params['gross_amount'] = $amount; | |
247 | return $params; | |
248 | } | |
249 | // createReoccCustomer() may return other, valid result codes... | |
250 | elseif (preg_match('/A\d+/', $trxn_result)) { | |
251 | $params['trxn_id'] = $trxn_result; | |
252 | $params['gross_amount'] = $amount; | |
253 | return $params; | |
254 | } | |
255 | else { | |
256 | return self::error($trxn_id); | |
257 | } | |
258 | } | |
259 | else { | |
260 | return self::error($iatslink1->getError()); | |
261 | } | |
262 | } | |
263 | ||
6c786a9b EM |
264 | /** |
265 | * @param null $error | |
266 | * | |
267 | * @return object | |
268 | */ | |
6a488035 TO |
269 | function &error($error = NULL) { |
270 | $e = CRM_Core_Error::singleton(); | |
271 | if (is_object($error)) { | |
272 | $e->push($error->getResponseCode(), | |
273 | 0, NULL, | |
274 | $error->getMessage() | |
275 | ); | |
276 | } | |
277 | elseif ($error && is_numeric($error)) { | |
278 | $e->push($error, | |
279 | 0, NULL, | |
280 | $this->errorString($error) | |
281 | ); | |
282 | } | |
283 | elseif (is_string($error)) { | |
284 | $e->push(9002, | |
285 | 0, NULL, | |
286 | $error | |
287 | ); | |
288 | } | |
289 | else { | |
290 | $e->push(9001, 0, NULL, "Unknown System Error."); | |
291 | } | |
292 | return $e; | |
293 | } | |
294 | ||
6c786a9b EM |
295 | /** |
296 | * @param $error_id | |
297 | * | |
298 | * @return string | |
299 | */ | |
6a488035 TO |
300 | function errorString($error_id) { |
301 | $errors = array( | |
302 | 1 => 'Agent Code has not been set up on the authorization system.', | |
303 | 2 => 'Unable to process transaction. Verify and re-enter credit card information.', | |
304 | 3 => 'Charge card expired.', | |
305 | 4 => 'Incorrect expiration date.', | |
306 | 5 => 'Invalid transaction. Verify and re-enter credit card information.', | |
307 | 6 => 'Transaction not supported by institution.', | |
308 | 7 => 'Lost or stolen card.', | |
309 | 8 => 'Invalid card status.', | |
310 | 9 => 'Restricted card status. Usually on corporate cards restricted to specific sales.', | |
311 | 10 => 'Error. Please verify and re-enter credit card information.', | |
312 | 11 => 'General decline code, may have different reasons for each card type. Please have your client call customer service.', | |
313 | 14 => 'This means that the credit card is over the limit.', | |
314 | 15 => 'Decline code, may have different reasons for each card type. Please have your client call customer service.', | |
315 | 16 => 'Invalid charge card number. Verify and re-enter credit card information.', | |
316 | 17 => 'Unable to authorize transaction. Verify card information with customer and re-enter. Could be invalid name or expiry date.', | |
317 | 18 => 'Card not supported by institution.', | |
318 | 19 => 'Incorrect CVV2.', | |
319 | 22 => 'Bank Timeout. Bank lines may be down or busy. Re-try transaction later.', | |
320 | 23 => 'System error. Re-try transaction later.', | |
321 | 24 => 'Charge card expired.', | |
322 | 25 => 'Capture card. Reported lost or stolen.', | |
323 | 27 => 'System error, please re-enter transaction.', | |
324 | 29 => 'Rejected by Ticketmaster.', | |
325 | 31 => 'Manual reject code ', | |
326 | 39 => 'Contact Ticketmaster 1-888-955-5455 ', | |
327 | 40 => 'Card not supported by Ticketmaster. Invalid cc number.', | |
328 | 41 => 'Invalid Expiry date ', | |
329 | 100 => 'Authorization system down. DO NOT REPROCESS.', | |
330 | ); | |
331 | return ' <strong>' . $errors[(integer) $error_id] . '</strong>'; | |
332 | } | |
333 | ||
334 | /** | |
335 | * This function checks to see if we have the right config values | |
336 | * | |
fd31fa4c | 337 | * @internal param string $mode the mode we are operating in (live or test) |
6a488035 TO |
338 | * |
339 | * @return string the error message if any | |
340 | * @public | |
341 | */ | |
342 | function checkConfig() { | |
343 | $error = array(); | |
344 | ||
345 | if (empty($this->_paymentProcessor['signature'])) { | |
346 | $error[] = ts('Agent Code is not set in the Administer CiviCRM » System Settings » Payment Processors.'); | |
347 | } | |
348 | ||
349 | if (empty($this->_paymentProcessor['password'])) { | |
350 | $error[] = ts('Password is not set in the Administer CiviCRM » System Settings » Payment Processors.'); | |
351 | } | |
352 | ||
353 | if (!empty($error)) { | |
354 | return implode('<p>', $error); | |
355 | } | |
356 | else { | |
357 | return NULL; | |
358 | } | |
359 | } | |
360 | } | |
361 |