feat(examples): add invoice-agent with X402 payment flow#428
feat(examples): add invoice-agent with X402 payment flow#428akash-kumar5 wants to merge 7 commits intoGetBindu:mainfrom
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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 ofanyfor the invoice parameter.The
anytype defeats TypeScript's type checking. Import and use theInvoicetype 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 ------ +## NotesAlso 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 ofany.All skill handlers use
anyfor 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_priceis 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
📒 Files selected for processing (10)
examples/invoice-agent/.env.exampleexamples/invoice-agent/README.mdexamples/invoice-agent/SKILL.mdexamples/invoice-agent/agent.tsexamples/invoice-agent/config.tsexamples/invoice-agent/invoice.tsexamples/invoice-agent/storage.tsexamples/invoice-agent/test/run.tsexamples/invoice-agent/types.tsexamples/invoice-agent/x402Handler.ts
| get_invoice: async ({ invoice_id }: any) => { | ||
| return { invoice: getInvoiceById(invoice_id) } | ||
| }, |
There was a problem hiding this comment.
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.
| 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 }.
| 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 | ||
| } |
There was a problem hiding this comment.
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.
| 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.
There was a problem hiding this comment.
🧹 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
📒 Files selected for processing (6)
examples/invoice-agent/.env.exampleexamples/invoice-agent/README.mdexamples/invoice-agent/agent.tsexamples/invoice-agent/storage.tsexamples/invoice-agent/test/run.tsexamples/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
There was a problem hiding this comment.
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
📒 Files selected for processing (1)
examples/invoice-agent/README.md
Paraschamoli
left a comment
There was a problem hiding this comment.
hey @akash-kumar5 try to use open router api key also bindufy your agent.
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
examples/invoice-agent/.env.exampleexamples/invoice-agent/README.mdexamples/invoice-agent/invoice_agent.pyexamples/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
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
🧹 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
📒 Files selected for processing (4)
examples/invoice-agent/.env.exampleexamples/invoice-agent/README.mdexamples/invoice-agent/invoice_agent.pyexamples/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
Paraschamoli
left a comment
There was a problem hiding this comment.
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. |
This PR introduces an invoice agent that demonstrates an end-to-end billing workflow using X402.
Features:
The agent exposes structured skills following the Bindu agent pattern:
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
Documentation
Documentation