商品目录存于 SQLite,由 data/products.json 经 npm run seed 写入
nextjs-commerce — Next.js 电商模板
作者 rekey-dev
基于 Next.js 与 SQLite 的数字商品店面模板,商品目录存于 SQLite,账户与支付由 Rekey 处理,购买后以 entitlement 记录所有权。
看看你的网站会是什么样
浏览真实页面截图,比较内容排版与手机效果。点击下方缩略图切换,放大查看整页细节。
全部页面与视图 3
截图用于了解视觉与布局,交互效果请以实际项目为准。
值得关注的模板亮点
从已有项目资料中,快速了解它的特点。
账户与支付接入 Rekey,所有权以 entitlement 记录而非订单表
lib/db.ts 只读打开数据库,应用无法写入所有权事实
下载路由自行校验 entitlement,并限制路径在 downloads/ 内
entitlements 调用失败时返回空集合,默认拒绝下载
一次只结算一件商品,没有购物车
模板详细介绍
这是一个可运行的数字商品店面模板:商品目录放在 SQLite 文件中,账户与支付接入 Rekey,买家买到的内容以 entitlement 的形式记录,而不是靠一张需要人工同步的订单表。仓库说明中明确,它一次只卖一件数字商品,没有购物车;这是计费模型带来的限制而非省略——Rekey checkout 按 plan 结算,四件商品放进购物车就意味着四次结算。如果需要真正的多件商品一次付款,模板作者建议不要以它作为起点,并指出 app/actions/buy.ts 是需要改动的文件。
目录与所有权记录被有意拆成两套系统:SQLite 保存商品是什么(名称、文案、价格、对应文件),Rekey 保存谁付了钱并以 entitlement 回答“这个人是否拥有它”。为避免退款、拒付或卡过期后出现两个互相矛盾的答案,lib/db.ts 以只读方式打开数据库,应用无法写入所有权事实,只有 npm run seed 会写入。
下载路由会自行校验 entitlement,而不是信任渲染链接的页面;同时把路径限制在 downloads/ 目录内,拒绝越界路径。entitlements 调用失败时返回空集合,即故障时默认无人可下载。仓库还说明,真实文件更适合放在带签名 URL 的对象存储后面,校验逻辑不变,只需改动路由最后几行。
适合你的项目吗?
适用场景
- 销售下载文件、字体、预设、电子书或授权等数字商品
- 需要一个把商品目录与支付/所有权分离的 Next.js 起步项目
- 想用 Rekey 处理账户、结算与 entitlement 校验的店面
- 作为后续把目录迁移到 CMS、把文件迁移到对象存储的起点
适合人群
- 销售单件数字商品的个人或小团队
- 接受无购物车、按 plan 逐件结算的计费模型的项目
- 愿意自行注册或自托管 Rekey Application 的开发者
- 使用 Next.js 并希望目录与所有权记录分离的开发者
上手与定制指南
结合仓库当前版本的说明,按下面的资料检查内容、配置与运行要求。
- 1
需要一个 Rekey Application:在 rekey.dev 注册,或从开源仓库自行部署
- 2
克隆仓库后执行 npm install,复制 .env.example 为 .env.local 并填入 Panel → Developer → API keys
- 3
执行 npm run seed 生成 shop.db,并按输出在 Panel → Billing → Plans 中创建对应 slug、价格与 feature entitlement,再在 Billing → Providers 连接支付服务商
- 4
环境变量包括 REKEY_SECRET(仅服务端)、REKEY_URL、NEXT_PUBLIC_REKEY_PUBLIC_KEY、NEXT_PUBLIC_REKEY_URL、NEXT_PUBLIC_APP_URL 与 DATABASE_FILE
- 5
部署时需保证商品目录文件存在(构建时执行 seed 或把 DATABASE_FILE 指向挂载卷),并把 NEXT_PUBLIC_APP_URL 设为真实源并加入面板允许的 origins
授权与来源资料
使用条件与限制
免费使用 · 保留许可声明本次收录的是公开仓库中采用 MIT 许可的免费版本;免费范围为该版本代码,不代表外部服务或第三方素材免费。
使用时需要遵守
- 允许免费使用、修改、商用和分发该版本代码。
- 分发代码副本或实质性部分时,保留原作者版权声明和完整 MIT 许可声明。
- 代码按现状提供;担保与责任限制以原许可证及适用法律为准。
额外费用与待核对事项
- 使用该模板必须拥有 Rekey Application:可在 rekey.dev 注册,或从开源仓库自行部署 Rekey。账户、结算与 entitlement 校验均依赖此外部服务,无法脱离 Rekey 独立运行。
- 需在 Rekey 面板中按 seed 输出手动创建每个 plan(slug、价格与 feature entitlement),并在 Billing → Providers 连接支付服务商,否则无法完成购买流程。
- 必须配置 REKEY_SECRET 等环境变量(含仅服务端的密钥),并自行申请 API keys;密钥泄露将导致该 Application 的完整 API 访问权限被滥用。
- 该模板一次只结算一件数字商品,没有购物车;Rekey checkout 按 plan 结算,多件商品需多次结算。需要多件商品一次付款的项目不适用。
- 生产环境下载真实文件时,建议改用带签名 URL 的对象存储而非由应用服务器流式传输,可能产生额外的对象存储服务成本。
- 部署时须保证商品目录文件存在:在构建时执行 npm run seed,或将 DATABASE_FILE 指向可写挂载卷,否则应用无法读取目录。
- 演示图片、字体、图标及其他第三方素材需按各自许可核对,不能仅凭代码的 MIT 许可判断。
- 域名、托管、数据库、支付或 AI 等外部服务可能另外收费。
查看原文依据
Permission is hereby granted, free of charge
许可证原文
You need a Rekey Application. Sign up at [rekey.dev](https://rekey.dev), or run it yourself from [the open source repo](https://github.com/rekey-dev/rekey).
README 原文
Create each one in **Panel → Billing → Plans** with that slug and price, and give it a feature entitlement with that key. Connect a payment provider under Billing → Providers.
README 原文
`REKEY_SECRET` | Server-only. Full API access for this Application. Never commit it.
README 原文
If you need a real multi-item basket with one payment, this template is the wrong starting point today.
README 原文
For real files, put them behind signed object-storage URLs rather than streaming from the app server.
README 原文
https://github.com/rekey-dev/nextjs-commerce/blob/main/LICENSEhttps://github.com/rekey-dev/nextjs-commerce/blob/main/README.mdThe catalogue is a file, so it needs to exist wherever the app runs: run `npm run seed` as part of the build, or point `DATABASE_FILE` at a mounted volume.
README 原文
DeepSeek 根据仓库许可证和 README 整理 ·2026-10-02。结果仅覆盖已读取资料,依赖、图片、字体及后续条款变更以各自原文为准。
项目核对记录与介绍依据
模板资料
- 来源平台
- GitHub
- 资料与维护参考分
- 资料较完整 · 85 / 100
- 技术栈
- Next.js · React
- 网站类型
- 电商 · 启动器
这个分数如何计算
本站按许可资料(最高30分)、演示链接(12分)、预览图(10分)、Star数(15分)、记录的维护时间(15分)、描述完整度(8分)、功能记录(5分)和分类资料(5分)计算。它不是代码、安全或性能测试,也不是 Google 评分;有链接不代表已实测可用。
这个项目需要核对什么
- 记录的仓库更新时间
- 2026-08-14
- 截图检查日期
- 2026-10-02
已收录演示链接。请用你的内容与屏幕尺寸检查;有演示不代表表单、认证或支付已经可用。
目录记录的主要技术栈是 Next.js。请通过依赖清单和锁文件核对实际版本与托管要求。这些说明基于收录资料,不代表代码审计或部署实测。
查看具体选型方法 →介绍的制作与核对状态
介绍由 AI 辅助整理目录与仓库资料。仓库作者不等于这篇介绍的审核者。
已完成服务器许可规则与 AI 来源核对;未经过人工编辑审核。
查看当时使用的来源资料
SHA-256: f06cbdc941ab441548b3041e196f6dc1744981df70c245c1861e2f18e05910df
2026-10-02T07:36:38.479Z
来源文件 / 仓库# Shelf: a digital shop on Next.js, SQLite and Rekey
A working storefront. The catalogue is a SQLite file, accounts and payments are
[Rekey](https://rekey.dev), and what a buyer owns is an entitlement rather than
a row somebody has to remember to update.
- Next.js 16 (App Router, Turbopack, React 19)
- SQLite via better-sqlite3, seeded from a JSON file
- Tailwind CSS 4
- `@rekey.dev/nextjs`, `@rekey.dev/react`, `@rekey.dev/node`
## What this is, and what it is not
It sells **digital goods, one at a time**. There is no basket, and that is a
property of the billing model rather than a corner cut: Rekey checkout sells a
plan, so four things in a basket would be four checkouts. For downloads, fonts,
presets, books and licences that is usually the right shape anyway.
If you need a real multi-item basket with one payment, this template is the
wrong starting point today. `app/actions/buy.ts` is the file that would have to
change, and [the tracking issue is here](https://github.com/rekey-dev/rekey/issues/16).
## Getting it running
You need a Rekey Application. Sign up at [rekey.dev](https://rekey.dev), or run
it yourself from [the open source repo](https://github.com/rekey-dev/rekey).
```bash
git clone https://github.com/rekey-dev/nextjs-commerce my-shop
cd my-shop
npm install
cp .env.example .env.local # fill in from Panel -> Developer -> API keys
npm run seed
npm run dev
```
`npm run seed` creates `shop.db` from `data/products.json` and then prints the
plans you need, like this:
```
grid-icons 49.00 USD grants product_grid_icons
field-notes-preset 29.00 USD grants product_field_notes
```
Create each one in **Panel → Billing → Plans** with that slug and price, and
give it a feature entitlement with that key. Connect a payment provider under
Billing → Providers. That is the whole setup.
### Environment
| Variable | What it is |
| --- | --- |
| `REKEY_SECRET` | Server-only. Full API access for this Application. Never commit it. |
| `REKEY_URL` | `https://api.rekey.dev`, or your own API if you self-host. |
| `NEXT_PUBLIC_REKEY_PUBLIC_KEY` | Safe in the browser. Identifies the Application. |
| `NEXT_PUBLIC_REKEY_URL` | Same API, the browser-visible copy. |
| `NEXT_PUBLIC_APP_URL` | Where the shop is reachable. Checkout returns the buyer here. |
| `DATABASE_FILE` | Where `shop.db` lives. Anywhere writable. |
## The one idea worth taking
**The catalogue and the ownership record are separate systems, on purpose.**
SQLite holds what a product *is*: name, copy, price, which file it ships. Rekey
holds who *paid*, and answers "does this person own it" as an entitlement.
The temptation is an `orders` table, because it is five minutes of work and it
feels like the shop owns its own data. Then a refund happens, or a chargeback,
or a card expires, and there are two answers to the same question with no rule
about which wins. The shop keeps serving files to somebody who got their money
back, and nothing anywhere looks broken.
So `lib/db.ts` opens the database **read-only**. The app physically cannot write
an ownership fact. `npm run seed` is the only writer.
## What is where
| File | What it does |
| --- | --- |
| `data/products.json` | The catalogue, in the form you edit. |
| `scripts/seed.mts` | Loads it into SQLite. The only thing that writes. |
| `lib/db.ts` | Read-only queries. Opens lazily, which matters at build time. |
| `lib/entitlements.ts` | What the signed-in buyer owns. Fails closed. |
| `app/actions/buy.ts` | One product, one checkout. |
| `app/api/download/[slug]/route.ts` | The only route that hands over a file. |
| `app/library/page.tsx` | What they bought, with download links. |
| `app/actions/auth.ts` | Sign up, sign in, sign out. |
| `proxy.ts` | A cookie-presence gate at the edge. The pages still do the real check. |
## Buying
```ts
const { url } = await rekey().billing.createCheckout(session.accessToken, {
planSlug: product.planSlug,
successUrl: `${appUrl}/library?bought=${slug}`,
cancelUrl: `${appUrl}/p/${slug}?checkout=canceled`,
});
redirect(url);
```
The subscription is `PENDING` until the provider webhook confirms payment, which
is why `/library` says "usually seconds" instead of showing an empty shelf as if
the purchase failed. Rekey handles that webhook; you do not need an endpoint.
## Downloads
The download route checks the entitlement itself rather than trusting the page
that rendered the link, because a link is a string and anyone can type one. It
also resolves the path inside `downloads/` and refuses anything that escapes,
so a bad row in the catalogue cannot become an arbitrary file read.
`lib/entitlements.ts` returns an empty set if the entitlements call fails. A
billing outage should mean nobody can download for a minute, not that everybody
can.
For real files, put them behind signed object-storage URLs rather than streaming
from the app server. The check stays exactly the same; only the last three lines
of the route change.
## Growing out of SQLite
A file is the right call for a starter and the wrong call once somebody who is
not you needs to edit product copy. When that happens, move the catalogue into
a CMS and leave everything else alone: only `lib/db.ts` knows where products
come from, and it is thirty lines.
[Payload](https://payloadcms.com) is the natural fit, and a Rekey plugin for it
is [tracked here](https://github.com/rekey-dev/rekey/issues/13). Until then,
Payload's local API drops into `allProducts()` and `productBySlug()` directly.
## Deploying
The catalogue is a file, so it needs to exist wherever the app runs: run
`npm run seed` as part of the build, or point `DATABASE_FILE` at a mounted
volume. `downloads/` is the same story, and is the first thing you should move
to object storage.
Set `NEXT_PUBLIC_APP_URL` to the real origin and add it to the Application's
allowed origins in the panel.
## Licence
MIT. Take it apart.
看看同类模板
对比同技术栈与用途的模板,找到更适合的视觉与内容结构。

ai-website-cloner-template
Clone any website with one command using AI coding agents
Next.js · 启动器

Next.js SaaS Starter
Next.js SaaS Starter 是 Next.js 官方维护的 SaaS 起点模板,定位介于学习资源和可直接部署的脚手架之间。
Next.js · SaaS

Next.js Commerce
Next.js Commerce 是 Vercel 团队维护的电商启动模板,基于 Next.js App Router 构建,采用服务端渲染、React Server Components、Server Actions …
Next.js · 电商

Next-js-Boilerplate
Next-js-Boilerplate 是一个面向 Next.js 16、Tailwind CSS 4 和 TypeScript 的启动器,核心定位是开发者体验优先。
Next.js · 启动器

tailwind-nextjs-starter-blog
tailwind-nextjs-starter-blog 是一个面向个人技术博客的 Next.js 与 Tailwind CSS 启动模板,其 v2 版本基于 App Router 与 React Server Comp…
Next.js · 博客

next-forge
Production-grade Turborepo template for Next.js apps.
Next.js · 启动器
