Completed
Push — master ( 5f244d...af54fe )
by thomas
01:44
created

GatewayService::performCardScrub()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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