Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 70 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ A monorepo of demo projects showing how to integrate [MoonPay](https://www.moonp

## Projects

| Project | SDK | Description |
|---------|-----|-------------|
| Project | Stack | Description |
|---------|-------|-------------|
| `moonpay-nextjs-buy-and-sell` | Next.js 16 + MoonPay React SDK | Full buy and sell interface with live CoinGecko market data, currency filtering, and server-side URL signing |
| `moonpay-react-buy` | React SDK | Buy crypto widget using `@moonpay/moonpay-react` |
| `moonpay-react-sell` | React SDK | Sell crypto widget using `@moonpay/moonpay-react` |
| `moonpay-websdk-buy` | Web SDK | Buy crypto widget using `@moonpay/moonpay-js` (vanilla JS) |
| `moonpay-websdk-sell` | Web SDK | Sell crypto widget using `@moonpay/moonpay-js` (vanilla JS) |
| `moonpay-react-sell-oninitiatedeposit` | React SDK | Sell widget with `onInitiateDeposit` callback + MetaMask wallet signing |
| `server` | Node.js | Shared HMAC-SHA256 URL signing server |
| `server` | Node.js | Shared HMAC-SHA256 URL signing server for non-Next.js demos |

## Quick Start

Expand All @@ -26,6 +27,7 @@ A monorepo of demo projects showing how to integrate [MoonPay](https://www.moonp
- Node.js 18+
- npm 9+
- A MoonPay developer account ([dashboard.moonpay.com](https://dashboard.moonpay.com/developers))
- A CoinGecko API key ([coingecko.com/en/api](https://www.coingecko.com/en/api)) — required for the Next.js project

### 1. Install dependencies

Expand All @@ -37,13 +39,29 @@ This installs dependencies for all workspaces in one command.

### 2. Configure environment variables

Copy the example `.env` files and fill in your keys:
#### Next.js project (moonpay-nextjs-buy-and-sell)

```bash
# Signing server (required)
cp server/.env.example server/.env
cp moonpay-nextjs-buy-and-sell/.env.example moonpay-nextjs-buy-and-sell/.env.local
```

Fill in your keys in `.env.local`:

```env
NEXT_PUBLIC_BASE_URL=http://localhost:3000

MOONPAY_API_KEY=pk_test_
MOONPAY_SECRET_KEY=sk_test_
COINGECKO_API_KEY=CG-
COINGECKO_BASE_URL=https://api.coingecko.com/api/v3
```

> All keys except `NEXT_PUBLIC_BASE_URL` are server-only — they are never bundled into client code.

#### Other demo projects (React / WebSDK)

# React projects (optional — defaults work for local dev)
```bash
cp server/.env.example server/.env
cp moonpay-react-buy/.env.example moonpay-react-buy/.env
cp moonpay-react-sell/.env.example moonpay-react-sell/.env
```
Expand All @@ -54,43 +72,57 @@ At minimum, set your `MOONPAY_SECRET_KEY` in `server/.env`:
MOONPAY_SECRET_KEY=sk_test_your_secret_key_here
```

### 3. Start the signing server
### 3. Start a project

#### Next.js project

No separate signing server needed — signing is handled internally via a Next.js Route Handler.

```bash
npm run start:server
npm run start:nextjs
```

This runs on `http://localhost:5000` by default. All demo projects point here for URL signing.
Open [http://localhost:3000](http://localhost:3000).

#### Other demo projects

Start the shared signing server first:

### 4. Start a demo project
```bash
npm run start:server
```

In a separate terminal, run any of:
Then in a separate terminal, run any of:

```bash
npm run start:react-buy # React buy widget (port 3000)
npm run start:react-sell # React sell widget (port 3000)
npm run start:websdk-buy # WebSDK buy widget (port 8080)
npm run start:websdk-sell # WebSDK sell widget (port 8080)
npm run start:react-sell-deposit # React sell + onInitiateDeposit (port 3000)
npm run start:wallet-page # MetaMask wallet signing page (port 3001)
npm run start:react-buy # React buy widget (port 3000)
npm run start:react-sell # React sell widget (port 3000)
npm run start:websdk-buy # WebSDK buy widget (port 8080)
npm run start:websdk-sell # WebSDK sell widget (port 8080)
npm run start:react-sell-deposit # React sell + onInitiateDeposit (port 3000)
npm run start:wallet-page # MetaMask wallet signing page (port 3001)
```

## How URL Signing Works

MoonPay requires widget URLs to be signed with HMAC-SHA256 to prevent parameter tampering. The secret key must never be exposed in frontend code.

```
1. Frontend builds widget URL with parameters (apiKey, walletAddress, amount, etc.)
2. Frontend sends the URL to the signing server (GET /sign-url?url=...)
3. Server signs the URL's query string with HMAC-SHA256 using the secret key
4. Signature is returned to the frontend, which passes it to the MoonPay SDK
1. Frontend builds a widget URL with parameters (walletAddress, amount, etc.)
2. The apiKey is stripped from the URL before it leaves the client
3. The URL is sent to the signing endpoint (/api/sign-url or the Express server)
4. The server re-injects the apiKey from the environment, then signs with HMAC-SHA256
5. The signature is returned to the frontend and passed to the MoonPay SDK
```

The Next.js project handles this entirely within Next.js Route Handlers. The other demos use the shared Express signing server in `server/`.

## Ports

| Service | Port | Notes |
|---------|------|-------|
| Signing server | 5000 | Configurable via `PORT` env var |
| Next.js project | 3000 | Built-in signing via Route Handler — no separate server needed |
| Signing server | 5000 | For non-Next.js demos. Configurable via `PORT` env var |
| React demos | 3000 | Vite dev server |
| Hosted wallet page | 3001 | For onInitiateDeposit flow |
| WebSDK demos | 8080 | live-server |
Expand All @@ -100,20 +132,24 @@ MoonPay requires widget URLs to be signed with HMAC-SHA256 to prevent parameter
All scripts can be run from the repo root:

```bash
npm run setup # Install all dependencies
npm run start:server # Start the signing server
npm run start:react-buy # Start React buy demo
npm run start:react-sell # Start React sell demo
npm run start:websdk-buy # Start WebSDK buy demo
npm run start:websdk-sell # Start WebSDK sell demo
npm run setup # Install all dependencies
npm run start:nextjs # Start the Next.js buy and sell project
npm run start:server # Start the shared signing server
npm run start:react-buy # Start React buy demo
npm run start:react-sell # Start React sell demo
npm run start:websdk-buy # Start WebSDK buy demo
npm run start:websdk-sell # Start WebSDK sell demo
npm run start:react-sell-deposit # Start React sell + deposit demo
npm run start:wallet-page # Start wallet signing page
npm run start:wallet-page # Start wallet signing page
npm run build # Build all projects
npm run test # Run tests across all workspaces
npm run clean # Remove all node_modules, dist, and .next folders
```

## Tech Stack

- **React SDK**: [Vite](https://vite.dev/) + React 18 + `@moonpay/moonpay-react`
- **Web SDK**: Vanilla JS + `@moonpay/moonpay-js` via CDN
- **Signing Server**: Express.js + `dotenv` + Node.js `crypto`
- **Next.js project**: Next.js 16 + React 19 + `@moonpay/moonpay-react` + CoinGecko API + Tailwind CSS v4
- **React SDK demos**: [Vite](https://vite.dev/) + React 18 + `@moonpay/moonpay-react`
- **Web SDK demos**: Vanilla JS + `@moonpay/moonpay-js` via CDN
- **Signing server**: Express.js + `dotenv` + Node.js `crypto`
- **Monorepo**: npm workspaces

Binary file added assets/nextjs_demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions moonpay-nextjs-buy-and-sell/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# PUBLIC — safe to expose in the browser

# Base URL of your Next.js app
# Development: http://localhost:3000
# Production: https://your-domain.com
NEXT_PUBLIC_BASE_URL=http://localhost:3000
NEXT_PUBLIC_SIGNING_SERVER_URL=http://localhost:3000

# SERVER ONLY — never prefix with NEXT_PUBLIC_
# These must never be exposed to the browser

# MoonPay
# Get your keys at https://dashboard.moonpay.com
MOONPAY_API_KEY=pk_test_
MOONPAY_SECRET_KEY=sk_test_

# CoinGecko
# Get your key at https://www.coingecko.com/en/api
COINGECKO_API_KEY=CG-
COINGECKO_BASE_URL=https://api.coingecko.com/api/v3
42 changes: 42 additions & 0 deletions moonpay-nextjs-buy-and-sell/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env
.env.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
5 changes: 5 additions & 0 deletions moonpay-nextjs-buy-and-sell/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
<!-- END:nextjs-agent-rules -->
1 change: 1 addition & 0 deletions moonpay-nextjs-buy-and-sell/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
74 changes: 74 additions & 0 deletions moonpay-nextjs-buy-and-sell/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<p align="center">
<img src="../assets/nextjs_demo.gif" alt="MoonPay Next.js Buy & Sell Demo" />
</p>

# MoonPay Next.js Buy & Sell

A crypto buy and sell interface built with Next.js 16, MoonPay, and CoinGecko. Users can browse live crypto prices, filter by currency, search for assets, and trigger buy/sell flows via the MoonPay widget — all with API keys kept securely server-side.

## Tech Stack

- **[Next.js 16](https://nextjs.org)** — App Router, Server Components, Route Handlers
- **[MoonPay React SDK](https://www.npmjs.com/package/@moonpay/moonpay-react)** — Buy and sell widget
- **[CoinGecko API](https://www.coingecko.com/en/api)** — Live crypto prices, logos, and metadata
- **[Tailwind CSS v4](https://tailwindcss.com)** — Styling
- **[React Icons](https://react-icons.github.io/react-icons)** — Icon set
- **TypeScript** — Full type safety

## Features

- Live crypto market data fetched server-side on initial load
- Filter by currency and search by coin name — both trigger a fresh API call
- MoonPay buy and sell widget with server-side URL signing (secret key never exposed)
- All sensitive API keys are server-only — never bundled into client code

## Getting Started

### 1. Clone the repo and install dependencies

```bash
npm install
```

### 2. Set up environment variables

Copy the example env file and fill in your keys:

```bash
cp .env.example .env.local
```

| Variable | Description | Where to get it |
|---|---|---|
| `NEXT_PUBLIC_BASE_URL` | Your app's base URL | `http://localhost:3000` for dev |
| `MOONPAY_API_KEY` | MoonPay publishable key | [dashboard.moonpay.com](https://dashboard.moonpay.com) |
| `MOONPAY_SECRET_KEY` | MoonPay secret key for URL signing | [dashboard.moonpay.com](https://dashboard.moonpay.com) |
| `COINGECKO_API_KEY` | CoinGecko API key | [coingecko.com/en/api](https://www.coingecko.com/en/api) |
| `COINGECKO_BASE_URL` | CoinGecko base URL | `https://api.coingecko.com/api/v3` |

### 3. Run the development server

```bash
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) in your browser.

## Security

MoonPay requires all widget URLs to be signed with your secret key (HMAC-SHA256). This signing happens in a Next.js Route Handler (`/api/sign-url`) so the secret key never leaves the server. The client strips the `apiKey` from the URL before sending it to be signed, and the server re-injects it from the environment before generating the signature.

CoinGecko requests are also proxied through a Route Handler (`/api/crypto`) — the API key is injected server-side and never appears in client network requests.

## Scripts

```bash
npm run dev # Start development server (Turbopack)
npm run build # Build for production
npm run start # Start production server
npm run lint # Run ESLint
```

## Deployment

Deploy to [Vercel](https://vercel.com) with zero config — it natively supports the Next.js App Router. Add your environment variables in the Vercel dashboard under **Settings → Environment Variables**. Make sure none of the server-only keys have the `NEXT_PUBLIC_` prefix.
38 changes: 38 additions & 0 deletions moonpay-nextjs-buy-and-sell/app/api/crypto/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { BaseCoinGeckoURL, CoinGeckoAPIKey } from "@/config";
import { NextRequest, NextResponse } from "next/server";

export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url);
const currency = searchParams.get("currency") ?? "usd";
const query = searchParams.get("query") ?? "";

let url: string;

if (query) {
// First search for matching coin IDs, then fetch their market data
const searchRes = await fetch(`${BaseCoinGeckoURL}/search?query=${query}`, {
headers: {
"x-cg-demo-api-key": CoinGeckoAPIKey,
},
});
const searchData = await searchRes.json();
const ids = searchData.coins
.slice(0, 10)
.map((c: { id: string }) => c.id)
.join(",");

url = `${BaseCoinGeckoURL}/coins/markets?vs_currency=${currency}&ids=${ids}&order=market_cap_desc&sparkline=false`;
} else {
url = `${BaseCoinGeckoURL}/coins/markets?vs_currency=${currency}&order=market_cap_desc&per_page=20&page=1&sparkline=false`;
}

const res = await fetch(url, {
headers: {
"x-cg-demo-api-key": CoinGeckoAPIKey,
},
next: { revalidate: 60 },
});

const data = await res.json();
return NextResponse.json(data);
}
Loading