A Web3 restaurant ordering platform with gasless payments, QR table ordering, and seamless onboarding
XStore is built for three Movement hackathon bounty tracks:
| Track | Focus | Key Innovation |
|---|---|---|
| Best Consumer App | Mobile-first restaurant ordering | Social login + gasless payments |
| Best x402 App | HTTP 402 Payment Protocol | Fee payer gas sponsorship |
| Best Privy Wallets App | Embedded wallet UX | Privy signature → Movement wallet derivation |
Traditional crypto payments create friction:
- Users need to buy gas tokens before transacting
- Complex wallet setup scares away mainstream users
- No real-world utility beyond speculation
XStore removes all friction for restaurant payments:
- Social Login: Email, Google, or wallet - your choice
- Gasless Payments: Platform sponsors all transaction fees
- Instant Onboarding: No gas tokens needed to start
- Real Utility: Order food, pay with stablecoins, get receipts
- QR Code Ordering - Scan table QR to browse menu and order
- Tab System - Accumulate orders, pay when ready (like a real restaurant tab)
- Gasless Payments - Pay only USDC, we cover gas fees
- Social Login - Email/Google via Privy, no seed phrases
- AI Chat - Get menu recommendations from AI assistant
- Easy Setup - Create store in 30 seconds
- Table Management - Generate QR codes for each table
- Real-time Tabs - Monitor open orders across tables
- Auto Settlement - Receive batch payouts automatically
- Reservation System - Accept crypto deposits for bookings
┌─────────────────────────────────────────────────────────────────────────┐
│ FRONTEND (Next.js 14) │
│ Port: 3000 │
├─────────────────────────────────────────────────────────────────────────┤
│ Privy Auth │ React Query │ Glass-morphism UI │
│ ├── Social Login │ ├── Server State │ ├── Mobile-first │
│ ├── Embedded Wallet │ ├── Optimistic UI │ ├── Staggered Animations │
│ └── Movement Derive │ └── Cache │ └── Responsive Grid │
├─────────────────────────────────────────────────────────────────────────┤
│ BACKEND (NestJS 10) │
│ Port: 3001 │
├─────────────────────────────────────────────────────────────────────────┤
│ x402 Protocol │ Facilitator │ Business Logic │
│ ├── 402 Response │ ├── Gas Sponsor │ ├── Stores/Tabs │
│ ├── Payment Verify │ ├── Fee Payer TX │ ├── Settlements │
│ └── Receipt Gen │ └── Registration │ └── Refunds │
├─────────────────────────────────────────────────────────────────────────┤
│ Prisma ORM + PostgreSQL │
├─────────────────────────────────────────────────────────────────────────┤
│ Movement Blockchain (Testnet) │
│ MOVE / USDC Stablecoin │
└─────────────────────────────────────────────────────────────────────────┘
Store ─────┬───── MenuItem ──── TabItem
├───── Table ─────── Tab ──────┬── TabItem
├───── Reservation ────────────┤
└───── Settlement ─────────────┴── Payment ── Refund
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Landing │────▶│ Privy Login │────▶│ Auto Wallet │────▶│ Start Order │
│ Page │ │ Email/Google │ │ Created │ │ (Gasless) │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
│ │
▼ ▼
No seed phrase Movement wallet
No gas purchase derived from Privy
- Glass-morphism UI: Semi-transparent cards with backdrop blur
- Responsive Grid: 1-col mobile → 3-col desktop
- Staggered Animations: Fade-in-up with delays
- Status Colors: Emerald/Yellow/Red for clear feedback
- Transaction Fees: 1-2% per payment (architecture ready)
- Reservation Deposits: 0.001 MOVE per booking
- Premium Features: Analytics, custom branding
- Gas Sponsorship: Platform bears cost for user acquisition
- Visit store listing → Browse stores
- Select store → View menu
- Add to tab → Adjust quantity & tip
- Close tab → Gasless USDC payment
- Receive on-chain receipt
XStore implements the full HTTP 402 Payment Required protocol with gas sponsorship innovation:
┌──────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐
│ Client │ │ Backend │ │Facilitator│ │Blockchain│
└────┬─────┘ └────┬─────┘ └─────┬─────┘ └────┬─────┘
│ │ │ │
│ 1. POST /close │ │ │
│───────────────────▶│ │ │
│ │ │ │
│ 2. HTTP 402 │ │ │
│ X-Payment-Required│ │ │
│◀───────────────────│ │ │
│ │ │ │
│ 3. Build Sponsored TX │ │
│─────────────────────────────────────────▶│ │
│ │ │ │
│ 4. TX Bytes │ │ │
│◀─────────────────────────────────────────│ │
│ │ │ │
│ 5. Sign as Sender │ │ │
│ (Local Ed25519) │ │ │
│ │ │ │
│ 6. Submit Signed │ │ │
│─────────────────────────────────────────▶│ │
│ │ │ │
│ │ 7. Co-sign as Fee Payer │
│ │ │──────────────────▶│
│ │ │ │
│ 8. TX Hash + Receipt │ │
│◀────────────────────────────────────────────────────────────│
| Innovation | Description |
|---|---|
| Gas Sponsorship Layer | x402 headers coordinate fee payer transactions |
| POS Tab Integration | Restaurant tabs accumulate → single x402 payment |
| Coin Registration Sponsor | New users register for USDC without gas |
| Auto Settlement Triggers | 30-second delay + daily cron for batch payouts |
| Refund Protocol | Integrated refund workflow with x402 receipts |
| Endpoint | Method | Purpose |
|---|---|---|
/x402/request |
POST | Get payment requirements (402 response) |
/x402/build-sponsored |
POST | Build fee payer transaction |
/x402/submit-sponsored |
POST | Submit with gas sponsorship |
/x402/verify |
POST | Verify on-chain payment |
/x402/info |
GET | Protocol capabilities |
- Direct Transfer Model: Funds → Store wallet (non-custodial)
- Fee Extraction Ready: PaymentsService architecture supports % deduction
- Settlement Batching: Could accumulate for cheaper batch transfers
XStore uses Privy embedded wallets for cryptographic wallet derivation, not just authentication:
// 1. User logs in with Privy (email/Google)
const { login, user } = usePrivy();
const { wallets } = useWallets();
// 2. Get embedded wallet and request signature
const embeddedWallet = wallets.find(w => w.walletClientType === 'privy');
const signature = await provider.request({
method: 'personal_sign',
params: [DERIVATION_MESSAGE, embeddedWallet.address],
});
// 3. Derive Movement wallet from signature
const seed = sha256(signature);
const privateKey = new Ed25519PrivateKey(seed);
const movementAccount = Account.fromPrivateKey({ privateKey });
// Result: Same Privy user → Same Movement address (deterministic)| Layer | Complexity Hidden |
|---|---|
| PrivyWalletContext | Wallet derivation, persistence, balance queries |
| useWallet Hook | Test vs production switching |
| usePayment Hook | x402 payment orchestration |
| UI Components | Just show "Pay" button |
- Click "Login with Google"
- See wallet address (auto-created)
- Click "Pay Tab"
- Done (no gas, no signing prompts)
- Privy creates embedded wallet
- App derives Movement Ed25519 key from Privy signature
- Transaction built with Facilitator as fee payer
- User's key signs locally
- Facilitator co-signs and submits
- User pays only USDC, gas covered
- EIP-191 Personal Sign: Standard signature request
- SHA256 Derivation: Cryptographically secure seed generation
- Ed25519 Keys: Movement-native key format
- LocalStorage Persistence: Wallet survives sessions
- Deterministic: Same user always gets same address
module xstore::payment {
// Generic payment function - works with MOVE, USDC, USDT
public entry fun pay<CoinType>(
payer: &signer,
store_address: address,
amount: u64,
payment_id: vector<u8>
) {
// Transfer coins
coin::transfer<CoinType>(payer, store_address, amount);
// Record payment on-chain
let record = PaymentRecord {
payment_id,
payer: signer::address_of(payer),
store: store_address,
amount,
timestamp: timestamp::now_seconds(),
};
// Emit event
event::emit(PaymentEvent { ... });
}
// Refund function
public entry fun refund<CoinType>(...) { ... }
// Store registration
public entry fun register_store(...) { ... }
}| Network | Token | Address |
|---|---|---|
| Testnet | TUSDC | 0x60a2f...::tusdc::TUSDC |
| Testnet | TUSDT | 0x60a2f...::tusdt::TUSDT |
| Mainnet | USDC | 0xbae20...::usdc::USDC (LayerZero) |
| Mainnet | USDT | 0xbae20...::usdt::USDT (LayerZero) |
- Node.js 18+
- Docker & Docker Compose
- Privy App ID (console.privy.io)
# 1. Clone and install
git clone <repo-url>
cd xstore
npm install
# 2. Configure environment
cp frontend/.env.example frontend/.env
cp backend/.env.example backend/.env
# 3. Start database
npm run docker:up
# 4. Initialize schema
npm run db:push
# 5. Start development
npm run dev# backend/.env
DATABASE_URL="postgresql://root:1234@localhost:5432/xstore"
MOVEMENT_NODE_URL="https://aptos.testnet.bardock.movementlabs.xyz/v1"
FACILITATOR_PRIVATE_KEY="your-ed25519-private-key"
# frontend/.env
NEXT_PUBLIC_PRIVY_APP_ID="your-privy-app-id"
NEXT_PUBLIC_API_URL="http://localhost:3001"- Frontend: http://localhost:3000
- Backend: http://localhost:3001
- API Docs: http://localhost:3001/api
xstore/
├── frontend/ # Next.js 14 App
│ ├── app/ # App Router pages
│ │ ├── stores/ # Store listing
│ │ ├── store/[id]/ # Store detail & order
│ │ ├── tab/[id]/ # Tab view & payment
│ │ ├── my-tabs/ # Customer's tabs
│ │ ├── manage/ # Store owner dashboard
│ │ ├── chat/ # AI assistant
│ │ └── charge/ # Faucet & registration
│ ├── components/ # React components
│ ├── context/ # Privy & Wallet contexts
│ ├── hooks/ # Custom hooks
│ └── lib/ # API client, utilities
│
├── backend/ # NestJS 10 API
│ ├── src/
│ │ ├── modules/
│ │ │ ├── x402/ # x402 protocol
│ │ │ ├── facilitator/ # Gas sponsorship
│ │ │ ├── payments/ # Payment processing
│ │ │ ├── stores/ # Store CRUD
│ │ │ ├── tabs/ # Tab management
│ │ │ ├── settlements/ # Batch payouts
│ │ │ └── refunds/ # Refund workflow
│ │ └── common/
│ │ └── x402/ # Protocol types & interceptors
│ └── prisma/
│ └── schema.prisma # Database schema
│
└── move-contracts/ # Move smart contracts
└── sources/
└── payment.move # Payment module
| Command | Description |
|---|---|
npm run dev |
Start frontend + backend |
npm run docker:up |
Start PostgreSQL |
npm run db:push |
Push Prisma schema |
npm run db:studio |
Open Prisma Studio |
npm run move:compile |
Compile Move contracts |
npm run move:deploy |
Deploy to testnet |
GET /stores- List all storesPOST /stores- Create storeGET /stores/:id- Get store details
POST /tabs- Create new tabGET /tabs- List tabs (filter by store, customer, status)POST /tabs/:id/items- Add item to tabPOST /tabs/:id/close- Close tab (triggers x402)
POST /x402/request- Initiate payment (returns 402)POST /x402/build-sponsored- Build fee payer TXPOST /x402/submit-sponsored- Submit with sponsorshipGET /x402/sponsorship-status- Check facilitator balance
GET /payments/:id- Get payment statusPOST /payments/process- Process x402 payment
- Browse: Visit
/storesto see restaurants - Select: Click store to view menu
- Order: Add items with quantity and tip
- Pay: Click "Pay Tab" - gasless USDC transfer
- Receipt: Get on-chain TX hash
- Create: Visit
/make-storeto set up - Tables: Add tables with QR codes
- Monitor: View open tabs in
/manage - Settle: Auto-receive batch payouts
| Layer | Technology |
|---|---|
| Frontend | Next.js 14, React 18, TailwindCSS |
| Backend | NestJS 10, Prisma, PostgreSQL |
| Auth | Privy (Social + Embedded Wallets) |
| Blockchain | Movement Network (Aptos-compatible) |
| Payments | x402 Protocol + Gas Sponsorship |
| AI | Google Generative AI |
- Non-custodial: Funds transfer directly to store wallets
- Local Signing: Private keys never leave browser
- Deterministic Derivation: Same user → same address
- Fee Payer Pattern: Users can't be drained for gas
- 5-minute Payment Window: Prevents stale transaction attacks
MIT License - see LICENSE for details.
Built on Movement Network
Gasless payments for the real world