-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathsecurepay.php
executable file
·1128 lines (1029 loc) · 43.6 KB
/
securepay.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
define('SECUREPAY_CURRENCIES','USD,AUD,CAD,CHF,DEM,EUR,FRF,GBP,GRD,HKD,ITL,JPY,NZD,SGD'); // CSV of valid SecurePay currencies
define('SECUREPAY_STATUS_UNKNOWN', 0);
define('SECUREPAY_STATUS_OK', 1);
define('SECUREPAY_STATUS_INVALID_USER', 2);
define('SECUREPAY_STATUS_INVALID_PASS', 3);
define('SECUREPAY_STATUS_INVALID_URL', 4);
define('SECUREPAY_STATUS_SERVER_DOWN', 5);
define('SECUREPAY_STATUS_TIMEOUT', 6);
define('SECUREPAY_STATUS_SERVER_ERR', 7);
define('SECUREPAY_STATUS_XML_ERR', 8);
define('SECUREPAY_STATUS_CONNECTION_ERR', 9);
define('SECUREPAY_STATUS_APPROVED', 100);
define('SECUREPAY_STATUS_DECLINED', 101);
define('SECUREPAY_REPEAT_NEVER',-1);
define('SECUREPAY_REPEAT_DAILY',0);
define('SECUREPAY_REPEAT_WEEKLY',1);
define('SECUREPAY_REPEAT_FORTNIGHTLY',2);
define('SECUREPAY_REPEAT_MONTHLY',3);
define('SECUREPAY_REPEAT_QUARTERLY',4);
define('SECUREPAY_REPEAT_HALF_YEARLY',5);
define('SECUREPAY_REPEAT_YEARLY',6);
/**
* SecurePay Credit-Card charging, pre-auth and periodic charging class
* @access public
* @author Matt Carter <[email protected]>
*/
class SecurePay {
// Variable Declarations {{{
/**
* The account name registed with Securepay.com.au
* @see Login()
* @access public
* @var string
*/
public $AccountName;
/**
* The account password for the $AccountName
* @see Login()
* @see $AccountName
* @access public
* @var string
*/
public $AccountPassword;
/**
* The seperate account password for the $AccountName when in test mode
* If this is blank and we are in test mode the $AccountPassword is assumed instead
* @see Login()
* @see $AccountName
* @access public
* @var string
*/
public $TestAccountPassword;
/**
* The last status code returned by Process()
* This code (since its a copy of the return of the Process() function)
* @see Process()
* @access Public
* @var int
*/
public $Status;
/**
* The last error to occur.
* This can be:
* * A validation error (e.g. 'Your Credit Card Number is Invalid')
* * Processing error (e.g. 'You're credit card details were declined')
*
* Use this variable as the error message if Valid() or Process() fails
* @access Public
* @var string
*/
Public $Error;
/**
* The merchant name for web logins
* @see WebLogin()
* @access Public
* @var string
*/
public $WebMerchantName;
/**
* The merchant username for web logins
* @see WebLogin()
* @access Public
* @var string
*/
public $WebUserName;
/**
* The merchant password for web logins
* @see WebLogin()
* @access Public
* @var string
*/
public $WebUserPassword;
/**
* The cookie jar used while using the web interface
* If this is null it can be assumed that we have not yet logged in
* @access public
* @see WebSignin()
* @var string
*/
public $WebCookieJar;
/**
* The web based merchant ID. Retrieved when requesting reports for the first time.
* @access public
* @var string
*/
public $WebMerchantId;
/**
* Indicates that the class should operate in test mode
* @access public
* @var bool
*/
public $TestMode;
/**
* Indicates if the transaction to be processed is actually a PreAuth rather than a normal charge
* @access public
* @var bool
*/
public $PreAuth;
/**
* Credit card holder name in credit card
* @access public
* @var string
*/
public $NameOnCard;
/**
* The credit card number to work with
* @access public
* @var int|string
*/
public $Cc;
/**
* Short string to denote the expiry date of the credit card
* This is in the form: 'mm/yy' where the first two are a zero padded month and zero padded year
* @access public
* @var char(5)
*/
public $ExpiryDate;
/**
* The Credit Card Verification number / CV2
* This is optional as to whether the card type supports it
* @var char(3)
* @access public
*/
public $Cvv;
/**
* The amount (as a float) that should be charged to the card
* This is listed in the currency specified in the $ChargeCurrency Variable
* @access public
* @var float
* @see $ChargeCurrency
*/
public $ChargeAmount;
/**
* The currency code relevent to the $ChargeAmount
* @access public
* @see $ChargeAmount
* @var char(3)
*/
public $ChargeCurrency;
/**
* Unique order number to identify this transaction.
* This is typically an invoice number
* Must be at least 1 characters
* Must be at most 60 characters
* @access public
* @var string
*/
public $OrderId;
/**
* Last message ID requested
* This code is only really useful for debugging with Securepay.com.au who track these codes
* @access public
* @var string
*/
public $LastMesageId;
/**
* Preauth ID if we are putting though a PreAuth payment (i.e. last call to Process() had $this->PreAuth == True)
* If a pre-auth is sent though before an actual transaction the preauth code is stored here to reserve the transaction for the next payment
* @access public
* @var string
*/
public $PreAuthId;
/**
* Last Transaction ID if successful
* If a transaction is successful, the transaction ID will be stored here for you to easily retrieve
* @access public
* @var int
* @since 2014-02-13
*/
public $TransactionId;
/**
* The last dispatched request
* @access public
* @var string
*/
public $RequestXml;
/**
* The XML returned by the server in response to RequestXml
* @access public
* @see RequestXml
* @var array
*/
public $ResponseXml;
/**
* The XML tree provided from the last transaction
* @access public
* @var array
*/
public $ResponseTree;
/**
* Securepay status code from the last transaction
* @access public
* @var int
*/
public $StatusCode;
/**
* Securepay status code text from the last transaction
* This is the english equivelent to the above $StatusCode value
* @access public
* @var string
* @see StatusCode
*/
public $StatusCodeText;
/**
* Securepay response code from the last transaction
* @access public
* @var int
*/
public $ResponseCode;
/**
* Securepay response code text from the last transaction
* This is the english equivelent to the above $ResponseCode value
* @access public
* @var string
* @see ResponseCode
*/
public $ResponseCodeText;
/**
* Whether to repeat the transaction as a periodic or once-off payment
* See the 'SECUREPAY_REPEAT_*' constants for a list of values
* Default value is SECUREPAY_REPEAT_NEVER
* @access public
* @var bool
*/
public $Repeat;
/**
* If $Repeat=SECUREPAY_REPEAT_DAILY this represents how many days should elapase before the next charge
* @access public
* @var int
*/
public $RepeatInterval;
/**
* Epoc timstamp on when to start the repeat payments
* @access public
* @var int
*/
public $RepeatStart;
/**
* Number of repeating payments to max out on
* Set to 'zero' for never ending periodic charge
* @access public
* @var int
*/
public $RepeatCount;
/**
* Automatically trigger the repeat payment after processing.
* The default is TRUE
* If this is disabled a manual trigger request will need to be made when putting though a repeat payment request
* @access public
* @var bool
*/
public $RepeatTrigger;
/**
* Maximum number of seconds a processing request is allowed to take
* The default is 60 seconds
*
* @access public
* @var integer
*/
public $Timeout;
// End of Variable Declarations }}}
// General use functionality {{{
/**
* Constructor function.
* This can optionally be passed the account_name and account_password variables.
* @see Login()
* @return void
*/
function __construct($AccountName = null, $AccountPassword = null, $TestMode = FALSE) {
if ($AccountName && $AccountPassword)
$this->Login($AccountName, $AccountPassword, $TestMode);
$this->ChargeCurrency = 'USD'; // Default currency to USD
$this->Repeat = SECUREPAY_REPEAT_NEVER;
$this->RepeatTrigger = TRUE;
$this->Timeout = 60;
}
/**
* Shorthand function to set the account_name and account_password variables
* @param string $AccountName The account name to use for payments (provided by SecurePay)
* @param string $AccountPassword The account password to use for payments (provided by SecurePay)
* @param bool $TestMode Whether to use TestMode with all transactions
* @see SecurePay()
* @see $AccountName
* @see $AccountPassword
* @return void
*/
function Login($AccountName, $AccountPassword, $TestMode = FALSE) {
$this->AccountName = $AccountName;
$this->AccountPassword = $AccountPassword;
$this->TestMode = $TestMode;
}
/**
* Login to the main website login page. This is used when retrieving periodic payment information
*/
function WebLogin($WebMerchantName, $WebUserName, $WebUserPassword) {
$this->WebMerchantName = $WebMerchantName;
$this->WebUserName = $WebUserName;
$this->WebUserPassword = $WebUserPassword;
}
/**
* Enable or disable the testing suite
* When TestMode is enabled all functionality uses the test SecureXML API servers instead
* @param bool $TestMode Optional indicator as to whether test mode is enabled or not. If omitted TestMode is enabled
*/
function TestMode($TestMode = TRUE) {
$this->TestMode = $TestMode;
}
/**
* Shorthand function to set all relevent fields and process a payment
* If any field is supplied it overrides the existing model function if not or no parameters are specified the transaction is just dispatched
* @param float $ChargeAmount Optional amount that is to be deducted from the given Credit Card (e.g. '4.12' => '$4.12')
* @param string $ChargeCurrency Optional currency that the above $ChargeAmount is specified in (e.g. 'USD')
* @param string $Cc Optional Credit Card number to use
* @param string $ExpiryDate Optional expiry date of the Credit Card (e.g. '07/09' => 'July 2009')
* @param string $Cvv Optional CVV code (if the card has one)
* @param string $OrderId Optional local order ID reference to use for this transaction. This must be unique
* @param bool $PreAuth Indicate that this transaction is a preauth
* @return int Returns the corresponding SECUREPAY_STATUS_* code
*/
function Process($ChargeAmount = null, $ChargeCurrency = null, $NameOnCard = null, $Cc = null, $ExpiryDate = null, $Cvv = null, $OrderId = null, $PreAuth = FALSE) {
// Set class variables from function call for later use {{{
if ($ChargeAmount) $this->ChargeAmount = $ChargeAmount;
if ($ChargeCurrency) $this->ChargeCurrency = $ChargeCurrency;
if ($NameOnCard) $this->NameOnCard = $NameOnCard;
if ($Cc) $this->Cc = $Cc;
if ($ExpiryDate) $this->ExpiryDate = $ExpiryDate;
if ($Cvv) $this->Cvv = $Cvv;
if ($OrderId) $this->OrderId = $OrderId;
if ($PreAuth) $this->PreAuth = $PreAuth;
// }}}
$this->ValidExpiryDate(); // Reformat the expiry date if necessary
if ($this->Cc)
$this->Cvv = str_pad($this->Cvv, 3, '0', STR_PAD_LEFT);
$this->RequestXml = $this->_ComposePayment();
$this->ResponseXml = $this->_Dispatch($this->RequestXml);
// If no XML response, assume processing timed out.
if ((false === $this->ResponseXml) || (false == is_string($this->ResponseXml))) {
return $this->_TranslateServerCode(512);
}
$this->ResponseTree = simplexml_load_string($this->ResponseXml);
$this->StatusCode = $this->ResponseTree->Status->statusCode;
$this->StatusCodeText = $this->ResponseTree->Status->statusDescription;
$server_code = $this->_TranslateServerCode($this->StatusCode);
if (isset($this->ResponseTree->Payment->TxnList->Txn->responseCode)) { // Has a response code
$this->ResponseCode = $this->ResponseTree->Payment->TxnList->Txn->responseCode;
$this->ResponseCodeText = $this->ResponseTree->Payment->TxnList->Txn->responseText;
if ($this->PreAuth) // Was requesting a PreAuth...
$this->PreAuthId = $this->ResponseTree->Payment->TxnList->Txn->preauthID; // Store the PreAuth return code in $this->PreAuth
$result = $this->_TranslateResponseCode($this->ResponseCode);
if ($result == SECUREPAY_STATUS_APPROVED && !empty($this->ResponseTree->Payment->TxnList->Txn->txnID))
$this->TransactionId = (string) $this->ResponseTree->Payment->TxnList->Txn->txnID;
} else if(isset($this->ResponseTree->Periodic->PeriodicList->PeriodicItem->responseCode)) { // Has a response code - periodic style
$this->ResponseCode = $this->ResponseTree->Periodic->PeriodicList->PeriodicItem->responseCode;
$this->ResponseCodeText = $this->ResponseTree->Periodic->PeriodicList->PeriodicItem->responseText;
if ($this->PreAuth) // Was requesting a PreAuth...
$this->PreAuthId = $this->ResponseTree->Payment->TxnList->Txn->preauthID; // Store the PreAuth return code in $this->PreAuth
$result = $this->_TranslateResponseCode($this->ResponseCode);
} else { // No success with the response code - return the server code error
$result = $server_code;
}
if ($this->IsRepeat() && $this->RepeatTrigger) // Automatically trigger the response
$this->Trigger();
return $this->Status = $result;
}
/**
* Refunds a Transaction on Secure Pay
* If any field is supplied it overrides the existing model function if not or no parameters are specified the transaction is just dispatched
* @param string $TransactionId The TransactionId as provided by SecurePay
* @param string $OrderId The order ID you have this transaction. Must match the original transaction
* @param float $ChargeAmount The amount to refund, up-to the total original transaction amount
* @return int Returns the corresponding SECUREPAY_STATUS_* code
* @author Phil Hawthorne <[email protected]>
* @since 2014-02-13
*/
function Refund($TransactionId = null,$OrderId = null,$ChargeAmount=null){
// Set class variables from function call for later use {{{
if ($TransactionId) $this->TransactionId = $TransactionId;
if ($ChargeAmount) $this->ChargeAmount = $ChargeAmount;
if ($OrderId) $this->OrderId = $OrderId;
// }}}
$this->RequestXml = $this->_ComposeRefund();
$this->ResponseXml = $this->_Dispatch($this->RequestXml);
// If no XML response, assume processing timed out.
if ((false === $this->ResponseXml) || (false == is_string($this->ResponseXml))) {
return $this->_TranslateServerCode(512);
}
$this->ResponseTree = simplexml_load_string($this->ResponseXml);
$this->StatusCode = $this->ResponseTree->Status->statusCode;
$this->StatusCodeText = $this->ResponseTree->Status->statusDescription;
$server_code = $this->_TranslateServerCode($this->StatusCode);
if (isset($this->ResponseTree->Payment->TxnList->Txn->responseCode)) { // Has a response code
$this->ResponseCode = $this->ResponseTree->Payment->TxnList->Txn->responseCode;
$this->ResponseCodeText = $this->ResponseTree->Payment->TxnList->Txn->responseText;
if ($this->PreAuth) // Was requesting a PreAuth...
$this->PreAuthId = $this->ResponseTree->Payment->TxnList->Txn->preauthID; // Store the PreAuth return code in $this->PreAuth
$result = $this->_TranslateResponseCode($this->ResponseCode);
if ($result == SECUREPAY_STATUS_APPROVED && !empty($this->ResponseTree->Payment->TxnList->Txn->txnID))
$this->TransactionId = (string) $this->ResponseTree->Payment->TxnList->Txn->txnID;
} else { // No success with the response code - return the server code error
$result = $server_code;
}
return $this->Status = $result;
}
/**
* Trigger (start) a repeating payment.
* This function is automatically invoked if $RepeatTrigger is boolean TRUE
* @param string $OrderID Optional order ID to use for the trigger. If unspecified the OrderID from the previous transaction is used instead
* @return int Returns the corresponding SECUREPAY_STATUS_* code
* @see RepeatTrigger
*/
function Trigger($OrderId = null) {
if ($OrderId) $this->OrderId = $OrderId;
$this->RequestXml = $this->_ComposeTrigger();
$this->ResponseXml = $this->_Dispatch($this->RequestXml);
// If no XML response, assume processing timed out.
if ((false === $this->ResponseXml) || (false == is_string($this->ResponseXml))) {
return $this->_TranslateServerCode(512);
}
$this->ResponseTree = simplexml_load_string($this->ResponseXml);
$server_code = $this->_TranslateServerCode($this->ResponseTree->Status->statusCode);
if (isset($this->ResponseTree->Payment->TxnList->Txn->responseCode)) { // Has a response code
return $this->_TranslateResponseCode($this->ResponseCode = $this->ResponseTree->Payment->TxnList->Txn->responseCode);
} else { // No success with the response code - return the server code error
return $server_code;
}
}
/**
* Preforms a simple test connection to the Securepay server and returns a boolean on success
* @return bool TRUE if the server connection and login information returned a correct result
*/
function TestConnection() {
$this->RequestXml = $this->_ComposeEcho();
$this->ResponseXml = $this->_Dispatch($this->RequestXml);
// If no XML response, assume processing timed out.
if ((false === $this->ResponseXml) || (false == is_string($this->ResponseXml))) {
$this->_TranslateServerCode(512);
return false;
}
$this->ResponseTree = simplexml_load_string($this->ResponseXml);
return ($this->_TranslateServerCode($this->ResponseTree->Status->statusCode) == SECUREPAY_STATUS_OK);
}
/**
* Signs into the web interface. This should only be done once to prevent unnecessay overhead
* This function can be called multiple times but will only actually do something the first time round.
* Thus all methods that use the web interface should start by calling this function
*/
function WebSignin() {
if (!empty($this->WebCookieJar))
return TRUE;
if (!function_exists('curl_init'))
trigger_error('You do not have Curl installed on this server', E_ERROR);
if (!function_exists('simplexml_load_string'))
trigger_error('You do not have an XML parser installed on this server', E_ERROR);
$tempdir = (function_exists('sys_get_temp_dir')) ? sys_get_temp_dir() : '/tmp'; // Attempt to figure out a temporary directory
$this->WebCookieJar = tempnam('/tmp', 'securepay-cookie-jar-');
touch($this->WebCookieJar);
$this->_WebRetrieve('https://login.securepay.com.au/login/verify.jsp', "merchantId={$this->WebMerchantName}&userName={$this->WebUserName}&password={$this->WebUserPassword}&loginType=M");
return TRUE; // FIXME: Query if the above succeded and return error if not
}
/**
* Retreieves all transaction information between two optional dates.
* If no dates are supplied. Today is assumed.
* The first parameter 'datefrom' also accepts the convenience value 'yesterday' to retrieve information from the previous day (NOTE: Not the previous working day)
* @param int|string $datefrom Optional date range to work from. This is a Unix Epoc. Also accepts 'yesterday' as a value. Today is assumed if omitted
* @param int $dateto Optional date range to. Today is assumed if omitted.
* @return array The transaction history matching the above criteria
*/
function GetTransactions($datefrom = null, $dateto = null) {
$this->WebSignin();
$output = array();
$post = 'view_type=screen&card_type=-2&search_when=date_range';
if ($datefrom == 'yesterday') {
$datefrom = mktime(0,0,0,0,-1);
$dateto = mktime(23,59,0,0,-1);
} elseif (!$datefrom && !$dateto) { // Nulls, assume today
$datefrom = mktime(0,0,0);
$dateto = mktime(23,59,59);
}
$post .= "&date_from=" . urlencode(date('j M Y', $datefrom)) . "&hours_from_val=" . date('H', $datefrom) . "&minutes_from_val=" . date('i', $datefrom);
$post .= "&date_to=" . urlencode(date('j M Y', $dateto)) . "&hours_to_val=" . date('H', $dateto) . "&minutes_to_val=" . date('i', $dateto);
$response = $this->_WebRetrieve('https://login.securepay.com.au/login/login.jsp?id=ntxnlist', $post);
preg_match_all('!<tr class="listing_payment" ><td >(.+?)</td><td ><a href="login.jsp\?id=txndetail&txn=(.+?)">(.+?)</a></td><td class="centered_col" >(.+?)</td><td class="centered_col" >(.+?)<img src="common/images/newlogos/.+?" height="17" /></td><td class="amount_col" >(.+?)</td><td class="centered_col" >(.+?)</td><td class=".+?" ><span class=".+?" >(.+?)</span><span class=".+?" > (.+?)</span></td><td class="txn_type" >(.+?)</td></tr>!', $response, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$output[$match[2]] = array(
'merchantid' => $match[1],
'transactionid' => $match[2],
'transactionref' => $match[3],
'date' => strtotime($match[4]),
'cc' => $match[5],
'amount' => $match[6],
'currency' => $match[7],
'code' => $match[8],
'result' => $match[9],
'type' => $match[10],
);
}
return $output;
}
/**
* Retrieves the client status information from SecurePay
* If given a specific client id (usually the username) this will return just that relevent hash of information
* @param int|string $clientid Optional single client ID that should be retrieved
* @return array Either all client Id's discovered as keys for information or the single client information hash
*/
function GetClientInfo($clientid = null) {
$this->WebSignin();
if (empty($this->WebMerchantId)) { // Not yet calculated the merchant ID
$page = $this->_WebRetrieve('https://login.securepay.com.au/login/login.jsp?id=perreport');
if (preg_match('/<input type="hidden" name="merchid" value="(.+)">/', $page, $matches)) {
$this->WebMerchantId = $matches[1];
} else {
trigger_error('Did not recieve a merchant ID from the periodic billing page. Perhaps SecurePay have changed their API?', E_ERROR);
}
}
$csvdef = array(); // CSV definition holder
$output = array();
foreach (explode("\n", $this->_WebRetrieve('https://login.securepay.com.au/login/merchant/periodic/report.jsp', "id=perview&merchid={$this->WebMerchantId}" . (($clientid) ? "&clientid={$clientid}" : '') )) as $line) {
if (($line = trim($line)) == '') continue; // Ignore blank lines
$bits = explode(",", $line);
$set = array();
if (empty($csvdef)) { // Not yet defined CSV fields
$csvdef = array_map('strtolower',$bits);
} else { // Already defined CSV fields
for ($b = 0; $b < count($bits); $b++)
$set[$csvdef[$b]] = trim($bits[$b],'"');
$output[$set['clientid']] = $set;
}
}
return ($clientid) ? current($output) : $output; // If it was a specific request just return that branch
}
/**
* Setup a recurring payment with error checking
* @param string|int $when The name of the recurring type (i.e. the constants SECUREPAY_REPEAT_* or a simple word e.g. 'weekly')
* @param int $interval The spacing between the payments (e.g. if 'when'='monthly' and 'interval'=2 - the payment will occur every two weeks).
* @param int $count The count of the payments to apply (i.e. '3 repeating payments'). Set to zero (the default) for continuous
*/
function SetupRepeat($when, $count = 0) {
if (is_string($when)) { // Translate $when to constant if supported
$translate = array(
'daily' => SECUREPAY_REPEAT_DAILY,
'weekly' => SECUREPAY_REPEAT_WEEKLY,
'fortnightly' => SECUREPAY_REPEAT_FORTNIGHTLY,
'monthly' => SECUREPAY_REPEAT_MONTHLY,
'quarterly' => SECUREPAY_REPEAT_QUARTERLY,
'half_yearly' => SECUREPAY_REPEAT_HALF_YEARLY,
'yearly' => SECUREPAY_REPEAT_YEARLY
);
if (isset($translate[$when])) {
$when = $translate[$when];
} else
trigger_error("Unknown repeat period '$when'. See the SECUREPAY_REPEAT_* constants for a list of supported time periods", E_USER_ERROR);
} elseif ($when < 0) {
trigger_error("Repeat period (\$when) must be above zero", E_USER_ERROR);
} elseif ($when > 6) {
trigger_error("Repeat period (\$when) must be below 6 (yearly)", E_USER_ERROR);
}
$this->Repeat = $when;
$this->RepeatCount = $count;
}
/**
* Quick boolean return for if the pending transaction is a repeat transaction
* @return bool TRUE if the pending transaction is a repeat transaction
*/
function IsRepeat() {
return ($this->Repeat > SECUREPAY_REPEAT_NEVER);
}
// End of General use functionality }}}
// Validation tests {{{
/**
* Global validation test that checks all other child validation tests
* @see Valid*
* @return bool FALSE if ANY validation test fails, TRUE if all pass successfully
*/
function Valid() {
return (
$this->ValidCC()
&& $this->ValidExpiryDate()
&& $this->ValidCvv()
&& $this->ValidChargeAmount()
&& $this->ValidChargeCurrency()
&& $this->ValidOrderId()
);
}
/**
* Validates Credit Card number and returns a bool indicating its validity
* @param string|int $Cc Optional credit card number to validate. If none is specified the objects Credit Card number is used instead
* @return bool TRUE if the CC number passes validation
*/
function ValidCc($Cc = null) {
$test_cc = ($Cc) ? $Cc : $this->Cc;
if (preg_match('/[0-9]{12,16}/',$test_cc) > 0) {
return TRUE;
} else {
$this->Error = 'Invalid Credit Card Number';
return FALSE;
}
}
/**
* Validates an expiry date and ensures that it confirms to the SecurePay standards
* @param string $ExpiryDate Optional expiry date to test. If none is specified the objects expiry date is used instead
* @return bool TRUE if the expiry date passes validation
*/
function ValidExpiryDate($ExpiryDate = null) {
$test_expiry = ($ExpiryDate) ? $ExpiryDate : $this->ExpiryDate;
if (preg_match('!([0-9]{1,2})/([0-9]{2,4})!',$test_expiry, $matches)) {
if (strlen($matches[1]) == 1)
$matches[1] = "0{$matches[1]}";
if (strlen($matches[2]) == 4)
$matches[2] = substr($matches[2],-2);
$this->ExpiryDate = "{$matches[1]}/{$matches[2]}";
return ( ($matches[1] > 0) && ($matches[1] < 13) && ($matches[2] >= date('y')) && ($matches[2] < date('y') + 30) ); // Check that month and years are valid
} else {
$this->Error = 'Invalid Expiry Date';
return FALSE; // Failed RegExp checks
}
}
/**
* Validates a CVV code and ensures that it confirms to the SecurePay standards
* @param string|int $CVV Optional CVV to test. If none is specified the objects CVV is used instead
* @param bool $ForceValue Optional value indicating that the Cvv HAS to contain a value
* @return bool TRUE if the CVV passes validation
*/
function ValidCvv($Cvv = null, $ForceValue = FALSE) {
$test_cvv = ($Cvv) ? $Cvv : $this->Cvv;
if ($test_cvv) {
return (preg_match('/[0-9]{3,4}/',$test_cvv, $matches));
} elseif ($ForceValue) { // Has to contain a value but doesn't
$this->Error = 'Invalid CVV code';
return FALSE;
} else // Does not have to contain and value and doesn't
return TRUE;
}
/**
* Validates a Charge Amount and ensures that it confirms to the SecurePay standards
* @param float $Amount Optional Charge Amount to test. If none is specified the objects Charge Amount is used instead
* @return bool TRUE if the Charge Amount passes validation
*/
function ValidChargeAmount($ChargeAmount = null) {
$test_amount = ($ChargeAmount) ? $ChargeAmount : $this->ChargeAmount;
if ($test_amount > 0) {
return TRUE;
} else {
$this->Error = 'Invalid charge amount';
return FALSE;
}
}
/**
* Validates a Charge Currency and ensures that it confirms to the SecurePay standards
* @param string $ChargeCurrency Optional Charge Currency to test. If none is specified the objects Charge Currency is used instead
* @return bool TRUE if the Charge Currency passes validation
*/
function ValidChargeCurrency($ChargeCurrency = null) {
$test_currency = ($ChargeCurrency) ? $ChargeCurrency : $this->ChargeCurrency;
$valid_currencies = explode(',',SECUREPAY_CURRENCIES);
if (in_array($test_currency, $valid_currencies)) {
return TRUE;
} else {
$this->Error = 'Invalid charge currency';
return FALSE;
}
}
/**
* Validates a Order ID and ensures that it confirms to the SecurePay standards
* @param string|int $Amount Optional Order ID to test. If none is specified the objects Order ID is used instead
* @return bool TRUE if the Order Id Amount passes validation
*/
function ValidOrderId($OrderId = null) {
$test_order = ($OrderId) ? $OrderId : $this->OrderId;
if ( (strlen($test_order) > 0) && (strlen($test_order) <= 60) ) {
return TRUE;
} else {
$this->Error = 'Invalid Order ID';
return FALSE;
}
}
// End of Validation tests }}}
// Private functions {{{
/**
* Generates a new message ID
* @return string A string of 30 random hex characters
*/
function _GetMessageId() {
$code = '';
foreach (range(1,30) as $offset)
$code .= dechex(rand(0,15));
return $code;
}
/**
* Translates a given SecurePay server status code into a constant equivelent
* @param int $SpCode The SecurePay status integer code
* @return const A constant of the SECUREPAY_STATUS_* types
*/
function _TranslateServerCode($SpCode) {
switch($SpCode) {
case 000:
$this->Error = '';
return SECUREPAY_STATUS_OK;
case 504:
$this->Error = 'We are currently experiencing technical difficulties (Error: Credential failure with merchant ID). Please try again later';
return SECUREPAY_STATUS_INVALID_USER;
case 505:
$this->Error = 'We are currently experiencing technical difficulties (Error: Invalid SecurePay URL). Please try again later';
return SECUREPAY_STATUS_INVALID_URL;
case 510:
$this->Error = 'The credit card processor is currently experiencing difficulties. Please try again later';
return SECUREPAY_STATUS_SERVER_DOWN;
case 512:
$this->Error = 'The credit card processor is currently experiencing difficulties. Please try again later';
return SECUREPAY_STATUS_TIMEOUT;
case 513:
case 514:
case 515:
case 545:
$this->Error = 'The credit card processor is currently experiencing difficulties. Please try again later';
return SECUREPAY_STATUS_SERVER_ERR;
case 516:
case 517:
case 518:
case 575:
case 577:
case 580:
$this->Error = 'We are currently experiencing technical difficulties (Error: XML Processing Fault). Please try again later';
return SECUREPAY_STATUS_XML_ERR;
case 524:
$this->Error = 'We are currently experiencing technical difficulties (Error: Connection fault). Please try again later';
return SECUREPAY_STATUS_CONNECTION_ERR;
case 550:
$this->Error = 'We are currently experiencing technical difficulties (Error: Credential failure with password). Please try again later';
return SECUREPAY_STATUS_INVALID_PASS;
case 595:
$this->Error = 'Credit card declined';
return SECUREPAY_STATUS_DENIED;
}
}
/**
* Translates a SecurePay server reposnse code into a constant equivelent
* @param int $SpCode The SecurePay response integer code
* @return const A constant of the SECUREPAY_STATUS_* types
*/
function _TranslateResponseCode($SpCode) {
if ( ($SpCode == 0) || ($SpCode == 8) || ($SpCode == 77) ) {
return SECUREPAY_STATUS_APPROVED;
} else {
$this->Error = 'Your credit card details were declined';
return SECUREPAY_STATUS_DECLINED;
}
}
/**
* Send the request to SecurePay.com.au via SSL
* Requires CURL to be installed
* @param string $Xml The XML request to be sent. This is composed by one of the _Compose* functions
* @access private
*/
function _Dispatch($xml) {
if (!function_exists('curl_init'))
trigger_error('You do not have Curl installed on this server', E_USER_ERROR);
$curl = curl_init();
if ($this->IsRepeat()) { // Periodic payment
$url = ($this->TestMode) ? 'https://test.securepay.com.au/xmlapi/periodic' : 'https://api.securepay.com.au/xmlapi/periodic';
} else // Once-off payment
$url = ($this->TestMode) ? 'https://test.securepay.com.au/xmlapi/payment' : 'https://api.securepay.com.au/xmlapi/payment';
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE); // Follow redirects
curl_setopt($curl, CURLOPT_POST, TRUE);
curl_setopt($curl, CURLOPT_POSTFIELDS, $xml);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); // Return the HTTP response from the curl_exec function
curl_setopt($curl, CURLOPT_TIMEOUT, $this->Timeout);
if (defined('CURL_SSLVERSION_TLSv1_2')) {
//Always use TLS v1.2 if its available.
curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
} else if (defined('CURL_SSLVERSION_TLSv1_0')) { // TLSv1_0 supported (PHP5.5+)
// Switch to SSLv3 due to the OpenSSL + Poodle fiascos
// thanks to cleathley for pointing this out and to beitsafedaniel for fixing the fix
// @url https://github.com/hash-bang/PHP-SecurePay/issues/6
curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0);
curl_setopt($curl, CURLOPT_SSL_CIPHER_LIST, 'TLSv1');
} else if (defined('CURL_SSLVERSION_TLSv1')) { // Covers all TLSv1 minor versions
curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
} // else - rely on PHP figuring out the best option for CURLOPT_SSLVERSION as per note at http://php.net/manual/en/function.curl-setopt.php
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
/**
* Creates the XML request for a SecurePay Echo
* @return string The XML string for a SecurePay Echo request
* @access private
*/
function _ComposeEcho() {
$this->LastMessageId = $this->_GetMessageId();
$timestamp = date('YdmHis000+Z'); // See Appendix E of the SecureXML standard for more details on this date format
$password = ($this->TestMode && $this->TestAccountPassword) ? $this->TestAccountPassword : $this->AccountPassword;
$message = "<?xml version=\"1.0\" encoding=\"UTF-8\"?" . ">\n";
$message .= "<SecurePayMessage>\n";
$message .= "\t<MessageInfo>\n";
$message .= "\t\t<messageID>{$this->LastMessageId}</messageID>\n";
$message .= "\t\t<messageTimestamp>$timestamp</messageTimestamp>\n";
$message .= "\t\t<timeoutValue>{$this->Timeout}</timeoutValue>\n";
$message .= "\t\t<apiVersion>xml-4.2</apiVersion>\n";
$message .= "\t</MessageInfo>\n";
$message .= "\t<MerchantInfo>\n";
$message .= "\t<merchantID>{$this->AccountName}</merchantID>\n";
$message .= "\t<password>{$password}</password>\n";
$message .= "\t</MerchantInfo>\n";
$message .= "\t<RequestType>Echo</RequestType>\n";
$message .= "</SecurePayMessage>";
return $message;
}
/**
* Creates the XML request for a SecurePay trigger
* @return string The XML string for a SecurePay trigger request
* @access private
*/
function _ComposeTrigger() {
$this->LastMessageId = $this->_GetMessageId();
$cents = round($this->ChargeAmount * 100); // Convert to cents
$timestamp = date('YdmHis000+Z'); // See Appendix E of the SecureXML standard for more details on this date format
$message = "<?xml version=\"1.0\" encoding=\"UTF-8\"?" . ">\n";
$password = ($this->TestMode && $this->TestAccountPassword) ? $this->TestAccountPassword : $this->AccountPassword;
$message .= "<SecurePayMessage>\n";
$message .= "\t<MessageInfo>\n";
$message .= "\t\t<messageID>{$this->LastMessageId}</messageID>\n";
$message .= "\t\t<messageTimestamp>$timestamp</messageTimestamp>\n";
$message .= "\t\t<timeoutValue>{$this->Timeout}</timeoutValue>\n";
$message .= "\t\t<apiVersion>spxml-3.0</apiVersion>\n";
$message .= "\t</MessageInfo>\n";
$message .= "\t<MerchantInfo>\n";
$message .= "\t\t<merchantID>{$this->AccountName}</merchantID>\n";
$message .= "\t\t<password>{$password}</password>\n";
$message .= "\t</MerchantInfo>\n";
$message .= "\t<RequestType>Periodic</RequestType>\n";
$message .= "\t<Periodic>\n";
$message .= "\t\t<PeriodicList count=\"1\">\n";
$message .= "\t\t\t<PeriodicItem ID=\"1\">\n";
$message .= "\t\t\t\t<actionType>trigger</actionType>\n";
$message .= "\t\t\t\t<clientID>{$this->OrderId}</clientID>\n"; // FIXME
$message .= "\t\t\t\t<amount>$cents</amount>\n";
$message .= "\t\t\t</PeriodicItem>\n";
$message .= "\t\t</PeriodicList>\n";
$message .= "\t</Periodic>\n";
$message .= "</SecurePayMessage>";
return $message;
}
/**
* Creates the XML request for a SecurePay Echo
* This function reads $this->PreAuth to determine whether the transaction is a PreAuth rather than a standard payment. If FALSE (the default) a standard payment is produced
* @return string The XML string for a SecurePay Echo request
* @access private
*/
function _ComposePayment() {
$this->LastMessageId = $this->_GetMessageId();
$cents = round($this->ChargeAmount * 100); // Convert to cents
$timestamp = date('YdmHis000+Z'); // See Appendix E of the SecureXML standard for more details on this date format
$message = "<?xml version=\"1.0\" encoding=\"UTF-8\"?" . ">\n";
$password = ($this->TestMode && $this->TestAccountPassword) ? $this->TestAccountPassword : $this->AccountPassword;
if ($this->IsRepeat()) {
$message .= "<SecurePayMessage>\n";
$message .= "\t<MessageInfo>\n";
$message .= "\t\t<messageID>{$this->LastMessageId}</messageID>\n";
$message .= "\t\t<messageTimestamp>$timestamp</messageTimestamp>\n";
$message .= "\t\t<timeoutValue>{$this->Timeout}</timeoutValue>\n";
$message .= "\t\t<apiVersion>spxml-3.0</apiVersion>\n";
$message .= "\t</MessageInfo>\n";
$message .= "\t<MerchantInfo>\n";
$message .= "\t\t<merchantID>{$this->AccountName}</merchantID>\n";
$message .= "\t\t<password>{$password}</password>\n";
$message .= "\t</MerchantInfo>\n";
$message .= "\t<RequestType>Periodic</RequestType>\n";
$message .= "\t<Periodic>\n";
$message .= "\t\t<PeriodicList count=\"1\">\n";
$message .= "\t\t\t<PeriodicItem ID=\"1\">\n";
$message .= "\t\t\t\t<actionType>add</actionType>\n";
$message .= "\t\t\t\t<clientID>{$this->OrderId}</clientID>\n"; // FIXME
$message .= "\t\t\t\t<CreditCardInfo>\n";
$message .= "\t\t\t\t\t<cardNumber>{$this->Cc}</cardNumber>\n";
$message .= "\t\t\t\t\t<expiryDate>{$this->ExpiryDate}</expiryDate>\n";
if ($this->Cvv) // Provided with CVV/CV2 number
$message .= "\t\t\t\t\t<cvv>{$this->Cvv}</cvv>\n";
if ($this->NameOnCard) // Provided with credit card holder name
$message .= "\t\t\t\t\t<cardHolderName>{$this->NameOnCard}</cardHolderName>\n";
$message .= "\t\t\t\t</CreditCardInfo>\n";
$message .= "\t\t\t\t<amount>$cents</amount>\n";
$message .= "\t\t\t\t<currency>{$this->ChargeCurrency}</currency>\n";
if ($this->Repeat == SECUREPAY_REPEAT_DAILY) {
$message .= "\t\t\t\t<periodicType>2</periodicType>\n";
if ($this->RepeatInterval)