PYUSD QR payments with instant transactions, expiration, and merchant analytics.
PayStream is a lightweight, open-source application that demonstrates how PayPal USD (PYUSD) can be accepted through a QR-based payment flow on Ethereum. It enables a merchant to generate a QR invoice, a customer to scan and pay it with their wallet on Ethereum Sepolia, and the merchant to view receipts in a streamlined dashboard. The project makes stablecoin payments feel as natural as “scan and pay,” combining simplicity with blockchain transparency.
Problem & Motivation Stablecoin payments are powerful but their user experience is still too complex for everyday use. Merchants want simple invoice creation, while customers expect a seamless wallet flow. Existing solutions feel closer to developer tools than payment systems. PayStream bridges this gap by offering a simple, three-step process: 1) Merchant creates invoice in seconds. 2) Customer scans and pays with their wallet. 3) Merchant verifies receipt via a blockchain-backed dashboard.
Core Features
QR Invoice Generation (/merchant) – Merchant inputs amount, address (default: connected wallet), and optional note. Generates invoice JSON with metadata (merchant, amountWei, invoiceId, expiresAt, etc.), an EIP-681 URI for ERC-20 transfer, and a QR code encoding both formats. Each invoice is unique and includes an expiry time to prevent replay.
Scan & Pay (/pay) – Customer scans QR or pastes invoice data. App previews merchant, amount, and note. Wallet flow: approve PYUSD allowance to router, then execute pay(token, merchant, amount, invoiceId, expiresAt). The contract enforces that the invoice has not expired and has not already been used. Success state shows transaction hash and block explorer link.
Merchant Dashboard (/dashboard) – Connect wallet to view receipts. Displays on-chain PaymentReceived events in a table with timestamp, invoiceId, payer, amount, token, and transaction link. Supports filtering by merchant or invoiceId, with live/periodic updates.
Technical Design Smart Contract: PaymentRouter.sol processes ERC-20 payments with built-in expiry and anti-replay protection. Core function: pay(address token, address merchant, uint256 amount, bytes32 invoiceId, uint256 expiresAt). The function checks that the invoice has not expired, has not been reused, and that merchant and amount are valid. It then transfers PYUSD to the merchant and emits a PaymentReceived event with full details. Frontend: React + TypeScript bootstrapped with Vite for fast builds. wagmi for contract interactions and dynamic.xyz for wallet connection. Tailwind CSS for styling. qrcode.react for QR generation and @yudiel/react-qr-scanner for scanning. Deployable to Vercel or Netlify with minimal setup. Data Model: Invoice JSON encodes merchant, amount, invoiceId, and expiry. EIP-681 URI ensures wallet compatibility. Reads come directly from chain without external databases.
Why PayStream Stands Out
Familiar UX: feels like everyday QR payments, lowering friction.
Security: invoices expire automatically and cannot be reused.
Transparency: blockchain receipts are instantly verifiable.
Extensible: invoice schema and router architecture can adapt to multi-token or multi-chain flows.
Future Extensions Escrow and refunds for buyer protection, fiat off-ramps, multi-chain expansion (using ChainLink's CCIP), and EIP-712 signed invoices.
Summary PayStream demonstrates how stablecoin payments can be intuitive, trustworthy, and ready for real-world adoption. By combining QR invoicing, wallet interoperability, invoice expiry, and single-use protection with a blockchain-backed receipt dashboard, it shows a complete flow for accepting PYUSD on Ethereum — simple for merchants, frictionless for customers, and secure for all.
PayStream was built as a full-stack demo optimized for speed and clarity. The smart contract, PaymentRouter.sol, is written in Solidity (^0.8.24) and compiled with Hardhat. It uses OpenZeppelin’s IERC20 interface and includes safeguards against replay attacks by tracking used invoice IDs as well as enforcing expiry with a timestamp check. When a payment is made, the contract transfers PYUSD from payer to merchant and emits a PaymentReceived event that the frontend listens for.
On the frontend, the application is built with React + TypeScript, bundled with Vite for fast iteration. Wallet connection is handled through Dynamic.xyz, and wagmi + viem are used for contract interactions. Styling is done with Tailwind CSS. QR codes are generated with qrcode.react, and invoice scanning uses @yudiel/react-qr-scanner. The frontend queries Sepolia directly without subgraphs or external databases, which keeps the stack lightweight and transparent.
A few hacky shortcuts were used to keep scope realistic: invoices are simple JSON objects encoded in QR codes rather than managed in a database, and live dashboard updates rely on direct polling of blockchain events instead of indexing services. These decisions made the project faster to build while still showcasing a realistic end-to-end flow.