Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
39de6fd5 | 4 | | CiviCRM version 4.6 | |
6a488035 TO |
5 | +--------------------------------------------------------------------+ |
6 | | This file is a part of CiviCRM. | | |
7 | | | | |
8 | | CiviCRM is free software; you can copy, modify, and distribute it | | |
9 | | under the terms of the GNU Affero General Public License | | |
10 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | | |
11 | | | | |
12 | | CiviCRM is distributed in the hope that it will be useful, but | | |
13 | | WITHOUT ANY WARRANTY; without even the implied warranty of | | |
14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | |
15 | | See the GNU Affero General Public License for more details. | | |
16 | | | | |
17 | | You should have received a copy of the GNU Affero General Public | | |
18 | | License and the CiviCRM Licensing Exception along | | |
19 | | with this program; if not, contact CiviCRM LLC | | |
20 | | at info[AT]civicrm[DOT]org. If you have questions about the | | |
21 | | GNU Affero General Public License or the licensing of CiviCRM, | | |
22 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | | |
23 | +--------------------------------------------------------------------+ | |
24 | */ | |
25 | ||
26 | ||
27 | /* | |
28 | * Copyright (C) 2009 | |
29 | * Licensed to CiviCRM under the Academic Free License version 3.0. | |
30 | * | |
31 | * Written and contributed by Kirkdesigns (http://www.kirkdesigns.co.uk) | |
32 | * | |
33 | */ | |
34 | ||
35 | /** | |
36 | * | |
37 | * @package CRM | |
38 | * @author Tom Kirkpatrick <tkp@kirkdesigns.co.uk> | |
39 | * $Id$ | |
40 | */ | |
41 | class CRM_Core_Payment_Realex extends CRM_Core_Payment { | |
7da04cde | 42 | const AUTH_APPROVED = '00'; |
6a488035 TO |
43 | |
44 | protected $_mode = NULL; | |
45 | ||
46 | protected $_params = array(); | |
47 | ||
48 | /** | |
49 | * We only need one instance of this object. So we use the singleton | |
50 | * pattern and cache the instance in this variable | |
51 | * | |
52 | * @var object | |
53 | * @static | |
54 | */ | |
55 | static private $_singleton = NULL; | |
56 | ||
57 | /** | |
58 | * Constructor | |
59 | * | |
6a0b768e TO |
60 | * @param string $mode |
61 | * The mode of operation: live or test. | |
6a488035 | 62 | * |
77b97be7 EM |
63 | * @param $paymentProcessor |
64 | * | |
65 | * @return \CRM_Core_Payment_Realex | |
6a488035 | 66 | */ |
00be9182 | 67 | public function __construct($mode, &$paymentProcessor) { |
6a488035 TO |
68 | $this->_mode = $mode; |
69 | $this->_paymentProcessor = $paymentProcessor; | |
70 | $this->_processorName = ts('Realex'); | |
71 | ||
72 | $this->_setParam('merchant_ref', $paymentProcessor['user_name']); | |
73 | $this->_setParam('secret', $paymentProcessor['password']); | |
74 | $this->_setParam('account', $paymentProcessor['subject']); | |
75 | ||
76 | $this->_setParam('emailCustomer', 'TRUE'); | |
77 | srand(time()); | |
78 | $this->_setParam('sequence', rand(1, 1000)); | |
79 | } | |
80 | ||
81 | /** | |
100fef9d | 82 | * Singleton function used to manage this object |
6a488035 | 83 | * |
6a0b768e TO |
84 | * @param string $mode |
85 | * The mode of operation: live or test. | |
6a488035 | 86 | * |
77b97be7 EM |
87 | * @param object $paymentProcessor |
88 | * | |
6a488035 TO |
89 | * @return object |
90 | * @static | |
6a488035 | 91 | */ |
2aa397bc | 92 | public static function &singleton($mode, &$paymentProcessor, &$paymentForm = NULL, $force = FALSE) { |
52767de0 EM |
93 | if (!empty($paymentProcessor['id'])) { |
94 | $cacheKey = $paymentProcessor['id']; | |
6a488035 | 95 | } |
52767de0 EM |
96 | else { |
97 | //@todo eliminated instances of this in favour of id-specific instances. | |
98 | $cacheKey = $mode . '_' . $paymentProcessor['name']; | |
99 | } | |
100 | if (self::$_singleton[$cacheKey] === NULL) { | |
101 | self::$_singleton[$cacheKey] = new CRM_Core_Payment_Realex($mode, $paymentProcessor); | |
102 | } | |
103 | return self::$_singleton[$cacheKey]; | |
6a488035 TO |
104 | } |
105 | ||
6c786a9b | 106 | /** |
c490a46a | 107 | * @param array $params |
6c786a9b EM |
108 | * |
109 | * @throws Exception | |
110 | */ | |
00be9182 | 111 | public function setExpressCheckOut(&$params) { |
6a488035 TO |
112 | CRM_Core_Error::fatal(ts('This function is not implemented')); |
113 | } | |
114 | ||
6c786a9b EM |
115 | /** |
116 | * @param $token | |
117 | * | |
118 | * @throws Exception | |
119 | */ | |
00be9182 | 120 | public function getExpressCheckoutDetails($token) { |
6a488035 TO |
121 | CRM_Core_Error::fatal(ts('This function is not implemented')); |
122 | } | |
123 | ||
6c786a9b | 124 | /** |
c490a46a | 125 | * @param array $params |
6c786a9b EM |
126 | * |
127 | * @throws Exception | |
128 | */ | |
00be9182 | 129 | public function doExpressCheckout(&$params) { |
6a488035 TO |
130 | CRM_Core_Error::fatal(ts('This function is not implemented')); |
131 | } | |
132 | ||
6c786a9b | 133 | /** |
c490a46a | 134 | * @param array $params |
6c786a9b EM |
135 | * |
136 | * @throws Exception | |
137 | */ | |
00be9182 | 138 | public function doTransferCheckout(&$params) { |
6a488035 TO |
139 | CRM_Core_Error::fatal(ts('This function is not implemented')); |
140 | } | |
141 | ||
142 | /** | |
143 | * Submit a payment using Advanced Integration Method | |
144 | * | |
6a0b768e TO |
145 | * @param array $params |
146 | * Assoc array of input parameters for this transaction. | |
6a488035 TO |
147 | * |
148 | * @return array the result in a nice formatted array (or an error object) | |
6a488035 | 149 | */ |
00be9182 | 150 | public function doDirectPayment(&$params) { |
6a488035 TO |
151 | |
152 | if (!defined('CURLOPT_SSLCERT')) { | |
153 | return self::error(9001, ts('RealAuth requires curl with SSL support')); | |
154 | } | |
155 | ||
156 | $result = $this->setRealexFields($params); | |
157 | ||
158 | if ($result !== TRUE) { | |
159 | return $result; | |
160 | } | |
161 | ||
162 | /********************************************************** | |
163 | * Check to see if we have a duplicate before we send | |
164 | **********************************************************/ | |
165 | if ($this->_checkDupe($this->_getParam('order_id'))) { | |
166 | return self::error(9004, ts('It appears that this transaction is a duplicate. Have you already submitted the form once? If so there may have been a connection problem. Check your email for a receipt from Authorize.net. If you do not receive a receipt within 2 hours you can try your transaction again. If you continue to have problems please contact the site administrator.')); | |
167 | } | |
168 | ||
169 | // Create sha1 hash for request | |
170 | $hashme = "{$this->_getParam('timestamp')}.{$this->_getParam('merchant_ref')}.{$this->_getParam('order_id')}.{$this->_getParam('amount')}.{$this->_getParam('currency')}.{$this->_getParam('card_number')}"; | |
171 | $sha1hash = sha1($hashme); | |
172 | $hashme = "$sha1hash.{$this->_getParam('secret')}"; | |
173 | $sha1hash = sha1($hashme); | |
174 | ||
6a488035 TO |
175 | // Generate the request xml that is send to Realex Payments. |
176 | $request_xml = "<request type='auth' timestamp='{$this->_getParam('timestamp')}'> | |
177 | <merchantid>{$this->_getParam('merchant_ref')}</merchantid> | |
178 | <account>{$this->_getParam('account')}</account> | |
179 | <orderid>{$this->_getParam('order_id')}</orderid> | |
180 | <amount currency='{$this->_getParam('currency')}'>{$this->_getParam('amount')}</amount> | |
181 | <card> | |
182 | <number>{$this->_getParam('card_number')}</number> | |
183 | <expdate>{$this->_getParam('exp_date')}</expdate> | |
184 | <type>{$this->_getParam('card_type')}</type> | |
185 | <chname>{$this->_getParam('card_name')}</chname> | |
186 | <issueno>{$this->_getParam('issue_number')}</issueno> | |
187 | <cvn> | |
188 | <number>{$this->_getParam('cvn')}</number> | |
189 | <presind>1</presind> | |
190 | </cvn> | |
191 | </card> | |
192 | <autosettle flag='1'/> | |
193 | <sha1hash>$sha1hash</sha1hash> | |
194 | <comments> | |
195 | <comment id='1'>{$this->_getParam('comments')}</comment> | |
196 | </comments> | |
197 | <tssinfo> | |
198 | <varref>{$this->_getParam('varref')}</varref> | |
199 | </tssinfo> | |
200 | </request>"; | |
201 | ||
202 | /********************************************************** | |
203 | * Send to the payment processor using cURL | |
204 | **********************************************************/ | |
205 | ||
206 | $submit = curl_init($this->_paymentProcessor['url_site']); | |
207 | ||
208 | if (!$submit) { | |
209 | return self::error(9002, ts('Could not initiate connection to payment gateway')); | |
210 | } | |
211 | ||
212 | curl_setopt($submit, CURLOPT_HTTPHEADER, array('SOAPAction: ""')); | |
213 | curl_setopt($submit, CURLOPT_RETURNTRANSFER, 1); | |
214 | curl_setopt($submit, CURLOPT_TIMEOUT, 60); | |
215 | curl_setopt($submit, CURLOPT_SSL_VERIFYPEER, CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'verifySSL')); | |
216 | curl_setopt($submit, CURLOPT_HEADER, 0); | |
217 | ||
218 | // take caching out of the picture | |
219 | curl_setopt($submit, CURLOPT_FORBID_REUSE, 1); | |
220 | curl_setopt($submit, CURLOPT_FRESH_CONNECT, 1); | |
221 | ||
222 | // Apply the XML to our curl call | |
223 | curl_setopt($submit, CURLOPT_POST, 1); | |
224 | curl_setopt($submit, CURLOPT_POSTFIELDS, $request_xml); | |
225 | ||
226 | $response_xml = curl_exec($submit); | |
227 | ||
228 | if (!$response_xml) { | |
229 | return self::error(curl_errno($submit), curl_error($submit)); | |
230 | } | |
231 | ||
232 | curl_close($submit); | |
233 | ||
234 | // Tidy up the responce xml | |
235 | $response_xml = preg_replace("/[\s\t]/", " ", $response_xml); | |
236 | $response_xml = preg_replace("/[\n\r]/", "", $response_xml); | |
237 | ||
238 | // Parse the response xml | |
239 | $xml_parser = xml_parser_create(); | |
240 | if (!xml_parse($xml_parser, $response_xml)) { | |
241 | return self::error(9003, 'XML Error'); | |
242 | } | |
243 | ||
244 | $response = $this->xml_parse_into_assoc($response_xml); | |
245 | $response = $response['#return']['RESPONSE']; | |
246 | ||
247 | // Log the Realex response for debugging | |
248 | // CRM_Core_Error::debug_var('REALEX --------- Response from Realex: ', $response, TRUE); | |
249 | ||
250 | // Return an error if authentication was not successful | |
251 | if ($response['RESULT'] !== self::AUTH_APPROVED) { | |
252 | return self::error($response['RESULT'], ' ' . $response['MESSAGE']); | |
253 | } | |
254 | ||
255 | // Check the response hash | |
256 | $hashme = "{$this->_getParam('timestamp')}.{$this->_getParam('merchant_ref')}.{$this->_getParam('order_id')}.{$response['RESULT']}.{$response['MESSAGE']}.{$response['PASREF']}.{$response['AUTHCODE']}"; | |
257 | $sha1hash = sha1($hashme); | |
258 | $hashme = "$sha1hash.{$this->_getParam('secret')}"; | |
259 | $sha1hash = sha1($hashme); | |
260 | ||
261 | if ($response['SHA1HASH'] != $sha1hash) { | |
262 | // FIXME: Need to actually check this - I couldn't get the | |
263 | // hashes to match so I'm commenting out for now' | |
264 | // return self::error( 9001, "Hash error, please report this to the webmaster" ); | |
265 | } | |
266 | ||
267 | // FIXME: We are using the trxn_result_code column to store all these extra details since there | |
268 | // seems to be nowhere else to put them. This is THE WRONG THING TO DO! | |
269 | $extras = array( | |
270 | 'authcode' => $response['AUTHCODE'], | |
271 | 'batch_id' => $response['BATCHID'], | |
272 | 'message' => $response['MESSAGE'], | |
273 | 'trxn_result_code' => $response['RESULT'], | |
274 | ); | |
275 | ||
276 | $params['trxn_id'] = $response['PASREF']; | |
277 | $params['trxn_result_code'] = serialize($extras); | |
278 | $params['currencyID'] = $this->_getParam('currency'); | |
279 | $params['gross_amount'] = $this->_getParam('amount'); | |
280 | $params['fee_amount'] = 0; | |
281 | ||
282 | return $params; | |
283 | } | |
284 | ||
285 | /** | |
286 | * Helper function to convert XML string to multi-dimension array. | |
287 | * | |
288 | * @param $xml | |
289 | * an XML string. | |
290 | * | |
fd31fa4c | 291 | * @return array An array of the result with following keys: |
6a488035 | 292 | */ |
00be9182 | 293 | public function xml_parse_into_assoc($xml) { |
6a488035 TO |
294 | $input = array(); |
295 | $result = array(); | |
296 | ||
297 | $result['#error'] = FALSE; | |
298 | $result['#return'] = NULL; | |
299 | ||
300 | $xmlparser = xml_parser_create(); | |
301 | $ret = xml_parse_into_struct($xmlparser, $xml, $input); | |
302 | ||
303 | xml_parser_free($xmlparser); | |
304 | ||
305 | if (empty($input)) { | |
306 | $result['#return'] = $xml; | |
307 | } | |
308 | else { | |
309 | if ($ret > 0) { | |
310 | $result['#return'] = $this->_xml_parse($input); | |
311 | } | |
312 | else { | |
313 | $result['#error'] = ts('Error parsing XML result - error code = %1 at line %2 char %3', | |
314 | array( | |
315 | 1 => xml_get_error_code($xmlparser), | |
316 | 2 => xml_get_current_line_number($xmlparser), | |
21dfd5f5 | 317 | 3 => xml_get_current_column_number($xmlparser), |
6a488035 TO |
318 | ) |
319 | ); | |
320 | } | |
321 | } | |
322 | return $result; | |
323 | } | |
324 | ||
325 | // private helper for xml_parse_into_assoc, to recusively parsing the result | |
6c786a9b EM |
326 | /** |
327 | * @param $input | |
328 | * @param int $depth | |
329 | * | |
330 | * @return array | |
331 | */ | |
00be9182 | 332 | public function _xml_parse($input, $depth = 1) { |
6a488035 TO |
333 | $output = array(); |
334 | $children = array(); | |
335 | ||
336 | foreach ($input as $data) { | |
337 | if ($data['level'] == $depth) { | |
338 | switch ($data['type']) { | |
339 | case 'complete': | |
340 | $output[$data['tag']] = isset($data['value']) ? $data['value'] : ''; | |
341 | break; | |
342 | ||
343 | case 'open': | |
344 | $children = array(); | |
345 | break; | |
346 | ||
347 | case 'close': | |
348 | $output[$data['tag']] = $this->_xml_parse($children, $depth + 1); | |
349 | break; | |
350 | } | |
351 | } | |
352 | else { | |
353 | $children[] = $data; | |
354 | } | |
355 | } | |
356 | return $output; | |
357 | } | |
358 | ||
359 | /** | |
360 | * Format the params from the form ready for sending to Realex. Also perform some validation | |
361 | */ | |
00be9182 | 362 | public function setRealexFields(&$params) { |
2aa397bc | 363 | if ((int) $params['amount'] <= 0) { |
6a488035 TO |
364 | return self::error(9001, ts('Amount must be positive')); |
365 | } | |
366 | ||
367 | // format amount to be in smallest possible units | |
368 | //list($bills, $pennies) = explode('.', $params['amount']); | |
369 | $this->_setParam('amount', 100 * $params['amount']); | |
370 | ||
371 | switch (strtolower($params['credit_card_type'])) { | |
372 | case 'mastercard': | |
373 | $this->_setParam('card_type', 'MC'); | |
374 | $this->_setParam('requiresIssueNumber', FALSE); | |
375 | break; | |
376 | ||
377 | case 'visa': | |
378 | $this->_setParam('card_type', 'VISA'); | |
379 | $this->_setParam('requiresIssueNumber', FALSE); | |
380 | break; | |
381 | ||
382 | case 'amex': | |
383 | $this->_setParam('card_type', 'AMEX'); | |
384 | $this->_setParam('requiresIssueNumber', FALSE); | |
385 | break; | |
386 | ||
387 | case 'laser': | |
388 | $this->_setParam('card_type', 'LASER'); | |
389 | $this->_setParam('requiresIssueNumber', FALSE); | |
390 | break; | |
391 | ||
392 | case 'maestro': | |
393 | case 'switch': | |
394 | case 'maestro/switch': | |
395 | case 'solo': | |
396 | $this->_setParam('card_type', 'SWITCH'); | |
397 | $this->_setParam('requiresIssueNumber', TRUE); | |
398 | break; | |
399 | ||
400 | default: | |
401 | return self::error(9001, ts('Credit card type not supported by Realex:') . ' ' . $params['credit_card_type']); | |
402 | } | |
403 | ||
404 | // get the card holder name - cater cor customized billing forms | |
405 | if (isset($params['cardholder_name'])) { | |
406 | $credit_card_name = $params['cardholder_name']; | |
407 | } | |
408 | else { | |
409 | $credit_card_name = $params['first_name'] . " "; | |
410 | if (!empty($params['middle_name'])) { | |
411 | $credit_card_name .= $params['middle_name'] . " "; | |
412 | } | |
413 | $credit_card_name .= $params['last_name']; | |
414 | } | |
415 | ||
416 | $this->_setParam('card_name', $credit_card_name); | |
417 | $this->_setParam('card_number', str_replace(' ', '', $params['credit_card_number'])); | |
418 | $this->_setParam('cvn', $params['cvv2']); | |
419 | $this->_setParam('country', $params['country']); | |
420 | $this->_setParam('post_code', $params['postal_code']); | |
421 | $this->_setParam('order_id', $params['invoiceID']); | |
422 | $params['issue_number'] = (isset($params['issue_number']) ? $params['issue_number'] : ''); | |
423 | $this->_setParam('issue_number', $params['issue_number']); | |
424 | $this->_setParam('varref', $params['contributionType_name']); | |
425 | $comment = $params['description'] . ' (page id:' . $params['contributionPageID'] . ')'; | |
426 | $this->_setParam('comments', $comment); | |
427 | //$this->_setParam('currency', $params['currencyID']); | |
428 | ||
429 | // set the currency to the default which can be overrided. | |
430 | $config = CRM_Core_Config::singleton(); | |
431 | $this->_setParam('currency', $config->defaultCurrency); | |
432 | ||
433 | // Format the expiry date to MMYY | |
2aa397bc | 434 | $expmonth = (string) $params['month']; |
6a488035 | 435 | $expmonth = (strlen($expmonth) === 1) ? '0' . $expmonth : $expmonth; |
2aa397bc | 436 | $expyear = substr((string) $params['year'], 2, 2); |
6a488035 TO |
437 | $this->_setParam('exp_date', $expmonth . $expyear); |
438 | ||
439 | if (isset($params['credit_card_start_date']) && (strlen($params['credit_card_start_date']['M']) !== 0) && | |
440 | (strlen($params['credit_card_start_date']['Y']) !== 0) | |
441 | ) { | |
2aa397bc | 442 | $startmonth = (string) $params['credit_card_start_date']['M']; |
6a488035 | 443 | $startmonth = (strlen($startmonth) === 1) ? '0' . $startmonth : $startmonth; |
2aa397bc | 444 | $startyear = substr((string) $params['credit_card_start_date']['Y'], 2, 2); |
6a488035 TO |
445 | $this->_setParam('start_date', $startmonth . $startyear); |
446 | } | |
447 | ||
448 | // Create timestamp | |
449 | $timestamp = strftime("%Y%m%d%H%M%S"); | |
450 | $this->_setParam('timestamp', $timestamp); | |
451 | ||
452 | return TRUE; | |
453 | } | |
454 | ||
455 | /** | |
456 | * Checks to see if invoice_id already exists in db | |
457 | * | |
6a0b768e TO |
458 | * @param int $invoiceId |
459 | * The ID to check. | |
6a488035 TO |
460 | * |
461 | * @return bool True if ID exists, else false | |
462 | */ | |
00be9182 | 463 | public function _checkDupe($invoiceId) { |
6a488035 TO |
464 | $contribution = new CRM_Contribute_DAO_Contribution(); |
465 | $contribution->invoice_id = $invoiceId; | |
466 | return $contribution->find(); | |
467 | } | |
468 | ||
469 | /** | |
470 | * Get the value of a field if set | |
471 | * | |
6a0b768e TO |
472 | * @param string $field |
473 | * The field. | |
6a488035 TO |
474 | * |
475 | * @return mixed value of the field, or empty string if the field is | |
476 | * not set | |
477 | */ | |
00be9182 | 478 | public function _getParam($field) { |
6a488035 TO |
479 | if (isset($this->_params[$field])) { |
480 | return $this->_params[$field]; | |
481 | } | |
482 | else { | |
483 | return ''; | |
484 | } | |
485 | } | |
486 | ||
487 | /** | |
488 | * Set a field to the specified value. Value must be a scalar (int, | |
489 | * float, string, or boolean) | |
490 | * | |
491 | * @param string $field | |
492 | * @param mixed $value | |
493 | * | |
494 | * @return bool false if value is not a scalar, true if successful | |
495 | */ | |
00be9182 | 496 | public function _setParam($field, $value) { |
6a488035 TO |
497 | if (!is_scalar($value)) { |
498 | return FALSE; | |
499 | } | |
500 | else { | |
501 | $this->_params[$field] = $value; | |
502 | } | |
503 | } | |
504 | ||
6c786a9b EM |
505 | /** |
506 | * @param null $errorCode | |
507 | * @param null $errorMessage | |
508 | * | |
509 | * @return object | |
510 | */ | |
00be9182 | 511 | public function &error($errorCode = NULL, $errorMessage = NULL) { |
6a488035 TO |
512 | $e = CRM_Core_Error::singleton(); |
513 | ||
514 | if ($errorCode) { | |
515 | if ($errorCode == '101' || $errorCode == '102') { | |
516 | $display_error = ts('Card declined by bank. Please try with a different card.'); | |
517 | } | |
518 | elseif ($errorCode == '103') { | |
519 | $display_error = ts('Card reported lost or stolen. This incident will be reported.'); | |
520 | } | |
521 | elseif ($errorCode == '501') { | |
522 | $display_error = ts("It appears that this transaction is a duplicate. Have you already submitted the form once? If so there may have been a connection problem. Check your email for a receipt for this transaction. If you do not receive a receipt within 2 hours you can try your transaction again. If you continue to have problems please contact the site administrator."); | |
523 | } | |
524 | elseif ($errorCode == '509') { | |
525 | $display_error = $errorMessage; | |
526 | } | |
527 | else { | |
528 | $display_error = ts('We were unable to process your payment at this time. Please try again later.'); | |
529 | } | |
530 | $e->push($errorCode, 0, NULL, $display_error); | |
531 | } | |
532 | else { | |
533 | $e->push(9001, 0, NULL, ts('We were unable to process your payment at this time. Please try again later.')); | |
534 | } | |
535 | return $e; | |
536 | } | |
537 | ||
538 | /** | |
539 | * This function checks to see if we have the right config values | |
540 | * | |
541 | * @return string the error message if any | |
6a488035 | 542 | */ |
00be9182 | 543 | public function checkConfig() { |
6a488035 TO |
544 | $error = array(); |
545 | if (empty($this->_paymentProcessor['user_name'])) { | |
546 | $error[] = ts('Merchant ID is not set for this payment processor'); | |
547 | } | |
548 | ||
549 | if (empty($this->_paymentProcessor['password'])) { | |
550 | $error[] = ts('Secret is not set for this payment processor'); | |
551 | } | |
552 | ||
553 | if (!empty($error)) { | |
554 | return implode('<p>', $error); | |
555 | } | |
556 | else { | |
557 | return NULL; | |
558 | } | |
559 | } | |
560 | } |