Skip to content

B2B Invoice Financing

**FinTech · Supply chain · Multi-party validation**

Overview

B2B Invoice Financing

FinTech · Supply chain · Multi-party validation

The problem

Invoice factoring is a $3 trillion global industry. A supplier ships goods to a buyer on NET-60 terms; the supplier needs cash before 60 days; a financier buys the invoice at a discount, assuming the buyer will pay in full.

Fraud risk is central:

  • Fake invoices, supplier creates a fictional invoice and tries to factor it.
  • Double-factoring, supplier factors the same invoice with two financiers.
  • Non-delivery, supplier factors an invoice for goods that were never shipped.
  • Buyer dispute, buyer refuses to pay due to quality or delivery issues.

Current industry solutions:

  • Central platforms (Tradeshift, Taulia), each platform has a walled garden. Multi-platform fraud is possible.
  • Bank-run programs, each bank has its own factoring program; cross-bank fraud detection is minimal.
  • Paper / PDF trails, still common, especially in emerging markets and small-to-medium B2B.

The fundamental need: multiple independent parties should attest to the same invoice lifecycle events, and financiers should see those attestations before buying.

Why Quidnug fits

An invoice is a “thing with multiple stakeholders who all need to attest to its state.” That’s what title + event streams + relational trust are designed for.

ProblemQuidnug primitive
”Is this invoice actually issued by the supplier?”TITLE transaction signed by supplier
”Did the buyer acknowledge receipt?”EVENT: buyer.acknowledged
”Was it factored before?”EVENTS on same title tracked by unique ID
”Is this supplier creditworthy?”Relational trust in supplier via credit bureau
”Is the carrier reliable?”Relational trust in shipping carrier
”Cross-platform fraud check”Quidnug domain unifies participants

High-level architecture

┌─────────────────────────────────────────┐
│ factoring.supply-chain (domain) │
│ │
│ Validators: credit-bureau, carrier- │
│ rating-agency, industry-group │
└─────────────────────────────────────────┘
┌───────────────┼───────────────┬─────────────┐
│ │ │ │
▼ ▼ ▼ ▼
Supplier A Buyer X Carrier Z Financier F
(quid) (quid) (quid) (quid)
│ │ │ │
│ TITLE │ EVENT │ EVENT │ EVENT
│ (invoice) │ (ack) │ (ship) │ (factor)
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────┐
│ Invoice title + event stream │
│ inv-2026-04-18-abc → [issued, shipped, delivered, │
│ acked, factored, paid] │
└─────────────────────────────────────────────────────┘

Data model

Quids

  • Supplier: company-level quid, with its own finance team’s guardian set.
  • Buyer: company-level quid.
  • Carrier: shipping provider; signs delivery events.
  • Financier: factoring company.
  • Credit bureau (e.g., Dun & Bradstreet, rating agency): runs a Quidnug node, declares trust in suppliers/buyers based on their own data.

Domain

factoring.supply-chain
├── factoring.supply-chain.us
├── factoring.supply-chain.eu
└── factoring.supply-chain.asia

Invoice as title

{
"type": "TITLE",
"assetId": "inv-supplier-a-2026-04-18-abc",
"domain": "factoring.supply-chain.us",
"titleType": "invoice",
"owners": [{"ownerId": "supplier-a", "percentage": 100.0}],
"attributes": {
"buyer": "buyer-x",
"amount": "50000.00",
"currency": "USD",
"dueDate": "2026-06-17",
"terms": "NET-60",
"lineItems": [
{"description": "Widget A", "qty": 1000, "unitPrice": "50.00"}
],
"poReference": "PO-BUYER-X-789",
"invoiceHash": "<sha256 of full invoice PDF>"
},
"creator": "supplier-a",
"signatures": {
"supplier-a": "<supplier's signature>"
}
}

The full PDF stays in the supplier’s system; only a hash and structured metadata go on-chain.

Events through the lifecycle

Event stream for "inv-supplier-a-2026-04-18-abc":
1. invoice.issued
payload: {issuer: supplier-a, amount: 50000, ...}
signer: supplier-a
2. carrier.shipped
payload: {carrier: carrier-z, bol: "BOL-...", shipDate: ...}
signer: carrier-z
3. carrier.delivered
payload: {deliveryProof: "hash-of-POD"}
signer: carrier-z
4. buyer.acknowledged
payload: {acknowledgedAmount: 50000, expectedPayDate: 2026-06-17}
signer: buyer-x
5. factor.purchased
payload: {financier: financier-f, discount: 0.03,
purchasePrice: 48500}
signer: financier-f
(after this, financier is the entity buyer should pay)
6. payment.received
payload: {amount: 50000, paidDate: 2026-06-15}
signer: financier-f (confirming receipt)

Validator trust

The domain has validators (credit bureau, carrier rating agency, industry group). They emit trust-edge transactions declaring their views on participants:

credit-bureau-dnb ──0.85──► supplier-a (verified; good payment history)
carrier-rating-ag ──0.92──► carrier-z (A-rated carrier)
industry-group ──0.8──► buyer-x (member in good standing)

Financiers consume these + add their own direct trust.

Financier decision flow

A financier considering buying the invoice:

type FactoringDecision struct {
BuyerCredit float64
SupplierReputation float64
CarrierReputation float64
FraudSignals []FraudSignal
DiscountOffered float64
Decision string
}
func (f *Financier) EvaluateInvoice(invoiceID string) FactoringDecision {
invoice := f.quidnug.GetTitle(invoiceID)
// Trust in supplier
supplierTrust := f.quidnug.GetTrust(f.selfQuid,
invoice.Attributes["creator"], "factoring.supply-chain.us")
// Trust in buyer (creditworthiness)
buyerTrust := f.quidnug.GetTrust(f.selfQuid,
invoice.Attributes["buyer"], "factoring.supply-chain.us")
// Check for prior factor events (double-factoring detection)
events := f.quidnug.GetSubjectEvents(invoiceID, "TITLE")
for _, ev := range events {
if ev.EventType == "factor.purchased" {
return FactoringDecision{Decision: "REJECT",
FraudSignals: []FraudSignal{ {Type: "double-factoring"} }}
}
}
// Check for delivery events
hasDelivery := false
for _, ev := range events {
if ev.EventType == "carrier.delivered" {
hasDelivery = true
}
}
// Buyer acknowledgment?
hasBuyerAck := false
for _, ev := range events {
if ev.EventType == "buyer.acknowledged" {
hasBuyerAck = true
}
}
// Decision logic
if !hasDelivery {
return FactoringDecision{Decision: "PENDING",
Reason: "No delivery event yet"}
}
if !hasBuyerAck {
return FactoringDecision{Decision: "PENDING",
Reason: "Buyer hasn't acknowledged"}
}
risk := 1.0 - (supplierTrust.TrustLevel * 0.3 + buyerTrust.TrustLevel * 0.7)
discount := 0.01 + risk * 0.1
return FactoringDecision{
BuyerCredit: buyerTrust.TrustLevel,
SupplierReputation: supplierTrust.TrustLevel,
DiscountOffered: discount,
Decision: "APPROVE",
}
}

Double-factoring prevention

Key pattern: a single factor.purchased event per invoice. If a supplier tries to factor with a second financier:

  • Financier-2 pulls the invoice’s event stream before buying.
  • Sees a prior factor.purchased event.
  • Rejects.

Even across Quidnug nodes in different jurisdictions, push gossip ensures the factor.purchased event reaches all interested parties within seconds.

For additional safety, the title’s ownership transfers:

factor.purchased event triggers app-layer logic to update
the invoice's TITLE ownership from supplier → financier.
A second financier sees the supplier no longer owns the
invoice and rejects.

Fraud signal propagation

The merchant-fraud-consortium pattern applies here too. Financiers can emit fraud signals against suppliers or invoices they’ve caught:

eventType: "fraud.signal.invoice-forged"
subjectId: <supplier quid>
payload: {invoiceId: "...", evidence: "..."}

Other financiers consume these signals with their own trust weighting.

Key Quidnug features

  • TITLE transactions for invoices with ownership transfer support.
  • Event streams for the invoice lifecycle.
  • Relational trust for supplier/buyer/carrier evaluation.
  • Push gossip (QDP-0005) for fast cross-participant propagation.
  • Guardian sets for supplier finance teams (HSM failure recovery).
  • Lazy epoch probe (QDP-0007) for cross-jurisdiction validation.
  • Domain hierarchy for regional scoping.

Value delivered

DimensionBeforeWith Quidnug
Double-factoring detectionPlatform-specificCross-platform via unified event stream
Invoice forgery detectionAfter-the-fact reconciliationCryptographic signatures at issuance
Buyer acknowledgmentEmailed confirmationOn-chain signed event
Delivery verificationPaper bill-of-lading + trustSigned event from rated carrier
Financier time-to-decisionDays (manual review)Seconds (automated evaluation)
Cross-border invoice factoringMajor integration projectDomain-scoped trust + gossip
Dispute resolution evidenceContested documentationSigned event chain, tamper-evident
Credit bureau integrationBureau-specific APIBureau publishes trust edges on-chain

What’s in this folder

Runnable POC

Full end-to-end demo at examples/b2b-invoice-financing/:

  • invoice_factor.py, pure decision logic: missing-events -> pending, double-factoring -> reject, trust-weighted discount, fraud-signal veto.
  • invoice_factor_test.py, 15 pytest cases covering the full decision surface.
  • demo.py, twelve-step end-to-end flow: register six actors across supplier / buyer / carrier / bureau / two financiers, issue invoice, cross-party lifecycle attestation, race two financiers on the same invoice (second rejected for double-factoring), second invoice rejected via fraud signal.
Terminal window
cd examples/b2b-invoice-financing
python demo.py

Implementation

Concrete API calls, pseudocode, signing shape.

Implementation: B2B Invoice Financing

0. Participants and setup

Each role runs a Quidnug node or uses a shared consortium node:

  • Supplier, small/medium businesses; often use a shared node operated by their industry group.
  • Buyer, corporate; runs its own node or uses shared.
  • Carrier, shipping provider; runs its own node and signs delivery events.
  • Financier, factoring firm or bank.
  • Credit bureau, validator, runs its own node, publishes trust edges on-chain.

1. Supplier issues an invoice

Terminal window
# Supplier's application creates a signed TITLE for the invoice
curl -X POST $NODE/api/v1/titles -d '{
"type":"TITLE",
"assetId":"inv-supplier-a-2026-04-18-abc",
"domain":"factoring.supply-chain.us",
"titleType":"invoice",
"owners":[{"ownerId":"supplier-a","percentage":100.0}],
"attributes":{
"buyer":"buyer-x",
"amount":"50000.00",
"currency":"USD",
"dueDate":"2026-06-17",
"terms":"NET-60",
"invoiceHash":"<sha256>",
"poReference":"PO-BUYER-X-789"
},
"creator":"supplier-a",
"signatures":{"supplier-a":"<sig>"}
}'

2. Carrier signs ship + delivery events

Terminal window
# When the carrier picks up the shipment
curl -X POST $NODE/api/v1/events -d '{
"type":"EVENT",
"subjectId":"inv-supplier-a-2026-04-18-abc",
"subjectType":"TITLE",
"eventType":"carrier.shipped",
"payload":{
"carrier":"carrier-z",
"bol":"BOL-2026-04-18-XYZ",
"shipDate":1713400000,
"origin":"supplier-a-warehouse-1",
"destination":"buyer-x-receiving"
},
"creator":"carrier-z",
"signature":"<carrier sig>"
}'
# When delivery is confirmed
curl -X POST $NODE/api/v1/events -d '{
"type":"EVENT",
"subjectId":"inv-supplier-a-2026-04-18-abc",
"subjectType":"TITLE",
"eventType":"carrier.delivered",
"payload":{
"carrier":"carrier-z",
"deliveryDate":1713600000,
"podHash":"<sha256 of proof-of-delivery>"
},
"creator":"carrier-z",
"signature":"<carrier sig>"
}'

3. Buyer acknowledges

Terminal window
curl -X POST $NODE/api/v1/events -d '{
"type":"EVENT",
"subjectId":"inv-supplier-a-2026-04-18-abc",
"subjectType":"TITLE",
"eventType":"buyer.acknowledged",
"payload":{
"buyer":"buyer-x",
"acknowledgedAmount":"50000.00",
"expectedPayDate":"2026-06-17",
"notes":"All items received and inspected"
},
"creator":"buyer-x",
"signature":"<buyer sig>"
}'

4. Financier evaluates and factors

package financier
func (f *Financier) OfferFinancing(ctx context.Context, invoiceID string) (*Offer, error) {
// 1. Get invoice title
title, err := f.client.GetTitle(ctx, invoiceID)
if err != nil { return nil, err }
// 2. Get event stream
events, err := f.client.GetSubjectEvents(ctx, invoiceID, "TITLE")
if err != nil { return nil, err }
// 3. Check double-factor
for _, ev := range events {
if ev.EventType == "factor.purchased" {
return nil, fmt.Errorf("already factored")
}
}
// 4. Prerequisite events
hasDelivery := containsEvent(events, "carrier.delivered")
hasBuyerAck := containsEvent(events, "buyer.acknowledged")
if !hasDelivery || !hasBuyerAck {
return &Offer{Decision: "PENDING"}, nil
}
// 5. Trust evaluation
supplier := title.Owners[0].OwnerID
buyer := title.Attributes["buyer"].(string)
supTrust, _ := f.client.GetTrust(ctx, f.quid, supplier,
"factoring.supply-chain.us", nil)
buyTrust, _ := f.client.GetTrust(ctx, f.quid, buyer,
"factoring.supply-chain.us", nil)
// 6. Pricing
risk := 1.0 - (supTrust.TrustLevel*0.3 + buyTrust.TrustLevel*0.7)
discount := 0.015 + risk*0.08 // 1.5% - ~10%
amount, _ := strconv.ParseFloat(title.Attributes["amount"].(string), 64)
return &Offer{
Decision: "APPROVE",
OfferAmount: amount * (1 - discount),
Discount: discount,
SupplierTrust: supTrust.TrustLevel,
BuyerTrust: buyTrust.TrustLevel,
}, nil
}
func (f *Financier) Factor(ctx context.Context, invoiceID string, offer *Offer) error {
// Emit factor.purchased event, binds the invoice to this financier
event := map[string]interface{}{
"type": "EVENT",
"subjectId": invoiceID,
"subjectType": "TITLE",
"eventType": "factor.purchased",
"payload": map[string]interface{}{
"financier": f.quid,
"purchasePrice": offer.OfferAmount,
"discount": offer.Discount,
"purchasedAt": time.Now().Unix(),
},
"creator": f.quid,
"signature": f.sign(/* ... */),
}
return f.submit(ctx, event)
// In a mature implementation, also submit a TITLE transfer
// moving ownership from supplier to financier.
}

5. Payment flow

Terminal window
# When the buyer pays the financier (now the invoice owner)
curl -X POST $NODE/api/v1/events -d '{
"type":"EVENT",
"subjectId":"inv-supplier-a-2026-04-18-abc",
"subjectType":"TITLE",
"eventType":"payment.received",
"payload":{
"financier":"financier-f",
"amount":"50000.00",
"paidDate":1716048000,
"paymentRef":"WIRE-..."
},
"creator":"financier-f",
"signature":"<financier sig>"
}'

6. Credit bureau trust declarations

The credit bureau periodically refreshes its trust edges:

Terminal window
curl -X POST $NODE/api/trust -d '{
"type":"TRUST",
"truster":"credit-bureau-dnb",
"trustee":"supplier-a",
"trustLevel":0.85,
"domain":"factoring.supply-chain.us",
"nonce":<next>,
"description":"Verified credit: A- rating",
"validUntil":<now + 90d>
}'

Expires in 90 days → forces re-attestation. Financiers see the edge as stale after that.

7. Buyer-declared trust in supplier

Buyers with long relationships can directly endorse their suppliers:

Terminal window
curl -X POST $NODE/api/trust -d '{
"type":"TRUST",
"truster":"buyer-x",
"trustee":"supplier-a",
"trustLevel":0.95,
"domain":"factoring.supply-chain.us",
"description":"5-year supplier; never defaulted"
}'

This transitive path gives supplier-a credit even if a particular financier hasn’t worked with them directly.

8. Dispute handling

Buyer disputes delivery quality:

Terminal window
curl -X POST $NODE/api/v1/events -d '{
"type":"EVENT",
"subjectId":"inv-...",
"subjectType":"TITLE",
"eventType":"buyer.disputed",
"payload":{
"reason":"quality-issue",
"evidence":"<hash>",
"disputedAmount":"5000.00",
"notes":"100 of 1000 widgets defective"
},
"creator":"buyer-x",
"signature":"<buyer sig>"
}'

Financier sees the dispute event. Contract terms define who bears the risk; dispute events give financier auditable reason for partial payment / return.

9. Fraud signal for forged invoice

Financier caught a forged invoice attempt:

Terminal window
curl -X POST $NODE/api/v1/events -d '{
"type":"EVENT",
"subjectId":"supplier-fraud-x", /* the fraudulent supplier's quid */
"subjectType":"QUID",
"eventType":"fraud.signal.invoice-forged",
"payload":{
"reporter":"financier-f",
"evidence":"Invoice claims PO-789, buyer denies ever issuing that PO",
"invoiceIds":["inv-..."]
},
"creator":"financier-f",
"signature":"<financier sig>"
}'

Propagates via push gossip to other financiers. See ../merchant-fraud-consortium/ for the full fraud-sharing pattern.

10. Testing

func TestFactoring_DoubleFactorDetection(t *testing.T) {
// First factor succeeds.
// Second factor attempt on same invoice → fails at
// event-stream check.
}
func TestFactoring_BuyerAckRequired(t *testing.T) {
// Factor without buyer.acknowledged → PENDING, not approved.
}
func TestFactoring_StaleCreditBureauTrustExpires(t *testing.T) {
// Credit bureau trust edge past validUntil → not counted.
}

Where to go next

Threat model

Adversaries, assumed capabilities, mitigations.

Threat Model: B2B Invoice Financing

Assets

  1. Financier capital, money paid to suppliers for invoices.
  2. Invoice event-stream integrity, the cryptographic record of the invoice’s lifecycle.
  3. Supplier / buyer reputation, trust edges that drive future financing decisions.

Attackers

AttackerCapabilityGoal
FraudsterCan create fake supplier identityFactor fake invoices
Rogue supplierLegitimate supplier, dishonest intentDouble-factor, forge docs
BuyerLegitimate, may dispute for cash-flow reasonsDelay payment
CarrierLegitimate, may mis-sign in exchange for bribesConfirm non-delivery
FinancierLegitimate, competitive intelAccess others’ invoice data

Threats and mitigations

T1. Fake invoice from fake supplier

Attack. Fraudster creates a quid called supplier-fake, attempts to factor a fictitious invoice.

Mitigations.

  • Low starting trust. Financiers use relational trust; a supplier with no credit-bureau or buyer endorsements has trust ~0. Their invoices aren’t factored.
  • Credit bureau participation, legitimate suppliers are endorsed by credit bureaus; fraudsters aren’t.
  • Buyer acknowledgment required, fraudster needs a real buyer to acknowledge a non-existent invoice. Financiers can require this before factoring.

T2. Forged invoice from real supplier

Attack. Real supplier creates a fake invoice for non- existent goods and tries to factor.

Mitigations.

  • Buyer acknowledgment, buyer’s signed event is the critical validation. Buyer won’t acknowledge an invoice they didn’t actually order.
  • Carrier delivery event, carrier (with industry reputation trust) signs delivery.
  • Both need to be compromised for a forged invoice to progress.

T3. Double factoring

Attack. Supplier tries to factor the same invoice with two financiers.

Mitigations.

  • Single event stream per invoice. factor.purchased event is visible to all financiers. Second attempt sees the first and rejects.
  • Push gossip propagates the factor event within seconds to all participating nodes.
  • Title-ownership transfer (app-layer): after factor.purchased, the invoice TITLE’s owner updates from supplier to financier. Second financier sees the supplier no longer owns it.

Residual risk. Narrow window (seconds) during push propagation where a very fast fraudster could hit two financiers simultaneously. Mitigated by operational delay in financier decision (trust evaluation takes milliseconds but approval workflow is minutes).

T4. Buyer collusion with supplier

Attack. Buyer and supplier collude: supplier issues invoice for fake goods, buyer acknowledges, they split the financier’s money.

Mitigations.

  • Carrier delivery event, carrier is an independent third party (high trust); colluding suppliers can’t fake the carrier’s signature.
  • Credit bureau + industry group as validators, they detect patterns (buyer suddenly ordering 10× normal volume, etc.).

Residual risk. Protocol can’t prevent all collusion; financier’s own KYC/UBO checks + credit analysis are the complementary controls.

T5. Carrier collusion

Attack. Carrier and supplier collude: carrier signs fake carrier.delivered events.

Mitigations.

  • Carrier rating trust, industry rating agencies publish trust in carriers. A low-trust carrier’s signature is weighted less.
  • Cross-reference, carrier’s own fleet / tracking data should match the event. If industry-group validators detect divergence, carrier’s reputation tanks.

T6. Replay

Attack. Attacker replays a valid buyer.acknowledged event on a different invoice.

Mitigations.

  • Subject-ID binding, the event’s subjectId is the invoice ID; replaying to a different invoice requires re-signing (can’t, no key).
  • Anchor nonce monotonicity.

T7. Buyer dispute abuse

Attack. Buyer routinely disputes invoices to delay payment, manipulating cash flow.

Mitigations.

  • Dispute patterns visible, an event-stream history shows a buyer’s dispute-rate. Financiers raise the discount they charge on that buyer’s invoices.
  • Buyer’s reputation trust drops with repeated disputes.

T8. Cross-border regulatory/legal complexity

Attack. Invoice spans US and EU jurisdictions; one party is subject to sanctions, injunction, etc.

Mitigations.

  • Domain scoping, .us and .eu subdomains have different rules (e.g., GDPR-compliant data retention).
  • Jurisdiction-specific validators at the domain level can refuse to admit sanctioned parties.

Residual risk. Legal compulsion is legal compulsion. Protocol documents the events; compliance is on each party.

T9. Privacy leaks

Attack. Competitor observes which financiers are buying which invoices, inferring customer relationships.

Mitigations.

  • Raw invoice contents stay off-chain (hashed).
  • Structured metadata on-chain is more limited, amount, due date, counterparty identity.
  • For high-privacy deployments, participants can use pseudonymous quids (supplier uses a different quid per financing relationship).

T10. Compromised financier

Attack. Financier’s signing key compromised; attacker signs fake factor.purchased events to claim ownership of invoices they didn’t actually buy.

Mitigations.

  • Guardian recovery, financier rotates away from the compromised key.
  • Anchor nonce check, attacker’s signature at an old nonce rejected.
  • Invalidation, the compromised epoch frozen immediately after detection.

Not defended against

  1. Physical commodity quality, invoice may say “1000 widgets,” actual goods may vary. Inspection is physical- world.
  2. Supplier insolvency post-factor, financier bought the invoice; if buyer doesn’t pay (legitimate dispute, bankruptcy), supplier owes back. Contract-level concern.
  3. Nation-state-level forgery, if a jurisdiction coerces a carrier or credit bureau to sign false events, Quidnug documents the coercion but can’t prevent it.
  4. Insurance and underwriting, which risks are taken by financier vs. supplier vs. insurance is contractual, not protocol.

References