Credit Policy

New Jersey Dispensary Credit Risk Report 2026

CCA Staff·September 9, 2026
New Jersey Dispensary Credit Risk Report 2026

<div class="njc-wrap" aria-label="NJ Cannabis Profit Margin & 280E Impact Calculator">
<div class="njc-card">
<div class="njc-head">
<h2 class="njc-title">NJ Cannabis Profit Margin &amp; 280E Impact Calculator</h2>
<p class="njc-subtitle">Estimate gross margin, operating margin, and after-tax profit under normal tax treatment vs. when <strong>IRC 280E</strong> applies. Enter your numbers, click calculate, and review the comparison below.</p>
</div>

<div class="njc-infobox" role="note" aria-label="Important context">
<div class="njc-infobox-title">How this works (simple estimate)</div>
<ul class="njc-infobox-list">
<li><strong>Normal tax treatment:</strong> taxable income  revenue  COGS  operating expenses.</li>
<li><strong>280E applies:</strong> operating expenses are not deductible; taxable income is estimated as <u>revenue  COGS</u>.</li>
<li>This is a planning tool, not tax advice. NJ specifics can vary by license type, accounting method, and what qualifies as COGS.</li>
</ul>
</div>

<form class="njc-form" aria-label="Calculator form">
<div class="njc-group">
<label class="njc-label" for="njc-period">Input period</label>
<select class="njc-select" id="njc-period" aria-label="Input period">
<option value="monthly">Monthly</option>
<option value="annual">Annual</option>
</select>
<div class="njc-help">Tip: If you enter monthly figures, results will also show an annualized view.</div>
</div>

<div class="njc-group">
<label class="njc-label" for="njc-revenue">Revenue ($)</label>
<input class="njc-input" id="njc-revenue" type="text" inputmode="decimal" placeholder="e.g., 250000" aria-label="Revenue in dollars" />
</div>

<div class="njc-group">
<label class="njc-label" for="njc-cogs">COGS  cost of goods sold ($)</label>
<input class="njc-input" id="njc-cogs" type="text" inputmode="decimal" placeholder="e.g., 120000" aria-label="COGS in dollars" />

<label class="njc-check">
<input id="njc-cogs-breakdown-toggle" type="checkbox" aria-label="Show optional COGS breakdown" />
<span>Show optional COGS breakdown</span>
</label>

<div class="njc-subpanel" id="njc-cogs-breakdown" hidden aria-label="COGS breakdown fields">
<div class="njc-help">Optional: these do not change the math unless you leave total COGS blank (then breakdown will be summed).</div>
<label class="njc-label" for="njc-cogs-inventory">Inventory / wholesale purchases ($)</label>
<input class="njc-input" id="njc-cogs-inventory" type="text" inputmode="decimal" placeholder="e.g., 70000" aria-label="Inventory or purchases COGS" />

<label class="njc-label" for="njc-cogs-directlab">Direct labor / production ($)</label>
<input class="njc-input" id="njc-cogs-directlab" type="text" inputmode="decimal" placeholder="e.g., 30000" aria-label="Direct labor COGS" />

<label class="njc-label" for="njc-cogs-other">Other COGS ($)</label>
<input class="njc-input" id="njc-cogs-other" type="text" inputmode="decimal" placeholder="e.g., 20000" aria-label="Other COGS" />
</div>
</div>

<div class="njc-group">
<label class="njc-label" for="njc-opex">Operating expenses (non-COGS) ($)</label>
<input class="njc-input" id="njc-opex" type="text" inputmode="decimal" placeholder="e.g., 90000" aria-label="Operating expenses in dollars" />
<div class="njc-help">Examples: payroll (non-direct), rent, marketing, admin, insurance, compliance, professional fees.</div>
</div>

<div class="njc-group">
<label class="njc-label" for="njc-entity">Entity type (for context)</label>
<select class="njc-select" id="njc-entity" aria-label="Entity type">
<option value="ccorp">C-Corp</option>
<option value="passthrough">Pass-through (LLC / S-Corp / Sole prop)</option>
</select>
<div class="njc-help">Entity type can change your effective tax rate. Use your best estimate below.</div>
</div>

<div class="njc-group">
<label class="njc-label" for="njc-taxrate">Estimated combined effective tax rate (%)</label>
<input class="njc-input" id="njc-taxrate" type="text" inputmode="decimal" placeholder="e.g., 30" aria-label="Combined effective tax rate percent" value="30" />
<div class="njc-help">Enter a single blended rate (federal + NJ + other). If unsure, start with 2535% and refine.</div>
</div>

<div class="njc-group">
<label class="njc-label" for="njc-scenario">Tax scenario to display</label>
<select class="njc-select" id="njc-scenario" aria-label="Tax scenario">
<option value="compare">Compare: Normal vs 280E</option>
<option value="normal">Normal tax treatment only</option>
<option value="280e">280E applies only</option>
</select>
</div>

<button class="njc-btn" type="submit" aria-label="Calculate profit margins and 280E impact">Calculate</button>

<div class="njc-error" id="njc-error" role="alert" aria-label="Form error" hidden></div>
</form>

<div class="njc-leadGate" id="njc-leadGate" hidden aria-label="Lead details">
<div class="njc-leadBox">
<div class="njc-leadHeading">Your results are ready</div>
<div class="njc-leadDesc">Enter your business details to view the full profit margin and 280E comparison.</div>

<div class="njc-group">
<label class="njc-label" for="njc-lead-company">Company name</label>
<input class="njc-input" id="njc-lead-company" type="text" placeholder="e.g., Garden State Cannabis LLC" />
</div>

<div class="njc-group">
<label class="njc-label" for="njc-lead-email">Business email</label>
<input class="njc-input" id="njc-lead-email" type="email" placeholder="you@company.com" />
<div class="njc-leadEmailErr" id="njc-leadEmailErr" hidden>Please enter a valid business email.</div>
</div>

<div class="njc-group">
<label class="njc-label" for="njc-lead-role">Which best describes you?</label>
<select class="njc-select" id="njc-lead-role">
<option value="">— Select —</option>
<option value="dispensary_retailer">Dispensary / Retailer</option>
<option value="cultivator_processor">Cultivator / Processor</option>
<option value="distributor">Distributor</option>
<option value="brand_manufacturer">Brand / Manufacturer</option>
<option value="consultant_advisor">Consultant / Advisor</option>
<option value="other">Other</option>
</select>
</div>

<button class="njc-btn" type="button" id="njc-show-results">Show My Results</button>
<div class="njc-leadError" id="njc-leadError" hidden></div>
<div class="njc-leadTrust">CannaBiz Credit Association · Cannabis credit reports &amp; risk monitoring · 30+ state markets</div>
</div>
</div>

<div class="njc-results" id="njc-results" hidden aria-label="Results section">
<div class="njc-results-head">
<h3 class="njc-results-title">Results</h3>
<p class="njc-results-sub" id="njc-results-sub"></p>
</div>

<div class="njc-sep" aria-hidden="true"></div>

<div id="njc-results-body"></div>

<div class="njc-note" aria-label="Disclaimer">
<strong>Note:</strong> This calculator treats 280E as disallowing <em>all</em> operating expense deductions and allowing only COGS. Actual outcomes depend on your accounting method, what is properly capitalizable to inventory, state conformity, and your specific facts.
</div>
</div>
</div>
</div>

<style>
.njc-wrap{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#111;}
.njc-card{max-width:760px;margin:0 auto;background:#fff;border-radius:12px;padding:40px;box-shadow:0 10px 30px rgba(0,0,0,.08);border:1px solid rgba(17,17,17,.06);}
.njc-head{text-align:center;margin-bottom:22px;}
.njc-title{font-size:32px;line-height:1.15;margin:0 0 10px;font-weight:800;letter-spacing:-0.02em;}
.njc-subtitle{font-size:16px;line-height:1.5;margin:0;color:rgba(17,17,17,.70);}

.njc-infobox{margin:22px 0 26px;padding:14px 16px;border-left:4px solid #1f7a4d;background:rgba(31,122,77,.06);border-radius:10px;}
.njc-infobox-title{font-weight:700;margin-bottom:6px;}
.njc-infobox-list{margin:0;padding-left:18px;color:rgba(17,17,17,.78);}
.njc-infobox-list li{margin:6px 0;}

.njc-form{margin-top:10px;}
.njc-group{margin:16px 0;}
.njc-label{display:block;font-weight:700;margin:0 0 8px;}
.njc-input,.njc-select{width:100%;padding:12px 12px;border-radius:10px;border:1px solid rgba(17,17,17,.18);background:#fff;font-size:16px;outline:none;}
.njc-input:focus,.njc-select:focus{border-color:rgba(31,122,77,.55);box-shadow:0 0 0 4px rgba(31,122,77,.12);}
.njc-help{margin-top:8px;font-size:13px;line-height:1.45;color:rgba(17,17,17,.65);}

.njc-check{display:flex;gap:10px;align-items:flex-start;margin-top:10px;font-size:14px;color:rgba(17,17,17,.78);}
.njc-check input{margin-top:3px;}

.njc-subpanel{margin-top:12px;padding:12px;border:1px solid rgba(17,17,17,.12);border-radius:10px;background:rgba(0,0,0,.02);}

.njc-btn{width:100%;margin-top:10px;background:#1f7a4d;color:#fff;border:0;border-radius:12px;padding:14px 16px;font-size:16px;font-weight:800;cursor:pointer;}
.njc-btn:hover{filter:brightness(0.98);}
.njc-btn:active{transform:translateY(1px);}

.njc-error{margin-top:12px;padding:12px 14px;border-radius:10px;border:1px solid rgba(176,0,32,.25);background:rgba(176,0,32,.06);color:#6b0015;}

.njc-results{margin-top:26px;}
.njc-results-head{text-align:left;}
.njc-results-title{font-size:22px;margin:0 0 6px;font-weight:800;}
.njc-results-sub{margin:0;color:rgba(17,17,17,.68);font-size:14px;line-height:1.45;}
.njc-sep{margin:16px 0;border-top:1px solid rgba(17,17,17,.12);}

.njc-table{width:100%;border-collapse:collapse;border:1px solid rgba(17,17,17,.12);border-radius:12px;overflow:hidden;}
.njc-table th,.njc-table td{padding:12px 12px;border-bottom:1px solid rgba(17,17,17,.10);vertical-align:top;}
.njc-table th{background:rgba(31,122,77,.06);text-align:left;font-size:13px;letter-spacing:.02em;text-transform:uppercase;color:rgba(17,17,17,.70);}
.njc-table td{font-size:14px;}
.njc-table tr:last-child td{border-bottom:0;}

.njc-metric{display:flex;justify-content:space-between;gap:14px;}
.njc-metric .njc-k{color:rgba(17,17,17,.72);}
.njc-metric .njc-v{font-weight:800;}

.njc-badge{display:inline-block;padding:4px 8px;border-radius:999px;font-size:12px;font-weight:800;}
.njc-badge-good{background:rgba(31,122,77,.10);color:#1f7a4d;}
.njc-badge-warn{background:rgba(179,92,0,.10);color:#8a4a00;}

.njc-note{margin-top:14px;padding:12px 14px;border-radius:10px;border:1px solid rgba(17,17,17,.10);background:rgba(0,0,0,.02);color:rgba(17,17,17,.72);font-size:13px;line-height:1.5;}

.njc-leadGate{margin-top:26px;}
.njc-leadBox{border:2px solid rgba(31,122,77,.22);border-radius:14px;background:linear-gradient(135deg,rgba(31,122,77,.06),#fff);padding:24px;}
.njc-leadHeading{font-size:18px;font-weight:800;color:#1f7a4d;margin-bottom:6px;}
.njc-leadDesc{font-size:14px;line-height:1.5;color:rgba(17,17,17,.68);margin-bottom:16px;}
.njc-leadEmailErr,.njc-leadError{margin-top:8px;color:#991b1b;font-size:13px;}
.njc-leadError{padding:10px 12px;border:1px solid rgba(176,0,32,.22);background:rgba(176,0,32,.05);border-radius:10px;}
.njc-leadTrust{font-size:11px;color:rgba(17,17,17,.45);text-align:center;margin-top:14px;}

@media (max-width:640px){
.njc-card{padding:22px;}
.njc-title{font-size:26px;}
}
</style>

<script>
(function(){
var el = function(id){ return document.getElementById(id); };

var form = document.querySelector('.njc-form');
var results = el('njc-results');
var resultsBody = el('njc-results-body');
var resultsSub = el('njc-results-sub');
var errorBox = el('njc-error');

var LEAD_CONFIG = {
API_URL: 'https://cannabiz-leads.vercel.app/api/lead',
SHARED_SECRET: 'cannabiz_lead_2025_Xk9mP7nQ2vR8wL4jT6hY3sF9dA1bC5eG0iN',
DOMAIN_LABEL: 'cannabizcredit.com',
PAGE_ID: 'nj-cannabis-profit-margin-280e-calculator'
};

var leadGate = el('njc-leadGate');
var leadCompanyEl = el('njc-lead-company');
var leadEmailEl = el('njc-lead-email');
var leadRoleEl = el('njc-lead-role');
var leadEmailErr = el('njc-leadEmailErr');
var leadError = el('njc-leadError');
var showResultsBtn = el('njc-show-results');

var pendingResult = null;
var leadSubmitted = false;
var emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

var periodEl = el('njc-period');
var revenueEl = el('njc-revenue');
var cogsEl = el('njc-cogs');
var opexEl = el('njc-opex');
var entityEl = el('njc-entity');
var taxRateEl = el('njc-taxrate');
var scenarioEl = el('njc-scenario');

var breakdownToggle = el('njc-cogs-breakdown-toggle');
var breakdownPanel = el('njc-cogs-breakdown');
var cogsInvEl = el('njc-cogs-inventory');
var cogsLabEl = el('njc-cogs-directlab');
var cogsOtherEl = el('njc-cogs-other');

breakdownToggle.addEventListener('change', function(){
breakdownPanel.hidden = !breakdownToggle.checked;
});

function parseMoney(v){
if(v == null) return NaN;
var s = String(v).trim();
if(!s) return NaN;
// allow $ and commas and parentheses
var neg = false;
if(s.indexOf('(') !== -1 && s.indexOf(')') !== -1){ neg = true; }
s = s.replace(/[^0-9.\-]/g,'');
var n = parseFloat(s);
if(!isFinite(n)) return NaN;
if(neg) n = -Math.abs(n);
return n;
}

function parsePercent(v){
if(v == null) return NaN;
var s = String(v).trim();
if(!s) return NaN;
s = s.replace(/%/g,'').replace(/,/g,'');
var n = parseFloat(s);
if(!isFinite(n)) return NaN;
return n / 100;
}

var usd0 = new Intl.NumberFormat(undefined,{style:'currency',currency:'USD',maximumFractionDigits:0});
var usd2 = new Intl.NumberFormat(undefined,{style:'currency',currency:'USD',maximumFractionDigits:2});

function fmtMoney(n){
if(!isFinite(n)) return '—';
var abs = Math.abs(n);
if(abs >= 1000) return usd0.format(n);
return usd2.format(n);
}

function fmtPct(n){
if(!isFinite(n)) return '—';
return (n*100).toFixed(1) + '%';
}

function clampMinZero(n){ return n > 0 ? n : 0; }

function badgeHtml(label, kind){
var cls = kind === 'warn' ? 'njc-badge njc-badge-warn' : 'njc-badge njc-badge-good';
return '<span class="' + cls + '">' + label + '</span>';
}

function buildScenario(name, rev, cogs, opex, taxRate, mode){
var grossProfit = rev - cogs;
var operatingProfit = rev - cogs - opex;
var taxable = (mode === '280e') ? grossProfit : operatingProfit;
var tax = clampMinZero(taxable) * taxRate;
var afterTax = operatingProfit - tax;

var grossMargin = rev !== 0 ? (grossProfit / rev) : NaN;
var operatingMargin = rev !== 0 ? (operatingProfit / rev) : NaN;
var afterTaxMargin = rev !== 0 ? (afterTax / rev) : NaN;

return {
name: name,
mode: mode,
revenue: rev,
cogs: cogs,
opex: opex,
grossProfit: grossProfit,
operatingProfit: operatingProfit,
taxableIncome: taxable,
tax: tax,
afterTax: afterTax,
grossMargin: grossMargin,
operatingMargin: operatingMargin,
afterTaxMargin: afterTaxMargin
};
}

function renderScenarioTable(rows){
var html = '';
html += '<table class="njc-table" aria-label="Scenario results table">';
html += '<thead><tr>';
html += '<th>Scenario</th>';
html += '<th>Margins</th>';
html += '<th>Profit &amp; Tax (estimate)</th>';
html += '</tr></thead>';
html += '<tbody>';

rows.forEach(function(r){
var flag = '';
if(r.mode === '280e') flag = badgeHtml('280E', 'warn');
else flag = badgeHtml('Normal', 'good');

html += '<tr>';
html += '<td>';
html += '<div style="font-weight:800; margin-bottom:6px;">' + r.name + ' ' + flag + '</div>';
html += '<div class="njc-metric"><span class="njc-k">Revenue</span><span class="njc-v">' + fmtMoney(r.revenue) + '</span></div>';
html += '<div class="njc-metric"><span class="njc-k">COGS</span><span class="njc-v">' + fmtMoney(r.cogs) + '</span></div>';
html += '<div class="njc-metric"><span class="njc-k">OpEx</span><span class="njc-v">' + fmtMoney(r.opex) + '</span></div>';
html += '</td>';

html += '<td>';
html += '<div class="njc-metric"><span class="njc-k">Gross margin</span><span class="njc-v">' + fmtPct(r.grossMargin) + '</span></div>';
html += '<div class="njc-metric"><span class="njc-k">Operating margin</span><span class="njc-v">' + fmtPct(r.operatingMargin) + '</span></div>';
html += '<div class="njc-metric"><span class="njc-k">After-tax margin</span><span class="njc-v">' + fmtPct(r.afterTaxMargin) + '</span></div>';
html += '</td>';

html += '<td>';
html += '<div class="njc-metric"><span class="njc-k">Gross profit</span><span class="njc-v">' + fmtMoney(r.grossProfit) + '</span></div>';
html += '<div class="njc-metric"><span class="njc-k">Operating profit</span><span class="njc-v">' + fmtMoney(r.operatingProfit) + '</span></div>';
html += '<div class="njc-metric"><span class="njc-k">Taxable income</span><span class="njc-v">' + fmtMoney(r.taxableIncome) + '</span></div>';
html += '<div class="njc-metric"><span class="njc-k">Estimated tax</span><span class="njc-v">' + fmtMoney(r.tax) + '</span></div>';
html += '<div class="njc-metric"><span class="njc-k">After-tax profit</span><span class="njc-v">' + fmtMoney(r.afterTax) + '</span></div>';
html += '</td>';
html += '</tr>';
});

html += '</tbody></table>';
return html;
}

function showError(msg){
errorBox.textContent = msg;
errorBox.hidden = false;
}
function clearError(){
errorBox.hidden = true;
errorBox.textContent = '';
}

function validateLead(){
var company = String(leadCompanyEl.value || '').trim();
var email = String(leadEmailEl.value || '').trim();
var role = leadRoleEl.value || '';
var okEmail = emailRe.test(email);
leadEmailErr.hidden = okEmail || !email;
if(!company || !okEmail || !role){
leadError.textContent = 'Please fill in all fields with a valid business email.';
leadError.hidden = false;
return false;
}
leadError.hidden = true;
return true;
}

function submitLead(context){
if(leadSubmitted) return;
leadSubmitted = true;

var payload = {
company: String(leadCompanyEl.value || '').trim(),
email: String(leadEmailEl.value || '').trim(),
role: leadRoleEl.value || '',
domain: LEAD_CONFIG.DOMAIN_LABEL,
page: LEAD_CONFIG.PAGE_ID,
context: context || {},
utm: {}
};

try {
var sp = new URLSearchParams(window.location.search || '');
['utm_source','utm_medium','utm_campaign','utm_term','utm_content'].forEach(function(k){
if(sp.get(k)) payload.utm[k] = sp.get(k);
});
} catch(e){}

(async function(){
try {
var ctrl = (typeof AbortSignal !== 'undefined' && AbortSignal.timeout) ? AbortSignal.timeout(2000) : undefined;
var ipRes = await fetch('https://api.ipify.org?format=json', {signal: ctrl});
var ipData = await ipRes.json();
payload.ip = ipData.ip;
} catch(e){}

try {
await fetch(LEAD_CONFIG.API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-shared-secret': LEAD_CONFIG.SHARED_SECRET
},
body: JSON.stringify(payload)
});
} catch(e){}
})();
}

function revealPendingResult(){
if(!pendingResult) return;
resultsSub.textContent = pendingResult.sub;
resultsBody.innerHTML = pendingResult.html;
results.hidden = false;
leadGate.hidden = true;
results.scrollIntoView({behavior:'smooth', block:'start'});
}

leadEmailEl.addEventListener('input', function(){
var email = String(leadEmailEl.value || '').trim();
leadEmailErr.hidden = emailRe.test(email) || !email;
});

showResultsBtn.addEventListener('click', function(){
if(!validateLead()) return;
submitLead(pendingResult ? pendingResult.context : {});
revealPendingResult();
});

form.addEventListener('submit', function(e){
e.preventDefault();
clearError();

var period = periodEl.value;
var rev = parseMoney(revenueEl.value);
var cogs = parseMoney(cogsEl.value);
var opex = parseMoney(opexEl.value);
var taxRate = parsePercent(taxRateEl.value);
var scenario = scenarioEl.value;

// If total COGS not provided, but breakdown is used, sum it.
if(!isFinite(cogs) && breakdownToggle.checked){
var b1 = parseMoney(cogsInvEl.value); if(!isFinite(b1)) b1 = 0;
var b2 = parseMoney(cogsLabEl.value); if(!isFinite(b2)) b2 = 0;
var b3 = parseMoney(cogsOtherEl.value); if(!isFinite(b3)) b3 = 0;
var sum = b1 + b2 + b3;
if(sum > 0) cogs = sum;
}

if(!isFinite(rev) || rev <= 0){
showError('Please enter a revenue amount greater than 0.');
results.hidden = true;
leadGate.hidden = true;
return;
}
if(!isFinite(cogs) || cogs < 0){
showError('Please enter a valid COGS amount (0 or more).');
results.hidden = true;
leadGate.hidden = true;
return;
}
if(!isFinite(opex) || opex < 0){
showError('Please enter a valid operating expense amount (0 or more).');
results.hidden = true;
leadGate.hidden = true;
return;
}
if(!isFinite(taxRate) || taxRate < 0 || taxRate > 0.7){
showError('Please enter a reasonable combined effective tax rate (for example, 25 to 40).');
results.hidden = true;
leadGate.hidden = true;
return;
}

var normal = buildScenario('Normal tax treatment', rev, cogs, opex, taxRate, 'normal');
var e280 = buildScenario('280E applies', rev, cogs, opex, taxRate, '280e');

var rows = [];
if(scenario === 'compare') rows = [normal, e280];
if(scenario === 'normal') rows = [normal];
if(scenario === '280e') rows = [e280];

var penaltyHtml = '';
if(scenario === 'compare'){
var taxPenalty = e280.tax - normal.tax;
var profitHit = e280.afterTax - normal.afterTax; // usually negative
penaltyHtml += '<div class="njc-note" aria-label="280E delta summary">'
+ '<div style="font-weight:800; margin-bottom:6px;">280E impact (difference vs. normal)</div>'
+ '<div class="njc-metric"><span class="njc-k">Additional estimated tax</span><span class="njc-v">' + fmtMoney(taxPenalty) + '</span></div>'
+ '<div class="njc-metric"><span class="njc-k">After-tax profit change</span><span class="njc-v">' + fmtMoney(profitHit) + '</span></div>'
+ '<div class="njc-help" style="margin-top:8px;">Under 280E, taxes can be due even when operating profit is low (or negative), because taxable income is based on gross profit.</div>'
+ '</div>';
}

var entityTxt = (entityEl.value === 'ccorp') ? 'C-Corp' : 'Pass-through';
var taxRateTxt = (taxRate*100).toFixed(1) + '%';

var sub = 'Period: ' + (period === 'monthly' ? 'Monthly' : 'Annual') + '  Entity: ' + entityTxt + '  Effective tax rate used: ' + taxRateTxt + '.';

var annualBlock = '';
if(period === 'monthly'){
var arev = rev * 12;
var acogs = cogs * 12;
var aopex = opex * 12;
var an = buildScenario('Normal tax treatment (annualized)', arev, acogs, aopex, taxRate, 'normal');
var a2 = buildScenario('280E applies (annualized)', arev, acogs, aopex, taxRate, '280e');

annualBlock += '<div class="njc-sep" aria-hidden="true"></div>';
annualBlock += '<h4 style="margin:0 0 10px; font-size:16px; font-weight:800;">Annualized view (monthly  12)</h4>';
if(scenario === 'compare') annualBlock += renderScenarioTable([an, a2]);
if(scenario === 'normal') annualBlock += renderScenarioTable([an]);
if(scenario === '280e') annualBlock += renderScenarioTable([a2]);
}

// Optional breakdown note
var breakdownNote = '';
if(breakdownToggle.checked){
var b1n = parseMoney(cogsInvEl.value); if(!isFinite(b1n)) b1n = 0;
var b2n = parseMoney(cogsLabEl.value); if(!isFinite(b2n)) b2n = 0;
var b3n = parseMoney(cogsOtherEl.value); if(!isFinite(b3n)) b3n = 0;
var sumN = b1n + b2n + b3n;
if(sumN > 0){
var delta = (isFinite(cogs) ? (cogs - sumN) : 0);
var mismatch = Math.abs(delta) > 1;
breakdownNote += '<div class="njc-note" aria-label="COGS breakdown summary">'
+ '<div style="font-weight:800; margin-bottom:6px;">COGS breakdown (optional)</div>'
+ '<div class="njc-metric"><span class="njc-k">Inventory / purchases</span><span class="njc-v">' + fmtMoney(b1n) + '</span></div>'
+ '<div class="njc-metric"><span class="njc-k">Direct labor</span><span class="njc-v">' + fmtMoney(b2n) + '</span></div>'
+ '<div class="njc-metric"><span class="njc-k">Other COGS</span><span class="njc-v">' + fmtMoney(b3n) + '</span></div>'
+ '<div class="njc-metric"><span class="njc-k">Breakdown total</span><span class="njc-v">' + fmtMoney(sumN) + '</span></div>'
+ (mismatch ? '<div class="njc-help" style="margin-top:8px;">Note: Your total COGS input (' + fmtMoney(cogs) + ') does not match the breakdown total. The calculator uses the total COGS field.</div>' : '')
+ '</div>';
}
}

var resultHtml = renderScenarioTable(rows) + penaltyHtml + breakdownNote + annualBlock;

pendingResult = {
sub: sub,
html: resultHtml,
context: {
tool: 'nj-cannabis-profit-margin-280e-calculator',
period: period,
revenue: rev,
cogs: cogs,
operating_expenses: opex,
entity_type: entityEl.value,
effective_tax_rate: (taxRate * 100).toFixed(1),
scenario: scenario,
normal_taxable_income: normal.taxableIncome,
normal_estimated_tax: normal.tax,
normal_after_tax_profit: normal.afterTax,
normal_after_tax_margin: normal.afterTaxMargin,
e280_taxable_income: e280.taxableIncome,
e280_estimated_tax: e280.tax,
e280_after_tax_profit: e280.afterTax,
e280_after_tax_margin: e280.afterTaxMargin,
additional_estimated_tax_280e: e280.tax - normal.tax,
after_tax_profit_change_280e: e280.afterTax - normal.afterTax
}
};

results.hidden = true;

if(leadSubmitted){
revealPendingResult();
} else {
leadGate.hidden = false;
leadGate.scrollIntoView({behavior:'smooth', block:'start'});
}
});
})();
</script>

Vendors selling into New Jersey’s adult-use cannabis market are dealing with a familiar problem: demand is real, but so is delayed payment. This New Jersey Dispensary Credit Risk Report 2026 — risk ratings + payment-behavior bands for NJ's largest retail operators is a practical, vendor-first way to make better credit decisions using the signals we can actually verify—payment behavior, A/R aging, collections activity, and regulatory compliance.

The goal isn’t to “blacklist” accounts. It’s to reduce surprises, set terms that match risk, and protect cash flow—especially in a market where credit exposure is increasing due to Net 30–90+ terms and increasing debt levels among operators, as described by the Cannabiz Credit Association’s NJ credit monitoring overview.

2026 NJ dispensary credit snapshot: why vendor exposure is climbing

New Jersey remains a high-revenue cannabis state, but vendors are seeing higher risk at the invoice level. The clearest drivers are operational—not theoretical. The Cannabiz Credit Association (CCA) summarizes the pressure points behind rising credit risk in New Jersey as:

  • Dependence on Net 30–90+ terms
  • Increasing debt levels among operators

CCA also notes why better visibility is needed: New Jersey is “one of the most profitable cannabis markets on the East Coast,” but with tightening margins and growing reliance on trade credit, vendors need real payment history rather than guesswork (source).

Meanwhile, retail growth continues. For example, NJBIZ reported that Nova Farms’ Woodbury location was the 30th recreational dispensary to open in New Jersey since adult-use sales began in April 2022 (NJBIZ slideshow). More stores can mean more purchase orders—while also expanding the number of accounts that need monitoring.

How this report assigns risk ratings + payment-behavior bands

Because vendors get hurt by what they don’t see, the scoring approach below prioritizes verifiable, repeatable inputs—mirroring CCA’s guidance to combine market risk with verified payment behavior in the same credit decision (source).

Risk ratings (A–E) based on the signals vendors can verify

Use the rating as a terms recommendation, not a label. CCA describes a workflow that includes running credit checks, monitoring for worsening performance, and tracking 30/60/90+ aging patterns (source). This report maps those same ideas into five practical risk tiers:

  • A (Low risk): Verified strong payment behavior; minimal compliance flags; low collections/aging signals.
  • B (Managed risk): Generally on-time; occasional slow pay; no major negative compliance indicators.
  • C (Watch): Mixed payment signals; rising aging; requires tighter limits and faster escalation.
  • D (Elevated): Clear late-pay trend or material operational/compliance concerns; require tighter terms and frequent monitoring.
  • E (High risk): Severe delinquency or repeated red flags; move to secured terms/COD and reduce exposure.

Important: CCA explicitly positions its reports around supporting data such as collections activity, payment behavior, account aging, and high-risk customer monitoring—so the “right” rating should follow what your monitoring shows, not a gut feeling (source).

Payment-behavior bands aligned to 30/60/90+ aging and COD triggers

To make A/R decisions simple across your team, assign every retail account a payment band—then tie each band to a standard action (limits, terms, and escalation). CCA calls out the importance of tracking 30/60/90+ day aging patterns across the NJ market (source). And a May 2026 public comment to the NJ Cannabis Regulatory Commission (shared publicly) notes that New Jersey already enforces 30-day payment terms and COD status for delinquent retailers (source).

Use these five bands:

  • Band 1 (Pays early / on receipt): Strongest behavior; consider small discounts for early pay.
  • Band 2 (On-time within agreed terms): Typical Net 30 behavior; keep standard limits.
  • Band 3 (1–30 days slow): Treat as “early warning;” tighten reorder controls and require payment plans for arrears.
  • Band 4 (31–60 days past due): Reduce limits; require partial prepay; increase contact cadence.
  • Band 5 (61–90+ days past due / chronic delinquency): Move to COD or secured terms; pause shipments if exposure grows.

Regulatory compliance is a real credit signal in NJ retail

Credit teams often treat compliance as “someone else’s problem,” but in cannabis retail it can directly impact operational stability (and therefore payment reliability). The NJ Cannabis Regulatory Commission has publicly documented enforcement actions that highlight breakdowns vendors should take seriously—especially when they involve recordkeeping, tax collection, access controls, and inventory procedures.

What NJ CRC enforcement tells you to watch for

In a CRC bulletin about identifying legal vs. noncompliant practices, the Commission described violations and fines that are directly relevant to vendor risk:

  • $20,000 fine (INV-10-26): Green Oasis Dispensary NJ LLC—failure to restrict dispensary access to authorized personnel, failure to maintain complete/accurate/confidential records of cannabis product sales, and failure to collect required taxes on those sales (NJ CRC bulletin).
  • $1,000 fine (INV-17-26): CREAM Retail Dispensary Corp.—failure to sell items directly to a consumer and failure to verify consumer ages via photo ID examination (NJ CRC bulletin).
  • $5,000 fine (case described in the same bulletin): violations included cannabis product being visible to the public, storing product in an unsecured area, preventing clear monitoring of activity within the vault, and not updating cannabis product inventories daily (NJ CRC bulletin).
For vendors, these aren’t abstract “compliance issues.” They’re indicators of whether an operator has the controls to manage inventory, cash handling, and auditable records—systems that also support timely invoice approval and payment.

How to use compliance signals without overreacting

A single fine doesn’t automatically mean “do not sell.” What it should do is trigger a structured response:

  • Verify current license and operating status before extending new limits.
  • Reduce discretionary exposure (smaller ship sizes, tighter reorder gates) while you monitor.
  • Require cleaner documentation (PO required, receiving signature required, invoice matching rules).
  • Escalate faster if payments begin drifting into 30/60/90+ buckets—exactly the aging pattern CCA recommends tracking (source).

2026 operator risk notes: ratings + payment-behavior bands (vendor-facing)

This section applies the New Jersey Dispensary Credit Risk Report 2026 — risk ratings + payment-behavior bands for NJ's largest retail operators framework to prominent, publicly referenced NJ retailers. Where the research provides compliance data, the rating reflects that signal. Where it does not, the guidance is to assign a provisional band and confirm with monitoring, consistent with CCA’s emphasis on verified payment behavior and ongoing alerts for late-pay trends (source).

Nova Farms (Woodbury) — Provisional rating based on market structure

NJBIZ describes Nova Farms as the Northeast’s largest privately owned, vertically integrated cannabis operator, and reports its Woodbury store opened June 10 and represented the 30th recreational dispensary to open in New Jersey since adult-use sales began (source).

  • Risk rating (provisional): B–C until payment behavior is verified through monitoring (CCA’s recommended approach).
  • Suggested payment-behavior band to start: Band 2 (standard terms) with strict credit limit controls and quick movement to Band 3 if any slowness appears.
  • Vendor action: Run a credit check and set terms before “the next invoice goes out,” aligning with CCA’s step to check risk score and supporting data (source).

Green Oasis Dispensary NJ LLC — Elevated operational control risk signal

The NJ CRC bulletin documents a $20,000 fine (INV-10-26) tied to failing to restrict access to authorized personnel, failing to maintain complete/accurate/confidential sales records, and failing to collect required taxes (source).

  • Risk rating (based on documented compliance signal): D (Elevated).
  • Suggested payment-behavior band: Start at Band 3 (1–30 days slow) or tighter until verified payment performance supports Band 2.
  • Vendor action: Reduce open-credit exposure, require strong documentation, and monitor aging closely (CCA highlights tracking 30/60/90+ patterns and flagged debtors as core monitoring tasks: source).

CREAM Retail Dispensary Corp. — Watch list, tighten process controls

The NJ CRC bulletin documents a $1,000 fine (INV-17-26) for failing to sell items directly to a consumer and failing to verify consumer ages by examining photo ID (source).

  • Risk rating (based on documented compliance signal): C (Watch).
  • Suggested payment-behavior band: Band 2 if payments are on time; move quickly to Band 3 if any slowness appears.
  • Vendor action: Maintain standard terms only with active monitoring—CCA’s process emphasizes reviewing payment behavior and tracking late-pay trends to decide the right terms (source).

Practical playbook: reduce A/R risk without killing sales

In New Jersey, the best-performing credit teams don’t just “deny credit.” They shape behavior with consistent rules, tighter processes, and faster escalation. The steps below are directly aligned with the workflow CCA promotes: Run checks, Monitor for flagged debtors and worsening trends, Track 30/60/90+ aging, and Compare performance across accounts (source).

1) Set terms before you ship (and tie limits to bands)

  • Assign a payment-behavior band at onboarding (Band 2 by default only if supported by your check/monitoring process).
  • Set a hard credit limit tied to the band. When exposure hits the cap, require payment to release the next order.
  • Document what triggers a band change (first slow-pay, first broken promise date, 31+ days past due).

2) Use monitoring to catch “quiet deterioration”

CCA’s approach emphasizes alerts for late pay trends and worsening payment performance (source). Operationally, that means:

  • Review A/R aging weekly for any drift into 30/60/90+ buckets.
  • Require a written payment plan when an account hits Band 3.
  • Pre-schedule escalation: Band 4 triggers partial prepay; Band 5 triggers COD.

3) Align your escalation policy with NJ’s COD reality

In a publicly shared May 2026 comment to the NJ Cannabis Regulatory Commission, it was noted that New Jersey already enforces 30-day payment terms and COD status for delinquent retailers (source). Vendors should reflect that reality in their own policies:

  • If an account is repeatedly late, don’t “extend and pretend.” Move to COD faster.
  • When COD is required, keep the door open: return to terms only after consecutive on-time payments.

4) Treat compliance breakdowns as a credit-control trigger

The CRC bulletin’s examples—like failure to maintain complete sales records, tax collection failures, and daily inventory update gaps—show the kinds of operational control failures that can create payment friction (source). Practical credit controls include:

  • PO-required invoicing for any account on Watch/Elevated status.
  • Shorter billing cycles (weekly invoicing) to reduce outstanding exposure.
  • Delivery and receiving discipline: signed receiving, photo proof of delivery, and invoice matching.

Frequently Asked Questions

What is the purpose of the New Jersey Dispensary Credit Risk Report 2026 — risk ratings + payment-behavior bands for NJ's largest retail operators?

It’s a vendor-facing framework to standardize credit decisions using verifiable signals emphasized by the Cannabiz Credit Association—risk score inputs, payment behavior, collections activity, and 30/60/90+ aging patterns—plus public compliance signals from the NJ Cannabis Regulatory Commission (CCA source; NJ CRC bulletin).

How do payment-behavior bands help my A/R team?

Bands translate “payment performance” into clear actions—when to tighten limits, require partial prepay, or move to COD. This aligns with CCA’s emphasis on tracking 30/60/90+ patterns and monitoring for late pay trends (source).

Why does regulatory compliance matter for credit risk?

Public enforcement actions can reveal weak operational controls that also drive payment issues—like incomplete sales records, tax collection failures, unsecured storage, and inventory process gaps. The NJ CRC bulletin documents fines including $20,000 (Green Oasis Dispensary NJ LLC) and $1,000 (CREAM Retail Dispensary Corp.) tied to such failures (source).

When should a vendor move an NJ dispensary account to COD?

Use aging and repeat behavior. If an account drifts into chronic delinquency (for example, repeated 61–90+ day past due behavior), COD becomes the safest default. This is consistent with (1) CCA’s focus on aging trends and high-risk monitoring (source) and (2) a publicly shared May 2026 comment noting NJ’s use of COD status for delinquent retailers (source).

How fast is the NJ retail market expanding, and why does that affect credit?

Expansion increases the number of accounts to evaluate and monitor. NJBIZ reported that Nova Farms’ Woodbury location was the 30th recreational dispensary to open in New Jersey since adult-use sales began in April 2022 (source). More storefronts can mean more purchasing—while also increasing the need for consistent credit checks and monitoring.

If you want, I can convert this framework into a one-page internal policy (bands, limits, COD triggers, and monitoring cadence) your sales and finance teams can use consistently.

Trusted by the cannabis industry since 2023

Jeeter logo
Kaviar logo
PAX logo
Rove logo
Weedmaps logo
Surfside logo
Leafly logo
eBottles logo
FundCanna logo
Headset logo
Apex Trading logo
Connected logo
Kaycha Labs logo
710 Labs logo
Ispire logo
Jeeter logo
Kaviar logo
PAX logo
Rove logo
Weedmaps logo
Surfside logo
Leafly logo
eBottles logo
FundCanna logo
Headset logo
Apex Trading logo
Connected logo
Kaycha Labs logo
710 Labs logo
Ispire logo