├── Nacha.php └── README.md /Nacha.php: -------------------------------------------------------------------------------- 1 | addDetailLine($paymentinfo); 82 | return true; 83 | } 84 | 85 | // Takes money from your account and puts it into someone elses. 86 | public function addCredit($paymentinfo){ 87 | if(!is_array($paymentinfo))return false; 88 | if(!isset($paymentinfo['Transcode'])){ 89 | if($paymentinfo['AccountType']){ 90 | if($paymentinfo['AccountType'] == 'CHECKING'){ 91 | $paymentinfo['Transcode'] = '22'; 92 | }elseif($paymentinfo['AccountType'] == 'SAVINGS'){ 93 | $paymentinfo['Transcode'] = '32'; 94 | }else{ 95 | return false; 96 | } 97 | }else{ 98 | $paymentinfo['Transcode'] = '22'; 99 | } 100 | } 101 | $this->addDetailLine($paymentinfo); 102 | return true; 103 | } 104 | 105 | private function addDetailLine($paymentinfo){ 106 | if(!$paymentinfo['AccountNumber'] || !$paymentinfo['TotalAmount'] || !$paymentinfo['BankAccountNumber'] || !$paymentinfo['RoutingNumber'] || !$paymentinfo['FormattedName'] || !$paymentinfo['AccountType']){ 107 | return false; 108 | } 109 | $paymentinfo['TranId'] = $this->tranid+1; 110 | if($this->createDetailRecord($paymentinfo)){ 111 | array_push($this->processedRecords, $paymentinfo); 112 | $this->tranid++; 113 | return true; 114 | }else{ 115 | $paymentinfo['TranId'] = false; 116 | array_push($this->errorRecords, $paymentinfo); 117 | return false; 118 | } 119 | return true; 120 | } 121 | 122 | public function setCompanyId($companyId){ 123 | $this->companyId = $companyId; 124 | return $this; 125 | } 126 | 127 | public function setSettlementAccount($settlementAccount){ 128 | $this->settlementAccount = $settlementAccount; 129 | return $this; 130 | } 131 | 132 | public function setFileID($fileId){ 133 | $this->fileId = $fileId; 134 | return $this; 135 | } 136 | 137 | public function setBankRT($rt){ 138 | $this->bankrt = $rt; 139 | return $this; 140 | } 141 | 142 | public function setOriginatingBank($originatingBank){ 143 | $this->originatingBank = $originatingBank; 144 | return $this; 145 | } 146 | 147 | public function setFileModifier($filemodifier){ 148 | $this->filemodifier = $filemodifier; 149 | return $this; 150 | } 151 | 152 | public function setCompanyName($companyName){ 153 | $this->companyName = $companyName; 154 | return $this; 155 | } 156 | 157 | public function setServiceClassCode($scc){ 158 | $this->scc = $scc; 159 | return $this; 160 | } 161 | 162 | public function setSECCode($sec){ 163 | $this->sec = $sec; 164 | return $this; 165 | } 166 | 167 | public function setRecordSize($size){ 168 | $this->recordsize = $size; 169 | return $this; 170 | } 171 | 172 | public function setBlockingFactor($block){ 173 | $this->blockingfactor = $block; 174 | return $this; 175 | } 176 | 177 | public function setFormatCode($code){ 178 | $this->formatcode = $code; 179 | return $this; 180 | } 181 | 182 | public function setReferenceCode($code){ 183 | $this->referencecode = $code; 184 | return $this; 185 | } 186 | 187 | public function setBatchInfo($batchinfo){ 188 | $this->batchInfo = $batchinfo; 189 | return $this; 190 | } 191 | 192 | public function setDescription($des=false, $date=false){ 193 | if($des)$this->description = $des; 194 | if($date)$this->descriptionDate = date('M d',strtotime($date)); 195 | return $this; 196 | } 197 | 198 | public function setEntryDate($date){ 199 | $this->entryDate = date('ymd',strtotime($date)); 200 | return $this; 201 | } 202 | 203 | public function setPaymentTypeCode($type){ 204 | $this->paymenttypecode = $type; 205 | return $this; 206 | } 207 | 208 | public function generateFile($filemodifier=false){ 209 | if($filemodifier)$this->setFileModifier($filemodifier); 210 | $this->createFileHeader(); 211 | $this->createBatchHeader(); 212 | $this->createBatchFooter(); 213 | $this->createFileFooter(); 214 | if(!$this->validFileHeader){ 215 | throw new Exception('Invalid File Header'); 216 | } 217 | if(!$this->validBatchHeader){ 218 | throw new Exception('Invalid Batch Header'); 219 | } 220 | if(!$this->validBatchFooter){ 221 | throw new Exception('Invalid Batch Footer'); 222 | } 223 | if(!$this->validFileFooter){ 224 | throw new Exception('Invalid File Footer'); 225 | } 226 | $this->fileContents = $this->fileHeader.$this::LINES_GLUE.$this->batchHeader.$this::LINES_GLUE.$this->batchLines.$this->batchFooter.$this::LINES_GLUE.$this->fileFooter; 227 | return true; 228 | } 229 | 230 | private function createFileHeader(){ 231 | $this->fileHeader = '101 '.$this->bankrt.$this->fileId.date('ymdHi').$this->filemodifier.$this->recordsize.$this->blockingfactor.$this->formatcode.$this->formatText($this->originatingBank,23).$this->formatText($this->companyName,23).$this->formatText($this->referencecode,8); 232 | if(strlen($this->fileHeader) == 94) $this->validFileHeader = true; 233 | return $this; 234 | } 235 | 236 | private function createBatchHeader(){ 237 | $this->batchHeader = '5'.$this->scc.$this->formatText($this->companyName,16).$this->formatText($this->batchInfo,20).$this->companyId.$this->sec.$this->formatText($this->description,10).$this->formatText($this->descriptionDate,6).$this->entryDate.' 1'.substr($this->bankrt,0,8).$this->formatNumeric($this->batchNumber,7); 238 | if(strlen($this->batchHeader) == 94) $this->validBatchHeader = true; 239 | return $this; 240 | } 241 | 242 | private function createDetailRecord($info){ 243 | $line = '6'.$info['Transcode'].$info['RoutingNumber'].$this->formatText($info['BankAccountNumber'],17).$this->formatNumeric(number_format($info['TotalAmount'],2),10).$this->formatText($info['AccountNumber'],15).$this->formatText($info['FormattedName'],22). $this->formatText($this->paymenttypecode,2). '0'.substr($this->bankrt,0,8).$this->formatNumeric($info['TranId'],7); 244 | if(strlen($line) == 94){ 245 | $this->batchLines .= $line.$this::LINES_GLUE; 246 | $this->detailRecordCount++; 247 | $this->routingHash += (int)substr($info['RoutingNumber'],0,8); 248 | if($info['Transcode'] == '27' || $info['Transcode'] == '37'){ 249 | $this->debitTotal += (float)$info['TotalAmount']; 250 | }else{ 251 | $this->creditTotal += (float)$info['TotalAmount']; 252 | } 253 | return true; 254 | } 255 | return false; 256 | } 257 | 258 | private function createBatchFooter(){ 259 | $this->batchFooter = '8'.$this->scc.$this->formatNumeric($this->detailRecordCount,6).$this->formatNumeric($this->routingHash,10).$this->formatNumeric(number_format($this->debitTotal,2),12).$this->formatNumeric(number_format($this->creditTotal,2),12).$this->formatText($this->companyId,10).$this->formatText('',25).substr($this->bankrt,0,8).$this->formatNumeric($this->batchNumber,7); 260 | if(strlen($this->batchFooter) == 94) $this->validBatchFooter = true; 261 | return $this; 262 | } 263 | 264 | private function createFileFooter(){ 265 | $linecount = $this->detailRecordCount+4; 266 | $blocks = ceil(($linecount)/10); 267 | $this->fileFooter = '9'.$this->formatNumeric('1',6).$this->formatNumeric($blocks,6).$this->formatNumeric($this->detailRecordCount,8).$this->formatNumeric($this->routingHash,10).$this->formatNumeric(number_format($this->debitTotal,2),12).$this->formatNumeric(number_format($this->creditTotal,2),12).$this->formatText('',39); 268 | if(strlen($this->fileFooter) == 94) $this->validFileFooter = true; 269 | // Add any additional '9' lines to get something evenly divisable by 10. 270 | $fillersToAdd = ($blocks*10)-$linecount; 271 | for($i=0;$i<$fillersToAdd;$i++){ 272 | $this->fileFooter .= $this::LINES_GLUE.str_pad('', 94,'9'); 273 | } 274 | return $this; 275 | } 276 | 277 | private function formatText($txt, $spaces){ 278 | return substr(str_pad(strtoupper($txt), $spaces, ' ', STR_PAD_RIGHT),0,$spaces); 279 | } 280 | 281 | private function formatNumeric($nums, $spaces){ 282 | return substr(str_pad(str_replace(array('.',','),'',(string)$nums), $spaces, '0', STR_PAD_LEFT),($spaces)*-1); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Create NACHA files in PHP 2 | 3 | Supports debit (collecting money from others) and credit (transferring money to others) transactions. 4 | If you need more functionality, I'm available for contract work at sparrish@nodeping.com 5 | 6 | ## Usage: 7 | 8 | ```php 9 | include 'Nacha.php'; 10 | $nacha = new NachaFile(); 11 | $nacha->setBankRT('123456789')// Your bank's routing number 12 | ->setCompanyId('9876543210')// Usually your company Fed tax id with something the bank tells you to put on the front. 13 | ->setSettlementAccount('44444444444') // Your bank account you want the money to go into 14 | ->setFileID('9876543210')// Probably the same as your Company ID but your bank will tell you what this should be. 15 | ->setOriginatingBank('BANK ON IT')// Text name of your bank 16 | ->setFileModifier('A')// Usually just A - for the first file of the day. Change to 'B' for second file of the day and so on. 17 | ->setCompanyName('MY COMPANY')//16 chars - your company name 18 | ->setBatchInfo('Monthly Subscriptions') // Text description for the batch 19 | ->setDescription('Subscription', '05/17/2012') // Description shown on customers statements and date of invoice 20 | ->setEntryDate(date('m/d/Y')); // The day you want the payments to be processed. This example shows today. 21 | ``` 22 | 23 | Then push in the customer payments. 24 | Each customer record should look like: 25 | 26 | ```php 27 | $payment = array("AccountNumber"=>'1234567', // The customer's CRM account number (not bank account number) 28 | "TotalAmount"=>30.00, // Amount they are paying you if it's a debit - or that you're paying them if it's a credit. 29 | "BankAccountNumber"=>'123456789', // Customer's bank account number 30 | "RoutingNumber"=>'987654321', // Customer's bank routing number 31 | "FormattedName"=>'Joe Smith', // Customer's name 32 | "AccountType"=> 'CHECKING'); // Could be 'CHECKING' or 'SAVINGS' - customer's bank account type 33 | 34 | // Add a debit - taking money from someone and puting it in your account 35 | if(!$nacha->addDebit($payment)){ 36 | // Error adding this debit. Must be something wrong. Check the $nacha->errorRecords. 37 | } 38 | 39 | // You can safely mix debits and credits in the same batch. 40 | // Add a credit - sending your money to someone elses bank account 41 | if(!$nacha->addCredit($payment)){ 42 | // Error adding this credit. Must be something wrong. Check the $nacha->errorRecords. 43 | } 44 | 45 | // Generate the NACHA file contents 46 | try{ 47 | $nacha->generateFile(); 48 | // Put the file contents on the file system 49 | if(!file_put_contents('./ACH_MyCompany_NACHA_file.txt', $nacha->fileContents)){ 50 | throw new Exception('Unable to save NACHA file'); 51 | } 52 | }catch(Exception $e){ 53 | // Something went wrong with the file generation 54 | print_r($e->getMessage()); 55 | } 56 | ``` 57 | 58 | Copyright 2012 sparrish@nodeping.com 59 | --------------------------------------------------------------------------------