Odyssey
aACHValidate.i
1 <?php
2 /**
3  * FILE: aACHValidate.i
4  *
5  * This file contains functions use to validate an ACH file. Mostly
6  * the functions will parse strings into the correct structure
7  * and return that structure. An error code and error will be returned
8  * if any mandatory data is missing. A warning will be returned if missing
9  * and required data.
10  *
11  * Entry Detail Transaction Code values:
12  * 22 Checking Credit Live
13  * 23 Checking Credit Prenote
14  * 24 Checking Credit Child Support prenote
15  * 27 Checking Debit Live
16  * 28 Checking Debit Prenote
17  * 29 Checking Debit Child Support prenote
18  * 32 Savings Credit Live
19  * 33 Savings Credit Prenote
20  * 34 Savings Credit Child Support prenote
21  * 37 Savings Debit Live
22  * 38 Savings Debit Prenote
23  * 39 Savings Debit Child Support prenote
24  * 52 Loan Credit Live
25  * 53 Loan Credit Prenote
26  */
27 //$acceptableACHCharacters = ["^", "!", "_", "@", "#", "$", "%", "&", ",", "*", ":", ".", "/", "+", "-"];
28 define( "ALPHAMERIC_STRING", '/^[\w\s\^\!\_\@\#\$\%\,\*\:\.\/\+\-]+$/' ); // use single quotes so PHP doesn't do any string replacement
29 define( "ACH_NOT_ALLOWED", '/[^\w\s\^\!\_\@\#\$\%\,\*\:\.\/\+\-]/' ); // used to strip out not allowed characters
30 
31 /**
32  * GetFileHeaderRecord
33  * @uses Return the File Header Record
34  *
35  * @param array $pAdmEnv: Admin environment information.
36  * @param string $strRecord: The fixed length string holding the file header.
37  *
38  * @return object $returnInfo: the array with the return information
39  */
40 function GetFileHeaderRecord( $pAdmEnv, $strRecord ) {
41  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
42 
43  try {
44  $returnData["RecordTypeCode"] = substr( $strRecord, 0, 1 );
45  $returnData["PriorityCode"] = intval( substr( $strRecord, 1, 2 ) );
46  $returnData["ImmediateDestination"] = substr( $strRecord, 3, 10 );
47  $returnData["ImmediateOrigin"] = substr( $strRecord, 13, 10 );
48  $returnData["FileCreateDate"] = substr( $strRecord, 23, 6 );
49  $returnData["FileCreateTime"] = substr( $strRecord, 29, 4 );
50  $returnData["FileIDModifier"] = substr( $strRecord, 33, 1 );
51  $returnData["RecordSize"] = substr( $strRecord, 34, 3 );
52  $returnData["BlockingFactor"] = substr( $strRecord, 37, 2 );
53  $returnData["FormatCode"] = substr( $strRecord, 39, 1 );
54  $returnData["ImmediateDestinationName"] = substr( $strRecord, 40, 23 );
55  $returnData["ImmediateOriginName"] = substr( $strRecord, 63, 23 );
56  $returnData["ReferenceCode"] = substr( $strRecord, 86, 8 );
57 
58  $return["data"] = $returnData;
59 
60  } catch (Exception $ex) {
61  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
62  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
63 
64  $return["code"] = "999";
65  $return["error"][] = "{$logInfo["error"]} ({$logInfo["code"]})";
66  }
67 
68  return $return;
69 } // end GetFileHeaderRecord
70 
71 /**
72  * ValidateFileHeaderRecord
73  * @uses Validate the File Header Record. Log the error to the terminal output if there is a failure.
74  *
75  * @param array $pAdmEnv: Admin environment information.
76  * @param array $pRecord: The file header information.
77  *
78  * @return array with errors and warnings
79  */
80 function ValidateFileHeaderRecord( $pAdmEnv, $pRecord ) {
81  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
82 
83  try {
84  if ( $pRecord["RecordTypeCode"] != "1" ) {
85  $return["error"][] = "Invalid ACH File Header Record Type Code";
86  }
87 
88  if ( strlen( trim( $pRecord["PriorityCode"] ) ) > 0 && !is_numeric( $pRecord["PriorityCode"] ) ) {
89  $return["error"][] = "Invalid ACH File Header Priority Code";
90  } else if ( strlen( trim( $pRecord["PriorityCode"] ) ) == 0 ) {
91  $return["warning"][] = "ACH File Header Priority Code is blank - a number is recommended";
92  }
93 
94  if ( !preg_match( "/ \d{9}/", $pRecord["ImmediateDestination"] ) ) {
95  $return["error"][] = "Invalid ACH File Header Immediate Destination";
96  }
97 
98  if ( !preg_match( "/[ d]\d{9}/", $pRecord["ImmediateOrigin"] ) ) {
99  $return["error"][] = "Invalid ACH File Header Immediate Origin";
100  }
101 
102  if ( !preg_match( "/\d{6}/", $pRecord["FileCreateDate"] ) ) {
103  $return["error"][] = "Invalid ACH File Header File Create Date";
104  } else {
105  // some rudimentary checking
106  $today = date( "ymd" );
107  if ( $today > $pRecord["FileCreateDate"] ) {
108  $return["warning"][] = "Invalid ACH File Header File Create Date old - today: $today (vs {$pRecord["FileCreateDate"]})";
109  }
110 
111  // test to make sure YYMMDD
112  $YY = substr( $pRecord["FileCreateDate"], 0, 2 );
113  $MM = substr( $pRecord["FileCreateDate"], 2, 2 );
114  $DD = substr( $pRecord["FileCreateDate"], 4, 2 );
115 
116  if ( $MM < 1 || $MM > 12 ) {
117  $return["error"][] = "Invalid ACH File Header File Create Date Month";
118  } else if ( $DD < 1 || $DD > 31 ) {
119  $return["error"][] = "Invalid ACH File Header File Create Date Day";
120  }
121  }
122 
123  if ( !preg_match( "/\d{4}/", $pRecord["FileCreateTime"] ) ) {
124  $return["error"][] = "Invalid ACH File Header File Create Time";
125  } else {
126  // some rudimentary checking
127  $HH = substr( $pRecord["FileCreateTime"], 0, 2 );
128  $MM = substr( $pRecord["FileCreateTime"], 2, 2 );
129 
130  if ( $HH < 1 || $HH > 24 ) {
131  $return["error"][] = "Invalid ACH File Header File Create Time Hour";
132  } else if ( $MM < 1 || $MM > 59 ) {
133  $return["error"][] = "Invalid ACH File Header File Create Time Minute";
134  }
135  }
136 
137  if ( !preg_match( "/[A-Z,0-9]/", $pRecord["FileIDModifier"] ) ) {
138  $return["error"][] = "Invalid ACH File Header File ID Modifier";
139  }
140 
141  if ( !is_numeric( $pRecord["RecordSize"] ) ) {
142  $return["error"][] = "Invalid ACH File Header Record Size";
143  }
144 
145  if ( !is_numeric( $pRecord["BlockingFactor"] ) ) {
146  $return["error"][] = "Invalid ACH File Header Blocking Factor";
147  }
148 
149  if ( $pRecord["FormatCode"] != 1 ) {
150  $return["error"][] = "Invalid ACH File Header Format Code";
151  }
152 
153  if ( !strlen( trim ( $pRecord["ImmediateDestinationName"] ) ) ) {
154  $return["error"][] = "Invalid ACH File Header Immediate Destination Name";
155  } else {
156  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["ImmediateDestinationName"] ) ) {
157  $return["error"][] = "Invalid ACH File Header Immediate Destination Name character";
158  }
159  }
160 
161  if ( !strlen( trim ( $pRecord["ImmediateOriginName"] ) ) ) {
162  $return["error"][] = "Invalid ACH File Header Immediate Origin Name";
163  } else {
164  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["ImmediateOriginName"] ) ) {
165  $return["error"][] = "Invalid ACH File Header Immediate Origin Name character";
166  }
167  }
168 
169  if ( strlen( trim ( $pRecord["ReferenceCode"] ) ) != 0 || strlen( $pRecord["ReferenceCode"] ) != 8 ) {
170  $return["error"][] = "Invalid ACH File Header Reference Code";
171  }
172  } catch (Exception $ex) {
173  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
174  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
175 
176  // return last error
177  $return["error"][] = "Exception: {$logInfo["error"]} ({$logInfo["code"]})";
178  }
179 
180  return $return;
181 } // ValidateFileHeaderRecord
182 
183 /**
184  * GetCompanyHeaderRecord
185  * @uses Return the Company Header Record
186  *
187  * @param array $pAdmEnv: Admin environment information.
188  * @param string $strRecord: The fixed length string holding the company header.
189  *
190  * @return object $returnInfo: the array with the return information
191  */
192 function GetCompanyHeaderRecord( $pAdmEnv, $strRecord ) {
193  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
194 
195  try {
196  $returnData["RecordTypeCode"] = substr( $strRecord, 0, 1 );
197  $returnData["ServiceClassCode"] = substr( $strRecord, 1, 3 );
198  $returnData["CompanyName"] = substr( $strRecord, 4, 16 );
199  $returnData["CompanyData"] = substr( $strRecord, 20, 20 );
200  $returnData["CompanyID"] = substr( $strRecord, 40, 10 );
201  $returnData["StandardEntryClassCode"] = substr( $strRecord, 50, 3 );
202  $returnData["CompanyEntryDescription"] = substr( $strRecord, 53, 10 );
203  $returnData["CompanyDescriptiveDate"] = substr( $strRecord, 63, 6 );
204  $returnData["EffectiveEntryDate"] = substr( $strRecord,69, 6 );
205  $returnData["SettlementDate"] = substr( $strRecord, 75, 3 );
206  $returnData["OriginatorStatusCode"] = substr( $strRecord, 78, 1 );
207  $returnData["OriginatingDFI"] = substr( $strRecord, 79, 8 );
208  $returnData["BatchNumber"] = substr( $strRecord, 87, 7 );
209 
210  $return["data"] = $returnData;
211 
212  } catch (Exception $ex) {
213  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
214  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
215 
216  $return["code"] = "999";
217  $return["error"] = "{$logInfo["error"]} ({$logInfo["code"]})";
218  }
219 
220  return $return;
221 } // end GetCompanyHeaderRecord
222 
223 /**
224  * ValidateCompanyHeaderRecord
225  * @uses Validate the Company Header Record. Log the error to the terminal output if there is a failure.
226  *
227  * @param array $pAdmEnv: Admin environment information.
228  * @param array $pRecord: The company header information.
229  *
230  * @return array with errors and warnings
231  */
232 function ValidateCompanyHeaderRecord( $pAdmEnv, $pRecord ) {
233  $standardEntryClassCodes = ["CIE", "DNE", "ENR", "PPD", "RCK", "TEL", "WEB", // consumer codes
234  "ADV", "COR", // other codes
235  "ACK", "ATX", "CCD", "CTX", // corporate codes
236  "ARC", "POP", "BOC", "TRC", "TRX", "XCK", "IAT", // consumer/corporate codes
237  "MTE", "POS", "SHR"]; // debit card/pos entries
238 
239  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
240 
241  try {
242  if ( $pRecord["RecordTypeCode"] != "5" ) {
243  $return["error"][] = "Invalid ACH Company Header Record Type Code";
244  }
245 
246  if ( strlen( trim( $pRecord["ServiceClassCode"] ) ) == 0 || !is_numeric( $pRecord["ServiceClassCode"] ) ) {
247  $return["error"][] = "Invalid ACH Company Header Service Class Code";
248  } else {
249  $code = trim( $pRecord["ServiceClassCode"] );
250  if ( $code != 200 && $code != 220 & $code != 225 ) {
251  $return["error"][] = "ACH Company Header Service Class Code is an unexpected number";
252  }
253  }
254 
255  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["CompanyName"] ) ) {
256  $return["error"][] = "Invalid ACH Company Header Company Name";
257  }
258 
259  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["CompanyData"] ) ) {
260  $return["error"][] = "Invalid ACH Company Header Company Data";
261  }
262 
263  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["CompanyID"] ) ) {
264  $return["error"][] = "Invalid ACH Company Header CompanyID";
265  }
266 
267  if ( !in_array( $pRecord["StandardEntryClassCode"], $standardEntryClassCodes ) ) {
268  $return["error"][] = "Invalid ACH Company Header Standard Entry Class Code: {$pRecord["StandardEntryClassCode"]}";
269  }
270 
271  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["CompanyEntryDescription"] ) ) {
272  $return["error"][] = "Invalid ACH Company Header Company Entry Description";
273  }
274 
275  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["CompanyDescriptiveDate"] ) ) {
276  $return["error"][] = "Invalid ACH Company Header Company Descriptive Date";
277  }
278 
279  if ( !preg_match( "/\d{6}/", $pRecord["EffectiveEntryDate"] ) ) {
280  $return["error"][] = "Invalid ACH Company Header Effective Entry Date";
281  } else {
282  // some rudimentary checking
283  $today = date( "ymd" );
284  if ( $today > $pRecord["EffectiveEntryDate"] ) {
285  $return["warning"][] = "Invalid ACH Company Header Effective Entry Date old - today: $today (vs {$pRecord["EffectiveEntryDate"]})";
286  }
287 
288  // test to make sure YYMMDD
289  $YY = substr( $pRecord["EffectiveEntryDate"], 0, 2 );
290  $MM = substr( $pRecord["EffectiveEntryDate"], 2, 2 );
291  $DD = substr( $pRecord["EffectiveEntryDate"], 4, 2 );
292 
293  if ( $MM < 1 || $MM > 12 ) {
294  $return["error"][] = "Invalid ACH Company Header Effective Entry Date Month";
295  } else if ( $DD < 1 || $DD > 31 ) {
296  $return["error"][] = "Invalid ACH Company Header Effective Entry Date Day";
297  }
298  }
299 
300  if ( strlen( trim ( $pRecord["SettlementDate"] ) ) != 0 || strlen( $pRecord["SettlementDate"] ) != 3 ) {
301  $return["warning"][] = "Invalid ACH Company Header Settlement Date (originator should leave blank)";
302  }
303 
304  if ( $pRecord["OriginatorStatusCode"] != "1" ) {
305  $return["error"][] = "Invalid ACH Company Header Originator Status Code";
306  }
307 
308  if ( !preg_match( "/\d{8}/", $pRecord["OriginatingDFI"] ) ) {
309  $return["error"][] = "Invalid ACH Company Header Originating DFI";
310  }
311 
312  if ( !preg_match( "/\d{7}/", $pRecord["BatchNumber"] ) ) {
313  $return["error"][] = "Invalid ACH Company Header Batch Number";
314  }
315  } catch (Exception $ex) {
316  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
317  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
318 
319  // return last error
320  $return["error"][] = "Exception: {$logInfo["error"]} ({$logInfo["code"]})";
321  }
322 
323  return $return;
324 } // end ValidateCompanyHeaderRecord
325 
326 /**
327  * GetEntryDetailRecord
328  * @uses Return an Entry Detail Record
329  *
330  * @param array $pAdmEnv: Admin environment information.
331  * @param string $strRecord: The fixed length string holding the entry detail record.
332  *
333  * @return object $returnInfo: the array with the return information
334  */
335 function GetEntryDetailRecord( $pAdmEnv, $strRecord ) {
336  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
337 
338  try {
339  $returnData["RecordTypeCode"] = substr( $strRecord, 0, 1 );
340  $returnData["TransactionCode"] = substr( $strRecord, 1, 2 );
341  $returnData["ReceivingDFI"] = substr( $strRecord, 3, 8 );
342  $returnData["CheckDigit"] = substr( $strRecord, 11, 1 );
343  $returnData["DFIAccountNumber"] = substr( $strRecord, 12, 17 );
344  $returnData["Amount"] = substr( $strRecord, 29, 10 );
345  $returnData["IndividualIdentificationNumber"] = substr( $strRecord, 39, 15 );
346  $returnData["IndividualName"] = substr( $strRecord, 54, 22 );
347  $returnData["DiscretionaryData"] = substr( $strRecord,76, 2 );
348  $returnData["AddendaRecordIndicator"] = substr( $strRecord, 78, 1 );
349  $returnData["TraceNumber"] = substr( $strRecord, 79, 15 );
350 
351  $return["data"] = $returnData;
352 
353  } catch (Exception $ex) {
354  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
355  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
356 
357  $return["code"] = "999";
358  $return["error"] = "{$logInfo["error"]} ({$logInfo["code"]})";
359  }
360 
361  return $return;
362 } // end GetEntryDetailRecord
363 
364 /**
365  * ValidateEntryDetailRecord
366  * @uses Validate the Entry Detail Record. Log the error to the terminal output if there is a failure.
367  *
368  * @param array $pAdmEnv: Admin environment information.
369  * @param array $pRecord: The entry detail information.
370  *
371  * @return array with errors and warnings
372  */
373 function ValidateEntryDetailRecord( $pAdmEnv, $pRecord, $pHeader ) {
374  $transactionCodes = [22, 23, 24, 27, 28, 29, 32, 33, 34, 37, 38, 39, 42, 47, 52, 53];
375 
376  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
377 
378  try {
379  if ( $pRecord["RecordTypeCode"] != "6" ) {
380  $return["error"][] = "Invalid ACH Entry Detail Record Type Code";
381  }
382 
383  if ( strlen( trim( $pRecord["TransactionCode"] ) ) == 0 || !is_numeric( $pRecord["TransactionCode"] ) ) {
384  $return["error"][] = "Invalid ACH Entry Detail Transaction Code";
385  } else {
386  $code = trim( $pRecord["TransactionCode"] );
387  if ( !in_array( $code, $transactionCodes ) ) {
388  $return["error"][] = "ACH Entry Detail Transaction Code is an unexpected number: {$pRecord["TransactionCode"]}";
389  print_r( $pRecord );
390  }
391  }
392 
393  if ( !preg_match( "/^[\d]+$/", $pRecord["ReceivingDFI"] ) ) {
394  $return["error"][] = "Invalid ACH Entry Detail Receiving DFI";
395  }
396 
397  if ( !preg_match( "/\d/", $pRecord["CheckDigit"] ) ) {
398  $return["error"][] = "Invalid ACH Entry Detail Check Digit";
399  }
400 
401  if ( !strlen( trim( $pRecord["DFIAccountNumber"] ) ) ) {
402  $return["warning"][] = "Missing ACH Entry Detail DFI Account Number";
403  }
404 
405  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["DFIAccountNumber"] ) ) {
406  $return["error"][] = "Invalid ACH Entry Detail DFI Account Number";
407  }
408 
409  if ( !preg_match( "/\d{10}/", $pRecord["Amount"] ) ) {
410  $return["error"][] = "Invalid ACH Entry Detail Amount";
411  }
412 
413  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["IndividualIdentificationNumber"] ) ) {
414  $return["error"][] = "Invalid ACH Entry Detail Individual Identification Number";
415  }
416 
417  // this can also be the receiving company name
418  if ( !strlen( trim( $pRecord["IndividualName"] ) ) ) {
419  $return["warning"][] = "Missing ACH Entry Detail Individual Name";
420  }
421 
422  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["IndividualName"] ) ) {
423  $return["error"][] = "Invalid ACH Entry Detail Individual Name";
424  }
425 
426  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["DiscretionaryData"] ) ) {
427  $return["error"][] = "Invalid ACH Entry Detail Discretionary Data";
428  }
429 
430  // Discretionary Data must be S (Single) or R (Recurring) for WEB transactions.
431  if ( $pHeader['StandardEntryClassCode'] == "WEB" && !preg_match("/^[SR]\s/", $pRecord["DiscretionaryData"] ) ) {
432  $return["error"][] = "Invalid ACH Entry Detail Discretionary Data for Standard Entry Class Code WEB";
433  }
434 
435  if ( !($pRecord["AddendaRecordIndicator"] == 0 || $pRecord["AddendaRecordIndicator"] == 1) ) {
436  $return["error"][] = "Invalid ACH Entry Detail Addenda Record Indicator";
437  }
438 
439  if ( !preg_match( "/\d{15}/", $pRecord["TraceNumber"] ) ) {
440  $return["error"][] = "Invalid ACH Entry Detail Trace Number";
441  }
442 
443  } catch (Exception $ex) {
444  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
445  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
446 
447  // return last error
448  $return["error"][] = "Exception: {$logInfo["error"]} ({$logInfo["code"]})";
449  }
450 
451  return $return;
452 } // end ValidateEntryDetailRecord
453 
454 /**
455  * GetAddendaRecord
456  * @uses Return an Addenda Record
457  *
458  * @param array $pAdmEnv: Admin environment information.
459  * @param string $strRecord: The fixed length string holding the addenda record.
460  *
461  * @return object $returnInfo: the array with the return information
462  */
463 function GetAddendaRecord( $pAdmEnv, $strRecord ) {
464  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
465 
466  try {
467  $returnData["RecordTypeCode"] = substr( $strRecord, 0, 1 );
468  $returnData["AddendaTypeCode"] = substr( $strRecord, 1, 2 );
469  $returnData["PaymentRelatedInformation"] = substr( $strRecord, 3, 80 );
470  $returnData["AddendaSequenceNumber"] = substr( $strRecord, 83, 4 );
471  $returnData["EntryDetailSequenceNumber"] = substr( $strRecord, 87, 7 );
472 
473  $return["data"] = $returnData;
474 
475  } catch (Exception $ex) {
476  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
477  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
478 
479  $return["code"] = "999";
480  $return["error"] = "{$logInfo["error"]} ({$logInfo["code"]})";
481  }
482 
483  return $return;
484 } // end GetAddendaRecord
485 
486 /**
487  * ValidateAddendaRecord
488  * @uses Validate the Addenda Record. Log the error to the terminal output if there is a failure.
489  *
490  * @param array $pAdmEnv: Admin environment information.
491  * @param array $pRecord: The addenda information.
492  * @param number $pEntrySequenceNumber: The entry sequence number related to this entry
493  *
494  * @return array with errors and warnings
495  */
496 function ValidateAddendaRecord( $pAdmEnv, $pRecord, $pEntrySequenceNumber ) {
497  $allowedAddendaTypeCodes = ["02", "05", "98", "99"];
498 
499  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
500 
501  try {
502  if ( $pRecord["RecordTypeCode"] != "7" ) {
503  $return["error"][] = "Invalid ACH Addenda Record Type Code";
504  }
505 
506  if ( !in_array( $pRecord["AddendaTypeCode"], $allowedAddendaTypeCodes ) ) {
507  $return["error"][] = "Invalid ACH Addenda Type Code";
508  }
509 
510  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["PaymentRelatedInformation"] ) ) {
511  $return["error"][] = "Invalid ACH Addenda Payment Related Information";
512  }
513 
514  if ( !preg_match( "/\d{4}/", $pRecord["AddendaSequenceNumber"] ) ) {
515  $return["error"][] = "Invalid ACH Addenda Sequence Number";
516  }
517 
518  if ( !preg_match( "/\d{7}/", $pRecord["EntryDetailSequenceNumber"] ) ) {
519  $return["error"][] = "Invalid ACH Addenda Entry Detail Sequence Number";
520  }
521 
522  if ( $pRecord["EntryDetailSequenceNumber"] != $pEntrySequenceNumber ) {
523  print "Test: {$pRecord["EntryDetailSequenceNumber"]}, $pEntrySequenceNumber";
524  $return["error"][] = "Unexpected ACH Addenda Entry Detail Sequence Number";
525  }
526 
527  } catch (Exception $ex) {
528  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
529  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
530 
531  // return last error
532  $return["error"][] = "Exception: {$logInfo["error"]} ({$logInfo["code"]})";
533  }
534 
535  return $return;
536 } // end ValidateAddendaRecord
537 
538 /**
539  * GetCompanyControlRecord
540  * @uses Return the Company Control Record
541  *
542  * @param array $pAdmEnv: Admin environment information.
543  * @param string $strRecord: The fixed length string holding the company control record.
544  *
545  * @return object $returnInfo: the array with the return information
546  */
547 function GetCompanyControlRecord( $pAdmEnv, $strRecord ) {
548  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
549 
550  try {
551  $returnData["RecordTypeCode"] = substr( $strRecord, 0, 1 );
552  $returnData["ServiceClassCode"] = substr( $strRecord, 1, 3 );
553  $returnData["EntryAddendaCount"] = substr( $strRecord, 4, 6 );
554  $returnData["EntryHash"] = substr( $strRecord, 10, 10 );
555  $returnData["TotalDebitEntryDollarAmount"] = substr( $strRecord, 20, 12 );
556  $returnData["TotalCreditEntryDollarAmount"] = substr( $strRecord, 32, 12 );
557  $returnData["CompanyID"] = substr( $strRecord, 44, 10 );
558  $returnData["MessageAuthenticationCode"] = substr( $strRecord, 54, 19 );
559  $returnData["Reserved"] = substr( $strRecord, 73, 6 );
560  $returnData["OriginatingDFIIdentification"] = substr( $strRecord, 79, 8 );
561  $returnData["BatchNumber"] = substr( $strRecord, 87, 7 );
562 
563  $return["data"] = $returnData;
564 
565  } catch (Exception $ex) {
566  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
567  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
568 
569  $return["code"] = "999";
570  $return["error"] = "{$logInfo["error"]} ({$logInfo["code"]})";
571  }
572 
573  return $return;
574 } // end GetCompanyControlRecord
575 
576 /**
577  * ValidateCompanyControlRecord
578  * @uses Validate the Company Control Record. Log the error to the terminal output if there is a failure.
579  *
580  * @param array $pAdmEnv: Admin environment information.
581  * @param array $pRecord: The company control information.
582  *
583  * @return array with errors and warnings
584  */
585 function ValidateCompanyControlRecord( $pAdmEnv, $pRecord ) {
586  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
587 
588  try {
589  if ( $pRecord["RecordTypeCode"] != "8" ) {
590  $return["error"][] = "Invalid ACH Company Control Record Type Code";
591  }
592 
593  if ( strlen( trim( $pRecord["ServiceClassCode"] ) ) == 0 || !is_numeric( $pRecord["ServiceClassCode"] ) ) {
594  $return["error"][] = "Invalid ACH Company Control Service Class Code";
595  } else {
596  $code = trim( $pRecord["ServiceClassCode"] );
597  if ( $code != 200 && $code != 220 & $code != 225 ) {
598  $return["error"][] = "ACH Company Control Service Class Code is an unexpected number";
599  }
600  }
601 
602  if ( !preg_match( "/\d{6}/", $pRecord["EntryAddendaCount"] ) ) {
603  $return["error"][] = "Invalid ACH Company Control Entry/Addenda Count";
604  }
605 
606  if ( !preg_match( "/\d{10}/", $pRecord["EntryHash"] ) ) {
607  $return["error"][] = "Invalid ACH Company Control Entry Hash";
608  }
609 
610  if ( !preg_match( "/\d{12}/", $pRecord["TotalDebitEntryDollarAmount"] ) ) {
611  $return["error"][] = "Invalid ACH Company Control Total Debit Entry dollar amount";
612  }
613 
614  if ( !preg_match( "/\d{12}/", $pRecord["TotalCreditEntryDollarAmount"] ) ) {
615  $return["error"][] = "Invalid ACH Company Control Total Credit Entry dollar amount";
616  }
617 
618  if ( !preg_match( ALPHAMERIC_STRING, $pRecord["CompanyID"] ) ) {
619  $return["error"][] = "Invalid ACH Company Control Company Idetntification";
620  }
621 
622  if ( strlen( trim ( $pRecord["MessageAuthenticationCode"] ) ) != 0 || strlen( $pRecord["MessageAuthenticationCode"] ) != 19 ) {
623  $return["error"][] = "Invalid ACH Company Control Message Authentication Code";
624  }
625 
626  if ( !preg_match( "/\s{6}/", $pRecord["Reserved"] ) ) {
627  $return["error"][] = "Invalid ACH Company Control Reserved Field";
628  }
629 
630  if ( !preg_match( "/\d{8}/", $pRecord["OriginatingDFIIdentification"] ) ) {
631  $return["error"][] = "Invalid ACH Company Control Originating DFI Identification";
632  }
633 
634  if ( !preg_match( "/\d{7}/", $pRecord["BatchNumber"] ) ) {
635  $return["error"][] = "Invalid ACH Company Control Batch Number";
636  }
637 
638  } catch (Exception $ex) {
639  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
640  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
641 
642  // return last error
643  $return["error"][] = "Exception: {$logInfo["error"]} ({$logInfo["code"]})";
644  }
645 
646  return $return;
647 } // end ValidateCompanyControlRecord
648 
649 /**
650  * GetFileControlRecord
651  * @uses Return the File Control Record
652  *
653  * @param array $pAdmEnv: Admin environment information.
654  * @param string $strRecord: The fixed length string holding the file control record.
655  *
656  * @return object $returnInfo: the array with the return information
657  */
658 function GetFileControlRecord( $pAdmEnv, $strRecord ) {
659  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
660 
661  try {
662  $returnData["RecordTypeCode"] = substr( $strRecord, 0, 1 );
663  $returnData["BatchCount"] = substr( $strRecord, 1, 6 );
664  $returnData["BlockCount"] = substr( $strRecord, 7, 6 );
665  $returnData["EntryAddendaCount"] = substr( $strRecord, 13, 8 );
666  $returnData["EntryHash"] = substr( $strRecord, 21, 10 );
667  $returnData["TotalDebitEntryDollarAmount"] = substr( $strRecord, 31, 12 );
668  $returnData["TotalCreditEntryDollarAmount"] = substr( $strRecord, 43, 12 );
669  $returnData["Reserved"] = substr( $strRecord, 55, 39 );
670 
671  $return["data"] = $returnData;
672 
673  } catch (Exception $ex) {
674  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
675  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
676 
677  $return["code"] = "999";
678  $return["error"] = "{$logInfo["error"]} ({$logInfo["code"]})";
679  }
680 
681  return $return;
682 } // end GetFileControlRecord
683 
684 /**
685  * ValidateFileControlRecord
686  * @uses Validate the File Control Record. Log the error to the terminal output if there is a failure.
687  *
688  * @param array $pAdmEnv: Admin environment information.
689  * @param array $pRecord: The file control information.
690  *
691  * @return array with errors and warnings
692  */
693 function ValidateFileControlRecord( $pAdmEnv, $pRecord ) {
694  $return = array("code" => "000", "warning" => array(), "error" => array(), "data" => array());
695 
696  try {
697  if ( $pRecord["RecordTypeCode"] != "9" ) {
698  $return["error"][] = "Invalid ACH File Control Record Type Code";
699  }
700 
701  if ( !preg_match( "/\d{6}/", $pRecord["BatchCount"] ) ) {
702  $return["error"][] = "Invalid ACH File Control Batch Count";
703  }
704 
705  if ( !preg_match( "/\d{6}/", $pRecord["BlockCount"] ) ) {
706  $return["error"][] = "Invalid ACH File Control Block Count";
707  }
708 
709  if ( !preg_match( "/\d{8}/", $pRecord["EntryAddendaCount"] ) ) {
710  $return["error"][] = "Invalid ACH File Control Entry/Addenda Count";
711  }
712 
713  if ( !preg_match( "/\d{12}/", $pRecord["TotalDebitEntryDollarAmount"] ) ) {
714  $return["error"][] = "Invalid ACH File Control Total Debit Entry Dollar Amount";
715  }
716 
717  if ( !preg_match( "/\d{12}/", $pRecord["TotalCreditEntryDollarAmount"] ) ) {
718  $return["error"][] = "Invalid ACH File Control Total Credit Entry Dollar Amount";
719  }
720  if ( !preg_match( "/\s{39}/", $pRecord["Reserved"] ) ) {
721  $return["error"][] = "Invalid ACH Company Control Reserved Field";
722  }
723 
724  } catch (Exception $ex) {
725  $logInfo = array( "error" => $ex->getMessage(), "code" => $ex->getCode() );
726  $pAdmEnv["SYSENV"]["logger"]->info( HCU_JsonEncode( $logInfo ) );
727 
728  // return last error
729  $return["error"][] = "Exception: {$logInfo["error"]} ({$logInfo["code"]})";
730  }
731 
732  return $return;
733 } // end ValidateFileControlRecord
734