{"id":51106089,"url":"https://github.com/faithererer/easy-shop","last_synced_at":"2026-06-24T14:01:14.379Z","repository":{"id":365935284,"uuid":"1254558471","full_name":"faithererer/easy-shop","owner":"faithererer","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-19T13:35:39.000Z","size":36,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-19T15:27:56.213Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/faithererer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-30T18:08:36.000Z","updated_at":"2026-06-19T13:35:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/faithererer/easy-shop","commit_stats":null,"previous_names":["faithererer/easy-shop"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/faithererer/easy-shop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faithererer%2Feasy-shop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faithererer%2Feasy-shop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faithererer%2Feasy-shop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faithererer%2Feasy-shop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/faithererer","download_url":"https://codeload.github.com/faithererer/easy-shop/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faithererer%2Feasy-shop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34735266,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-24T02:00:07.484Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-24T14:00:59.707Z","updated_at":"2026-06-24T14:01:14.367Z","avatar_url":"https://github.com/faithererer.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Easy Shop\n\n一个轻量发卡网站：用户使用 Sub2API 账号登录，选择套餐后跳转易支付，支付回调验签成功后通过 Sub2API 管理端接口给对应用户发放订阅。\n\n## 能实现的闭环\n\n- Sub2API 邮箱账号登录，兼容 TOTP 二次验证。\n- 易支付 `submit.php` 下单跳转。\n- 易支付异步通知 MD5 验签、金额校验、订单幂等处理。\n- 待支付订单默认 15 分钟过期，可配置倒计时和过期关闭。\n- 支付成功后调用 Sub2API `POST /api/v1/admin/redeem-codes/create-and-redeem` 发放订阅。\n- 登录后读取 Sub2API 当前账号活跃订阅，并在账号卡片显示真实订阅到期时间。\n- 用户页包含购买、订单中心、账号绑定；后台独立在 `/admin`。\n- 页面内增删改商品，商品数据保存在 `data/products.json`。\n- 本地 `data/orders.json` 保存订单状态，适合先跑 MVP。\n\n## 快速启动\n\n1. 复制 `.env.example` 为 `.env`，填入你的 Sub2API 地址、Admin API Key、易支付 PID/KEY、管理员口令。\n2. 运行：\n\n```bash\nnpm start\n```\n\n3. 打开 `http://localhost:3000`。\n\n## Docker 部署\n\n1. 准备配置：\n\n```bash\ncp .env.example .env\n```\n\n编辑 `.env`，至少填写：\n\n```text\nPUBLIC_BASE_URL=https://你的域名\nSESSION_SECRET=换成一串长随机字符\nADMIN_PASSWORD=后台管理口令\nSUB2API_BASE_URL=https://你的-sub2api-域名\nSUB2API_ADMIN_API_KEY=你的-sub2api-admin-api-key\nEASYPAY_API_BASE=https://你的易支付域名\nEASYPAY_PID=你的商户ID\nEASYPAY_KEY=你的商户密钥\n```\n\n`PUBLIC_BASE_URL` 必须是易支付服务器能访问到的公网地址，不能是 `localhost`。否则易支付会把通知发到它自己机器的 localhost，本站收不到回调，页面会显示“未通知成功”。\n\n2. 构建并启动：\n\n```bash\ndocker compose up -d --build\n```\n\n3. 查看日志：\n\n```bash\ndocker compose logs -f easy-shop\n```\n\n4. 停止服务：\n\n```bash\ndocker compose down\n```\n\n订单和商品数据保存在 Docker volume `easy-shop-data` 里，对应容器内 `/app/data`。不要把 `.env`、`data/*.json` 提交到公开仓库。\n\n容器内固定监听 `3000`，需要改宿主机端口时改 `.env` 的 `HOST_PORT`，例如 `HOST_PORT=8088`。\n\n如果 Sub2API 也跑在同一个 `docker-compose.yml` 网络里，`SUB2API_BASE_URL` 可以写服务名，例如：\n\n```text\nSUB2API_BASE_URL=http://sub2api:8080\n```\n\n如果 Sub2API 跑在宿主机而 Easy Shop 跑在 Docker 容器里，不能写 `http://localhost:8080`，应写：\n\n```text\nSUB2API_BASE_URL=http://host.docker.internal:8080\n```\n\n如果 Sub2API 是公网服务，直接写公网域名，例如：\n\n```text\nSUB2API_BASE_URL=https://sub2api.example.com\n```\n\n生产环境请把 `PUBLIC_BASE_URL` 改成 HTTPS 域名，并把易支付后台的异步通知地址设置为：\n\n```text\nhttps://你的域名/payment/notify/easypay\n```\n\n同步跳转地址：\n\n```text\nhttps://你的域名/payment/return/easypay\n```\n\n## Sub2API 配置要点\n\n- 在 Sub2API 后台生成 Admin API Key，填到 `SUB2API_ADMIN_API_KEY`。\n- 商品里的 `groupId` 必须是 Sub2API 已存在的订阅分组 ID。\n- 当前订阅到期时间默认从 `GET /api/v1/subscriptions/active` 读取；如果你的 Sub2API 路径不同，改 `.env` 里的 `SUB2API_ACTIVE_SUBSCRIPTIONS_PATH`。\n- 支付成功后本站会发送：\n\n```json\n{\n  \"code\": \"ES-订单号\",\n  \"type\": \"subscription\",\n  \"value\": 30,\n  \"user_id\": 123,\n  \"group_id\": 1,\n  \"validity_days\": 30,\n  \"notes\": \"Easy Shop order ES...\"\n}\n```\n\n## 说明\n\n当前版本故意不引入数据库和框架，核心链路更容易部署和排错。订单量上来后，可以把 `src/orders.js` 换成 SQLite/MySQL，并加一个管理员订单后台。\n\n## 商品管理\n\n首次启动时，系统会用 `.env` 里的 `PLANS_JSON` 初始化 `data/products.json`。之后商品以页面管理为准：\n\n1. 打开 `/admin`。\n2. 输入 `.env` 中的 `ADMIN_PASSWORD`。如果没有配置，商品管理接口会返回“管理员口令未配置”。\n3. 点击“载入商品”。\n4. 在页面里新增、编辑、上架、下架或删除商品。\n\n删除商品不会影响已经创建的订单，因为订单会保存商品快照。\n\n## 订单超时\n\n默认待支付订单有效期是 15 分钟。可在 `.env` 中调整：\n\n```text\nORDER_EXPIRE_MINUTES=15\n```\n\n订单过期后前端会显示“已关闭”，继续支付会被拒绝。若易支付已经成功扣款并发送异步通知，系统仍会按支付成功处理并发放订阅，避免用户付款后丢单。\n\n## 支付成功但未发放\n\n易支付回调验签和金额校验通过后，本站会立即返回 `success`，避免支付平台反复提示通知失败。若 Sub2API 暂时不可达或发放失败，订单会变成“发放失败”，后台 `/admin` 最近订单里点击“模拟成功”或用户订单里“重试发放”即可重新走真实发放流程。\n\n常见部署问题：\n\n- 易支付订单里的通知地址是 `http://localhost:3000/...`：把 `.env` 的 `PUBLIC_BASE_URL` 改成公网 HTTPS 域名，重启容器后重新下单。\n- 日志出现 `Sub2API request timed out` 或 `fetch failed`：容器访问不到 `SUB2API_BASE_URL`，按上面的 Docker 网络说明改地址。\n- 反代后公网打不开 `/payment/notify/easypay`：检查 Nginx/Caddy 是否把该路径转发到 Easy Shop 容器。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaithererer%2Feasy-shop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffaithererer%2Feasy-shop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaithererer%2Feasy-shop/lists"}