<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Reinvoice LLC</title>
    <description>The latest articles on DEV Community by Reinvoice LLC (@reinvoice).</description>
    <link>https://dev.to/reinvoice</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3971655%2F03ced65a-b340-433d-b230-53d627c79a48.png</url>
      <title>DEV Community: Reinvoice LLC</title>
      <link>https://dev.to/reinvoice</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/reinvoice"/>
    <language>en</language>
    <item>
      <title>Best Invoice Software for Software Engineers</title>
      <dc:creator>Reinvoice LLC</dc:creator>
      <pubDate>Thu, 02 Jul 2026 00:07:04 +0000</pubDate>
      <link>https://dev.to/reinvoice/best-invoice-software-for-software-engineers-4i92</link>
      <guid>https://dev.to/reinvoice/best-invoice-software-for-software-engineers-4i92</guid>
      <description>&lt;p&gt;For many small business owners, invoicing starts with a spreadsheet, a document template, or a manually edited PDF. That can work for one or two invoices, but it becomes difficult to manage once more clients, repeat projects, overdue payments, and tax records enter the picture.&lt;/p&gt;

&lt;p&gt;This is why using dedicated invoice software for freelancers and contractors can make a big difference. A good invoicing system helps independent workers save time, reduce payment confusion, and keep cleaner financial records throughout the year.&lt;/p&gt;

&lt;p&gt;One tool worth considering is &lt;a href="https://reinvoice.co" rel="noopener noreferrer"&gt;Reinvoice&lt;/a&gt;, an invoice and tax tracking platform designed for freelancers, 1099 contractors, consultants, and self-employed professionals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why freelancers and contractors need invoice software
&lt;/h2&gt;

&lt;p&gt;Freelancers and contractors usually handle their own administrative work. That includes sending invoices, following up with clients, tracking paid and unpaid invoices, organizing client information, and preparing income records for taxes.&lt;/p&gt;

&lt;p&gt;Without a reliable system, important details can easily get lost.&lt;/p&gt;

&lt;p&gt;An invoice might be sent but never followed up on. A client might pay late without a clear reminder process. A contractor might forget which invoices are still outstanding. Tax season can also become more stressful if income records are scattered across email, spreadsheets, bank statements, and PDFs.&lt;/p&gt;

&lt;p&gt;Invoice software helps centralize these tasks in one place.&lt;/p&gt;

&lt;p&gt;Instead of manually updating files every time something changes, freelancers can use invoicing software to manage the full invoice lifecycle: draft, sent, viewed, paid, overdue, and exported.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to look for in freelancer invoice software
&lt;/h2&gt;

&lt;p&gt;The best invoice software for freelancers should be simple enough to use quickly, but complete enough to support real business workflows.&lt;/p&gt;

&lt;p&gt;Important features include:&lt;/p&gt;

&lt;p&gt;Invoice creation, so users can create professional invoices without starting from scratch each time.&lt;/p&gt;

&lt;p&gt;Client management, so customer names, email addresses, billing details, and invoice history stay organized.&lt;/p&gt;

&lt;p&gt;Payment status tracking, so freelancers can see which invoices are paid, unpaid, overdue, or still pending.&lt;/p&gt;

&lt;p&gt;Invoice reminders, so clients can be notified when payment is due or overdue.&lt;/p&gt;

&lt;p&gt;PDF exports, so invoices can be downloaded, saved, or sent as records.&lt;/p&gt;

&lt;p&gt;CSV exports, so income and invoice data can be reviewed, shared, or prepared for bookkeeping.&lt;/p&gt;

&lt;p&gt;Tax tracking, so 1099 contractors and self-employed workers can better understand how much income they have earned and what they may need to set aside.&lt;/p&gt;

&lt;p&gt;For independent workers, the goal is not always advanced accounting. Often, the goal is clarity. Who owes money? How much came in this month? Which invoices need attention? What should be saved for taxes?&lt;/p&gt;

&lt;p&gt;That is where a focused invoicing tool can be especially useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reinvoice: invoice software for 1099 contractors and freelancers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://reinvoice.co" rel="noopener noreferrer"&gt;Reinvoice&lt;/a&gt; is built for people who send invoices and need a clear way to track them. The platform is especially useful for freelancers, contractors, consultants, side gig workers, and self-employed professionals who want a more organized workflow than spreadsheets or static invoice templates.&lt;/p&gt;

&lt;p&gt;Reinvoice helps users create invoices, manage clients, track payment status, send scheduled invoice emails, use payment reminders, export records, and monitor tax set-asides.&lt;/p&gt;

&lt;p&gt;The platform is designed around the needs of independent workers, not large finance teams. That makes it a practical option for people who want invoice management without unnecessary complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invoice creation and client management
&lt;/h2&gt;

&lt;p&gt;Creating a professional invoice is one of the most basic needs for any freelancer or contractor. A clear invoice should include the client’s information, the service provided, pricing, due date, invoice number, payment terms, and any notes needed to explain the work.&lt;/p&gt;

&lt;p&gt;Reinvoice gives users a structured way to create and manage invoices, which helps reduce the risk of missing important billing details.&lt;/p&gt;

&lt;p&gt;Client management is also important. Instead of repeatedly typing the same client information into new documents, users can keep client records organized and reuse that information when creating future invoices.&lt;/p&gt;

&lt;p&gt;This is especially helpful for freelancers who work with repeat clients, consultants who bill monthly, or contractors who manage several jobs at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Payment status tracking
&lt;/h2&gt;

&lt;p&gt;One of the biggest benefits of invoice software is payment visibility.&lt;/p&gt;

&lt;p&gt;Sending an invoice does not guarantee that it will be paid on time. Freelancers and contractors need a way to know which invoices are still open, which ones are overdue, and which ones have already been paid.&lt;/p&gt;

&lt;p&gt;Reinvoice includes payment status tracking so users can keep an eye on where each invoice stands.&lt;/p&gt;

&lt;p&gt;This is valuable because cash flow matters for independent workers. Late payments can affect bills, savings, project planning, and tax preparation. A clear invoice dashboard can help users avoid guessing and focus on the invoices that need action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduled invoice emails and reminders
&lt;/h2&gt;

&lt;p&gt;Following up with clients can be uncomfortable, but it is a normal part of getting paid.&lt;/p&gt;

&lt;p&gt;Invoice reminders make that process easier. Rather than manually writing a new message every time an invoice is due or overdue, users can rely on a more consistent follow-up system.&lt;/p&gt;

&lt;p&gt;Reinvoice supports scheduled invoice emails and reminders, which can help freelancers and contractors stay on top of client communication.&lt;/p&gt;

&lt;p&gt;This is useful for recurring work, project-based billing, retainer agreements, and any situation where invoices need to be sent or followed up on at specific times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tax tracking for 1099 income
&lt;/h2&gt;

&lt;p&gt;Tax planning is one of the most important parts of freelance and contractor work.&lt;/p&gt;

&lt;p&gt;Unlike traditional employees, 1099 contractors usually do not have taxes automatically withheld from each payment. That means they need to be aware of their income and set aside money for federal taxes, self-employment taxes, state taxes, and estimated quarterly payments when applicable.&lt;/p&gt;

&lt;p&gt;Reinvoice includes tax set-aside tracking to help self-employed workers understand their income and prepare more effectively.&lt;/p&gt;

&lt;p&gt;This feature does not replace professional tax advice, but it can make the process less chaotic. When invoice income is tracked throughout the year, it becomes easier to review earnings and prepare records for a CPA, bookkeeper, or tax software.&lt;/p&gt;

&lt;h2&gt;
  
  
  PDF and CSV exports
&lt;/h2&gt;

&lt;p&gt;Clean records matter.&lt;/p&gt;

&lt;p&gt;Freelancers and contractors often need to export invoices for clients, bookkeeping, accounting, taxes, or personal record keeping.&lt;/p&gt;

&lt;p&gt;Reinvoice supports PDF and CSV exports, which gives users flexibility. PDF exports are useful for saving and sharing individual invoices. CSV exports are useful for reviewing invoice data in spreadsheets, preparing reports, or organizing records before tax time.&lt;/p&gt;

&lt;p&gt;For small business owners, exportable data is important because it prevents financial information from being trapped inside one system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credit memos and invoice adjustments
&lt;/h2&gt;

&lt;p&gt;Not every invoice stays exactly the same after it is created.&lt;/p&gt;

&lt;p&gt;Sometimes a client receives a discount. Sometimes a billing correction is needed. Sometimes a partial credit needs to be applied to a future invoice.&lt;/p&gt;

&lt;p&gt;Reinvoice includes credit memo functionality, which helps users manage invoice adjustments more cleanly.&lt;/p&gt;

&lt;p&gt;This can be useful for contractors, consultants, and small service businesses that need a professional way to document corrections instead of editing records manually or relying on informal notes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digital invoice signatures
&lt;/h2&gt;

&lt;p&gt;Invoice integrity matters, especially when invoices are shared, downloaded, or referenced later.&lt;/p&gt;

&lt;p&gt;Reinvoice includes digital invoice signatures to help verify invoice authenticity and protect important invoice details from silent changes.&lt;/p&gt;

&lt;p&gt;For freelancers and contractors, this adds a stronger layer of trust to invoice records. It can also be useful when invoices need to be reviewed later for disputes, bookkeeping, or business documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should use Reinvoice?
&lt;/h2&gt;

&lt;p&gt;Reinvoice is a good fit for independent workers and small businesses that want a simple invoicing workflow without a heavy accounting system.&lt;/p&gt;

&lt;p&gt;It is especially relevant for:&lt;/p&gt;

&lt;p&gt;Freelancers who send client invoices and want better payment tracking.&lt;/p&gt;

&lt;p&gt;1099 contractors who need to track invoice income and tax set-asides.&lt;/p&gt;

&lt;p&gt;Consultants who bill clients for services, retainers, or project work.&lt;/p&gt;

&lt;p&gt;Handymen, electricians, and construction contractors who need invoice records for jobs.&lt;/p&gt;

&lt;p&gt;IT consultants and software contractors who invoice clients for technical work.&lt;/p&gt;

&lt;p&gt;Artists, creators, and side gig workers who want a cleaner way to manage payments.&lt;/p&gt;

&lt;p&gt;Self-employed professionals who want organized invoice records before tax season.&lt;/p&gt;

&lt;p&gt;The common thread is simple: these users need to get paid, track income, and stay organized without wasting time on complicated financial software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reinvoice vs invoice templates
&lt;/h2&gt;

&lt;p&gt;Invoice templates can be helpful when someone is just getting started, but they have limits.&lt;/p&gt;

&lt;p&gt;A template can help create one invoice. It usually does not help track whether that invoice was viewed, paid, overdue, exported, adjusted, or connected to a client record.&lt;/p&gt;

&lt;p&gt;As freelance work grows, templates often become harder to manage.&lt;/p&gt;

&lt;p&gt;Users may end up with multiple versions of files, inconsistent invoice numbers, scattered client details, and manual payment tracking. This can create confusion when reviewing income or preparing taxes.&lt;/p&gt;

&lt;p&gt;Invoice software like Reinvoice is more useful when someone needs an ongoing system, not just a single document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reinvoice vs full accounting software
&lt;/h2&gt;

&lt;p&gt;Full accounting platforms can be powerful, but not every freelancer needs that level of complexity right away.&lt;/p&gt;

&lt;p&gt;Some independent workers mainly need invoicing, payment status tracking, client records, tax awareness, reminders, and exports.&lt;/p&gt;

&lt;p&gt;Reinvoice focuses on those practical needs.&lt;/p&gt;

&lt;p&gt;That makes it a strong option for freelancers and contractors who want something more organized than templates, but simpler than a large accounting platform built for bigger businesses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why organized invoicing improves cash flow
&lt;/h2&gt;

&lt;p&gt;Cash flow is one of the most important parts of running a freelance or contractor business.&lt;/p&gt;

&lt;p&gt;When invoices are not tracked properly, it is easy to miss late payments or forget to follow up. Even one unpaid invoice can create stress, especially for independent workers who rely on steady client payments.&lt;/p&gt;

&lt;p&gt;Organized invoicing helps users see what is owed, what has been paid, and what needs attention.&lt;/p&gt;

&lt;p&gt;This makes it easier to plan ahead, follow up professionally, and avoid surprises.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why tax organization matters for self-employed workers
&lt;/h2&gt;

&lt;p&gt;Many freelancers and contractors only think about taxes when tax season arrives. By then, income records may be spread across different tools, emails, bank deposits, and invoice files.&lt;/p&gt;

&lt;p&gt;That creates unnecessary stress.&lt;/p&gt;

&lt;p&gt;A better approach is to track income throughout the year. When invoices, payments, and exports are organized, it becomes easier to prepare for tax filing, estimate obligations, and work with a CPA or tax preparer.&lt;/p&gt;

&lt;p&gt;Reinvoice supports this workflow by combining invoicing with tax set-aside tracking, which is especially helpful for 1099 workers and self-employed professionals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Freelancers, contractors, consultants, and self-employed workers need a reliable way to manage invoices and stay organized.&lt;/p&gt;

&lt;p&gt;Spreadsheets and templates can work at the beginning, but they often become messy as more clients, invoices, payments, reminders, and tax records are added.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://reinvoice.co" rel="noopener noreferrer"&gt;Reinvoice&lt;/a&gt; is a practical invoice software option for independent workers who want to create invoices, track payment status, manage clients, send reminders, export records, and monitor tax set-asides in one place.&lt;/p&gt;

&lt;p&gt;For anyone looking for simple invoice software for freelancers, 1099 contractors, or self-employed professionals, Reinvoice is worth checking out.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How We Built Cryptographic Invoice Signatures for a SaaS Invoicing Platform</title>
      <dc:creator>Reinvoice LLC</dc:creator>
      <pubDate>Sat, 06 Jun 2026 18:21:00 +0000</pubDate>
      <link>https://dev.to/reinvoice/how-we-built-cryptographic-invoice-signatures-for-a-saas-invoicing-platform-1mia</link>
      <guid>https://dev.to/reinvoice/how-we-built-cryptographic-invoice-signatures-for-a-saas-invoicing-platform-1mia</guid>
      <description>&lt;h1&gt;
  
  
  How Reinvoice Uses HMAC Signatures to Detect Invoice Tampering
&lt;/h1&gt;

&lt;p&gt;Every invoice sent through Reinvoice includes a cryptographic integrity signature.&lt;/p&gt;

&lt;p&gt;It is not a PDF stamp, a visual badge, or a checkbox. It is an HMAC-SHA256 hash generated from the invoice payload and a server-side signing secret. If signed invoice data changes after creation, Reinvoice can recompute the hash, compare it to the stored signature, and flag the invoice as potentially tampered with.&lt;/p&gt;

&lt;p&gt;Here is why we built it, how it works, and what we learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Integrity Checks Matter for Invoicing
&lt;/h2&gt;

&lt;p&gt;Invoices are high-value documents. A single altered field could change a payment amount, tax calculation, client record, or audit trail.&lt;/p&gt;

&lt;p&gt;Most invoicing systems treat invoices as ordinary database records. That works for normal CRUD workflows, but it does not automatically prove that the invoice data being viewed today is the same data that was created and sent.&lt;/p&gt;

&lt;p&gt;Reinvoice adds an integrity layer.&lt;/p&gt;

&lt;p&gt;When an invoice is created, we sign the fields that define the invoice. Later, when someone verifies the invoice, we recompute the signature from the current data and compare it against the original stored signature. If the values do not match, the invoice is flagged.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Implementation
&lt;/h2&gt;

&lt;p&gt;The signature is stored in two places: on the invoice record in the database, and behind a public verification endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timingSafeEqual&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SIGNATURE_FIELDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invoiceNumber&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;issuerName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clientName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;totalAmount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;currency&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;taxAmount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;issuedAt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dueDate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lineItems&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subtotal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;discountAmount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shippingAmount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateInvoiceHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InvoiceData&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SIGNATURE_FIELDS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;InvoiceData&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SIGNING_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verification endpoint accepts an invoice identifier or verification token, loads the invoice, recomputes the hash, and checks whether the stored signature still matches the current invoice data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verifyInvoiceSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoiceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;invoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findFirst&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;invoiceId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;signatureHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expectedHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateInvoiceHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signatureHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;timingSafeEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use &lt;code&gt;timingSafeEqual&lt;/code&gt; instead of a normal string comparison because signature comparison should not leak useful timing information to an attacker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why HMAC Instead of Public-Key Signatures?
&lt;/h2&gt;

&lt;p&gt;HMAC-SHA256 is a good fit for our current use case because verification is server-mediated. The signing secret stays on the Reinvoice server, and recipients verify invoices through a public endpoint rather than verifying locally inside the PDF.&lt;/p&gt;

&lt;p&gt;That gives us a few practical benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The signing secret never needs to be distributed to clients.&lt;/li&gt;
&lt;li&gt;There is no certificate chain, expiration, or renewal process to manage.&lt;/li&gt;
&lt;li&gt;The signature is small and easy to store.&lt;/li&gt;
&lt;li&gt;Verification can be integrated directly into the invoice page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The tradeoff is that verification requires Reinvoice to be online. You cannot independently verify the invoice offline with only the PDF. If we ever need offline verification, we would add public-key signatures alongside the current HMAC-based integrity check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the Signature Appears
&lt;/h2&gt;

&lt;p&gt;Every invoice page includes a verification badge:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Signed: This invoice was cryptographically verified by Reinvoice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When someone clicks “Verify signature,” Reinvoice checks the stored signature against the current invoice data. If the values match, the invoice is shown as authentic and unchanged. If they do not match, the badge changes state and the mismatch is logged for investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Sign structured data, not rendered PDFs
&lt;/h3&gt;

&lt;p&gt;Our first approach was too close to the PDF generation step. That made verification fragile because small rendering differences could change the final PDF bytes.&lt;/p&gt;

&lt;p&gt;Signing the structured invoice payload is more reliable. The invoice data is the source of truth, so that is what we protect.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Be explicit about signed fields
&lt;/h3&gt;

&lt;p&gt;The field list matters. If a field affects the invoice total, tax amount, payment expectations, or client-facing record, it should be considered for signing.&lt;/p&gt;

&lt;p&gt;We learned this when reviewing fields like &lt;code&gt;discountAmount&lt;/code&gt; and &lt;code&gt;shippingAmount&lt;/code&gt;. Leaving out financial fields creates gaps where invoice data could change without invalidating the signature.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Separate immutable invoice data from changing workflow state
&lt;/h3&gt;

&lt;p&gt;Some fields change naturally after an invoice is sent. Payment status is a good example. An invoice may move from &lt;code&gt;sent&lt;/code&gt; to &lt;code&gt;paid&lt;/code&gt; without meaning the original invoice was tampered with.&lt;/p&gt;

&lt;p&gt;For that reason, the signed payload should focus on the invoice data that should remain stable after sending. Workflow state can be tracked separately in the audit log.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Public verification needs rate limits
&lt;/h3&gt;

&lt;p&gt;The verification endpoint is intentionally public because clients receiving invoices by email should not need a Reinvoice account to verify authenticity.&lt;/p&gt;

&lt;p&gt;Public does not mean unlimited. The endpoint should still be rate-limited, use non-guessable verification tokens where possible, and avoid exposing sensitive invoice details.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Log failures carefully
&lt;/h3&gt;

&lt;p&gt;Verification failures are useful signals. They can reveal tampering attempts, data corruption, serialization bugs, or migration issues.&lt;/p&gt;

&lt;p&gt;We log signature mismatches for audit and debugging, but we avoid exposing sensitive details in public responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Picture
&lt;/h2&gt;

&lt;p&gt;HMAC signatures are one layer in Reinvoice’s broader invoice integrity system.&lt;/p&gt;

&lt;p&gt;Combined with tax calculation, payment tracking, and audit logs, they help freelancers and contractors trust that the invoice they created is the same invoice their client sees later.&lt;/p&gt;

&lt;p&gt;For a document tied to income, taxes, and client records, that trust matters.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>typescript</category>
      <category>saas</category>
    </item>
  </channel>
</rss>
