Skip to content

feat(examples): add invoice-agent with X402 payment flow#428

Open
akash-kumar5 wants to merge 7 commits intoGetBindu:mainfrom
akash-kumar5:feat/invoice-agent
Open

feat(examples): add invoice-agent with X402 payment flow#428
akash-kumar5 wants to merge 7 commits intoGetBindu:mainfrom
akash-kumar5:feat/invoice-agent

Conversation

@akash-kumar5
Copy link
Copy Markdown

@akash-kumar5 akash-kumar5 commented Mar 31, 2026

This PR introduces an invoice agent that demonstrates an end-to-end billing workflow using X402.

Features:

  • Invoice generation with structured payloads
  • X402-compatible payment request emission
  • Payment verification and invoice state updates
  • In-memory storage with pluggable design

The agent exposes structured skills following the Bindu agent pattern:

  • generate_invoice
  • get_invoice
  • list_invoices
  • verify_payment

It demonstrates the full lifecycle:
create → pay → verify → settled

This example is implemented in TypeScript for clarity, while following the same agent + skill structure as existing examples.

This adds a real-world financial workflow example to the repository.

Summary by CodeRabbit

  • New Features

    • Added an Invoice Agent example demonstrating end-to-end billing with X402 payment flow: create, get, list invoices and verify payments.
  • Documentation

    • Added a comprehensive README with setup, environment variables, usage examples, sample payloads/responses, and operational notes.
  • Documentation

    • Updated example environment template to include wallet address and optional API key entries.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new Invoice Agent example implementing an X402 payment flow: an agent implementation, README documentation, environment template, and a skill YAML for Bindu integration.

Changes

Cohort / File(s) Summary
Configuration & Environment
examples/invoice-agent/.env.example
Added AGENT_WALLET_ADDRESS and OPENROUTER_API_KEY entries.
Implementation
examples/invoice-agent/invoice_agent.py
New invoice agent: in-memory db, helpers (save_invoice, get_invoice_by_id, list_invoices), create_invoice validation and total calculation, verify_payment (marks paid, records tx), and handler(messages) routing (generate_invoice, get_invoice, list_invoices, verify_payment).
Skill Definition
examples/invoice-agent/skills/invoice-agent-skill/skill.yaml
Added skill metadata (id, name, description, version, tags) and JSON input/output modes.
Documentation
examples/invoice-agent/README.md
New README describing features, setup (dependencies, env vars), usage, example payloads/responses, and notes about mocked verification and in-memory storage.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Handler as Handler
    participant Storage as "Invoice Storage"
    participant Verify as "Verification Logic"

    Client->>Handler: send request (type + payload)
    alt generate_invoice
        Handler->>Storage: create and save invoice
        Storage-->>Handler: saved invoice
        Handler-->>Client: {invoice_id, total, payment_header}
    else get_invoice / list_invoices
        Handler->>Storage: read invoice(s)
        Storage-->>Handler: invoice data
        Handler-->>Client: {invoice / invoices}
    else verify_payment
        Handler->>Storage: get_invoice_by_id(invoice_id)
        alt Invoice Found
            Storage-->>Handler: invoice
            Handler->>Storage: update status to "paid", set tx_hash
            Storage-->>Handler: updated invoice
            Handler-->>Client: {verified: true, settled_amount, reason: null}
        else Invoice Not Found
            Storage-->>Handler: null
            Handler-->>Client: {verified: false, reason: "Invoice not found"}
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • Paraschamoli

Poem

🐰 I hopped in with a ledger bright,

I added invoices, totals right,
X402 headers, payments in sight,
A little DB that stores by night,
Hop! The agent hums — billing takes flight.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete relative to the provided template. While it lists features and skills, it omits critical required sections: Change Type, Scope, Linked Issue, Security Impact, Verification, Human Verification, and Checklist. Complete the PR description by filling in all required template sections, particularly Change Type (Feature), Scope checkboxes, Security Impact assessment, and verification details.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding an invoice-agent example with X402 payment flow support, which matches the PR's primary objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (9)
examples/invoice-agent/.env.example (1)

1-1: Add trailing newline for POSIX compliance.

Static analysis flagged that the file is missing a trailing newline. While minor, POSIX text files should end with a newline for compatibility with various tools.

Proposed fix
 AGENT_WALLET_ADDRESS=0x_your_wallet_here
+
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/.env.example` at line 1, The .env.example file ends
with the single variable declaration AGENT_WALLET_ADDRESS=0x_your_wallet_here
but lacks a trailing newline; update the file
(examples/invoice-agent/.env.example) by adding a final newline character after
the existing line so the file ends with a newline to satisfy POSIX/text-tooling
expectations.
examples/invoice-agent/x402Handler.ts (1)

4-7: Use proper typing instead of any for the invoice parameter.

The any type defeats TypeScript's type checking. Import and use the Invoice type for better safety.

Proposed fix
+import type { Invoice } from "./types.js"
 import { getInvoiceById } from "./storage.js"

 // mock interface (real lib can replace later)
 export async function verifyTransaction(
   tx_hash: string,
-  invoice: any
+  invoice: Invoice
 ) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/x402Handler.ts` around lines 4 - 7, The
verifyTransaction function currently accepts invoice: any which bypasses
TypeScript checks; import and use the concrete Invoice type and change the
signature to verifyTransaction(tx_hash: string, invoice: Invoice). Update the
file-level imports to bring in Invoice from its module (where Invoice is
defined) and adjust any property accesses inside verifyTransaction to match the
Invoice shape; if necessary, add runtime guards or optional chaining for
nullable fields to satisfy type checking.
examples/invoice-agent/test/run.ts (1)

44-44: Add error handling for the top-level async call.

If testFlow() throws, the promise rejection will be unhandled. Adding a .catch() ensures errors are visible.

Proposed fix
-testFlow()
+testFlow().catch((err) => {
+  console.error("Test flow failed:", err)
+  process.exit(1)
+})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/test/run.ts` at line 44, The top-level call to
testFlow() can produce unhandled promise rejections; update the invocation of
testFlow() to handle errors by appending a .catch(...) that logs or rethrows the
error (or wrap the call in an async IIFE with try/catch). Specifically, locate
the call to testFlow() and replace it with testFlow().catch(err => { /* log or
handle error, e.g. console.error('testFlow failed', err); process.exit(1); */ })
or an equivalent try/catch wrapper around an awaited testFlow() to ensure any
thrown errors are surfaced.
examples/invoice-agent/README.md (2)

112-116: Format the example input JSON for readability.

The compressed single-line JSON is hard to read. Consider formatting it like the output examples.

Proposed fix
 ## Example Input
 -------------
-```json
-   {  "recipient": "acme@example.com",  "items": [    { "description": "API access", "quantity": 1, "unit_price": 50 },    { "description": "Compute", "quantity": 2, "unit_price": 20 }  ],  "currency": "USDC"}   
-   ```
+```json
+{
+  "recipient": "acme@example.com",
+  "items": [
+    { "description": "API access", "quantity": 1, "unit_price": 50 },
+    { "description": "Compute", "quantity": 2, "unit_price": 20 }
+  ],
+  "currency": "USDC"
+}
+```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/README.md` around lines 112 - 116, Replace the
compressed single-line JSON in the "Example Input" code block with a prettified,
multi-line JSON object matching the style of the output examples: use a ```json
fenced block, put each key on its own line with two-space indentation, format
the "items" array as separate objects on separate lines, and ensure closing
braces/array are on their own lines; update the block that currently contains
the one-line JSON so it becomes the formatted JSON example shown in the proposed
fix.

33-34: Consider using consistent ATX heading style throughout.

Static analysis flagged inconsistent heading styles. The document mixes ATX (##) and setext (--- underline) styles.

Proposed fix for consistent ATX style
-Example Run
------------
+## Example Run

-Setup
------
+## Setup

-Notes
------
+## Notes

Also applies to: 130-131, 146-147

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/README.md` around lines 33 - 34, Convert all
setext-style headings (underlined with === or ---) to ATX-style headings using
leading hashes to ensure consistency; locate occurrences like the "Example Run"
heading and other setext pairs (the underlined headings around those sections)
and replace each underlined pair with the equivalent ATX form (e.g., "## Example
Run" for second-level headings), keeping the same heading text and level, and
update any surrounding spacing to match existing ATX headings in the document.
examples/invoice-agent/storage.ts (1)

9-14: Consider using nullish coalescing and explicit return type.

Using ?? instead of || is more semantically correct for checking undefined values. Also, adding an explicit return type improves API clarity.

Proposed refinements
-export function getInvoiceById(id: string): Invoice | null {
-  return db[id] || null
+export function getInvoiceById(id: string): Invoice | null {
+  return db[id] ?? null
 }

-export function listInvoices() {
+export function listInvoices(): Invoice[] {
   return Object.values(db)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/storage.ts` around lines 9 - 14, Replace the loose
falsy check in getInvoiceById with nullish coalescing: use db[id] ?? null to
avoid treating valid falsy values as missing; also add an explicit return type
for listInvoices (e.g., Invoice[]) so its signature is clear. Update the
functions getInvoiceById and listInvoices accordingly, keeping the same runtime
behavior but improving type precision and semantics.
examples/invoice-agent/agent.ts (1)

11-11: Consider using proper TypeScript types instead of any.

All skill handlers use any for their input parameters, which defeats TypeScript's type safety benefits. Since this is an example meant to demonstrate best practices, consider defining explicit input types.

♻️ Suggested typing improvement

Define input types (could be in types.ts):

interface GenerateInvoiceInput {
  recipient: string;
  recipient_wallet?: string;
  items: InvoiceItem[];
  currency: string;
  due_date?: string;
  webhook_url?: string;
}

interface GetInvoiceInput {
  invoice_id: string;
}

interface VerifyPaymentInput {
  invoice_id: string;
  tx_hash: string;
}

Then use them in the handlers:

-    generate_invoice: async (input: any) => {
+    generate_invoice: async (input: GenerateInvoiceInput) => {

Also applies to: 21-21, 29-29

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/agent.ts` at line 11, Replace the loose any types on
the skill handlers with explicit TypeScript interfaces: define
GenerateInvoiceInput, GetInvoiceInput, and VerifyPaymentInput (e.g., with fields
like recipient, recipient_wallet, items, currency, due_date, webhook_url for
GenerateInvoiceInput; invoice_id for GetInvoiceInput; invoice_id and tx_hash for
VerifyPaymentInput) either in a new types.ts or at the top of
examples/invoice-agent/agent.ts, then update the handlers generate_invoice,
get_invoice, and verify_payment to use these concrete input types instead of any
to restore type safety.
examples/invoice-agent/invoice.ts (2)

7-11: Consider floating-point precision for currency calculations.

Using floating-point arithmetic for monetary values can lead to precision errors (e.g., 0.1 + 0.2 !== 0.3). For an example demonstrating financial workflows, consider noting this limitation or using integer-based calculations (cents/smallest unit).

💡 Alternative using integer arithmetic

If unit_price is stored in cents (or smallest currency unit):

export function calculateTotal(items: InvoiceItem[]): number {
  const totalCents = items.reduce((sum, item) => {
    return sum + Math.round(item.quantity * item.unit_price)
  }, 0)
  return totalCents // Keep as cents, format for display elsewhere
}

Or document the limitation if keeping float-based calculation for simplicity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/invoice.ts` around lines 7 - 11, The calculateTotal
function uses floating-point arithmetic which can introduce currency precision
errors; update calculateTotal (and related InvoiceItem handling) to operate in
integer smallest-units (e.g., cents) by converting unit_price to cents and
summing integers (or Math.round per-item) and return the total in cents, and
document that the function returns cents so formatting/decimal conversion
happens at presentation time; ensure all code that constructs
InvoiceItem.unit_price or consumes calculateTotal is updated/annotated to use
the same smallest-unit convention.

34-43: Add return type annotation and consider avoiding direct mutation.

The function is missing a return type annotation. Also, directly mutating the object retrieved from storage works for in-memory storage but could cause issues if the storage implementation changes (e.g., returns a copy or uses a database).

♻️ Suggested improvement
-export function markInvoicePaid(id: string, tx_hash: string) {
+export function markInvoicePaid(id: string, tx_hash: string): Invoice {
   const invoice = getInvoiceById(id)
   if (!invoice) throw new Error("Invoice not found")

-  invoice.status = "paid"
-  invoice.tx_hash = tx_hash
+  const updatedInvoice: Invoice = {
+    ...invoice,
+    status: "paid",
+    tx_hash
+  }

-  saveInvoice(invoice)
-  return invoice
+  saveInvoice(updatedInvoice)
+  return updatedInvoice
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/invoice.ts` around lines 34 - 43, markInvoicePaid
lacks a return type and mutates the retrieved invoice in place; change it to
declare a proper return type (e.g., Invoice) and avoid direct mutation by
creating a new object (e.g., {...invoice, status: "paid", tx_hash}) and passing
that to saveInvoice; keep the existing not-found check using getInvoiceById and
return the newly saved invoice from markInvoicePaid to satisfy the annotated
return type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/invoice-agent/agent.ts`:
- Around line 21-23: The get_invoice handler currently returns whatever
getInvoiceById(invoice_id) yields (which can be undefined); update the
get_invoice implementation to check the result of getInvoiceById and handle the
not-found case explicitly: if the invoice is undefined, either throw a
descriptive error (e.g., throw new Error(`Invoice not found: ${invoice_id}`)) or
return a clear error object (e.g., { error: 'Invoice not found', invoice_id }),
otherwise return the found invoice. Locate the get_invoice async function in
agent.ts and add this check around the call to getInvoiceById to prevent
silently returning { invoice: undefined }.

In `@examples/invoice-agent/invoice.ts`:
- Around line 13-32: The createInvoice function calls
calculateTotal(payload.items) without validating payload or payload.items
causing possible runtime errors; add input validation at the start of
createInvoice: check that payload is an object and payload.items is a non-empty
array (or at least an array) and that each item has required fields used by
calculateTotal, and if validation fails throw a clear Error (e.g., "Invalid
payload: items must be an array") or return a rejected result; ensure you
reference and protect the calculateTotal call and preserve subsequent calls to
saveInvoice and uuidv4 by only proceeding when validation passes.

In `@examples/invoice-agent/SKILL.md`:
- Around line 1-12: The SKILL.md currently uses a bare YAML list and must be
converted to frontmatter; open SKILL.md and replace the top-level "skills:"
array with a proper YAML frontmatter block (--- ... ---) per the loader
schema—for example create either a single SKILL.md with frontmatter containing
required fields (name: generate_invoice, id, version, author, tags, input_modes,
output_modes) and move the skill description below, or split into separate
SKILL.md files (one per skill) each starting with --- and containing name, id,
version, author, tags, input_modes, output_modes and the description body;
ensure the frontmatter keys match examples like the research skill (id, name,
version, author, tags, input_modes, output_modes) so the loader can parse them.

In `@examples/invoice-agent/x402Handler.ts`:
- Around line 28-31: The handler mutates invoice.status and invoice.tx_hash when
result.success is true but never persists the change; update the code to call
the persistence function (saveInvoice) after modifying the invoice so the new
state is stored—e.g., inside the block where result.success is checked, set
invoice.status = "paid" and invoice.tx_hash = tx_hash and then call
saveInvoice(invoice) (or the project’s equivalent persistence API) and handle
any errors returned by saveInvoice to ensure the update is durable.

---

Nitpick comments:
In `@examples/invoice-agent/.env.example`:
- Line 1: The .env.example file ends with the single variable declaration
AGENT_WALLET_ADDRESS=0x_your_wallet_here but lacks a trailing newline; update
the file (examples/invoice-agent/.env.example) by adding a final newline
character after the existing line so the file ends with a newline to satisfy
POSIX/text-tooling expectations.

In `@examples/invoice-agent/agent.ts`:
- Line 11: Replace the loose any types on the skill handlers with explicit
TypeScript interfaces: define GenerateInvoiceInput, GetInvoiceInput, and
VerifyPaymentInput (e.g., with fields like recipient, recipient_wallet, items,
currency, due_date, webhook_url for GenerateInvoiceInput; invoice_id for
GetInvoiceInput; invoice_id and tx_hash for VerifyPaymentInput) either in a new
types.ts or at the top of examples/invoice-agent/agent.ts, then update the
handlers generate_invoice, get_invoice, and verify_payment to use these concrete
input types instead of any to restore type safety.

In `@examples/invoice-agent/invoice.ts`:
- Around line 7-11: The calculateTotal function uses floating-point arithmetic
which can introduce currency precision errors; update calculateTotal (and
related InvoiceItem handling) to operate in integer smallest-units (e.g., cents)
by converting unit_price to cents and summing integers (or Math.round per-item)
and return the total in cents, and document that the function returns cents so
formatting/decimal conversion happens at presentation time; ensure all code that
constructs InvoiceItem.unit_price or consumes calculateTotal is
updated/annotated to use the same smallest-unit convention.
- Around line 34-43: markInvoicePaid lacks a return type and mutates the
retrieved invoice in place; change it to declare a proper return type (e.g.,
Invoice) and avoid direct mutation by creating a new object (e.g., {...invoice,
status: "paid", tx_hash}) and passing that to saveInvoice; keep the existing
not-found check using getInvoiceById and return the newly saved invoice from
markInvoicePaid to satisfy the annotated return type.

In `@examples/invoice-agent/README.md`:
- Around line 112-116: Replace the compressed single-line JSON in the "Example
Input" code block with a prettified, multi-line JSON object matching the style
of the output examples: use a ```json fenced block, put each key on its own line
with two-space indentation, format the "items" array as separate objects on
separate lines, and ensure closing braces/array are on their own lines; update
the block that currently contains the one-line JSON so it becomes the formatted
JSON example shown in the proposed fix.
- Around line 33-34: Convert all setext-style headings (underlined with === or
---) to ATX-style headings using leading hashes to ensure consistency; locate
occurrences like the "Example Run" heading and other setext pairs (the
underlined headings around those sections) and replace each underlined pair with
the equivalent ATX form (e.g., "## Example Run" for second-level headings),
keeping the same heading text and level, and update any surrounding spacing to
match existing ATX headings in the document.

In `@examples/invoice-agent/storage.ts`:
- Around line 9-14: Replace the loose falsy check in getInvoiceById with nullish
coalescing: use db[id] ?? null to avoid treating valid falsy values as missing;
also add an explicit return type for listInvoices (e.g., Invoice[]) so its
signature is clear. Update the functions getInvoiceById and listInvoices
accordingly, keeping the same runtime behavior but improving type precision and
semantics.

In `@examples/invoice-agent/test/run.ts`:
- Line 44: The top-level call to testFlow() can produce unhandled promise
rejections; update the invocation of testFlow() to handle errors by appending a
.catch(...) that logs or rethrows the error (or wrap the call in an async IIFE
with try/catch). Specifically, locate the call to testFlow() and replace it with
testFlow().catch(err => { /* log or handle error, e.g. console.error('testFlow
failed', err); process.exit(1); */ }) or an equivalent try/catch wrapper around
an awaited testFlow() to ensure any thrown errors are surfaced.

In `@examples/invoice-agent/x402Handler.ts`:
- Around line 4-7: The verifyTransaction function currently accepts invoice: any
which bypasses TypeScript checks; import and use the concrete Invoice type and
change the signature to verifyTransaction(tx_hash: string, invoice: Invoice).
Update the file-level imports to bring in Invoice from its module (where Invoice
is defined) and adjust any property accesses inside verifyTransaction to match
the Invoice shape; if necessary, add runtime guards or optional chaining for
nullable fields to satisfy type checking.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 51650b15-5cc5-4e57-a06d-5e2f876dd107

📥 Commits

Reviewing files that changed from the base of the PR and between 0526d56 and 0ca9d8c.

📒 Files selected for processing (10)
  • examples/invoice-agent/.env.example
  • examples/invoice-agent/README.md
  • examples/invoice-agent/SKILL.md
  • examples/invoice-agent/agent.ts
  • examples/invoice-agent/config.ts
  • examples/invoice-agent/invoice.ts
  • examples/invoice-agent/storage.ts
  • examples/invoice-agent/test/run.ts
  • examples/invoice-agent/types.ts
  • examples/invoice-agent/x402Handler.ts

Comment thread examples/invoice-agent/agent.ts Outdated
Comment on lines +21 to +23
get_invoice: async ({ invoice_id }: any) => {
return { invoice: getInvoiceById(invoice_id) }
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing error handling when invoice is not found.

getInvoiceById returns undefined when the invoice doesn't exist, resulting in { invoice: undefined } being returned silently. This can lead to confusing behavior for callers. Consider throwing an error or returning a clear error response.

🛡️ Proposed fix
     get_invoice: async ({ invoice_id }: any) => {
-      return { invoice: getInvoiceById(invoice_id) }
+      const invoice = getInvoiceById(invoice_id)
+      if (!invoice) {
+        throw new Error(`Invoice not found: ${invoice_id}`)
+      }
+      return { invoice }
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
get_invoice: async ({ invoice_id }: any) => {
return { invoice: getInvoiceById(invoice_id) }
},
get_invoice: async ({ invoice_id }: any) => {
const invoice = getInvoiceById(invoice_id)
if (!invoice) {
throw new Error(`Invoice not found: ${invoice_id}`)
}
return { invoice }
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/agent.ts` around lines 21 - 23, The get_invoice
handler currently returns whatever getInvoiceById(invoice_id) yields (which can
be undefined); update the get_invoice implementation to check the result of
getInvoiceById and handle the not-found case explicitly: if the invoice is
undefined, either throw a descriptive error (e.g., throw new Error(`Invoice not
found: ${invoice_id}`)) or return a clear error object (e.g., { error: 'Invoice
not found', invoice_id }), otherwise return the found invoice. Locate the
get_invoice async function in agent.ts and add this check around the call to
getInvoiceById to prevent silently returning { invoice: undefined }.

Comment thread examples/invoice-agent/invoice.ts Outdated
Comment on lines +13 to +32
export function createInvoice(payload: any): Invoice {
const total = calculateTotal(payload.items)


const invoice: Invoice = {
id: `inv_${uuidv4()}`,
recipient: payload.recipient,
recipient_wallet: payload.recipient_wallet ?? CONFIG.AGENT_WALLET_ADDRESS,
items: payload.items,
currency: payload.currency,
total,
status: "pending",
due_date: payload.due_date ?? null,
webhook_url: payload.webhook_url ?? null,
created_at: new Date().toISOString()
}

saveInvoice(invoice)
return invoice
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add input validation to prevent runtime errors.

If payload.items is undefined or null, calculateTotal(payload.items) will throw a runtime error (Cannot read properties of undefined). Since the function accepts any, basic validation is necessary to provide meaningful error messages.

🛡️ Proposed fix with validation
 export function createInvoice(payload: any): Invoice {
+  if (!payload.items || !Array.isArray(payload.items)) {
+    throw new Error("Invoice items are required and must be an array")
+  }
+  if (!payload.recipient) {
+    throw new Error("Invoice recipient is required")
+  }
+  if (!payload.currency) {
+    throw new Error("Invoice currency is required")
+  }
+
   const total = calculateTotal(payload.items)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function createInvoice(payload: any): Invoice {
const total = calculateTotal(payload.items)
const invoice: Invoice = {
id: `inv_${uuidv4()}`,
recipient: payload.recipient,
recipient_wallet: payload.recipient_wallet ?? CONFIG.AGENT_WALLET_ADDRESS,
items: payload.items,
currency: payload.currency,
total,
status: "pending",
due_date: payload.due_date ?? null,
webhook_url: payload.webhook_url ?? null,
created_at: new Date().toISOString()
}
saveInvoice(invoice)
return invoice
}
export function createInvoice(payload: any): Invoice {
if (!payload.items || !Array.isArray(payload.items)) {
throw new Error("Invoice items are required and must be an array")
}
if (!payload.recipient) {
throw new Error("Invoice recipient is required")
}
if (!payload.currency) {
throw new Error("Invoice currency is required")
}
const total = calculateTotal(payload.items)
const invoice: Invoice = {
id: `inv_${uuidv4()}`,
recipient: payload.recipient,
recipient_wallet: payload.recipient_wallet ?? CONFIG.AGENT_WALLET_ADDRESS,
items: payload.items,
currency: payload.currency,
total,
status: "pending",
due_date: payload.due_date ?? null,
webhook_url: payload.webhook_url ?? null,
created_at: new Date().toISOString()
}
saveInvoice(invoice)
return invoice
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/invoice.ts` around lines 13 - 32, The createInvoice
function calls calculateTotal(payload.items) without validating payload or
payload.items causing possible runtime errors; add input validation at the start
of createInvoice: check that payload is an object and payload.items is a
non-empty array (or at least an array) and that each item has required fields
used by calculateTotal, and if validation fails throw a clear Error (e.g.,
"Invalid payload: items must be an array") or return a rejected result; ensure
you reference and protect the calculateTotal call and preserve subsequent calls
to saveInvoice and uuidv4 by only proceeding when validation passes.

Comment thread examples/invoice-agent/SKILL.md Outdated
Comment thread examples/invoice-agent/x402Handler.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
examples/invoice-agent/README.md (3)

1-2: Inconsistent heading style—choose ATX or Setext.

The title mixes ATX-style (#) with a Setext-style underline (====). Use one or the other for clarity.

📝 Proposed fix: remove redundant underline
 # Invoice Agent with X402 Payment Flow
-====================================

Alternatively, remove the # and keep the underline for Setext style:

-# Invoice Agent with X402 Payment Flow
+Invoice Agent with X402 Payment Flow
 ====================================
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/README.md` around lines 1 - 2, The heading in README
uses mixed styles ("# Invoice Agent with X402 Payment Flow" plus a Setext
underline), causing inconsistent Markdown; choose one style—remove the redundant
Setext underline (the line of === characters) so the file uses the ATX header
defined by the "#" on the "Invoice Agent with X402 Payment Flow" line, or
alternatively remove the leading "#" and keep the underline if you prefer
Setext—apply the chosen single style consistently.

115-115: Format the example input JSON for readability.

The input JSON is compressed on a single line, unlike the nicely formatted output examples above. Multi-line formatting would improve consistency and readability.

📝 Proposed fix
-   {  "recipient": "acme@example.com",  "items": [    { "description": "API access", "quantity": 1, "unit_price": 50 },    { "description": "Compute", "quantity": 2, "unit_price": 20 }  ],  "currency": "USDC"}   
+{
+  "recipient": "acme@example.com",
+  "items": [
+    {
+      "description": "API access",
+      "quantity": 1,
+      "unit_price": 50
+    },
+    {
+      "description": "Compute",
+      "quantity": 2,
+      "unit_price": 20
+    }
+  ],
+  "currency": "USDC"
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/README.md` at line 115, The example input JSON in the
README is currently a single compressed line; update the example block to use
multi-line, indented JSON for readability (e.g., break out "recipient", "items"
array with each item on its own line, and "currency" on its own line) so it
matches the formatted output examples above; locate and replace the compressed
JSON example string in the README (the example starting with { "recipient":
"acme@example.com", "items": [...], "currency": "USDC" }) with a properly
indented, multi-line JSON block.

34-34: Standardize horizontal rule lengths.

The horizontal rules use varying numbers of dashes (5, 6, 11, 13 vs. the 8 used elsewhere). Standardize to 8 dashes (--------) for consistency.

📝 Proposed fix
 ## Example Run
------------
+--------
 ## Example Input
--------------
+--------
 ## Skills
-------
+--------
 ## Setup
------
+--------
 ## Notes
------
+--------

As per static analysis hints, this addresses the MD035 (hr-style) warnings.

Also applies to: 113-113, 119-119, 131-131, 147-147

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/README.md` at line 34, Several horizontal rules in the
invoice-agent README use inconsistent dash counts; replace each HR that
currently has 5, 6, 11, or 13 dashes with exactly eight dashes (--------) so all
separators are standardized, ensure there are blank lines above and below each
HR and no trailing spaces, and update every instance flagged by static analysis
so all section separators in the README are uniform.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@examples/invoice-agent/README.md`:
- Around line 1-2: The heading in README uses mixed styles ("# Invoice Agent
with X402 Payment Flow" plus a Setext underline), causing inconsistent Markdown;
choose one style—remove the redundant Setext underline (the line of ===
characters) so the file uses the ATX header defined by the "#" on the "Invoice
Agent with X402 Payment Flow" line, or alternatively remove the leading "#" and
keep the underline if you prefer Setext—apply the chosen single style
consistently.
- Line 115: The example input JSON in the README is currently a single
compressed line; update the example block to use multi-line, indented JSON for
readability (e.g., break out "recipient", "items" array with each item on its
own line, and "currency" on its own line) so it matches the formatted output
examples above; locate and replace the compressed JSON example string in the
README (the example starting with { "recipient": "acme@example.com", "items":
[...], "currency": "USDC" }) with a properly indented, multi-line JSON block.
- Line 34: Several horizontal rules in the invoice-agent README use inconsistent
dash counts; replace each HR that currently has 5, 6, 11, or 13 dashes with
exactly eight dashes (--------) so all separators are standardized, ensure there
are blank lines above and below each HR and no trailing spaces, and update every
instance flagged by static analysis so all section separators in the README are
uniform.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 371f126a-c370-4349-a0c5-f91b47156a49

📥 Commits

Reviewing files that changed from the base of the PR and between 0ca9d8c and 3e549a0.

📒 Files selected for processing (6)
  • examples/invoice-agent/.env.example
  • examples/invoice-agent/README.md
  • examples/invoice-agent/agent.ts
  • examples/invoice-agent/storage.ts
  • examples/invoice-agent/test/run.ts
  • examples/invoice-agent/x402Handler.ts
✅ Files skipped from review due to trivial changes (3)
  • examples/invoice-agent/.env.example
  • examples/invoice-agent/storage.ts
  • examples/invoice-agent/test/run.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/invoice-agent/x402Handler.ts
  • examples/invoice-agent/agent.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
examples/invoice-agent/README.md (3)

39-39: Consider removing the colon from the heading.

The "Output:" heading includes a colon, which is inconsistent with other section headings in the document.

♻️ Proposed fix
-## Output:
+## Output
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/README.md` at line 39, Change the heading "## Output:"
to "## Output" (remove the trailing colon) so it matches the style of other
section headings; locate the markdown heading text "## Output:" in the README
and edit it to remove the colon.

7-14: Consider adding a brief explanation of X402.

The README mentions "X402 payment flow" and "X402-compatible payment requests" but doesn't explain what X402 is. A brief one-line explanation would help readers unfamiliar with the protocol understand the example better.

📚 Suggested addition

Add a brief explanation after line 7, for example:

 This example implements a billing agent that:
 
+*   Uses X402 protocol (HTTP-based payment flow for blockchain transactions)
 *   Generates invoices with structured line items
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/README.md` around lines 7 - 14, Add a one-line
definition of "X402" right after the first mention of the payment flow so
readers understand the protocol; update the README phrase "X402 payment flow" /
"X402-compatible payment requests" to include a short parenthetical or sentence
such as "X402 — a lightweight decentralized payment protocol for
streaming/settling microtransactions" (or another accurate one-line description
appropriate to the project) near that occurrence so the example remains
self-contained and clear.

4-5: Fix inconsistent heading style.

The document mixes ATX-style heading markers (##) with setext-style underlines (--------). Pick one style consistently. ATX style (using ## without underlines) is more common and easier to maintain. As per static analysis hints, markdownlint flags these as style violations (MD003).

♻️ Proposed fix: use pure ATX style

Remove the decorative dashes under each ## heading:

 ## Overview
---------
 ## Features
---------
 ## Example Run
---------
 ## Example Input
---------
 ## Skills
---------
 ## Setup
---------
 ## Notes
---------

Also applies to: 21-22, 33-34, 112-113, 118-119, 130-131, 146-147

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/README.md` around lines 4 - 5, The README mixes ATX
headings (e.g., "## Overview") with setext-style underline dashes; remove the
decorative dashed underline lines that follow ATX headings and keep each section
as a pure ATX heading (retain the "## ..." lines and delete the subsequent
"--------" lines) across the file wherever an ATX heading appears so all
headings use a consistent "##" style.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/invoice-agent/README.md`:
- Line 115: Reformat the single-line JSON example so it's pretty-printed with
standard indentation and line breaks for keys like "recipient", "items", and
"currency" to improve readability; update the README example block so the JSON
shows each object/property on its own line (nested array "items" should list
each item object on its own indented lines) and preserve the same
content/values.

---

Nitpick comments:
In `@examples/invoice-agent/README.md`:
- Line 39: Change the heading "## Output:" to "## Output" (remove the trailing
colon) so it matches the style of other section headings; locate the markdown
heading text "## Output:" in the README and edit it to remove the colon.
- Around line 7-14: Add a one-line definition of "X402" right after the first
mention of the payment flow so readers understand the protocol; update the
README phrase "X402 payment flow" / "X402-compatible payment requests" to
include a short parenthetical or sentence such as "X402 — a lightweight
decentralized payment protocol for streaming/settling microtransactions" (or
another accurate one-line description appropriate to the project) near that
occurrence so the example remains self-contained and clear.
- Around line 4-5: The README mixes ATX headings (e.g., "## Overview") with
setext-style underline dashes; remove the decorative dashed underline lines that
follow ATX headings and keep each section as a pure ATX heading (retain the "##
..." lines and delete the subsequent "--------" lines) across the file wherever
an ATX heading appears so all headings use a consistent "##" style.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 36c6e3ef-9699-4682-8516-0a90a84a860b

📥 Commits

Reviewing files that changed from the base of the PR and between 75f47da and 368b57b.

📒 Files selected for processing (1)
  • examples/invoice-agent/README.md

Comment thread examples/invoice-agent/README.md Outdated
Copy link
Copy Markdown
Member

@Paraschamoli Paraschamoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @akash-kumar5 try to use open router api key also bindufy your agent.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/invoice-agent/.env.example`:
- Around line 1-2: The .env.example file is missing a trailing newline which
triggers dotenv-linter's EndingBlankLine rule; update the file that defines
AGENT_WALLET_ADDRESS and OPENROUTER_API_KEY to end with a single blank line
(i.e., add a newline character after the last line) so the file terminates with
an empty line to satisfy the linter.

In `@examples/invoice-agent/invoice_agent.py`:
- Around line 37-38: After constructing recipient_wallet from
payload.get("recipient_wallet") or os.getenv("AGENT_WALLET_ADDRESS"), validate
it immediately and fail fast if it's falsy: raise a clear exception (e.g.,
ValueError) or return an HTTP error response so you never reach the code that
emits the X402 None:<amount> payment string; update the handler or function that
sets recipient_wallet to perform this check and provide a descriptive error
message.
- Around line 29-33: The code currently sums payload["items"] without validating
each item's fields; update the validation before computing total to ensure
payload is a dict with "items" as a list and that each item is a dict containing
numeric, non-negative "quantity" and "unit_price" (and optionally integer for
"quantity"); for any item failing validation raise ValueError with a clear
message referencing the offending item index; then compute total from validated
item["quantity"] * item["unit_price"] so negative or malformed invoice lines
cannot produce an invalid total.
- Around line 124-125: The except block currently returns raw exception text via
`return f"Error: {str(e)}"`, which leaks internal details; instead log the
exception internally (use logging.exception or your logger) to capture
stacktrace and return a sanitized, stable error response (e.g., a fixed message
like "An internal error occurred" or a structured error code) from the function
that contains this except block; update the `except Exception as e:` handler to
call the logger with the exception and replace the raw return with the generic
message or structured error.
- Around line 99-103: The payment_header currently formats a string; instead
build a structured X402 payload object with keys amount, token, network, and
pay_to_address so X402AgentExtension can parse it. Use invoice["total"] for
amount, invoice.get("currency", "USDC") for token, the runtime/network env var
used elsewhere in the module for network, and invoice["recipient_wallet"] for
pay_to_address, then return that object as payment_header instead of the inline
f-string so X402AgentExtension receives the required fields.

In `@examples/invoice-agent/README.md`:
- Around line 40-41: The README mixes underlined/HR-style headings with ATX
headings causing markdownlint MD003/MD035; replace the underline
separator/horizontal-rule lines that follow headings like "Run the agent:",
other occurrences around lines containing "Also applies to: 47-48, 89-90,
98-99", and any "-----" or "=====" separators with consistent ATX headings
(e.g., prefix headings with one or more # characters) and remove the redundant
underline lines so all headings use the same ATX format.
- Line 25: Update the README line that currently reads “Persistent invoice state
tracking (in-memory, pluggable)” to accurately reflect the implementation:
change it to something like “In-memory invoice state tracking (non-persistent,
pluggable)” or “Volatile (in-memory) invoice state tracking — pluggable, not
persisted across restarts” so users aren’t misled about persistence; modify the
README.md entry where this bullet is defined.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a00481af-7868-4f4a-b098-a6749839fa38

📥 Commits

Reviewing files that changed from the base of the PR and between 368b57b and 437da41.

📒 Files selected for processing (4)
  • examples/invoice-agent/.env.example
  • examples/invoice-agent/README.md
  • examples/invoice-agent/invoice_agent.py
  • examples/invoice-agent/skills/invoice-agent-skill/skill.yaml
✅ Files skipped from review due to trivial changes (1)
  • examples/invoice-agent/skills/invoice-agent-skill/skill.yaml

Comment thread examples/invoice-agent/.env.example Outdated
Comment thread examples/invoice-agent/invoice_agent.py
Comment thread examples/invoice-agent/invoice_agent.py Outdated
Comment thread examples/invoice-agent/invoice_agent.py
Comment thread examples/invoice-agent/invoice_agent.py Outdated
Comment thread examples/invoice-agent/README.md Outdated
Comment thread examples/invoice-agent/README.md Outdated
@akash-kumar5
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
examples/invoice-agent/invoice_agent.py (1)

156-156: Inconsistent return type for unknown request.

Other error paths return structured dicts (e.g., {"error": "bad_request", ...}), but this returns a plain string. For consistent API behavior, consider returning a structured error.

Suggested fix
-        return "Unknown request type"
+        return {"error": "bad_request", "message": "Unknown request type"}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/invoice-agent/invoice_agent.py` at line 156, The function currently
returns a plain string ("Unknown request type") for unknown requests which is
inconsistent with other error paths that return structured dicts; update the
unknown-request return in invoice_agent.py (look for the handler or function
that ends with return "Unknown request type") to return a structured error dict
matching the project's pattern (e.g., include keys like "error" and "message" or
"status" so it parallels other responses such as {"error": "bad_request",
"message": "..."}).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@examples/invoice-agent/invoice_agent.py`:
- Line 156: The function currently returns a plain string ("Unknown request
type") for unknown requests which is inconsistent with other error paths that
return structured dicts; update the unknown-request return in invoice_agent.py
(look for the handler or function that ends with return "Unknown request type")
to return a structured error dict matching the project's pattern (e.g., include
keys like "error" and "message" or "status" so it parallels other responses such
as {"error": "bad_request", "message": "..."}).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 025a55a8-4b7b-4c3d-89a5-fd8d88f798e8

📥 Commits

Reviewing files that changed from the base of the PR and between 368b57b and 785dd8d.

📒 Files selected for processing (4)
  • examples/invoice-agent/.env.example
  • examples/invoice-agent/README.md
  • examples/invoice-agent/invoice_agent.py
  • examples/invoice-agent/skills/invoice-agent-skill/skill.yaml
✅ Files skipped from review due to trivial changes (3)
  • examples/invoice-agent/.env.example
  • examples/invoice-agent/skills/invoice-agent-skill/skill.yaml
  • examples/invoice-agent/README.md

Copy link
Copy Markdown
Member

@Paraschamoli Paraschamoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey which agentic framework this agent is using?

@akash-kumar5
Copy link
Copy Markdown
Author

hey which agentic framework this agent is using?

This agent doesn't use an agentic framework, it's a plain Python handler wrapped with bindufy. Since the invoice workflow is fully deterministic, i didn't felt any need for LLM reasoning or tool-calling, so no framework like Agno/Crewai/Langchain is added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants