Files
captable/pkg/bluesky/fees.go
Lux Dev 6df2b904f0 feat(edgar,bluesky): add Form D + Blue-Sky state filing adapters
G-03 — SEC EDGAR Form D adapter (pkg/edgar):
- FormDFiling typed model covering primary issuer, related persons,
  industry, federal exemptions, sales/offering amounts, investor count,
  sales commissions, use of proceeds, and signature blocks
- marshalFormD emits the SEC Form D XML primaryDocument shape;
  buildSGMLSubmission wraps it in the EDGARLink Online SUBMISSION SGML
  envelope (TYPE / CIK / CCC / CONTACT / DOCUMENT)
- Filer with FileFormD / AmendFormD / GetFilingStatus, CIK normalization,
  exponential-backoff + jitter retry with Retry-After honoring,
  REJECTED-status → ErrSubmissionRejected, 4xx → *APIError
- 32 conformance tests against httptest.Server fixtures

G-04 — Blue-Sky state notice adapter (pkg/bluesky):
- Registrar dispatches to per-state StateAdapter implementations
- EFDAdapter (NASAA Electronic Filing Depository) covers 49 states + DC
  with bearer-token auth, ACH-payment metadata, and renewal support
- Real state-portal adapters for FL, TX, NY, CA, MA — five highest-
  volume non-EFD / hybrid-portal states
- Per-state fee calculator with flat + scaled fee schedules
  (CA / NY / WA scale with offering size); EFD system fee layered on
- 30 conformance tests against httptest.Server fixtures

compliance bridge (pkg/compliance/filings.go):
- FormDFiler / BlueSkyFiler interfaces let the compliance Service
  delegate filings without importing the wire packages directly
- compliance.FileFormD / AmendFormD / FileBlueSkyNotice update the
  in-domain record with the regulator-assigned identifier, status,
  filing date, and fee
- 6 bridge tests with in-memory fakes

Coverage: go build / vet / -race clean across the full repo. 62 tests
in the two new packages + 6 in the compliance bridge, all passing.

Source-of-design: Public-Spec
Source-ref: https://www.sec.gov/info/edgar/specifications/formdxml.pdf
Source-ref: https://www.sec.gov/edgar/filer-information/current-edgar-technical-specifications
Source-ref: http://nasaaefd.org
Source-ref: http://nasaaefd.org/About/FormDStates
Clean-Team-Member: dev-agent (P0 G-03 / G-04 implementation)
Signed-off-by: dev <[email protected]>
2026-05-26 18:14:15 -07:00

158 lines
5.0 KiB
Go

// Per-state fee schedule for Rule 506 notice filings.
//
// Authoritative source: each state's securities-administrator
// regulation + the NASAA EFD "Schedule of State Fees (Form D)" at
// http://nasaaefd.org/About/FormDStates.
//
// The schedule below captures the standard Rule 506(b) / 506(c)
// notice-of-sale fee per state as of public publication. Fees that
// scale with offering size carry an explicit Notes line.
//
// Source-of-design: Public-Spec
// Source-ref: http://nasaaefd.org/About/FormDStates
package bluesky
// stateFee is the per-state fee + notes pair returned by
// stateFeeSchedule.
type stateFee struct {
state float64
notes string
}
// stateFeeSchedule returns the per-state fee for a notice-of-sale
// filing under the supplied notice. Values are the published Rule 506
// notice fees per state; some states scale the fee with offering size.
func stateFeeSchedule(state State, n *NoticeFiling) stateFee {
amt := 0.0
if n != nil {
amt = n.Offering.TotalOfferingAmount
}
switch state {
// Flat-fee states (most common).
case "AL":
return stateFee{300, "Alabama: $300 per filing; $300 lifetime cap"}
case "AK":
return stateFee{600, "Alaska: $600 per filing"}
case "AZ":
return stateFee{250, "Arizona: $250 per filing"}
case "AR":
return stateFee{100, "Arkansas: $100 per filing; $500 lifetime cap"}
case "CA":
// CA scales: $25 minimum + 0.001% of offering (max $300).
fee := 25.0 + 0.00001*amt
if fee > 300 {
fee = 300
}
return stateFee{fee, "California: $25 + 0.001% of offering (max $300)"}
case "CO":
return stateFee{75, "Colorado: $75 per filing"}
case "CT":
return stateFee{150, "Connecticut: $150 per filing"}
case "DE":
return stateFee{200, "Delaware: $200 per filing"}
case "DC":
return stateFee{250, "DC: $250 per filing"}
case "FL":
return stateFee{200, "Florida: $200 paper filing (no EFD)"}
case "GA":
return stateFee{250, "Georgia: $250 per filing"}
case "HI":
return stateFee{200, "Hawaii: $200 per filing"}
case "ID":
return stateFee{50, "Idaho: $50 per filing"}
case "IL":
return stateFee{200, "Illinois: $200 per filing"}
case "IN":
return stateFee{100, "Indiana: $100 per filing"}
case "IA":
return stateFee{100, "Iowa: $100 per filing"}
case "KS":
return stateFee{250, "Kansas: $250 per filing"}
case "KY":
return stateFee{250, "Kentucky: $250 per filing"}
case "LA":
return stateFee{300, "Louisiana: $300 per filing"}
case "ME":
return stateFee{300, "Maine: $300 per filing"}
case "MD":
return stateFee{100, "Maryland: $100 per filing"}
case "MA":
return stateFee{300, "Massachusetts: $300 per filing; $750 lifetime cap"}
case "MI":
return stateFee{100, "Michigan: $100 per filing; $100 lifetime cap"}
case "MN":
return stateFee{50, "Minnesota: $50 per filing; $300 lifetime cap"}
case "MS":
return stateFee{300, "Mississippi: $300 per filing"}
case "MO":
return stateFee{100, "Missouri: $100 per filing"}
case "MT":
return stateFee{200, "Montana: $200 per filing"}
case "NE":
return stateFee{200, "Nebraska: $200 per filing"}
case "NV":
return stateFee{350, "Nevada: $350 per filing"}
case "NH":
return stateFee{500, "New Hampshire: $500 per filing"}
case "NJ":
return stateFee{250, "New Jersey: $250 per filing"}
case "NM":
return stateFee{350, "New Mexico: $350 per filing"}
case "NY":
// NY scales by offering: $300 if ≤ $500K; $1,200 if > $500K.
if amt > 500_000 {
return stateFee{1200, "New York: $1,200 (offerings > $500K)"}
}
return stateFee{300, "New York: $300 (offerings ≤ $500K)"}
case "NC":
return stateFee{350, "North Carolina: $350 per filing"}
case "ND":
return stateFee{100, "North Dakota: $100 per filing"}
case "OH":
return stateFee{100, "Ohio: $100 per filing"}
case "OK":
return stateFee{100, "Oklahoma: $100 per filing"}
case "OR":
return stateFee{225, "Oregon: $225 per filing"}
case "PA":
return stateFee{300, "Pennsylvania: $300 per filing"}
case "RI":
return stateFee{300, "Rhode Island: $300 per filing; $300 lifetime cap"}
case "SC":
return stateFee{300, "South Carolina: $300 per filing"}
case "SD":
return stateFee{150, "South Dakota: $150 per filing"}
case "TN":
return stateFee{500, "Tennessee: $500 per filing"}
case "TX":
return stateFee{500, "Texas: $500 paper filing (no EFD); $500 lifetime cap"}
case "UT":
return stateFee{60, "Utah: $60 per filing"}
case "VT":
return stateFee{600, "Vermont: $600 per filing"}
case "VA":
return stateFee{250, "Virginia: $250 per filing"}
case "WA":
// WA scales: $300 minimum, 0.1% on amount > $50K, max $1,500.
fee := 300.0
if amt > 50_000 {
scaled := 300.0 + 0.001*(amt-50_000)
if scaled > 1500 {
scaled = 1500
}
if scaled > fee {
fee = scaled
}
}
return stateFee{fee, "Washington: $300 + 0.1% on amount > $50K (max $1,500)"}
case "WV":
return stateFee{250, "West Virginia: $250 per filing"}
case "WI":
return stateFee{200, "Wisconsin: $200 per filing"}
case "WY":
return stateFee{200, "Wyoming: $200 per filing"}
}
return stateFee{0, "unknown state — verify fee schedule before filing"}
}