GatewayService::setTestMode()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 1
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * Copyright notice:
5
 * (c) Copyright 2017 RocketGate
6
 * All rights reserved.
7
 *
8
 * The copyright notice must not be removed without specific, prior
9
 * written permission from RocketGate.
10
 *
11
 * This software is protected as an unpublished work under the U.S. copyright
12
 * laws. The above copyright notice is not intended to effect a publication of
13
 * this work.
14
 * This software is the confidential and proprietary information of RocketGate.
15
 * Neither the binaries nor the source code may be redistributed without prior
16
 * written permission from RocketGate.
17
 *
18
 * The software is provided "as-is" and without warranty of any kind, express, implied
19
 * or otherwise, including without limitation, any warranty of merchantability or fitness
20
 * for a particular purpose.  In no event shall RocketGate be liable for any direct,
21
 * special, incidental, indirect, consequential or other damages of any kind, or any damages
22
 * whatsoever arising out of or in connection with the use or performance of this software,
23
 * including, without limitation, damages resulting from loss of use, data or profits, and
24
 * whether or not advised of the possibility of damage, regardless of the theory of liability.
25
 */
26
27
namespace RocketGate\Sdk;
28
29
class GatewayService extends GatewayAbstract
30
{
31
    /**
32
     * Gateway hostname.
33
     *
34
     * @var string
35
     */
36
    public $rocketGateHost;
37
38
    /**
39
     * Message protocol.
40
     *
41
     * @var string
42
     */
43
    public $rocketGateProtocol;
44
45
    /**
46
     * Network connection port.
47
     *
48
     * @var int
49
     */
50
    public $rocketGatePortNo;
51
52
    /**
53
     * Destination servlet.
54
     *
55
     * @var string
56
     */
57
    public $rocketGateServlet;
58
59
    /**
60
     * Timeout for network connection.
61
     *
62
     * @var int
63
     */
64
    public $rocketGateConnectTimeout;
65
66
    /**
67
     * Timeout for network read.
68
     *
69
     * @var int
70
     */
71
    public $rocketGateReadTimeout;
72
73
    /**
74
     * Set the standard production destinations for the service.
75
     *
76
     * @param bool $testMode
77
     */
78
    public function __construct(bool $testMode = true)
79
    {
80
        $this->setTestMode($testMode);
81
        $this->rocketGateServlet        = 'gateway/servlet/ServiceDispatcherAccess';
82
        $this->rocketGateConnectTimeout = 10;
83
        $this->rocketGateReadTimeout    = 90;
84
    }
85
86
    /**
87
     * Perform an auth-only transaction.
88
     *
89
     * @param GatewayRequest  $request
90
     * @param GatewayResponse $response
91
     *
92
     * @return bool
93
     */
94
    public function performAuthOnly(GatewayRequest $request, GatewayResponse $response)
95
    {
96
        $request->set(GatewayRequest::transactionType(), 'CC_AUTH');
97
        if (!empty($request->get(GatewayRequest::referenceGuid()))) {
98
            if ($this->performTargetedTransaction($request, $response) === false) {
99
                return false;
100
            }
101
        } elseif ($this->performTransaction($request, $response) === false) {
102
            return false;
103
        }
104
105
        return $this->performConfirmation($request, $response);
106
    }
107
108
    /**
109
     * Perform a ticket operation for a previous auth-only transaction.
110
     *
111
     * @param GatewayRequest  $request
112
     * @param GatewayResponse $response
113
     *
114
     * @return bool
115
     */
116
    public function performTicket(GatewayRequest $request, GatewayResponse $response)
117
    {
118
        $request->set(GatewayRequest::transactionType(), 'CC_TICKET');
119
120
        return $this->performTargetedTransaction($request, $response);
121
    }
122
123
    /**
124
     * Perform a complete purchase transaction using the information contained in a request.
125
     *
126
     * @param GatewayRequest  $request
127
     * @param GatewayResponse $response
128
     *
129
     * @return bool
130
     */
131
    public function performPurchase(GatewayRequest $request, GatewayResponse $response)
132
    {
133
        $request->set(GatewayRequest::transactionType(), 'CC_PURCHASE');
134
        if (!empty($request->get(GatewayRequest::referenceGuid()))) {
135
            if ($this->performTargetedTransaction($request, $response) === false) {
136
                return false;
137
            }
138
        } elseif ($this->performTransaction($request, $response) === false) {
139
            return false;
140
        }
141
142
        return $this->performConfirmation($request, $response);
143
    }
144
145
    /**
146
     * Perform a credit operation for a previously completed transaction.
147
     *
148
     * @param GatewayRequest  $request
149
     * @param GatewayResponse $response
150
     *
151
     * @return bool
152
     */
153
    public function performCredit(GatewayRequest $request, GatewayResponse $response)
154
    {
155
        $request->set(GatewayRequest::transactionType(), 'CC_CREDIT');
156
157
        // If the credit references a previous transaction, we
158
        // need to send it back to the origination site.  Otherwise,
159
        // it can be sent to any server.
160
161
        if (!empty($request->get(GatewayRequest::referenceGuid()))) {
162
            return $this->performTargetedTransaction($request, $response);
163
        }
164
165
        return $this->performTransaction($request, $response);
166
    }
167
168
    /**
169
     * Perform a void operation for a previously completed transaction.
170
     *
171
     * @param GatewayRequest  $request
172
     * @param GatewayResponse $response
173
     *
174
     * @return bool
175
     */
176
    public function performVoid(GatewayRequest $request, GatewayResponse $response)
177
    {
178
        $request->set(GatewayRequest::transactionType(), 'CC_VOID');
179
180
        return $this->performTargetedTransaction($request, $response);
181
    }
182
183
    /**
184
     * Perform scrubbing on a card/customer.
185
     *
186
     * @param GatewayRequest  $request
187
     * @param GatewayResponse $response
188
     *
189
     * @return bool
190
     */
191
    public function performCardScrub(GatewayRequest $request, GatewayResponse $response)
192
    {
193
        $request->set(GatewayRequest::transactionType(), 'CARDSCRUB');
194
195
        return $this->performTransaction($request, $response);
196
    }
197
198
    /**
199
     * Schedule cancellation of rebilling.
200
     *
201
     * @param GatewayRequest  $request
202
     * @param GatewayResponse $response
203
     *
204
     * @return bool
205
     */
206
    public function performRebillCancel(GatewayRequest $request, GatewayResponse $response)
207
    {
208
        $request->set(GatewayRequest::transactionType(), 'REBILL_CANCEL');
209
210
        return $this->performTransaction($request, $response);
211
    }
212
213
    /**
214
     * Update terms of rebilling.
215
     *
216
     * @param GatewayRequest  $request
217
     * @param GatewayResponse $response
218
     *
219
     * @return bool
220
     */
221
    public function performRebillUpdate(GatewayRequest $request, GatewayResponse $response)
222
    {
223
        $request->set(GatewayRequest::transactionType(), 'REBILL_UPDATE');
224
225
        $amount = $request->get(GatewayRequest::amount());
226
        if (empty($amount) || $amount <= 0) {
227
            return $this->performTransaction($request, $response);
228
        }
229
230
        if ($this->performTransaction($request, $response) === false) {
231
            return false;
232
        }
233
234
        return $this->performConfirmation($request, $response);
235
    }
236
237
    /**
238
     * Lookup previous transaction.
239
     *
240
     * @param GatewayRequest  $request
241
     * @param GatewayResponse $response
242
     *
243
     * @return bool
244
     */
245
    public function performLookup(GatewayRequest $request, GatewayResponse $response)
246
    {
247
        $request->set(GatewayRequest::transactionType(), 'LOOKUP');
248
249
        if (!empty($request->get(GatewayRequest::referenceGuid()))) {
250
            return $this->performTargetedTransaction($request, $response);
251
        }
252
253
        return $this->performTransaction($request, $response);
254
    }
255
256
    /**
257
     * Upload card data to the servers.
258
     *
259
     * @param GatewayRequest  $request
260
     * @param GatewayResponse $response
261
     *
262
     * @return bool
263
     */
264
    public function performCardUpload(GatewayRequest $request, GatewayResponse $response)
265
    {
266
        $request->set(GatewayRequest::transactionType(), 'CARDUPLOAD');
267
268
        return $this->performTransaction($request, $response);
269
    }
270
271
    /**
272
     * Add an entry to the XsellQueue.
273
     *
274
     * @param GatewayRequest  $request
275
     * @param GatewayResponse $response
276
     *
277
     * @return bool
278
     */
279
    public function generateXsell(GatewayRequest $request, GatewayResponse $response)
280
    {
281
        $request->set(GatewayRequest::transactionType(), 'GENERATEXSELL');
282
        $request->set(GatewayRequest::referenceGuid(), $request->get(GatewayRequest::xsellReferenceXact()));
283
284
        if (!empty($request->get(GatewayRequest::referenceGuid()))) {
285
            return $this->performTargetedTransaction($request, $response);
286
        }
287
288
        return $this->performTransaction($request, $response);
289
    }
290
291
    /**
292
     * Set the communications parameters for production or test mode.
293
     *
294
     * @param bool $testFlag
295
     */
296
    public function setTestMode(bool $testFlag)
297
    {
298
        if ($testFlag) {
299
            $this->rocketGateHost     = 'dev-gateway.rocketgate.com';
300
            $this->rocketGateProtocol = 'https';
301
            $this->rocketGatePortNo   = 443;
302
        } else {
303
            $this->rocketGateHost     = 'gateway.rocketgate.com';
304
            $this->rocketGateProtocol = 'https';
305
            $this->rocketGatePortNo   = 443;
306
        }
307
    }
308
309
    /**
310
     * Set the host used by the service.
311
     *
312
     * @param string $hostname
313
     */
314
    public function setHost(string $hostname)
315
    {
316
        $this->rocketGateHost = $hostname;
317
    }
318
319
    /**
320
     * Set the communications protocol used by the service.
321
     *
322
     * @param string $protocol
323
     */
324
    public function setProtocol(string $protocol)
325
    {
326
        $this->rocketGateProtocol = $protocol;
327
    }
328
329
    /**
330
     * Set the port number used by the service.
331
     *
332
     * @param int $portNo
333
     */
334
    public function setPortNo(int $portNo)
335
    {
336
        $this->rocketGatePortNo = $portNo;
337
    }
338
339
    /**
340
     * Set the servlet used by the service.
341
     *
342
     * @param string $servlet
343
     */
344
    public function setServlet(string $servlet)
345
    {
346
        $this->rocketGateServlet = $servlet;
347
    }
348
349
    /**
350
     * Set the timeout used during connection to the servlet.
351
     *
352
     * @param int $timeout
353
     */
354
    public function setConnectTimeout(int $timeout)
355
    {
356
        $this->rocketGateConnectTimeout = $timeout;
357
    }
358
359
    /**
360
     * Set the timeout used while waiting for the servlet to answer.
361
     *
362
     * @param int $timeout
363
     */
364
    public function setReadTimeout(int $timeout)
365
    {
366
        $this->rocketGateReadTimeout = $timeout;
367
    }
368
369
    /**
370
     * Perform the transaction outlined in a GatewayRequest.
371
     *
372
     * @param GatewayRequest  $request
373
     * @param GatewayResponse $response
374
     *
375
     * @return bool
376
     */
377
    public function performTransaction(GatewayRequest $request, GatewayResponse $response)
378
    {
379
        $this->prepareRequest($request);
380
381
        $serverName = (!empty($request->get('gatewayServer'))) ?
382
            $request->get('gatewayServer') : $this->rocketGateHost;
383
384
        if (strcmp($serverName, 'gateway.rocketgate.com') !== 0) {
385
            $hostList = [$serverName];
386
        } else {
387
            $hostList = gethostbynamel($serverName);
388
            if (empty($hostList)) {
389
                $hostList = [
390
                    'gateway-16.rocketgate.com',
391
                    'gateway-17.rocketgate.com',
392
                ];
393
            } else {
394
                $listSize = count($hostList);
395
                for ($index = 0; $index < $listSize; $index++) {
396
                    if (strcmp($hostList[$index], '69.20.127.91') === 0) {
397
                        $hostList[$index] = 'gateway-16.rocketgate.com';
398
                    } elseif (strcmp($hostList[$index], '72.32.126.131') === 0) {
399
                        $hostList[$index] = 'gateway-17.rocketgate.com';
400
                    }
401
                }
402
            }
403
        }
404
405
        if (($listSize = count($hostList)) > 1) {
406
            $index = rand(0, ($listSize - 1));
407
            if ($index > 0) {
408
                $swapper          = $hostList[0];
409
                $hostList[0]      = $hostList[$index];
410
                $hostList[$index] = $swapper;
411
            }
412
        }
413
414
        for ($index = 0; $index < $listSize; $index++) {
415
            $results = $this->performCURLTransaction($hostList[$index], $request, $response);
416
            if ((int) $results === self::RESPONSE_SUCCESS) {
417
                return true;
418
            }
419
420
            if ((int) $results !== self::RESPONSE_SYSTEM_ERROR) {
421
                return false;
422
            }
423
424
            $request->set(GatewayRequest::failedServer(), $hostList[$index]);
425
            $request->set(GatewayRequest::failedResponseCode(), $response->get(GatewayResponse::responseCode()));
426
            $request->set(GatewayRequest::failedReasonCode(), $response->get(GatewayResponse::reasonCode()));
427
            $request->set(GatewayRequest::failedGuid(), $response->get(GatewayResponse::transactId()));
428
        }
429
430
        return false;
431
    }
432
433
    /**
434
     * Send a transaction to a server based upon the reference GUID.
435
     *
436
     * @param GatewayRequest  $request
437
     * @param GatewayResponse $response
438
     *
439
     * @return bool
440
     */
441
    public function performTargetedTransaction(GatewayRequest $request, GatewayResponse $response)
442
    {
443
        $this->prepareRequest($request);
444
445
        $referenceGUID = $request->get(GatewayRequest::referenceGuid());
446
        if (empty($referenceGUID)) {
447
            $response->setResults(self::RESPONSE_REQUEST_ERROR, self::REASON_INVALID_REFGUID);
448
449
            return false;
450
        }
451
452
        if (strlen($referenceGUID) > 15) {
453
            $siteNo = substr($referenceGUID, 0, 2);
454
        } else {
455
            $siteNo = substr($referenceGUID, 0, 1);
456
        }
457
        $siteNo = hexdec($siteNo);
458
459
        $serverName = $request->get('gatewayServer');
460
        if (empty($serverName)) {
461
            $serverName = $this->rocketGateHost;
462
            if (($separator = strpos($serverName, '.')) > 0) {
463
                $prefix     = substr($serverName, 0, $separator);
464
                $serverName = substr($serverName, $separator);
465
                $serverName = $prefix.'-'.$siteNo.$serverName;
466
            }
467
        }
468
469
        $results = $this->performCURLTransaction($serverName, $request, $response);
470
        if ((int) $results === self::RESPONSE_SUCCESS) {
471
            return true;
472
        }
473
474
        return false;
475
    }
476
477
    /**
478
     * @param GatewayRequest $request
479
     */
480
    private function prepareRequest(GatewayRequest &$request)
481
    {
482
        $request->clear(GatewayRequest::failedServer());
483
        $request->clear(GatewayRequest::failedResponseCode());
484
        $request->clear(GatewayRequest::failedReasonCode());
485
        $request->clear(GatewayRequest::failedGuid());
486
487
        $fullUrl = (!empty($request->get('gatewayURL'))) ?
488
            $request->get('gatewayURL') : $request->get('embeddedFieldsToken');
489
490
        if (!empty($fullUrl)) {
491
            $urlBits = parse_url($fullUrl); // Split the URL
492
            if (empty($request->get('gatewayServer'))) {
493
                $request->set('gatewayServer', $urlBits['host']);
494
            }
495
            $request->set('gatewayProtocol', $urlBits['scheme']);
496
            if (array_key_exists('port', $urlBits)) {
497
                $request->set('gatewayPortNo', $urlBits['port']);
498
            }
499
            $request->set('gatewayServlet', $urlBits['path'].'?'.$urlBits['query']);
500
        }
501
    }
502
503
    /**
504
     * Perform the confirmation pass that tells the server we have received transaction reply.
505
     *
506
     * @param GatewayRequest  $request
507
     * @param GatewayResponse $response
508
     *
509
     * @return bool
510
     */
511
    public function performConfirmation(GatewayRequest $request, GatewayResponse $response)
512
    {
513
        $confirmGUID = $response->get(GatewayResponse::transactId());
514
        if (empty($confirmGUID)) {
515
            $response->set(GatewayResponse::EXCEPTION(), 'BUG-CHECK - Missing confirmation GUID');
516
            $response->setResults(self::RESPONSE_SYSTEM_ERROR, self::REASON_BUGCHECK);
517
518
            return false;
519
        }
520
521
        $confirmResponse = new GatewayResponse();
522
        $request->set(GatewayRequest::transactionType(), 'CC_CONFIRM');
523
        $request->set(GatewayRequest::referenceGuid(), $confirmGUID);
524
        if ($this->performTargetedTransaction($request, $confirmResponse)) {
525
            return true;
526
        }
527
528
        if ((int) $confirmResponse->get(GatewayResponse::responseCode()) === self::RESPONSE_SYSTEM_ERROR) {
529
            sleep(2);
530
            if ($this->performTargetedTransaction($request, $confirmResponse)) {
531
                return true;
532
            }
533
        }
534
535
        $response->setResults(
536
            $confirmResponse->get(GatewayResponse::responseCode()),
537
            $confirmResponse->get(GatewayResponse::reasonCode())
538
        );
539
        $response->set(GatewayResponse::exception(), $confirmResponse->get(GatewayResponse::exception()));
540
541
        return false;
542
    }
543
544
    /**
545
     * Perform a transaction exchange with a given host.
546
     *
547
     * @param string          $host
548
     * @param GatewayRequest  $request
549
     * @param GatewayResponse $response
550
     *
551
     * @return int
552
     */
553
    public function performCURLTransaction(string $host, GatewayRequest $request, GatewayResponse $response)
554
    {
555
        $response->reset();
556
557
        $requestBytes   = $request->toXMLString();
558
        $url            = $this->getGatewayUrl($host, $request);
559
        $connectTimeout = !empty($request->get('gatewayConnectTimeout')) ? $request->get('gatewayConnectTimeout') :
560
            $this->rocketGateConnectTimeout;
561
        $readTimeout    = !empty($request->get('gatewayReadTimeout')) ? $request->get('gatewayReadTimeout') :
562
            $this->rocketGateReadTimeout;
563
564
        if (!$handle = curl_init()) {
565
            $response->set(GatewayResponse::exception(), 'curl_init() error');
566
            $response->setResults(self::RESPONSE_REQUEST_ERROR, self::REASON_INVALID_URL);
567
568
            return self::RESPONSE_REQUEST_ERROR;
569
        }
570
571
        /*
572
         * Set timeout values used in the operation.
573
         */
574
        curl_setopt($handle, CURLOPT_NOSIGNAL, true);
575
        curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, $connectTimeout);
576
        curl_setopt($handle, CURLOPT_TIMEOUT, $readTimeout);
577
578
        /*
579
         * Setup the call to the URL.
580
         */
581
        curl_setopt($handle, CURLOPT_POST, true);
582
        curl_setopt($handle, CURLOPT_POSTFIELDS, $requestBytes);
583
        curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
584
        curl_setopt($handle, CURLOPT_URL, $url);
585
        curl_setopt($handle, CURLOPT_FAILONERROR, true);
586
        curl_setopt($handle, CURLOPT_USERAGENT, 'RG PHP Client '.$this->getVersion());
587
        curl_setopt($handle, CURLOPT_HTTPHEADER, ['Content-Type: text/xml']);
588
589
        $results = curl_exec($handle);
590
        if (empty($results)) {
591
            $errorCode   = curl_errno($handle);
592
            $errorString = curl_error($handle);
593
            curl_close($handle);
594
595
            switch ($errorCode) {
596
                case CURLE_SSL_CONNECT_ERROR:
597
                case CURLE_COULDNT_CONNECT:
598
                    $internalCode = self::REASON_UNABLE_TO_CONNECT;
599
                    break;
600
                case CURLE_SEND_ERROR:
601
                    $internalCode = self::REASON_REQUEST_XMIT_ERROR;
602
                    break;
603
                case CURLE_OPERATION_TIMEOUTED:
604
                    $internalCode = self::REASON_RESPONSE_READ_TIMEOUT;
605
                    break;
606
                case CURLE_RECV_ERROR:
607
                case CURLE_READ_ERROR:
608
                default:
609
                    $internalCode = self::REASON_RESPONSE_READ_ERROR;
610
            }
611
612
            if (!empty($errorString)) {
613
                $response->set(GatewayResponse::exception(), $errorString);
614
            }
615
            $response->setResults(self::RESPONSE_SYSTEM_ERROR, $internalCode);
616
617
            return self::RESPONSE_SYSTEM_ERROR;
618
        }
619
620
        curl_close($handle);
621
        $response->setFromXML($results);
622
623
        return (int) $response->get(GatewayResponse::responseCode());
624
    }
625
626
    /**
627
     * @param string         $host
628
     * @param GatewayRequest $request
629
     *
630
     * @return string
631
     */
632
    private function getGatewayUrl(string $host, GatewayRequest $request)
633
    {
634
        $urlServlet  = !empty($request->get('gatewayServlet')) ? $request->get('gatewayServlet') :
635
            $this->rocketGateServlet;
636
        $urlProtocol = !empty($request->get('gatewayProtocol')) ? $request->get('gatewayProtocol') :
637
            $this->rocketGateProtocol;
638
        $urlPortNo   = !empty($request->get('gatewayPortNo')) ? $request->get('gatewayPortNo') :
639
            $this->rocketGatePortNo;
640
641
        return $urlProtocol.'://'.$host.':'.$urlPortNo.'/'.$urlServlet;
642
    }
643
}
644