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