Skip to content

feat: Download receipt as PDF#875

Merged
Woody4618 merged 20 commits intosolana-foundation:masterfrom
hoodieshq:development-download-receipt
Mar 20, 2026
Merged

feat: Download receipt as PDF#875
Woody4618 merged 20 commits intosolana-foundation:masterfrom
hoodieshq:development-download-receipt

Conversation

@C0mberry
Copy link
Copy Markdown
Contributor

@C0mberry C0mberry commented Mar 11, 2026

Description

  • adding ability to download receipt as pdf

Type of change

  • New feature

Screenshots

Screenshot 2026-03-11 at 18 03 11 Screenshot 2026-03-11 at 18 03 24

Testing

  1. open http://localhost:3000/tx/4izwTCUeRGAMGReXeXDumiBzAgXPGz6KzccacCf1WU5YXCCpSDjAQT7J6D6dY45bL1NW9AiqwCuWEnz3hbGtZS2y?view=receipt&cluster=mainnet-beta
  2. click on download > pdf btn
  3. see the pdf

Related Issues

HOO-326

Checklist

  • My code follows the project's style guidelines
  • All tests pass locally and in CI
  • I have run build:info script to update build information
  • CI/CD checks pass
  • I have included screenshots for protocol screens (if applicable)

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 11, 2026

@C0mberry is attempting to deploy a commit to the Solana Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 11, 2026

Greptile Summary

This PR adds a "Download as PDF" feature to the Solana Explorer receipt page, generating a structured jsPDF document client-side that includes all on-chain payment details (sender, receiver, amount, fee, signature, memo), an editable supplier/items section for accounting use, a QR code linking to the receipt, and an optional live USD value fetched from a new internal Jupiter Price v3 proxy API.

Key additions:

  • generateReceiptPdf — full PDF layout with a page-overflow guard, try/finally SVG blob URL cleanup, and a proper sigStartY anchor for the clickable transaction signature link.
  • useDownloadReceipt — state machine hook (idle → downloading → downloaded/errored → idle) using both useState and a stateRef to prevent double-trigger races without stale closures.
  • /api/receipt/price/[mintAddress] — server-side proxy to Jupiter Price v3, with PublicKey validation, 502 normalisation for non-429 upstream errors, and superstruct schema validation on the response.
  • useTokenPrice + price-cache — SWR-backed hook limited to mainnet-beta, with intentional no-revalidate config matching the wider codebase pattern.
  • formatUsdValue — utility with NaN and negative-value guards, fully unit-tested.
  • The wrapped SOL mint (So111...112) is used as a fallback for native SOL transfers so USD values appear for the most common transaction type.

Minor findings:

  • WRAPPED_SOL_MINT is declared inside the ReceiptContent component rather than at module scope — no runtime impact but causes a redundant allocation per render.
  • The API route imports node-fetch explicitly rather than the platform-native global fetch already available in Next.js 13+ / Node.js 18+, adding an unnecessary dependency.
  • In use-download-receipt.ts, mountedRef.current = true inside the useEffect body is technically redundant given useRef(true), though it intentionally supports Strict Mode remount safety — consider initialising with useRef(false) to make the effect the single source of truth.

Confidence Score: 4/5

  • Safe to merge — all previously flagged critical issues have been addressed and no new runtime bugs were found.
  • The implementation is well-structured with comprehensive test coverage across PDF generation, download state management, and the USD formatting utility. All critical issues raised in earlier review rounds (blob URL leak, stale closure, link anchor position, page overflow, wrapped SOL mint fallback) have been correctly resolved. The remaining findings are minor style issues (module-scope constant, node-fetch vs native fetch, redundant ref initialisation) that carry no functional risk.
  • No files require special attention — the minor issues in receipt-page.tsx and route.ts are low-risk style improvements.

Important Files Changed

Filename Overview
app/features/receipt/lib/generate-receipt-pdf.ts Core PDF generation logic using jsPDF — well-structured with proper try/finally for SVG blob URL cleanup, correct sigStartY anchor for the clickable link region, and a page-overflow guard before the footer. No critical issues found.
app/features/receipt/lib/use-download-receipt.ts Download state machine hook using both useState and a stateRef to prevent double-trigger races. Minor: mountedRef.current = true in the effect body is redundant given the useRef(true) initialiser, but the pattern is intentional for Strict Mode safety.
app/features/receipt/receipt-page.tsx Integrates price fetching and PDF generation into the receipt page. Minor: WRAPPED_SOL_MINT constant is declared inside the component body rather than at module scope, causing a redundant allocation per render.
app/api/receipt/price/[mintAddress]/route.ts Next.js API route proxying Jupiter Price v3. Correctly validates the mint address as a valid PublicKey, normalises non-429 upstream errors to 502, and uses superstruct for response shape validation. Minor: imports node-fetch instead of the platform-native global fetch available in Node.js 18+ / Next.js 13+.
app/features/receipt/model/use-price.ts SWR hook for fetching token USD prices from the internal proxy. Only active on mainnet-beta; correctly short-circuits for undefined mint addresses. No issues found.
app/utils/index.ts Adds formatUsdValue utility with NaN and negative-value guards. Well-tested with dedicated unit tests covering edge cases.
app/features/receipt/ui/DownloadReceiptItem.tsx Clean UI component wiring useDownloadReceipt to PopoverMenuItem with appropriate loading/success/error icon and label states.
app/features/receipt/ui/ReceiptView.tsx Adds the Download popover button alongside the existing Share button. Layout updated from column to row flex. No issues found.

Sequence Diagram

sequenceDiagram
    participant User
    participant ReceiptPage as ReceiptContent (receipt-page.tsx)
    participant useTokenPrice
    participant PriceAPI as /api/receipt/price/[mint]
    participant Jupiter as Jupiter Price v3 API
    participant DownloadBtn as DownloadReceiptItem
    participant useDownloadReceipt
    participant generateReceiptPdf

    ReceiptPage->>useTokenPrice: useTokenPrice(mint)
    useTokenPrice->>PriceAPI: GET /api/receipt/price/{mintAddress}
    PriceAPI->>Jupiter: fetch price/v3?ids={mint}
    Jupiter-->>PriceAPI: { usdPrice: number }
    PriceAPI-->>useTokenPrice: { price: number } (cached 4h CDN)
    useTokenPrice-->>ReceiptPage: PriceResult { price, status }
    ReceiptPage->>ReceiptPage: formatUsdValue(parseFloat(total.formatted), price)
    ReceiptPage->>DownloadBtn: downloadPdf callback (with usdValue)

    User->>DownloadBtn: clicks Download > PDF
    DownloadBtn->>useDownloadReceipt: trigger()
    useDownloadReceipt->>useDownloadReceipt: stateRef → 'downloading'
    useDownloadReceipt->>generateReceiptPdf: download() → loadPdfDeps() + generateReceiptPdf(...)
    generateReceiptPdf->>generateReceiptPdf: build jsPDF doc (layout, fields, QR, logo)
    generateReceiptPdf-->>useDownloadReceipt: resolved
    useDownloadReceipt->>useDownloadReceipt: stateRef → 'downloaded', scheduleReset(2s)
    DownloadBtn-->>User: shows ✓ Downloaded!, resets to idle after 2s
Loading

Last reviewed commit: d29ad15

@C0mberry C0mberry requested a review from askov March 12, 2026 13:39
@C0mberry
Copy link
Copy Markdown
Contributor Author

@greptile-apps issue was addressed

@C0mberry
Copy link
Copy Markdown
Contributor Author

@greptile-apps issue was addressed

@C0mberry C0mberry force-pushed the development-download-receipt branch from dd50d0a to 0ce635e Compare March 16, 2026 15:19
@C0mberry
Copy link
Copy Markdown
Contributor Author

@greptile-apps issue was addressed

@C0mberry
Copy link
Copy Markdown
Contributor Author

@greptile-apps issue was addressed

@C0mberry
Copy link
Copy Markdown
Contributor Author

@greptile-apps issue was addressed

@C0mberry C0mberry requested a review from rogaldh March 17, 2026 16:12
Copy link
Copy Markdown
Contributor

@rogaldh rogaldh left a comment

Choose a reason for hiding this comment

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

Let's fix conflicts, and this is gtg. LGTM

@C0mberry C0mberry force-pushed the development-download-receipt branch from b0fe018 to 1ba0b44 Compare March 19, 2026 19:33
@C0mberry C0mberry force-pushed the development-download-receipt branch from fd2ef32 to eddd80c Compare March 20, 2026 15:06
@Woody4618 Woody4618 merged commit a60f28d into solana-foundation:master Mar 20, 2026
5 of 6 checks passed
@rogaldh rogaldh deleted the development-download-receipt branch March 20, 2026 16:31
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.

5 participants