FrancodesystemsFran<code>systems
May 28, 2026 · 11 min read · By Juan Cantón Rodríguez

Verifactu and ecommerce: how a bad integration breaks the hash chain

When Shopify, Stripe or Amazon create your invoices automatically, Verifactu turns into an integration problem, not an accounting one. Here is how the SHA-256 hash chain works, the three patterns that break it and what to audit on each platform.

Quick decision
Company deadline1 January 2027 (Royal Decree-law 15/2025)
Self-employed deadline1 July 2027
Maximum penalty50,000€ (art. 201 bis LGT)
What breaks the chainNon-idempotent retries · parallel webhooks · empty fields at the source
Window to audit/migrate without a bottleneckH1 2026 (companies) · Q3 2026 (self-employed)

The specific problem for ecommerce

If your business sells online, you are not the one creating the invoice. Shopify creates it when it confirms an order. A Stripe webhook creates it when a charge goes through. Amazon creates it when the customer buys. No human touches anything, and that is the good part except when Verifactu arrives, which assumes the invoicing software is a single one and the records are cryptographically chained.

Verifactu (regulated by Royal Decree 1007/2023 plus Royal Decree 254/2025, Order HAC/1177/2024 and the latest extension in Royal Decree-law 15/2025) requires every invoice to carry a SHA-256 hash computed over the invoice content chained to the hash of the immediately preceding invoice. If the order breaks, if a duplicate slips in, if an invoice shows up "with no previous hash", the AEAT detects the gap in an automated inspection. There is no "we will fix it next month" margin.

People coming from Excel or a single-seat program think: "I have Holded with native Verifactu, done". And for manual issuing that is true. The problem shows up when you let Shopify, Stripe or Amazon create invoices for you via an integration. If the integration does not respect the three principles we will see below, the chain breaks without anyone noticing until the inspection arrives.

How the SHA-256 hash chain works (the no-fluff version)

For each new invoice, the software computes:

// pseudo-code
hash(N) = SHA256(
invoice_content(N)
+ hash(N-1) // the immediately preceding one
)

The "invoice content" includes issuer tax ID, recipient tax ID, issue date, total amount, VAT rate, series and other fields defined by Order HAC/1177/2024. If any field changes after issuing, the hash no longer matches the next invoice, and that raises a red flag with the AEAT.

What this means in practice: the order in which invoices are created matters. You cannot process 10 webhooks in parallel and let the database resolve the order by timestamp. You have to serialise the entry into the Verifactu module, or make sure your queue guarantees order.

Three integration patterns that break the chain (and how to avoid them)

#1 — Non-idempotent retries that duplicate invoices

Shopify retries webhooks that do not return 200 within 5 seconds up to 19 times over 48 hours. If your integration creates a Holded invoice but fails right before returning 200, Shopify retries. If you have no deduplication by X-Shopify-Webhook-Id or by order_id, you create two invoices for the same order. Two different SHA-256 hashes for the same operation. The chain becomes inconsistent.

Fix: deduplication at the queue level (BullMQ with a fixed jobId per webhook_id), a processed-webhooks table with a UNIQUE constraint, or an idempotency token towards the Holded API. Any of the three. But one has to be there.

#2 — Parallel processing with no order guarantee

If your queue processes 4 jobs in parallel and two Shopify orders come in milliseconds apart, you can end up creating the invoice for order B before the one for order A. The Verifactu chain in Holded is assigned in the order the module receives the call, not in order-placement order. Result: the hash of invoice A points to the hash of invoice B instead of the real preceding one.

Fix: concurrency 1 for the worker that creates Verifactu invoices, or a distributed lock (Redis SETNX) over the invoicing module. The throughput loss is negligible. Holded's Verifactu module processes hundreds of invoices per minute on a single connection, and the guarantee is worth the cost.

#3 — Required fields that arrive empty from the source

Verifactu requires specific fields: recipient tax ID (valid for B2B), payment method, correct VAT rate. Shopify does not always give you the tax ID (Customer Tax ID is optional). Stripe gives you the email but not the company tax ID unless you enable Stripe Tax. Amazon Seller Central, for end customers, does not provide a tax ID.

If your integration creates an invoice with no tax ID when it is an intra-EU B2B that does require one, the invoice is technically non-compliant. The hash is computed the same and the chain does not technically break, but in an inspection the AEAT can penalise you for a badly issued invoice, which falls under the same art. 201 bis LGT.

Fix: validation of required fields in the integration before sending to Holded. If a tax ID is missing on an order that requires a B2B invoice, better to queue it for manual review than to create a bad invoice.

What to audit specifically on each platform

Shopify → Holded

  • The orders/create webhook with deduplication by order_id + the correct Holded series (B2C / intra-EU B2B / export).
  • VIES validation of the intra-EU tax ID before marking the invoice as VAT-exempt. If VIES says "invalid" and the invoice goes out at 0% VAT, they will claim the VAT back from you in an inspection.
  • The refunds/create webhook that fires a chained Holded corrective invoice, not an edit of the original.
  • Multi-currency with the EUR equivalent recorded at the order's exchange rate.

Full technical detail at /en/integrations/holded-shopify.

Stripe → Holded

  • Decide who issues the formal invoice: Stripe (with Stripe Tax) or Holded. Recommendation: Holded, because of native Verifactu.
  • The charge.succeeded webhook made idempotent with charge_id as the deduplication key.
  • Periodic reconciliation: compare the month's Stripe transactions against the Holded invoices for the same period. Any gap gets investigated.

Technical detail at /en/integrations/holded-stripe.

Amazon Seller Central → Holded

  • Distinguish Amazon invoices (Amazon Tax / Amazon Business Invoices) from your own invoices. Only your own enter your Verifactu chain.
  • Settlement reports downloaded from the SP-API processed in order, not in parallel.
  • FBA return handling with a chained corrective invoice in Holded.

More detail at /en/integrations/holded-amazon.

Real case: DTC brand with 500 Shopify orders/month

In a recent project for a DTC brand selling fashion on Shopify, the client had Holded connected via a community plugin. When we audited it, we found the following:

  • There was no deduplication by webhook_id. They had had a network failure during a Black Friday and Shopify had retried a webhook 4 times. Result: 4 duplicate Holded invoices for the same order, each with a different hash. The Verifactu chain technically still did not break (because each hash chained to the previous one) but accounting-wise there were 4 invoices that should not exist. The AEAT could detect it by cross-checking with Shopify Tax.
  • Worker concurrency = 5. Randomly, the invoicing order did not match the order-placement order. Inconsistent Verifactu chain.
  • VIES validation disabled. 12 invoices to Portuguese and Italian companies marked as exempt without validating the tax ID. Risk of a VAT claim.

After 5 days of audit + 2 weeks of rewriting the integration (a BullMQ queue with concurrency 1 over the Verifactu module, dedupe by webhook_id, mandatory VIES validation, nightly reconciliation): zero duplicates for 4 months, an auditable Verifactu chain, a calm accountant.

When to audit your setup (realistic timing)

The current dates after Royal Decree-law 15/2025:

  • 1 January 2027 — companies (SL, SA, cooperatives) subject to Corporate Income Tax must issue all invoices with Verifactu-compliant software. If your ecommerce is an S.L. and you issue with Shopify integrated, you have to audit NOW to leave yourself room.
  • 1 July 2027 — the self-employed (personal income tax), income-attribution entities, non-residents with a permanent establishment.

If your company has ecommerce and the integration is old or community-built: start in H1 2026. For the self-employed it is fine to leave it to Q3 2026 but no later. Q4 2026 will be a bottleneck at technical partners. Everyone is going to arrive late.

Frequently asked questions

Does Verifactu affect the invoices Shopify generates automatically?

Yes. The Verifactu obligation (Royal Decree 1007/2023, current dates 1 January 2027 for companies and 1 July 2027 for the self-employed per Royal Decree-law 15/2025) applies to ANY invoice issued, no matter whether a human creates it in the ERP or a webhook fires it from Shopify. The Shopify to Holded integration has to respect the chained SHA-256 hash and the QR code for every invoice created automatically, exactly as if it were manual.

What exactly is the Verifactu 'hash chain'?

Every Verifactu-compliant invoice carries a SHA-256 cryptographic hash computed over the invoice content (issuer tax ID, recipient tax ID, amount, date, etc.) AND the hash of the immediately preceding invoice. That chains all invoices together. If one is modified or inserted in the middle of the chain, the next computation no longer matches and the AEAT detects the gap. That is what prevents after-the-fact edits.

Why can a badly built integration break that chain?

Three typical reasons: (1) non-idempotent retries that create a duplicate invoice unknowingly, (2) parallel webhook processing that records invoices out of order, (3) required fields that arrive empty from the source (Shopify, Stripe) and the invoice is created without going through the ERP's Verifactu module. If your integration writes invoices and does not respect the three points, you are at risk.

If I use Holded and the integration was built by Shopify's official plugin, am I safe?

Probably yes for basic cases, but not guaranteed. Holded has native Verifactu (it is an official AEAT Social Collaborator) and official plugins usually point in the right direction. What to verify manually: that every webhook forces creation inside the Verifactu module, that retries are idempotent (no duplicates), that invoices with per-line discounts and multi-currency keep the required fields. If your volume is high or you have intra-EU B2B, we recommend an external audit.

What happens with the invoices Stripe generates automatically on charge?

If your model is ecommerce and Stripe is the gateway but Holded issues the formal invoice, what matters is that the charge.succeeded webhook to Holded preserves idempotency (key: the Stripe charge_id as the unique identifier). If Stripe Tax or Stripe Billing issues the invoice directly without going through Holded, that invoice is NOT in your Verifactu chain. You have to decide whether you want Stripe to be the official invoicing system (in which case Stripe must comply with Verifactu, hard in practice) or whether Holded re-issues the invoice with the correct QR and hash after the charge.

Amazon Seller Central issues invoices to end customers. Are they Verifactu valid?

The invoices Amazon issues on its own (Amazon Tax / Amazon Business Invoices) are Amazon's invoices, not the seller's. If you issue your own invoices in parallel (increasingly recommended for B2B), those do have to be Verifactu compliant. The Amazon to Holded integration must distinguish between 'Amazon invoices' (accounting record, they do not break your chain) and 'your invoices' (they enter the Verifactu chain). It is a distinction that is often forgotten and brings surprises.

Do refunds and partial returns break the chain?

They should not. The chain holds because the corrective invoice generated on return carries its own hash chained to the immediately preceding one. What does happen: a bad integration that cancels and re-issues the original invoice instead of generating a corrective one breaks the tax logic even if the technical chain is fine. Check that every Shopify refund fires a Holded corrective invoice, not an edit of the original invoice.

How much does auditing a Verifactu-compliant integration cost?

Auditing an existing integration (Shopify, Stripe or Amazon into Holded): 4 to 7 business days, fixed price after a 30-minute diagnosis with no commitment. A new integration with a Verifactu-compliant flow: 2 to 3 weeks depending on volume and edge cases (multi-currency, intra-EU B2B, multi-store). At Francodesystems we do it as a fixed-scope service, see /en/verifactu-for-foreign-companies for details.

Author

Juan Cantón Rodríguez

Founder & lead developer at Francodesystems. Maintainer of the open-source node n8n-nodes-holded with 100% coverage of the Holded API v2 (42 resources · 323 ops). Designs DDD architectures with idempotent queues and retries with backoff for Verifactu-compliant integrations in Spanish SMEs.

Want to audit your Verifactu integration before 2027?

30 minutes. We tell you which patterns break your chain, what to audit and what it costs to leave it clean. No commitment.