CardAccessServices_Casmtp - Version 1.0.0

Version Notes

Release 1.0.0 added purchase & refund

Download this release

Release Info

Developer Card Access Services
Extension CardAccessServices_Casmtp
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

app/code/community/CardAccessServices/Casmtp/Helper/Data.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Card Access Services CASMTP
4
+ *
5
+ * Copyright 2012 Card Access Services
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ *
19
+ * @author Card Access Services
20
+ * @copyright Copyright (c) 2012 Card Access Services (http://www.cardaccess.com.au)
21
+ * @license http://opensource.org/licenses/Apache-2.0 Apache Software License (ASL 2.0)
22
+ */
23
+
24
+ /**
25
+ * Data class for CASMTP - doesn't do anything additional at this point in time
26
+ */
27
+ class CardAccessServices_Casmtp_Helper_Data extends Mage_Core_Helper_Abstract
28
+ {
29
+ }
30
+ ?>
app/code/community/CardAccessServices/Casmtp/Model/PaymentMethod.php ADDED
@@ -0,0 +1,444 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Card Access Services CASMTP
4
+ *
5
+ * Copyright 2012 Card Access Services
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ *
19
+ * @author Card Access Services
20
+ * @copyright Copyright (c) 2012 Card Access Services (http://www.cardaccess.com.au)
21
+ * @license http://opensource.org/licenses/Apache-2.0 Apache Software License (ASL 2.0)
22
+ */
23
+
24
+ /* Load our CASMTP utility classes */
25
+ require_once('CardAccessServices/Casmtp/casmtpprotocol.php');
26
+ require_once('CardAccessServices/Casmtp/casmiscutil.php');
27
+
28
+ /**
29
+ * Implements the gateway extension for transaction processing via the CASMTP method
30
+ */
31
+ class CardAccessServices_Casmtp_Model_PaymentMethod extends Mage_Payment_Model_Method_Cc {
32
+ /**
33
+ * Unique payment identifier
34
+ *
35
+ * @var string
36
+ * @access protected
37
+ */
38
+ protected $_code = 'casmtp';
39
+
40
+ /**
41
+ * Whether we are a gateway extension
42
+ *
43
+ * @var boolean
44
+ * @access protected
45
+ */
46
+ protected $_isGateway = true;
47
+
48
+ /**
49
+ * Whether we support the Authorize operation
50
+ *
51
+ * @var boolean
52
+ * @access protected
53
+ */
54
+ protected $_canAuthorize = false;
55
+
56
+ /**
57
+ * Whether we support the Capture operation
58
+ *
59
+ * @var boolean
60
+ * @access protected
61
+ */
62
+ protected $_canCapture = true;
63
+
64
+ /**
65
+ * Whether we support the CapturePartial operation
66
+ *
67
+ * @var boolean
68
+ * @access protected
69
+ */
70
+ protected $_canCapturePartial = true;
71
+
72
+ /**
73
+ * Whether we support the Void operation
74
+ *
75
+ * @var boolean
76
+ * @access protected
77
+ */
78
+ protected $_canVoid = false;
79
+
80
+ /**
81
+ * Whether this extension can be used by cardholders during checkout
82
+ *
83
+ * @var boolean
84
+ * @access protected
85
+ */
86
+ protected $_canUseCheckout = true;
87
+
88
+ /**
89
+ * Whether this extension can be used by the admin interface
90
+ *
91
+ * @var boolean
92
+ * @access protected
93
+ */
94
+ protected $_canUseInternal = true;
95
+
96
+ /**
97
+ * Whether this extension can be used for multiple shipping
98
+ *
99
+ * @var boolean
100
+ * @access protected
101
+ */
102
+ protected $_canUseForMultishipping = true;
103
+
104
+ /**
105
+ * Whether this extension can be used for saving credit cards
106
+ *
107
+ * This is not in the base module, but for some reason most of the other payment methods
108
+ * declare it, so we'll do the same here just to be safe
109
+ *
110
+ * @var boolean
111
+ * @access protected
112
+ */
113
+ protected $_canSaveCc = false;
114
+
115
+ /**
116
+ * Whether we can do a refund
117
+ *
118
+ * @return boolean whether we can do a refund
119
+ * @access private
120
+ */
121
+ public function canRefund() {
122
+ $version = Mage::getVersionInfo();
123
+ $major = intval ($version['major']);
124
+ $minor = intval ($version['minor']);
125
+ if ($major > 1) {
126
+ return true;
127
+ }
128
+ else if ($major == 1) {
129
+ return $minor > 5;
130
+ }
131
+ else {
132
+ return false;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Whether we can do a partial refund
138
+ *
139
+ * @return boolean whether we can do a partial refund
140
+ * @access private
141
+ */
142
+ public function canRefundPartialPerInvoice() {
143
+ return $this->canRefund();
144
+ }
145
+
146
+ /**
147
+ * Get the specified configuration value
148
+ *
149
+ * @param string $key name of the key
150
+ *
151
+ * @return string the configuration value
152
+ * @access private
153
+ */
154
+ private function _getConfig($key) {
155
+ return Mage::getStoreConfig("payment/casmtp/" . $key);
156
+ }
157
+
158
+ /**
159
+ * Get the merchant ID from the user configuration
160
+ *
161
+ * @return string the merchant ID
162
+ * @access private
163
+ */
164
+ private function _getMerchantId() {
165
+ return $this->_getConfig("etx_merchant");
166
+ }
167
+
168
+ /**
169
+ * Get the hash key from the user configuration
170
+ *
171
+ * @return string the hash key
172
+ * @access private
173
+ */
174
+ private function _getHashKey() {
175
+ return $this->_getConfig("hash_auth");
176
+ }
177
+
178
+ /**
179
+ * Figure out the gateway URL from the user configuration
180
+ *
181
+ * @return string the gateway URL
182
+ * @access private
183
+ */
184
+ private function _getUrl() {
185
+ return $this->_getConfig('is_test')? Casmtp::TEST_URL: Casmtp::LIVE_URL;
186
+ }
187
+
188
+ /**
189
+ * Whether to log events into the system log file
190
+ *
191
+ * NB: If this is a high volume site then you will either want to turn this setting off or
192
+ * ensure log rotation is being used
193
+ *
194
+ * @return boolean whether to log events
195
+ * @access private
196
+ */
197
+ private function _getLogEvents() {
198
+ return $this->_getConfig('log_events');
199
+ }
200
+
201
+ /**
202
+ * Create a new preconfigured CASMTP proxy instance
203
+ *
204
+ * @return string the CASMTP proxy instance
205
+ * @access private
206
+ */
207
+ private function _createCasmtpProxy() {
208
+ try {
209
+ return new CasmtpProxy($this->_getConfig('proxy_server_host'), $this->_getConfig('proxy_server_port'), $this->_getConfig('proxy_login_username'), Mage::helper('core')->decrypt($this->_getConfig('proxy_login_password')));
210
+ }
211
+ catch (Exception $e) {
212
+ Mage::log("$this->_code: proxy misconfigured");
213
+ Mage::throwException(Mage::helper('casmtp')->__($e->getMessage()));
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Create a new preconfigured CASMTP instance
219
+ *
220
+ * @return string the CASMTP instance
221
+ * @access private
222
+ */
223
+ private function _createCasmtp() {
224
+ return new Casmtp($this->_getUrl(), $this->_createCasmtpProxy(), $this->_getMerchantId(), Mage::helper('core')->decrypt($this->_getHashKey()));
225
+ }
226
+
227
+ /**
228
+ * Log the event
229
+ *
230
+ * @return void
231
+ * @access private
232
+ */
233
+ private function _logPaymentEvent($payment, $msg) {
234
+ if ($this->_getLogEvents()) {
235
+ $cust_ref = CasMiscUtil::getCustRefFromPayment($payment);
236
+ Mage::log($this->_code . "[" . $cust_ref . "]:" . $msg);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Retrieve audit number
242
+ *
243
+ * @param string $casmtp casmtp instance
244
+ * @param string $payment payment instance
245
+ * @param string $record_audit whether to record the audit number
246
+ *
247
+ * @return void
248
+ * @access private
249
+ */
250
+ private function _getAuditNumber($casmtp, $payment, $record_audit = true) {
251
+ $cust_ref = CasMiscUtil::getCustRefFromPayment($payment);
252
+
253
+ /* Get the audit number, this is used to trace the transaction */
254
+ $this->_logPaymentEvent($payment, "getting audit number for order");
255
+ try {
256
+ $audit = $casmtp->getAudit();
257
+ }
258
+ catch (Exception $e) {
259
+ /*
260
+ * This almost always indicates a misconfiguration issue
261
+ *
262
+ * But on the up side no transaction has gone to the bank, so it is always safe to retry. Note that
263
+ * the error message from the exception is probably more confusing the end user than helpful, so we
264
+ * do not expose this (although we do log it)
265
+ */
266
+ $this->_logPaymentEvent($payment, "error encountered: " . $e->getMessage());
267
+ Mage::throwException(Mage::helper('casmtp')->__("Error attempting transaction - please try again later"));
268
+ }
269
+
270
+ /*
271
+ * Record the audit number against the payment, if specified
272
+ *
273
+ * This can be quoted during support calls
274
+ */
275
+ $this->_logPaymentEvent($payment, "got audit number $audit");
276
+ if ($record_audit) {
277
+ /* Fill in the transaction ID */
278
+ $payment->setCcTransId($audit);
279
+ //$payment->setLastTransId($audit);
280
+ $payment->setTransactionId($audit);
281
+ }
282
+
283
+ return $audit;
284
+ }
285
+
286
+ /**
287
+ * Interpret the transaction result
288
+ *
289
+ * @param string $result the CASMTP transaction result
290
+ * @param string $payment payment instance
291
+ *
292
+ * @return void
293
+ * @access private
294
+ */
295
+ private function _interpretTxnResult ($result, $payment) {
296
+ $cust_ref = CasMiscUtil::getCustRefFromPayment($payment);
297
+
298
+ /* Log the payment event for the benefit of debugging */
299
+ $this->_logPaymentEvent($payment, "scode = " . $result->GetStatusCode());
300
+ $this->_logPaymentEvent($payment, "rcode = " . $result->GetResponseCode());
301
+ $this->_logPaymentEvent($payment, "setl_date = " . $result->GetSettlementDate());
302
+ $this->_logPaymentEvent($payment, "auth_code = " . $result->GetAuthorizationCode());
303
+ $this->_logPaymentEvent($payment, "err_msg = " . $result->GetDiagnosticMessage());
304
+
305
+ /* Figure out the result */
306
+ if ($result->isApproved()) {
307
+ /* Hooray, transaction approved */
308
+ $this->_logPaymentEvent($payment, "transaction approved");
309
+ }
310
+ else {
311
+ /*
312
+ * Generate the error message
313
+ *
314
+ * We keep it in general terms to avoid confusing the end user - the real error and
315
+ * all relevant scodes etc have already been logged above for the administrator to inspect
316
+ */
317
+ $errmsg = $result->isAbnormalResult()? "An error ocurred during transaction processing - please contact your site administrator for more details": "The transaction was declined - please try again with a different credit card";
318
+ $this->_logPaymentEvent($payment, "displayed message = " . $errmsg);
319
+ Mage::throwException(Mage::helper('casmtp')->__($errmsg));
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Validate the configuration
325
+ *
326
+ * This method is the only one where we expose inner configuration details - presumably the administrator
327
+ * will run through a test transaction to make sure all is configured before unleashing it to the internet!
328
+ *
329
+ * @return void
330
+ * @access public
331
+ */
332
+ public function validate() {
333
+ /* Let the parent do it's usual validation */
334
+ parent::validate ();
335
+
336
+ /* Check the ETX merchant ID is present and numeric */
337
+ $merid = $this->_getMerchantId();
338
+ if (!is_numeric($merid)) {
339
+ Mage::log("$this->_code: merchant ID misconfigured as $merid");
340
+ Mage::throwException(Mage::helper('casmtp')->__('The "ETX Merchant ID" configuration value is not numeric, please set this to the value assigned by Card Access Services'));
341
+ }
342
+
343
+ /* Check the hash key is present */
344
+ $hash_key = $this->_getHashKey();
345
+ if (empty ($hash_key)) {
346
+ Mage::log("$this->_code: hash key misconfigured");
347
+ Mage::throwException(Mage::helper('casmtp')->__('The "Hash authentication" configuration value is empty, please set this to the value assigned by Card Access Services'));
348
+ }
349
+
350
+ /* Check the proxy settings are ok */
351
+ $proxy = $this->_createCasmtpProxy();
352
+
353
+ return $this;
354
+ }
355
+
356
+ /**
357
+ * Perform a capture of the transaction
358
+ *
359
+ * @param string $payment payment instance
360
+ * @param float $amount amount to capture
361
+ *
362
+ * @return void
363
+ * @access public
364
+ */
365
+ public function capture(Varien_Object $payment, $amount) {
366
+ $cust_ref = CasMiscUtil::getCustRefFromPayment($payment);
367
+ $this->_logPaymentEvent($payment, "capture invoked");
368
+ $casmtp = $this->_createCasmtp();
369
+
370
+ /* Get an audit number first */
371
+ $audit = $this->_getAuditNumber($casmtp, $payment);
372
+
373
+ /* Now do the actual purchase */
374
+ try {
375
+ $pan = $payment->getCcNumber();
376
+ $cvv = $payment->getCcCid();
377
+ $expiry = CasMiscUtil::formatExpiryFromPayment($payment);
378
+ $this->_logPaymentEvent($payment, "starting capture for $audit (pan = xxxx-" . $payment->getCcLast4() . ", amount = $amount)");
379
+
380
+ $result = $casmtp->purchase($audit, $pan, $expiry, $cvv, CasMiscUtil::flattenAmount($amount), $cust_ref);
381
+ }
382
+ catch (Exception $e) {
383
+ /* This usually indicates some kind of wierd networking issue */
384
+ Mage::log("$this->_code $cust_ref: error encountered: " . $e->getMessage());
385
+ Mage::throwException(Mage::helper('casmtp')->__("There was an error encountered while performing the transaction"));
386
+ }
387
+
388
+ /* Interpret the transaction result */
389
+ $this->_interpretTxnResult($result, $payment);
390
+
391
+ return $this;
392
+ }
393
+
394
+ /**
395
+ * Perform a refund of the transaction
396
+ *
397
+ * @param string $payment payment instance
398
+ * @param float $amount amount to refund
399
+ *
400
+ * @return void
401
+ * @access public
402
+ */
403
+ public function refund(Varien_Object $payment, $amount) {
404
+ $cust_ref = CasMiscUtil::getCustRefFromPayment($payment);
405
+ $this->_logPaymentEvent($payment, "refund invoked");
406
+
407
+ /*
408
+ * Filter out zero amounts just in case the care framework doesn't do this
409
+ *
410
+ * The gateway has no issue with this, but it's probably less confusing to the merchant if we block off
411
+ * the transaction immediately
412
+ */
413
+ if (empty($amount)) {
414
+ Mage::throwException(Mage::helper('casmtp')->__("Can't refund a zero amount"));
415
+ }
416
+
417
+ /* Make sure we have the audit number of the previous transaction */
418
+ $old_audit = $payment->getLastTransId();
419
+ if (empty($old_audit)) {
420
+ Mage::throwException(Mage::helper('casmtp')->__("Previous transaction reference is empty - unable to locate previous transaction to refund"));
421
+ }
422
+
423
+ $casmtp = $this->_createCasmtp();
424
+
425
+ /* Get a new audit number */
426
+ $audit = $this->_getAuditNumber($casmtp, $payment);
427
+
428
+ /* Do the refund */
429
+ try {
430
+ $this->_logPaymentEvent($payment, "starting refund for $audit (pan = xxxx-" . $payment->getCcLast4() . ", amount = $amount)");
431
+ $result = $casmtp->refund($audit, $old_audit, CasMiscUtil::flattenAmount($amount));
432
+ }
433
+ catch (Exception $e) {
434
+ // This usually indicates some kind of wierd networking issue
435
+ $this->_logPaymentEvent($payment, "error encountered: " . $e->getMessage());
436
+ Mage::throwException(Mage::helper('casmtp')->__("There was an error encountered while performing the transaction"));
437
+ }
438
+
439
+ /* Interpret the transaction result */
440
+ $this->_interpretTxnResult($result, $payment);
441
+
442
+ return $this;
443
+ }
444
+ }
app/code/community/CardAccessServices/Casmtp/casmiscutil.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Card Access Services CASMTP
4
+ *
5
+ * Copyright 2012 Card Access Services
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ *
19
+ * @author Card Access Services
20
+ * @copyright Copyright (c) 2012 Card Access Services (http://www.cardaccess.com.au)
21
+ * @license http://opensource.org/licenses/Apache-2.0 Apache Software License (ASL 2.0)
22
+ */
23
+
24
+ /**
25
+ * Utility class
26
+ *
27
+ * Most of the methods are highly specific to this particular shopping cart
28
+ */
29
+ class CasMiscUtil {
30
+ /**
31
+ * Flatten the supplied amount
32
+ *
33
+ * @param string $amt amount to flatten
34
+ *
35
+ * @return string the flattened amount
36
+ * @access public
37
+ */
38
+ public static function flattenAmount($amt) {
39
+ return number_format($amt, 2, '', '');
40
+ }
41
+
42
+ /**
43
+ * Format the expiry date from a payment object
44
+ *
45
+ * @param string $payment payment instance
46
+ *
47
+ * @return string the formatted expiry date
48
+ * @access public
49
+ */
50
+ public static function formatExpiryFromPayment(&$payment) {
51
+ return sprintf("%02d%02d", $payment->getCcExpYear() % 100, $payment->getCcExpMonth());
52
+ }
53
+
54
+ /**
55
+ * Get a suitable transaction reference from a payment object
56
+ *
57
+ * @param string $payment payment instance
58
+ *
59
+ * @return string the transaction reference
60
+ * @access private
61
+ */
62
+ public static function getCustRefFromPayment($payment) {
63
+ return $payment->getOrder()->getIncrementId();
64
+ }
65
+
66
+ /**
67
+ * Retrieve the value for a given key, throwing an exception if it doesn't exist
68
+ *
69
+ * @param string $reply the response array
70
+ * @param string $name the name of the expected key
71
+ * @param string $errmsg the error message to include in the exception if the key doesn't exist
72
+ *
73
+ * @return string the formatted expiry date
74
+ * @access public
75
+ */
76
+ public static function getRequiredValue(&$reply, $name, $errmsg) {
77
+ if (isset($reply[$name])) {
78
+ return $reply[$name];
79
+ }
80
+ else {
81
+ throw new Exception($errmsg);
82
+ }
83
+ }
84
+ }
app/code/community/CardAccessServices/Casmtp/casmtpprotocol.php ADDED
@@ -0,0 +1,771 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Card Access Services CASMTP
4
+ *
5
+ * Copyright 2012 Card Access Services
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ *
19
+ * @author Card Access Services
20
+ * @copyright Copyright (c) 2012 Card Access Services (http://www.cardaccess.com.au)
21
+ * @license http://opensource.org/licenses/Apache-2.0 Apache Software License (ASL 2.0)
22
+ */
23
+
24
+ /**
25
+ * This class implements exceptions which are pegged as CASMTP specific
26
+ */
27
+ class CasmtpException extends Exception {
28
+ /**
29
+ * Constructor
30
+ *
31
+ * @param string $message the error message
32
+ * @param string $code the error code (optional)
33
+ * @param string $previous the previous exception (optional)
34
+ *
35
+ * @return void
36
+ * @access public
37
+ */
38
+ public function __construct($message, $code = 0, Exception $previous = null) {
39
+ /* We don't do much here */
40
+ parent::__construct($message, $code, $previous);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * This class implements parsing of the CASMTP transaction result
46
+ */
47
+ class CasmtpTxnResult {
48
+ /**
49
+ * The scode
50
+ *
51
+ * @var string
52
+ * @access private
53
+ */
54
+ private $_statusCode;
55
+
56
+ /**
57
+ * The response code
58
+ *
59
+ * @var string
60
+ * @access private
61
+ */
62
+ private $_respCode;
63
+
64
+ /**
65
+ * Settlement date (YYMM)
66
+ *
67
+ * @var string
68
+ * @access private
69
+ */
70
+ private $_setlDate;
71
+
72
+ /**
73
+ * Authorization code
74
+ *
75
+ * @var string
76
+ * @access private
77
+ */
78
+ private $_authCode;
79
+
80
+ /**
81
+ * The request audit number
82
+ *
83
+ * @var string
84
+ * @access private
85
+ */
86
+ private $_requestedAudit;
87
+
88
+ /**
89
+ * Diagnostic message
90
+ *
91
+ * @var string
92
+ * @access private
93
+ */
94
+ private $_msg;
95
+
96
+ /**
97
+ * Constructor
98
+ *
99
+ * @param string $message error message
100
+ * @param string $code error code (optional)
101
+ * @param string $previous previous exception (optional)
102
+ *
103
+ * @return void
104
+ * @access public
105
+ */
106
+ public function __construct($resp) {
107
+ $this->_statusCode = CasmtpTxnResult::_getDe048Kvp ("CAS.RESPONSE.STATUSCODE", $resp);
108
+ $this->_respCode = CasmtpTxnResult::_getKvp ("DE039", $resp);
109
+ $this->_setlDate = CasmtpTxnResult::_getKvp ("DE015", $resp);
110
+ $this->_authCode = CasmtpTxnResult::_getKvp ("DE038", $resp);
111
+ $this->_requestedAudit = CasmtpTxnResult::_getDe048Kvp("CAS.RESPONSE.AUDIT1", $resp);
112
+ $this->_msg = CasmtpTxnResult::_getDe048Kvp ("CAS.RESPONSE.MSG", $resp);
113
+ }
114
+
115
+ /**
116
+ * Get the scode
117
+ *
118
+ * Typically blank, numeric (positive or negative)
119
+ *
120
+ * @return string status code
121
+ * @access public
122
+ */
123
+ public function getStatusCode() {
124
+ return $this->_statusCode;
125
+ }
126
+
127
+ /**
128
+ * Get the response code
129
+ *
130
+ * Typically blank or 2 characters
131
+ *
132
+ * @return string response code
133
+ * @access public
134
+ */
135
+ public function getResponseCode() {
136
+ return $this->_respCode;
137
+ }
138
+
139
+ /**
140
+ * Get the settlement date
141
+ *
142
+ * Typically blank or in YYMM format
143
+ *
144
+ * @return string settlement date
145
+ * @access public
146
+ */
147
+ public function getSettlementDate() {
148
+ return $this->_setlDate;
149
+ }
150
+
151
+ /**
152
+ *
153
+ * Get the authorization code
154
+ *
155
+ * Format varies wildly, very bank specific, some banks insert trailing spaces (these are
156
+ * considered normal and part of the auth code)
157
+ *
158
+ * @return string authcode
159
+ * @access public
160
+ */
161
+ public function getAuthorizationCode() {
162
+ return $this->_authCode;
163
+ }
164
+
165
+ /**
166
+ *
167
+ * Get the requested audit number
168
+ *
169
+ * NB: This field is only filled in when a new audit number is requested. It is possible to
170
+ * piggyback an audit number request onto a transaction operation (but the sample code doesn't
171
+ * take advantage of this)
172
+ *
173
+ * @return string the requested audit number
174
+ * @access public
175
+ */
176
+ public function getRequestedAuditNumber() {
177
+ return $this->_requestedAudit;
178
+ }
179
+
180
+ /**
181
+ * Diagnostic message
182
+ *
183
+ * Common for it to be blank if casmtp doesn't want to add any additional commentary. This
184
+ * message is intended for developers only, it may be confusing or make no sense if exposed
185
+ * to an end user
186
+ *
187
+ * @return string diagnostic message
188
+ * @access public
189
+ */
190
+ public function getDiagnosticMessage() {
191
+ return $this->_msg;
192
+ }
193
+
194
+ /**
195
+ * Whether the transaction was approved
196
+ *
197
+ * @return boolean if the transaction was approved
198
+ * @access public
199
+ */
200
+ public function isApproved() {
201
+ return ($this->_statusCode === "0") && in_array ($this->_respCode, array ("00", "08", "10", "11", "16", "77"));
202
+ }
203
+
204
+ /**
205
+ * Whether the transaction was not approved and not a normal decline (e.g. a time out)
206
+ *
207
+ * @return boolean if the transaction was not approved and not a normal decline (e.g. a time out)
208
+ * @access public
209
+ */
210
+ public function isAbnormalResult() {
211
+ return $this->_statusCode !== "0";
212
+ }
213
+
214
+ /**
215
+ * Utility method to retrieve value from the main KVPs
216
+ *
217
+ * @return string KVP value
218
+ * @access private
219
+ */
220
+ private static function _getKvp($key, $kvp_array, $default = "") {
221
+ if (!array_key_exists($key, $kvp_array))
222
+ return $default;
223
+ else
224
+ return $kvp_array[$key];
225
+ }
226
+
227
+ /**
228
+ * Utility method to retrieve value from the "miscellaneous" KVPs
229
+ *
230
+ * @return string KVP value
231
+ * @access public
232
+ */
233
+ private static function _getDe048Kvp($key, $kvp_array) {
234
+ return CasmtpTxnResult::_getKvp($key, CasmtpTxnResult::_getKvp("DE048", $kvp_array, array()));
235
+ }
236
+ }
237
+
238
+ /**
239
+ * This class implements the casmtp proxy parameters
240
+ *
241
+ * NB: There is no online validation performed
242
+ */
243
+ class CasmtpProxy {
244
+ /**
245
+ * Proxy server
246
+ *
247
+ * @var string
248
+ * @access private
249
+ */
250
+ private $_server;
251
+
252
+ /**
253
+ * Proxy server
254
+ *
255
+ * @var string
256
+ * @access private
257
+ */
258
+ private $_login;
259
+
260
+ /**
261
+ * Constructor
262
+ *
263
+ * @param string $proxyHost proxy host
264
+ * @param string $proxyPort proxy port
265
+ * @param string $proxyUsername proxy username
266
+ * @param string $proxyPassword proxy password
267
+ *
268
+ * @return void
269
+ * @access public
270
+ */
271
+ public function __construct($host = "", $port = "", $username = "", $password = "") {
272
+ /* Set defaults */
273
+ $this->_server = "";
274
+ $this->_login = "";
275
+
276
+ /* Set server if present */
277
+ if (!empty($host)) {
278
+ if (empty($port)) {
279
+ $port = "8080";
280
+ }
281
+ else {
282
+ if (!is_numeric($port)) {
283
+ throw new CasmtpException("Proxy port must be numeric, please check your configuration");
284
+ }
285
+ }
286
+ /* As per RFC 1123; we are actually a bit too loose, but this is ok as this is an admin only setting */
287
+ if (strpos($host, ':') !== FALSE) {
288
+ throw new CasmtpException("Proxy host is not allowed to contain a colon, please check your configuration");
289
+ }
290
+ $this->_server = $host . ':' . $port;
291
+ }
292
+ else {
293
+ if (!empty($port)) {
294
+ throw new CasmtpException("Proxy port has been specified, but host name was left empty, please check your configuration");
295
+ }
296
+ }
297
+
298
+ /* Set login if present */
299
+ if (!empty($username)) {
300
+ if (empty($this->_server)) {
301
+ throw new CasmtpException("Proxy login specified but the host was left empty, please check your configuration");
302
+ }
303
+ if (empty($password)) {
304
+ $password = "";
305
+ }
306
+ /* As per RFC 2068 */
307
+ if (strpos($username, ':') !== FALSE) {
308
+ throw new CasmtpException("Proxy username is not allowed to contain a colon, please check your configuration");
309
+ }
310
+ $this->_login = $username . ':' . $password;
311
+ }
312
+ else {
313
+ if (!empty($password)) {
314
+ throw new CasmtpException("Proxy password has been specified, but username was left empty, please check your configuration");
315
+ }
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Get the proxy server
321
+ *
322
+ * @return string proxy server
323
+ * @access public
324
+ */
325
+ public function getServer() {
326
+ return $this->_server;
327
+ }
328
+
329
+ /**
330
+ * Get the proxy login
331
+ *
332
+ * @return string proxy login
333
+ * @access public
334
+ */
335
+ public function getLogin() {
336
+ return $this->_login;
337
+ }
338
+ }
339
+
340
+ /**
341
+ * This class implements the request portion of the CASMTP protocol
342
+ *
343
+ * Currently only purchases and refunds are implemented - for other methods such as preauth, completion,
344
+ * card present transactions etc, see the CASMTP userguide
345
+ */
346
+ class Casmtp {
347
+ /**
348
+ * The gateway URL
349
+ *
350
+ * @var string
351
+ * @access private
352
+ */
353
+ private $_targetUrl;
354
+
355
+ /**
356
+ * The ETX merchant ID
357
+ *
358
+ * @var string
359
+ * @access private
360
+ */
361
+ private $_merchantId;
362
+
363
+ /**
364
+ * The hash key
365
+ *
366
+ * @var string
367
+ * @access private
368
+ */
369
+ private $_hashKey;
370
+
371
+ /**
372
+ * The proxy server
373
+ *
374
+ * @var string
375
+ * @access private
376
+ */
377
+ private $_proxy;
378
+
379
+ /**
380
+ * The live gateway URL
381
+ *
382
+ * @var string
383
+ * @access public
384
+ * @const
385
+ */
386
+ const LIVE_URL = 'https://etx.cardaccess.com.au/casmtp/casmtp.php';
387
+
388
+ /**
389
+ * The test gateway URL
390
+ *
391
+ * @var string
392
+ * @access public
393
+ * @const
394
+ */
395
+ const TEST_URL = 'https://etx.cardaccess.com.au/casmtp/testcasmtp.php';
396
+
397
+ /**
398
+ * The default HTTP timeout
399
+ *
400
+ * @var string
401
+ * @access public
402
+ * @const
403
+ */
404
+ const DEFAULT_HTTPS_TIMEOUT = 120;
405
+
406
+ /**
407
+ * Constructor
408
+ *
409
+ * @param string $targetUrl URL of the gateway (typically LIVE_URL or TEST_URL)
410
+ * @param string $proxy Proxy server
411
+ * @param string $merchantId ETX merchant ID, as assigned by Card Access Services
412
+ * @param string $hashKey hash key, as assigned by Card Access Services
413
+ *
414
+ * @return void
415
+ * @access public
416
+ */
417
+ public function __construct($targetUrl, $proxy, $merchantId, $hashKey) {
418
+ $this->_targetUrl = $targetUrl;
419
+ $this->_proxy = $proxy;
420
+ $this->_merchantId = $merchantId;
421
+ $this->_hashKey = $hashKey;
422
+ }
423
+
424
+ /**
425
+ * Retrieve an audit number from the gateway
426
+ *
427
+ * An audit number is required before performing most operations. It is always safe to get
428
+ * another audit number if the first request timed out etc
429
+ *
430
+ * @return string audit number
431
+ * @access public
432
+ */
433
+ public function getAudit() {
434
+ /* Send off the request */
435
+ $kvps = array('CAS.REQUEST.AUDIT' => '1');
436
+ $resp = $this->_sendRequest(
437
+ array(
438
+ 'dataformat' => 'HTTP_AS2805',
439
+ 'DE001' => '0800',
440
+ 'DE042' => $this->_getFormattedMerchantId()
441
+ ),
442
+ $kvps,
443
+ $this->_hashKey
444
+ );
445
+
446
+ /* Try to get at the audit number */
447
+ $result = new CasmtpTxnResult($resp);
448
+ $audit = $result->getRequestedAuditNumber();
449
+
450
+ /* Throw an exception if no audit number is present */
451
+ if ($audit == "" or $audit == "-1") {
452
+ $result = new CasmtpTxnResult($resp);
453
+ throw new CasmtpException ($result->getDiagnosticMessage());
454
+ }
455
+
456
+ /* Return the audit number for use in a follow up transaction */
457
+ return $audit;
458
+ }
459
+
460
+ /**
461
+ * Make a purchase
462
+ *
463
+ * Each purchase must be made against a unique audit number. While the customer reference
464
+ * is optional it is highly recommended. CVV is also optional, although banks may declined
465
+ * transactions sent without a CVV
466
+ *
467
+ * @param string $audit audit number retrieved using $this->getAudit()
468
+ * @param string $pan credit card number
469
+ * @param string $expiry expiry date in YYMM format
470
+ * @param string $cvv card verification value
471
+ * @param string $amt amount, in the minor denomination of the merchant account
472
+ * @param string $custRef customer reference field. Appears in the MMI reports
473
+ *
474
+ * @return void
475
+ * @access public
476
+ */
477
+ public function purchase($audit, $pan, $expiry, $cvv, $amt, $custref = "") {
478
+ /* Send the request */
479
+ $kvps = array('CAS.AUDIT' => $audit);
480
+ if (!empty($custref)) {
481
+ $kvps['CAS.CUSTREF'] = $custref;
482
+ }
483
+ if (!empty($cvv)) {
484
+ $kvps['CAS.CARD.CVC'] = $cvv;
485
+ }
486
+ $resp = $this->_sendRequest(
487
+ array(
488
+ 'dataformat' => 'HTTP_AS2805',
489
+
490
+ 'DE001' => '0200',
491
+ 'DE003' => '003000',
492
+
493
+ 'DE042' => $this->_getFormattedMerchantId(),
494
+
495
+ 'DE002' => $pan,
496
+ 'DE014' => $expiry,
497
+ 'DE004' => sprintf("%d", $amt)
498
+ ),
499
+ $kvps,
500
+ $this->_hashKey
501
+ );
502
+
503
+ /* Interpret the request */
504
+ return new CasmtpTxnResult($resp);
505
+ }
506
+
507
+ /**
508
+ * Refund a prior transaction
509
+ *
510
+ * Each refund must be made against a unique audit number. The audit number of the transaction
511
+ * to refund will also be required.
512
+ *
513
+ * We have encountered some banks in the past who do NOT allow merchants to perform refunds - if
514
+ * this is the case then the CASMTP refund operation will be rejected by the bank
515
+ *
516
+ * @param string $audit audit number retrieved using $this->getAudit()
517
+ * @param string $audit_to_refund audit number of transaction to refund
518
+ * @param string $amt amount to refund, in the minor denomination of the merchant account
519
+ * @param string $custRef customer reference field. Appears in the MMI reports
520
+ *
521
+ * @return void
522
+ * @access public
523
+ */
524
+ public function refund($audit, $audit_to_refund, $amt, $custref = "") {
525
+ /* Send request */
526
+ $kvps = array('CAS.AUDIT' => $audit, 'CAS.REFUNDAUDIT' => $audit_to_refund);
527
+ if (!empty($custref))
528
+ $kvps['CAS.CUSTREF'] = $custref;
529
+ $resp = $this->_sendRequest(
530
+ array(
531
+ 'dataformat' => 'HTTP_AS2805',
532
+
533
+ // Refund method
534
+ 'DE001' => '0200',
535
+ 'DE003' => '200030',
536
+
537
+ 'DE042' => $this->_getFormattedMerchantId(),
538
+
539
+ 'DE004' => sprintf("%d", $amt)
540
+ ),
541
+ $kvps,
542
+ $this->_hashKey
543
+ );
544
+
545
+ /* Interpret result */
546
+ return new CasmtpTxnResult($resp);
547
+ }
548
+
549
+ /**
550
+ * Parses a transaction response into an array
551
+ *
552
+ * @param string $lines raw response text from CASMTP
553
+ * @param string $lineSep line separator to use
554
+ * @param string $kvpSep kvp separator to use
555
+ *
556
+ * @return array response kvps
557
+ * @access private
558
+ */
559
+ private static function _explodeKvps($lines, $lineSep, $kvpSep) {
560
+ $resp = array();
561
+ $lines = explode($lineSep, $lines);
562
+ foreach ($lines as $line) {
563
+ $kvp = explode($kvpSep, $line, 2);
564
+ if (count($kvp) != 2)
565
+ continue;
566
+ $key = $kvp[0];
567
+ $value = $kvp[1];
568
+ $resp[$key] = $value;
569
+ }
570
+ return $resp;
571
+ }
572
+
573
+ /**
574
+ * Encode a specific DE048 KVP for use in the hash calculation
575
+ *
576
+ * @param string $key key name
577
+ * @param string $value key value
578
+ *
579
+ * @return string the encoded DE048 KVP value
580
+ * @access private
581
+ */
582
+ private static function _encodeDe048KvpForHash($key, $value) {
583
+ return $key . urlencode('=') . base64_encode($value);
584
+ }
585
+
586
+ /**
587
+ * Encode a specific DE048 KVP for use in the post
588
+ *
589
+ * @param string $key key name
590
+ * @param string $value key value
591
+ *
592
+ * @return string the encoded DE048 KVP value
593
+ * @access private
594
+ */
595
+ private static function _encodeDe048KvpForPost($key, $value) {
596
+ return $key . '=' . base64_encode($value);
597
+ }
598
+
599
+ /**
600
+ * Encode the entire DE048 KVP for use in the hash calculation
601
+ *
602
+ * @param string $de048 DE048 array
603
+ *
604
+ * @return string the encoded DE048 value
605
+ * @access private
606
+ */
607
+ private static function _encodeDe048ForHash($de048) {
608
+ return array("DE048" => implode(urlencode ('&'), array_map('Casmtp::_encodeDe048KvpForHash', array_keys($de048), array_values($de048))));
609
+ }
610
+
611
+ /**
612
+ * Encode the entire DE048 KVP for use in the post
613
+ *
614
+ * @param string $de048 DE048 array
615
+ *
616
+ * @return string the encoded DE048 value
617
+ * @access private
618
+ */
619
+ private static function _encodeDe048ForPost($de048) {
620
+ return array("DE048" => implode('&', array_map('Casmtp::_encodeDe048KvpForPost', array_keys($de048), array_values($de048))));
621
+ }
622
+
623
+ /**
624
+ * Send a CASMTP request
625
+ *
626
+ * @param string $postFieldsWithoutDe048 main post fields
627
+ * @param string $de048 supplementary post fields
628
+ *
629
+ * @return void
630
+ * @access private
631
+ */
632
+ private function _sendRequest($postFieldsWithoutDe048, $de048)
633
+ {
634
+ /* POST fields for transaction */
635
+ $postFieldsForPost = array_merge($postFieldsWithoutDe048, Casmtp::_encodeDe048ForPost($de048));
636
+
637
+ /* Insert hash */
638
+ $this->_insertHash($postFieldsForPost, $postFieldsWithoutDe048, $de048);
639
+
640
+ /* Encode POST data */
641
+ $postData = http_build_query($postFieldsForPost);
642
+
643
+ /* Do POST */
644
+ /*
645
+ $opts = array(
646
+ 'http' =>
647
+ array(
648
+ 'method' => 'POST',
649
+ 'header' => 'Content-type: application/x-www-form-urlencoded',
650
+ 'content' => $postData
651
+ )
652
+ );
653
+ $context = stream_context_create($opts);
654
+ $lines = file_get_contents($this->_targetUrl, false, $context);
655
+ */
656
+
657
+ /* CURL method */
658
+ $crl = curl_init();
659
+
660
+ /* Specify URL + automatically follow redirects */
661
+ curl_setopt($crl, CURLOPT_URL, $this->_targetUrl);
662
+ curl_setopt($crl, CURLOPT_FOLLOWLOCATION, 1);
663
+
664
+ /* Specify POST data + indicate we want the response back for processing */
665
+ curl_setopt($crl, CURLOPT_POST, 1);
666
+ curl_setopt($crl, CURLOPT_POSTFIELDS, $postData);
667
+ curl_setopt($crl, CURLOPT_RETURNTRANSFER, 1);
668
+
669
+ /* Make sure we always use a fresh connection */
670
+ curl_setopt($crl, CURLOPT_FORBID_REUSE, 1);
671
+ curl_setopt($crl, CURLOPT_FRESH_CONNECT, 1);
672
+
673
+ /* Make sure we specify a sane timeout */
674
+ curl_setopt($crl, CURLOPT_CONNECTTIMEOUT, Casmtp::DEFAULT_HTTPS_TIMEOUT);
675
+ curl_setopt($crl, CURLOPT_TIMEOUT, Casmtp::DEFAULT_HTTPS_TIMEOUT);
676
+
677
+ /* Specify proxy, if required */
678
+ $proxyServer = $this->_proxy->getServer();
679
+ if (!empty($proxyServer)) {
680
+ curl_setopt($crl, CURLOPT_HTTPPROXYTUNNEL, true);
681
+ curl_setopt($crl, CURLOPT_PROXY, $proxyServer);
682
+ $proxyLogin = $this->_proxy->getLogin();
683
+ if (!empty($proxyLogin)) {
684
+ curl_setopt($crl, CURLOPT_PROXYUSERPWD, $proxyLogin);
685
+ }
686
+ }
687
+
688
+ /* Send POST */
689
+ $lines = curl_exec($crl);
690
+
691
+ /* Check the result */
692
+ $err = curl_errno($crl);
693
+ if ($err != CURLE_OK) {
694
+ throw new CasmtpException("CURL error performing transaction: " . curl_error($crl));
695
+ }
696
+ $status = curl_getinfo($crl, CURLINFO_HTTP_CODE);
697
+ if ($status != '200') {
698
+ throw new CasmtpException("HTTP error performing transaction: " . $status);
699
+ }
700
+
701
+ /* Decode main key value pairs */
702
+ $resp = Casmtp::_explodeKvps($lines, "\n", "=");
703
+
704
+ /* Further decode DE048 which has its own key value pairs (also base 64 encoded) */
705
+ if (array_key_exists("DE048", $resp)) {
706
+ /* Decode DE048 */
707
+ $items = Casmtp::_explodeKvps($resp["DE048"], "&", "=");
708
+ $resp["DE048"] = array ();
709
+ foreach ($items as $key=>$value) {
710
+ $resp["DE048"][$key] = base64_decode($value);
711
+ }
712
+ }
713
+ else {
714
+ /* Make sure DE048 always exists, even if it's just empty */
715
+ $resp["DE048"] = array();
716
+ }
717
+
718
+ /* Finalized response */
719
+ return $resp;
720
+ }
721
+
722
+ /**
723
+ * Insert hash value into the request
724
+ *
725
+ * NB: The time stamp of the server machine is embedded into the hash value. This value is checked
726
+ * by CASMTP - make sure your server clock is set correctly!
727
+ *
728
+ * @param string $destFields the destination for the hash KVPs
729
+ * @param string $postFields main post fields
730
+ * @param string $de048 supplementary post fields
731
+ *
732
+ * @return void
733
+ * @access private
734
+ */
735
+ private function _insertHash(&$destFields, $postFields, $de048) {
736
+ $postFields_for_hash = array_merge($postFields, Casmtp::_encodeDe048ForHash($de048));
737
+
738
+ /* Insert the current UTC timestamp */
739
+ $timestamp = gmdate("Y/m/d G:i:s");
740
+ $destFields["CAS_SECURITY_TIMESTAMP"] = $timestamp;
741
+ $postFields_for_hash["CAS_SECURITY_TIMESTAMP"] = $timestamp;
742
+
743
+ /* Make sure the post fields are appended in the correct order */
744
+ $request_keys = array_keys($postFields_for_hash);
745
+ asort($request_keys);
746
+
747
+ /* Concatenate all the post fields together */
748
+ $temp_str = "";
749
+ foreach ($request_keys as $k) {
750
+ $v = $postFields_for_hash[$k];
751
+ if ($temp_str != "")
752
+ $temp_str .= "&";
753
+ $temp_str .= ($k . "=" . $v);
754
+ }
755
+
756
+ /* Calculate the hash */
757
+ $hash_value = hash_hmac("sha256", $temp_str, $this->_hashKey);
758
+ $destFields["CAS_SECURITY_TYPE"] = "Hash";
759
+ $destFields["CAS_SECURITY_VALUE"] = $hash_value;
760
+ }
761
+
762
+ /**
763
+ * Format the merchant ID for use in a CASMTP request
764
+ *
765
+ * @return string formatted merchant ID
766
+ * @access private
767
+ */
768
+ private function _getFormattedMerchantId() {
769
+ return str_pad($this->_merchantId, 15, "0", STR_PAD_LEFT);
770
+ }
771
+ }
app/code/community/CardAccessServices/Casmtp/etc/config.xml ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <CardAccessServices_Casmtp>
5
+ <version>1.0.0</version>
6
+ </CardAccessServices_Casmtp>
7
+ </modules>
8
+ <frontend>
9
+ <routers>
10
+ <casmtp>
11
+ <use>standard</use>
12
+ <args>
13
+ <module>CardAccessServices_Casmtp</module>
14
+ <frontName>casmtp</frontName>
15
+ </args>
16
+ </casmtp>
17
+ </routers>
18
+ </frontend>
19
+ <global>
20
+ <blocks>
21
+ <casmtp>
22
+ <class>CardAccessServices_Casmtp_Block</class>
23
+ </casmtp>
24
+ </blocks>
25
+ <helpers>
26
+ <casmtp>
27
+ <class>CardAccessServices_Casmtp_Helper</class>
28
+ </casmtp>
29
+ </helpers>
30
+ <models>
31
+ <casmtp>
32
+ <class>CardAccessServices_Casmtp_Model</class>
33
+ </casmtp>
34
+ </models>
35
+ <resources>
36
+ <casmtp_setup>
37
+ <setup>
38
+ <module>CardAccessServices_Casmtp</module>
39
+ </setup>
40
+ <connection>
41
+ <use>core_setup></use>
42
+ </connection>
43
+ </casmtp_setup>
44
+ <casmtp_write>
45
+ <connection>
46
+ <use>core_write</use>
47
+ </connection>
48
+ </casmtp_write>
49
+ <casmtp_read>
50
+ <connection>
51
+ <use>core_read</use>
52
+ </connection>
53
+ </casmtp_read>
54
+ </resources>
55
+ </global>
56
+ <default>
57
+ <payment>
58
+ <casmtp>
59
+ <model>casmtp/paymentMethod</model>
60
+ <active>0</active>
61
+ <title>Credit Card (Casmtp)</title>
62
+ <order_status>processing</order_status>
63
+ <is_test>no</is_test>
64
+ <log_events>no</log_events>
65
+ <useccv>1</useccv>
66
+ <cctypes>VI,MC</cctypes>
67
+ <proxy_server_host/>
68
+ <proxy_server_port/>
69
+ <proxy_server_username/>
70
+ <proxy_server_password/>
71
+ <payment_action>authorize_capture</payment_action>
72
+ <allowspecific>0</allowspecific>
73
+ </casmtp>
74
+ </payment>
75
+ </default>
76
+ </config>
app/code/community/CardAccessServices/Casmtp/etc/system.xml ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <sections>
4
+ <payment>
5
+ <groups>
6
+ <casmtp translate="label">
7
+ <label>Card Access Services Casmtp</label>
8
+ <comment><![CDATA[<a href="http://www.cardaccess.com.au" target="_blank">For more information please visit http://www.cardaccess.com.au</a>]]></comment>
9
+ <sort_order>1</sort_order>
10
+ <show_in_default>1</show_in_default>
11
+ <show_in_website>1</show_in_website>
12
+ <show_in_store>1</show_in_store>
13
+ <fields>
14
+ <active translate="label">
15
+ <label>Enabled</label>
16
+ <comment><![CDATA[Enable this to allow transaction processing through Card Access Services using hosted payment pages]]></comment>
17
+ <frontend_type>select</frontend_type>
18
+ <source_model>adminhtml/system_config_source_yesno</source_model>
19
+ <sort_order>1</sort_order>
20
+ <show_in_default>1</show_in_default>
21
+ <show_in_website>1</show_in_website>
22
+ <show_in_store>1</show_in_store>
23
+ </active>
24
+ <title translate="label">
25
+ <label>Title</label>
26
+ <comment><![CDATA[The name that will be shown on the checkout pages]]></comment>
27
+ <frontend_type>text</frontend_type>
28
+ <sort_order>2</sort_order>
29
+ <show_in_default>1</show_in_default>
30
+ <show_in_website>1</show_in_website>
31
+ <show_in_store>1</show_in_store>
32
+ </title>
33
+ <is_test translate="label">
34
+ <label>Use Test Gateway</label>
35
+ <comment><![CDATA[Send transactions to the test gateway?<br><b>Please be sure to set this to "No" before going live</b>]]></comment>
36
+ <frontend_type>select</frontend_type>
37
+ <source_model>adminhtml/system_config_source_yesno</source_model>
38
+ <sort_order>3</sort_order>
39
+ <show_in_default>1</show_in_default>
40
+ <show_in_website>1</show_in_website>
41
+ <show_in_store>1</show_in_store>
42
+ </is_test>
43
+ <etx_merchant translate="label">
44
+ <label>ETX Merchant ID</label>
45
+ <comment><![CDATA[The ETX merchant ID<br><i>This must be set to the value assigned by Card Access Services</i>]]></comment>
46
+ <frontend_type>text</frontend_type>
47
+ <sort_order>4</sort_order>
48
+ <show_in_default>1</show_in_default>
49
+ <show_in_website>1</show_in_website>
50
+ <show_in_store>1</show_in_store>
51
+ </etx_merchant>
52
+ <hash_auth translate="label">
53
+ <label>Hash authentication</label>
54
+ <comment><![CDATA[The hash authentication password<br><i>This must be set to the value assigned by Card Access Services</i>]]></comment>
55
+ <frontend_type>password</frontend_type>
56
+ <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
57
+ <sort_order>5</sort_order>
58
+ <show_in_default>1</show_in_default>
59
+ <show_in_website>1</show_in_website>
60
+ <show_in_store>1</show_in_store>
61
+ </hash_auth>
62
+ <useccv translate="label">
63
+ <label>Require CVV</label>
64
+ <comment><![CDATA[If this is enabled then the cardholder will be required to enter a CVV with each transaction. This may be mandated by your bank as a condition of transaction processing - double check with your bank for more details]]></comment>
65
+ <frontend_type>select</frontend_type>
66
+ <source_model>adminhtml/system_config_source_yesno</source_model>
67
+ <sort_order>6</sort_order>
68
+ <show_in_default>1</show_in_default>
69
+ <show_in_website>1</show_in_website>
70
+ <show_in_store>1</show_in_store>
71
+ </useccv>
72
+ <cctypes translate="label">
73
+ <label>Credit Card Types</label>
74
+ <comment><![CDATA[List the card types offered during checkout and on the admin screens<br><i>Not all credit card types will be supported by your merchant account; in addition, some card types may require an additional merchant account<br>Once your have setup the merchant account(s) with the relevant financial institutions, please let us know the details as well as which card types you wish to support, this will be configured at the same time as the ETX merchant above</i>]]></comment>
75
+ <frontend_type>multiselect</frontend_type>
76
+ <source_model>adminhtml/system_config_source_payment_cctype</source_model>
77
+ <sort_order>7</sort_order>
78
+ <show_in_default>1</show_in_default>
79
+ <show_in_website>1</show_in_website>
80
+ <show_in_store>0</show_in_store>
81
+ </cctypes>
82
+ <order_status translate="label">
83
+ <label>New order status</label>
84
+ <comment><![CDATA[Update the order to this status after the transaction has been completed]]></comment>
85
+ <frontend_type>select</frontend_type>
86
+ <source_model>adminhtml/system_config_source_order_status</source_model>
87
+ <sort_order>8</sort_order>
88
+ <show_in_default>1</show_in_default>
89
+ <show_in_website>1</show_in_website>
90
+ <show_in_store>1</show_in_store>
91
+ </order_status>
92
+ <sort_order translate="label">
93
+ <label>Sort order</label>
94
+ <comment><![CDATA[The relative order in which this payment method is shown. The lower the number, the higher up this payment method will appear in the list]]></comment>
95
+ <frontend_type>text</frontend_type>
96
+ <sort_order>9</sort_order>
97
+ <show_in_default>1</show_in_default>
98
+ <show_in_website>1</show_in_website>
99
+ <show_in_store>1</show_in_store>
100
+ </sort_order>
101
+ <allowspecific translate="label">
102
+ <label>Payment from applicable countries</label>
103
+ <comment><![CDATA[Whether to restrict cardholders to certain countries]]></comment>
104
+ <frontend_type>allowspecific</frontend_type>
105
+ <sort_order>10</sort_order>
106
+ <source_model>adminhtml/system_config_source_payment_allspecificcountries</source_model>
107
+ <show_in_default>1</show_in_default>
108
+ <show_in_website>1</show_in_website>
109
+ <show_in_store>1</show_in_store>
110
+ </allowspecific>
111
+ <specificcountry translate="label">
112
+ <label>Payment from specific countries</label>
113
+ <comment><![CDATA[Select the specific countries to restrict payments to<br><i>This only has effect if "Specific Countries" has been selected</i>]]></comment>
114
+ <frontend_type>multiselect</frontend_type>
115
+ <sort_order>11</sort_order>
116
+ <source_model>adminhtml/system_config_source_country</source_model>
117
+ <show_in_default>1</show_in_default>
118
+ <show_in_website>1</show_in_website>
119
+ <show_in_store>1</show_in_store>
120
+ <can_be_empty>1</can_be_empty>
121
+ </specificcountry>
122
+ <proxy_server_host translate="label">
123
+ <label>Proxy server host</label>
124
+ <comment><![CDATA[The hostname or IP address of the proxy server<br>This is only required if you are using a proxy, otherwise it can be left blank]]></comment>
125
+ <frontend_type>text</frontend_type>
126
+ <sort_order>12</sort_order>
127
+ <show_in_default>1</show_in_default>
128
+ <show_in_website>1</show_in_website>
129
+ <show_in_store>0</show_in_store>
130
+ </proxy_server_host>
131
+ <proxy_server_port translate="label">
132
+ <label>Proxy server port</label>
133
+ <comment><![CDATA[The port number of the proxy server<br>This is only required if you are using a proxy, otherwise it can be left blank. If a host is specified but the port number is left blank then the port number will default to 8080]]></comment>
134
+ <frontend_type>text</frontend_type>
135
+ <sort_order>13</sort_order>
136
+ <show_in_default>1</show_in_default>
137
+ <show_in_website>1</show_in_website>
138
+ <show_in_store>0</show_in_store>
139
+ </proxy_server_port>
140
+ <proxy_login_username translate="label">
141
+ <label>Proxy server username</label>
142
+ <comment><![CDATA[The username to use when accessing the proxy server<br>This is only required if you are using a proxy and it requires authentication, otherwise it can be left blank]]></comment>
143
+ <frontend_type>text</frontend_type>
144
+ <sort_order>14</sort_order>
145
+ <show_in_default>1</show_in_default>
146
+ <show_in_website>1</show_in_website>
147
+ <show_in_store>0</show_in_store>
148
+ </proxy_login_username>
149
+ <proxy_login_password translate="label">
150
+ <label>Proxy server password</label>
151
+ <comment><![CDATA[The password to use when accessing the proxy server<br>This is only required if you are using a proxy and it requires a password, otherwise it can be left blank]]></comment>
152
+ <frontend_type>password</frontend_type>
153
+ <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
154
+ <sort_order>15</sort_order>
155
+ <show_in_default>1</show_in_default>
156
+ <show_in_website>1</show_in_website>
157
+ <show_in_store>0</show_in_store>
158
+ </proxy_login_password>
159
+ <log_events translate="label">
160
+ <label>Log Events</label>
161
+ <comment><![CDATA[Log each payment event into the system log (if enabled)?]]></comment>
162
+ <frontend_type>select</frontend_type>
163
+ <source_model>adminhtml/system_config_source_yesno</source_model>
164
+ <sort_order>16</sort_order>
165
+ <show_in_default>1</show_in_default>
166
+ <show_in_website>1</show_in_website>
167
+ <show_in_store>1</show_in_store>
168
+ </log_events>
169
+ </fields>
170
+ </casmtp>
171
+ </groups>
172
+ </payment>
173
+ </sections>
174
+ </config>
app/etc/modules/CardAccessServices_Casmtp.xml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <CardAccessServices_Casmtp>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </CardAccessServices_Casmtp>
8
+ <depends>
9
+ <Mage_Payment/>
10
+ </depends>
11
+ </modules>
12
+ </config>
package.xml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>CardAccessServices_Casmtp</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://opensource.org/licenses/Apache-2.0">Apache Software License (ASL)</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Merchant hosted transaction processing using the Card Access Services gateway</summary>
10
+ <description>This gateway extension allows you to process merchant hosted transactions through the Card Access Services payment&#xD;
11
+ &#xD;
12
+ You will need a compatible merchant account with any of the numerous financial institutions we are connected to worldwide, as well as an account with us&#xD;
13
+ &#xD;
14
+ Please contact info@cardaccess.com.au (http://www.cardaccess.com.au) for more information&#xD;
15
+ </description>
16
+ <notes>Release 1.0.0 added purchase &amp; refund</notes>
17
+ <authors><author><name>Card Access Services</name><user>cardaccessservices</user><email>techadmin@rt.cardaccess.com.au</email></author></authors>
18
+ <date>2012-06-26</date>
19
+ <time>23:42:33</time>
20
+ <contents><target name="mageetc"><dir name="modules"><file name="CardAccessServices_Casmtp.xml" hash="78d9b601cf91a93dddc4356edff54870"/></dir></target><target name="magecommunity"><dir name="CardAccessServices"><dir name="Casmtp"><dir name="Helper"><file name="Data.php" hash="018d64d703c1eb68c28068c0de721cbc"/></dir><dir name="Model"><file name="PaymentMethod.php" hash="3c3b66c09004e2a4d8b9dcdcee7d2013"/></dir><file name="casmiscutil.php" hash="23e8224f830b7041a69290d642db27e1"/><file name="casmtpprotocol.php" hash="09dc63f173f10e2fdd4b8c8cf02d53bf"/><dir name="etc"><file name="config.xml" hash="e3da56d0f4bee8f61bd7aa365d15301f"/><file name="system.xml" hash="814ae364c5b84f5949601a4a8693e0a3"/></dir></dir></dir></target></contents>
21
+ <compatible/>
22
+ <dependencies><required><php><min>5.1.2</min><max>6.0.0</max></php><extension><name>hash</name><min>1.1</min><max></max></extension></required></dependencies>
23
+ </package>