Skip to content

Invoice Object

The Invoice object represents a payment request from a freelancer to a client.

Object Structure

typescript
interface Invoice {
  // Identifiers
  id: string;
  invoiceNumber: string;
  status: InvoiceStatus;

  // Freelancer (creator) information
  freelancerWallet: string;
  freelancerName?: string;
  freelancerEmail?: string;
  freelancerCompany?: string;

  // Client (payer) information
  clientName: string;
  clientEmail: string;
  clientCompany?: string;
  clientAddress?: string;
  clientWallet?: string;

  // Invoice details
  title: string;
  description?: string;
  notes?: string;

  // Financial details
  subtotal: string;
  taxRate?: string;
  taxAmount?: string;
  discount?: string;
  total: string;
  currency: Currency;

  // Timestamps
  createdAt: string;
  updatedAt: string;
  dueDate?: string;
  paidAt?: string;

  // Blockchain reference
  transactionHash?: string;
  ledgerNumber?: number;
  payerWallet?: string;
  networkPassphrase: string;

  // Relations
  lineItems: LineItem[];
  payments?: Payment[];
  auditLogs?: InvoiceAuditLog[];
}

Field Reference

Identifiers

FieldTypeDescription
idstringUnique invoice ID (CUID format: cm123abc456def)
invoiceNumberstringHuman-readable invoice number (INV-0001, INV-0002, etc.)
statusInvoiceStatusCurrent invoice status

Example:

json
{
  "id": "cm3g4h5i6j7k8l9m0n",
  "invoiceNumber": "INV-0042",
  "status": "PAID"
}

Invoice Status

typescript
type InvoiceStatus =
  | "DRAFT"      // Created but not sent
  | "PENDING"    // Sent to client, awaiting payment
  | "PROCESSING" // Payment intent created, awaiting confirmation
  | "PAID"       // Payment confirmed on blockchain
  | "FAILED"     // Payment failed
  | "EXPIRED"    // Passed due date without payment
  | "CANCELLED"; // Cancelled by creator

Status Flow:


Freelancer Information

Information about the invoice creator (service provider).

FieldTypeRequiredDescription
freelancerWalletstringYesStellar wallet address (payment recipient)
freelancerNamestringNoFreelancer name
freelancerEmailstringNoFreelancer email
freelancerCompanystringNoFreelancer company name

Example:

json
{
  "freelancerWallet": "GAIXVVI3IHXPCFVD4NF6NFMYNHF7ZO5J5KN3AEVD67X3ZGXNCRQQ2AIC",
  "freelancerName": "John Doe",
  "freelancerEmail": "john@freelancer.com",
  "freelancerCompany": "JD Web Services"
}

Client Information

Information about the invoice recipient (payer).

FieldTypeRequiredDescription
clientNamestringYesClient name
clientEmailstringYesClient email
clientCompanystringNoClient company name
clientAddressstringNoClient billing address
clientWalletstringNoClient Stellar wallet (set after payment)

Example:

json
{
  "clientName": "Acme Corporation",
  "clientEmail": "billing@acme.com",
  "clientCompany": "Acme Corp",
  "clientAddress": "123 Main St, San Francisco, CA 94105",
  "clientWallet": null
}

Note: clientWallet is automatically populated when the invoice is paid.


Invoice Details

FieldTypeRequiredDescription
titlestringYesInvoice title (e.g., "Website Development - March 2024")
descriptionstringNoDetailed description
notesstringNoAdditional notes (terms, payment instructions)

Example:

json
{
  "title": "Web Development Services",
  "description": "Frontend development for e-commerce platform",
  "notes": "Payment due within 30 days. Thank you for your business!"
}

Financial Details

FieldTypeRequiredDescription
subtotalstringYesSum of all line items (before tax/discount)
taxRatestringNoTax percentage (e.g., "7.50" for 7.5%)
taxAmountstringNoCalculated tax amount
discountstringNoDiscount amount
totalstringYesFinal amount to be paid
currencyCurrencyYesPayment currency

Currency Types:

typescript
type Currency = "XLM" | "USDC" | "EURC";

Calculation:

total = subtotal + taxAmount - discount

Example:

json
{
  "subtotal": "1000.00",
  "taxRate": "7.50",
  "taxAmount": "75.00",
  "discount": "50.00",
  "total": "1025.00",
  "currency": "USDC"
}

Precision:

  • All amounts stored as Decimal(18, 7) in database
  • Supports up to 7 decimal places
  • Returned as strings in API responses to prevent floating-point errors

Timestamps

FieldTypeDescription
createdAtstringISO 8601 timestamp of creation
updatedAtstringISO 8601 timestamp of last update
dueDatestringISO 8601 timestamp of payment deadline
paidAtstringISO 8601 timestamp of payment confirmation

Example:

json
{
  "createdAt": "2024-03-01T10:00:00.000Z",
  "updatedAt": "2024-03-07T14:30:00.000Z",
  "dueDate": "2024-03-31T23:59:59.000Z",
  "paidAt": "2024-03-07T14:25:00.000Z"
}

Blockchain Reference

FieldTypeDescription
transactionHashstringStellar transaction hash (64 hex chars)
ledgerNumbernumberStellar ledger number
payerWalletstringWallet address that made the payment
networkPassphrasestringStellar network used

Network Passphrases:

  • Testnet: Test SDF Network ; September 2015
  • Mainnet: Public Global Stellar Network ; September 2015

Example (Paid):

json
{
  "transactionHash": "7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b",
  "ledgerNumber": 123456,
  "payerWallet": "GDPYEQVXKP7VVXV6XJZXJQVXQVXQVXQVXQVXQVXQVXQVXQVXQVXQVXQV",
  "networkPassphrase": "Test SDF Network ; September 2015"
}

Example (Unpaid):

json
{
  "transactionHash": null,
  "ledgerNumber": null,
  "payerWallet": null,
  "networkPassphrase": "Test SDF Network ; September 2015"
}

Relations

Line Items

typescript
interface LineItem {
  id: string;
  invoiceId: string;
  description: string;
  quantity: string;  // Decimal(10, 2)
  rate: string;      // Decimal(18, 7)
  amount: string;    // Decimal(18, 7) - quantity * rate
}

Example:

json
{
  "lineItems": [
    {
      "id": "li_abc123",
      "invoiceId": "cm3g4h5i6j7k8l9m0n",
      "description": "Website Homepage Design",
      "quantity": "1.00",
      "rate": "500.00",
      "amount": "500.00"
    },
    {
      "id": "li_def456",
      "invoiceId": "cm3g4h5i6j7k8l9m0n",
      "description": "Contact Page Development",
      "quantity": "5.00",
      "rate": "100.00",
      "amount": "500.00"
    }
  ]
}

Payments

typescript
interface Payment {
  id: string;
  invoiceId: string;
  transactionHash: string;
  ledgerNumber: number;
  fromWallet: string;
  toWallet: string;
  amount: string;
  asset: string;
  status: PaymentStatus;
  confirmedAt: string;
}

type PaymentStatus = "CONFIRMED" | "FAILED" | "REFUNDED";

Example:

json
{
  "payments": [
    {
      "id": "pay_xyz789",
      "invoiceId": "cm3g4h5i6j7k8l9m0n",
      "transactionHash": "7a8b9c0d...",
      "ledgerNumber": 123456,
      "fromWallet": "GDPYEQVX...",
      "toWallet": "GAIXVVI3...",
      "amount": "1025.00",
      "asset": "USDC",
      "status": "CONFIRMED",
      "confirmedAt": "2024-03-07T14:25:00.000Z"
    }
  ]
}

Audit Logs

typescript
interface InvoiceAuditLog {
  id: string;
  invoiceId: string;
  action: AuditAction;
  actorWallet: string;
  changes?: Record<string, any>;
  createdAt: string;
}

type AuditAction =
  | "CREATED"
  | "UPDATED"
  | "SENT"
  | "PAID"
  | "EXPIRED"
  | "CANCELLED"
  | "DELETED";

Example:

json
{
  "auditLogs": [
    {
      "id": "log_aaa111",
      "invoiceId": "cm3g4h5i6j7k8l9m0n",
      "action": "CREATED",
      "actorWallet": "GAIXVVI3...",
      "changes": null,
      "createdAt": "2024-03-01T10:00:00.000Z"
    },
    {
      "id": "log_bbb222",
      "invoiceId": "cm3g4h5i6j7k8l9m0n",
      "action": "SENT",
      "actorWallet": "GAIXVVI3...",
      "changes": {
        "status": { "from": "DRAFT", "to": "PENDING" }
      },
      "createdAt": "2024-03-01T11:00:00.000Z"
    },
    {
      "id": "log_ccc333",
      "invoiceId": "cm3g4h5i6j7k8l9m0n",
      "action": "PAID",
      "actorWallet": "GDPYEQVX...",
      "changes": {
        "status": { "from": "PROCESSING", "to": "PAID" },
        "transactionHash": "7a8b9c0d..."
      },
      "createdAt": "2024-03-07T14:25:00.000Z"
    }
  ]
}

Complete Example

json
{
  "id": "cm3g4h5i6j7k8l9m0n",
  "invoiceNumber": "INV-0042",
  "status": "PAID",

  "freelancerWallet": "GAIXVVI3IHXPCFVD4NF6NFMYNHF7ZO5J5KN3AEVD67X3ZGXNCRQQ2AIC",
  "freelancerName": "John Doe",
  "freelancerEmail": "john@freelancer.com",
  "freelancerCompany": "JD Web Services",

  "clientName": "Acme Corporation",
  "clientEmail": "billing@acme.com",
  "clientCompany": "Acme Corp",
  "clientAddress": "123 Main St, San Francisco, CA 94105",
  "clientWallet": "GDPYEQVXKP7VVXV6XJZXJQVXQVXQVXQVXQVXQVXQVXQVXQVXQVXQVXQV",

  "title": "Web Development Services - March 2024",
  "description": "Full-stack development for e-commerce platform",
  "notes": "Payment due within 30 days. Thank you for your business!",

  "subtotal": "1000.0000000",
  "taxRate": "7.50",
  "taxAmount": "75.0000000",
  "discount": "50.0000000",
  "total": "1025.0000000",
  "currency": "USDC",

  "createdAt": "2024-03-01T10:00:00.000Z",
  "updatedAt": "2024-03-07T14:30:00.000Z",
  "dueDate": "2024-03-31T23:59:59.000Z",
  "paidAt": "2024-03-07T14:25:00.000Z",

  "transactionHash": "7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b",
  "ledgerNumber": 123456,
  "payerWallet": "GDPYEQVXKP7VVXV6XJZXJQVXQVXQVXQVXQVXQVXQVXQVXQVXQVXQVXQV",
  "networkPassphrase": "Test SDF Network ; September 2015",

  "lineItems": [
    {
      "id": "li_abc123",
      "invoiceId": "cm3g4h5i6j7k8l9m0n",
      "description": "Website Homepage Design",
      "quantity": "1.00",
      "rate": "500.0000000",
      "amount": "500.0000000"
    },
    {
      "id": "li_def456",
      "invoiceId": "cm3g4h5i6j7k8l9m0n",
      "description": "Additional Pages (×5)",
      "quantity": "5.00",
      "rate": "100.0000000",
      "amount": "500.0000000"
    }
  ],

  "payments": [
    {
      "id": "pay_xyz789",
      "invoiceId": "cm3g4h5i6j7k8l9m0n",
      "transactionHash": "7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b",
      "ledgerNumber": 123456,
      "fromWallet": "GDPYEQVXKP7VVXV6XJZXJQVXQVXQVXQVXQVXQVXQVXQVXQVXQVXQVXQV",
      "toWallet": "GAIXVVI3IHXPCFVD4NF6NFMYNHF7ZO5J5KN3AEVD67X3ZGXNCRQQ2AIC",
      "amount": "1025.0000000",
      "asset": "USDC",
      "status": "CONFIRMED",
      "confirmedAt": "2024-03-07T14:25:00.000Z"
    }
  ]
}

Public vs Owner View

Public View (GET /api/invoices/:id)

Returns limited fields suitable for the payment page:

json
{
  "id": "cm3g4h5i6j7k8l9m0n",
  "invoiceNumber": "INV-0042",
  "status": "PENDING",
  "title": "Web Development Services",
  "description": "Full-stack development",
  "total": "1025.0000000",
  "currency": "USDC",
  "freelancerWallet": "GAIXVVI3...",
  "clientName": "Acme Corporation",
  "networkPassphrase": "Test SDF Network ; September 2015",
  "lineItems": [...]
}

Hidden fields:

  • freelancerEmail
  • clientEmail
  • clientAddress
  • notes (may contain sensitive info)
  • auditLogs

Owner View (GET /api/invoices/:id/owner)

Returns all fields including sensitive data:

json
{
  "id": "cm3g4h5i6j7k8l9m0n",
  "invoiceNumber": "INV-0042",
  // ... all fields including emails, addresses, etc.
  "auditLogs": [...]
}

Requires: Authentication + ownership verification


Database Schema

prisma
model Invoice {
  id                String        @id @default(cuid())
  invoiceNumber     String        @unique @map("invoice_number")
  status            InvoiceStatus @default(DRAFT)

  freelancerWallet  String        @map("freelancer_wallet")
  freelancerName    String?       @map("freelancer_name")
  freelancerEmail   String?       @map("freelancer_email")
  freelancerCompany String?       @map("freelancer_company")

  clientName        String        @map("client_name")
  clientEmail       String        @map("client_email")
  clientCompany     String?       @map("client_company")
  clientAddress     String?       @map("client_address")
  clientWallet      String?       @map("client_wallet")

  title             String
  description       String?
  notes             String?

  subtotal          Decimal       @db.Decimal(18, 7)
  taxRate           Decimal?      @map("tax_rate") @db.Decimal(5, 2)
  taxAmount         Decimal?      @map("tax_amount") @db.Decimal(18, 7)
  discount          Decimal?      @db.Decimal(18, 7)
  total             Decimal       @db.Decimal(18, 7)
  currency          Currency      @default(XLM)

  createdAt         DateTime      @default(now()) @map("created_at")
  updatedAt         DateTime      @updatedAt @map("updated_at")
  dueDate           DateTime?     @map("due_date")
  paidAt            DateTime?     @map("paid_at")

  transactionHash   String?       @map("transaction_hash")
  ledgerNumber      Int?          @map("ledger_number")
  payerWallet       String?       @map("payer_wallet")
  networkPassphrase String        @default("Test SDF Network ; September 2015")

  deletedAt         DateTime?     @map("deleted_at")

  lineItems         LineItem[]
  payments          Payment[]
  auditLogs         InvoiceAuditLog[]

  @@index([freelancerWallet])
  @@index([status])
  @@index([freelancerWallet, status])
  @@map("invoices")
}

Next Steps

Built on Stellar blockchain