Source Code

;; Invoice Manager Contract
;; On-chain invoice creation and payment tracking
;; Halal - legitimate business
;; Clarity 4 compatible

(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-NOT-FOUND (err u404))
(define-constant ERR-ALREADY-PAID (err u405))
(define-constant ERR-WRONG-AMOUNT (err u406))

(define-data-var invoice-count uint u0)

(define-map invoices uint {
  issuer: principal, payer: principal, amount: uint,
  description: (string-utf8 200), due-block: uint,
  paid: bool, paid-at: uint
})
(define-map issuer-invoices principal uint)
(define-map payer-invoices principal uint)

(define-public (create-invoice (payer principal) (amount uint) (description (string-utf8 200)) (due-blocks uint))
  (let ((id (+ (var-get invoice-count) u1)))
    (map-set invoices id {
      issuer: tx-sender, payer: payer, amount: amount,
      description: description, due-block: (+ stacks-block-height due-blocks),
      paid: false, paid-at: u0
    })
    (map-set issuer-invoices tx-sender (+ (default-to u0 (map-get? issuer-invoices tx-sender)) u1))
    (map-set payer-invoices payer (+ (default-to u0 (map-get? payer-invoices payer)) u1))
    (var-set invoice-count id) (ok id)))

(define-public (pay-invoice (invoice-id uint))
  (let ((inv (unwrap! (map-get? invoices invoice-id) ERR-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get payer inv)) ERR-NOT-AUTHORIZED)
    (asserts! (not (get paid inv)) ERR-ALREADY-PAID)
    (try! (stx-transfer? (get amount inv) tx-sender (get issuer inv)))
    (map-set invoices invoice-id (merge inv { paid: true, paid-at: stacks-block-height }))
    (print { event: "invoice-paid", id: invoice-id, amount: (get amount inv) })
    (ok true)))

(define-public (cancel-invoice (invoice-id uint))
  (let ((inv (unwrap! (map-get? invoices invoice-id) ERR-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get issuer inv)) ERR-NOT-AUTHORIZED)
    (asserts! (not (get paid inv)) ERR-ALREADY-PAID)
    (map-set invoices invoice-id (merge inv { paid: true, paid-at: u0 })) (ok true)))

(define-read-only (get-invoice (id uint)) (map-get? invoices id))
(define-read-only (get-invoice-count) (ok (var-get invoice-count)))
(define-read-only (get-issuer-count (issuer principal)) (ok (default-to u0 (map-get? issuer-invoices issuer))))
(define-read-only (get-payer-count (payer principal)) (ok (default-to u0 (map-get? payer-invoices payer))))
(define-read-only (is-overdue (invoice-id uint))
  (match (map-get? invoices invoice-id)
    inv (ok (and (not (get paid inv)) (> stacks-block-height (get due-block inv))))
    (ok false)))

Functions (8)

FunctionAccessArgs
create-invoicepublicpayer: principal, amount: uint, description: (string-utf8 200
pay-invoicepublicinvoice-id: uint
cancel-invoicepublicinvoice-id: uint
get-invoiceread-onlyid: uint
get-invoice-countread-only
get-issuer-countread-onlyissuer: principal
get-payer-countread-onlypayer: principal
is-overdueread-onlyinvoice-id: uint