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