[MD]
# 🎮 YuPay —— 专为 Minecraft 服务器打造的现代化赞助支付插件
> **让赞助管理不再是腐竹的噩梦。**
> 微信支付、支付宝、卡密、退款、排行榜、PAPI、多语言、订单审计、跨服发奖、开发者 API —— 从玩家扫码到奖励到账,从异常排查到退款复核,YuPay 帮你把服务器赞助流程做成一套完整闭环。
---
## 📌 YuPay 是什么?
**YuPay** 是一款面向 Minecraft 服务器腐竹的现代化赞助支付插件。
它的目标很简单:
让玩家赞助更方便,让腐竹收款更安全,让奖励发放更自动,让每一笔订单都有记录可查。
YuPay 支持 **微信支付** 与 **支付宝** 官方接口对接,玩家在游戏内发起赞助后即可扫码支付。支付成功后,插件会自动完成订单确认、验签、奖励发放、排行榜刷新、PAPI 数据更新和日志记录。
资金不经过第三方代收平台,直接进入你自己的微信 / 支付宝商户账户。
> ⚠️ YuPay 面向“玩家自愿赞助服务器运营”场景。请确保你的使用方式符合当地法律法规、服务器运营规范以及微信支付 / 支付宝平台规则。
---
## ✨ 核心亮点速览
| 模块 | 你能获得什么 |
|---|---|
| 💳 官方支付 | 微信支付 + 支付宝,支持扫码支付,资金直达商户账户 |
| 🧾 订单系统 | 订单创建、取消、过期、成功、异常、退款、补发全生命周期记录 |
| 🎁 自动奖励 | 经济奖励 + 自定义命令,支持单笔、累计、近期累计等多层触发 |
| 🔁 补发机制 | 支付状态与发奖状态分离,玩家可自助补发,管理员可重试奖励 |
| 🔑 卡密系统 | 免费码、付费码、礼包码、激活码、次数限制、过期时间、核销日志 |
| 💵 退款系统 | 退款申请、管理员审核、直接退款、部分退款、退款状态同步 |
| 📊 数据统计 | 个人总额、全服总额、排行榜、历史订单、订单详情、回调流水 |
| 🌐 多语言 | 默认中文 / 英文,可扩展任意语言,支持玩家个人语言偏好 |
| 🔌 PAPI 支持 | 个人、全服、排行榜、卡密多维度占位符 |
| 🛡️ 安全审计 | 签名验证、IP 白名单、请求体限制、敏感日志脱敏、API 授权审计 |
| 🧩 开发者 API | Bukkit ServicesManager API + 完整事件监听,方便商城 / 菜单 / 跨服系统集成 |
| 🌍 跨服发奖 | 支持共享数据库队列,多子服领取发奖任务,适合群组服网络 |
| ⚡ 性能友好 | 异步订单处理、HikariCP 连接池、Undertow 回调服务、启动健康检查 |
| 📚 插件 Wiki / 使用文档 | [插件文档](https://vicuna-main.github.io/YuPluginHub) |
---
## 🧭 一次完整赞助流程
玩家只需要输入一条命令:
```bash
/donate 50 wechat map
```
然后 YuPay 会自动完成:
1. 校验玩家权限、金额上下限、每日限额、支付渠道可用性
2. 创建本地订单并写入数据库
3. 调用微信 / 支付宝接口生成支付二维码
4. 按配置展示支付方式:
- 可点击链接
- 聊天栏文本二维码
- 背包地图二维码
5. 玩家扫码完成支付
6. 支付平台向服务器回调
7. YuPay 验证签名、校验金额、确认订单状态
8. 写入回调流水与订单快照
9. 自动发放经济奖励和命令奖励
10. 更新排行榜、PAPI 数据和玩家历史记录
11. 若发奖失败,可由玩家或管理员重试补发
腐竹不需要手动确认收款,不需要手动发奖励,也不需要翻聊天记录对账。
---
## 💳 官方双通道支付
YuPay 支持两种主流支付方式:
| 支付渠道 | 支持能力 |
|---|---|
| 微信支付 | Native 扫码支付、支付回调、退款、退款回调、退款查询 |
| 支付宝 | 扫码支付、公钥模式、证书模式、支付回调、退款、退款查询 |
支持配置默认支付方式:
```yaml
pay:
default-method: "wechat"
```
玩家也可以在命令中指定支付方式:
```bash
/donate 10 wechat
/donate 10 alipay
```
如果玩家不指定支付方式,则使用服主配置的默认渠道。
---
## 🧾 不只是收款:完整订单生命周期
YuPay 将订单状态和发奖状态分离管理,让排障更加清晰。
### 支付订单状态
| 状态 | 含义 |
|---|---|
| `PENDING` | 待支付 |
| `SUCCESS` | 支付成功 |
| `CANCELLED_LOCAL` | 玩家本地取消 |
| `CLOSED` | 平台订单关闭 |
| `FAILED` | 支付失败 |
| `EXPIRED` | 待支付订单过期 |
| `AMOUNT_MISMATCH` | 回调金额与订单金额不一致 |
| `HELD` / `PAID_AFTER_CANCELLED` | 取消后到账等历史异常状态 |
| `RESOLVED_REWARDED` | 异常订单已人工判定并补发 |
| `RESOLVED_REFUNDED` | 异常订单已人工判定并退款 |
| `RESOLVED_IGNORED` | 异常订单已人工判定忽略 |
### 发奖状态
| 状态 | 含义 |
|---|---|
| `PENDING` | 等待发奖 |
| `PROCESSING` | 正在处理 |
| `DONE` | 已完成 |
| `FAILED` | 发奖失败 |
| `SKIPPED` | 无奖励赞助或无需发奖 |
这意味着:
即使玩家已经付款成功,但命令奖励因为权限插件、经济插件、服务器瞬时异常导致失败,YuPay 也能保留订单成功状态,并允许后续重试补发。
---
## 🔁 自助补发与管理员补发
玩家可使用:
```bash
/yp retry
/yp retry <订单号>
```
当订单已支付成功但奖励未完成时,玩家可以自行提交补发请求。
管理员也可以使用:
```bash
/yp order retry-reward <订单号>
/yp order steps <订单号>
```
查看发奖步骤流水并手动重试。
YuPay 会记录每一步奖励执行状态,避免重复发放。
---
## 🎁 四层奖励系统,运营玩法随便搭
YuPay 的奖励系统支持多层触发,既能满足普通赞助奖励,也能做充值档位、累计成就、限时活动。
| 奖励类型 | 触发时机 | 适合场景 |
|---|---|---|
| 基础奖励 | 每次支付成功 | 每笔赞助都送基础礼包 |
| 单笔达标 | 单次金额达到门槛 | 50 元档、100 元档、300 元档 |
| 累计达标 | 历史累计金额达到门槛 | VIP、SVIP、永久称号 |
| 近期累计 | 指定时间范围内累计达标 | 周末冲榜、节日活动、限时礼包 |
| 退款命令 | 退款完成后 | 通知管理员、撤销标记、记录日志 |
### 基础奖励
```yaml
commands:
on-success:
- "give {player} diamond 1"
- "effect give {player} minecraft:speed 30 1"
```
### 单笔达标奖励
```yaml
commands:
on-single-achieved:
- amount: 50
commands:
- "say {player} 单笔赞助达到 50 元!"
- "give {player} minecraft:gold_block 5"
```
### 累计达标奖励
```yaml
commands:
on-total-achieved:
- total: 500
commands:
- "lp user {player} parent set svip"
- "give {player} minecraft:netherite_block 5"
```
### 限时累计奖励
```yaml
commands:
on-recent-achieved:
- time: 86400000
total: 200
commands:
- "give {player} minecraft:enchanted_golden_apple 5"
```
> `time` 单位为毫秒:
> 24 小时 = `86400000`
> 7 天 = `604800000`
### 可用变量
| 变量 | 含义 |
|---|---|
| `{player}` | 玩家名 |
| `{amount}` | 支付金额 |
| `{points}` | 经济奖励数量 |
| `{order}` | 订单号,主要用于退款命令 |
---
## 💛 无奖励赞助
有些玩家只是想支持服务器,不想领取任何道具或权限。
YuPay 支持无奖励赞助:
```bash
/donate 10 -n
```
玩家添加 `-n` 参数后,该订单会正常收款、记录、统计,但不会触发经济奖励和命令奖励。
适合:
- 纯支持服务器运营
- 不影响生存平衡
- 避免 P2W 争议
- 公益服 / 原版服赞助场景
---
## 💰 Vault + PlayerPoints 双经济支持
YuPay 支持两类经济奖励:
| 类型 | 说明 |
|---|---|
| `vault` | 对接 Vault,兼容 EssentialsX、CMI 等主流经济插件 |
| `playerpoints` | 对接 PlayerPoints 点券系统 |
配置示例:
```yaml
economy:
enabled: true
type: "vault"
rate: 100.0
```
表示:
```text
1 元 = 100 游戏币 / 点数
```
还支持管理员进行两种经济之间的换算:
```bash
/yp convert <玩家> <vault|points> <vault|points> <数量>
```
---
## 🔑 卡密系统:活动码、礼包码、付费激活码都能做
YuPay 的卡密系统不是简单的“输入兑换码领奖励”,而是一套可支付、可核销、可追踪的兑换系统。
### 卡密可以配置
| 能力 | 说明 |
|---|---|
| 免费核销 | 玩家输入卡密后直接领取奖励 |
| 付费核销 | 玩家输入卡密后需扫码支付指定金额 |
| 经济奖励 | 核销后发放 Vault 货币或 PlayerPoints 点数 |
| 命令奖励 | 核销后执行任意控制台命令 |
| 使用次数 | 每张卡密可限制最大核销次数 |
| 过期时间 | 支持永久、相对时间、指定日期 |
| 状态管理 | 启用、禁用、用尽、过期 |
| 日志追踪 | 每次核销都有记录可查 |
| 支付锁 | 付费卡密支付中会加锁,防止多人同时抢占 |
### 免费礼包码
```bash
/yp code create --code FREEGIFT --max-uses -1 --economy vault --eco-amount 1000 --remark "新手福利"
```
### 付费激活码
```bash
/yp code create --code PREMIUM --amount 50 --economy vault --eco-amount 5000 --commands "lp user {player} parent set vip" --max-uses 100 --expire 30d
```
### 纯命令卡密
```bash
/yp code create --code WELCOME --commands "give {player} diamond 5; say {player} 欢迎回来!"
```
### 卡密管理命令
| 命令 | 功能 |
|---|---|
| `/yp code create ...` | 创建卡密 |
| `/yp code remove <卡密>` | 彻底移除卡密 |
| `/yp code disable <卡密>` | 禁用卡密 |
| `/yp code enable <卡密>` | 启用卡密 |
| `/yp code modify <卡密> <字段> <值>` | 修改卡密属性 |
| `/yp code info <卡密>` | 查看卡密详情 |
| `/yp code list [页数]` | 查看卡密列表 |
| `/yp code logs [卡密] [页数]` | 查看核销日志 |
| `/yp code redeem <卡密>` | 玩家核销卡密 |
---
## 💵 退款系统:从申请到平台退款全流程打通
退款不再靠手动转账、不再靠聊天记录确认。
YuPay 支持完整退款流程:
| 功能 | 说明 |
|---|---|
| 玩家申请退款 | 玩家提交退款请求 |
| 管理员审核 | 管理员通过或拒绝 |
| 直接退款 | 管理员直接对订单发起退款 |
| 部分退款 | 可指定退款金额 |
| 退款流水 | 记录退款单号、状态、金额、渠道 |
| 退款查询 | 查询平台退款结果 |
| 退款重试 | 对失败 / 未知退款进行重试 |
| 手动确认 | 必要时手动确认退款结果 |
| 状态同步 | 定时查询平台退款状态 |
| 退款命令 | 退款完成后执行自定义命令 |
### 玩家申请退款
```bash
/yp refund request <订单号> [金额]
```
### 管理员查看待审核
```bash
/yp refund pending
```
### 审核通过 / 拒绝
```bash
/yp refund approve <请求ID>
/yp refund reject <请求ID>
```
### 管理员直接退款
```bash
/yp refund <订单号> [金额]
```
### 退款流水查询
```bash
/yp refund list
/yp refund info <退款单号>
/yp refund query <退款单号>
/yp refund retry <退款单号>
/yp refund confirm <退款单号>
```
---
## 📊 数据统计:对账、排行、历史一目了然
YuPay 内置常用运营统计命令。
### 排行榜
```bash
/ytop
```
显示全服赞助排行榜 Top10。
### 查询个人 / 全服总额
```bash
/ytotal
/ytotal <玩家名>
/ytotal all
```
### 玩家订单列表
```bash
/yp orders
/yp history
```
### 单个订单详情
```bash
/yp order <订单号>
```
### 管理员订单中心
```bash
/yp order info <订单号>
/yp order abnormal [页数]
/yp order risk [页数]
/yp order list <abnormal|pending|cancelled|risk|reward_failed|refund_failed|success> [页数]
/yp order player <玩家名|UUID> [页数]
/yp order steps <订单号>
/yp order callbacks <订单号|recent|failed|duplicate> [页数]
/yp order refunds <订单号> [页数]
/yp order mark-reviewed <订单号> [备注]
/yp order resolve <订单号> <reward|refund|ignore|hold>
/yp order expire
```
这套订单中心非常适合处理:
- 玩家说“我付了但没到账”
- 取消后平台仍然到账
- 金额异常
- 发奖失败
- 回调重复
- 平台退款状态不明确
- 多服网络中发奖节点异常
---
## 🌐 多语言系统:同一服务器,不同玩家不同语言
YuPay 默认提供:
- 简体中文
- English
并支持服主自定义任意语言。
配置示例:
```yaml
language:
mappings:
zh_cn:
file: "messages.yml"
aliases: [zh, cn, chinese]
client-locales: ["zh_cn", "zh_*"]
en:
file: "messages_en.yml"
aliases: [english, eng]
client-locales: ["en_*"]
```
玩家可自行切换语言:
```bash
/yp lang en
/yp lang zh_cn
```
语言偏好会写入数据库,下次进服自动生效。
首次加入服务器时,YuPay 会尝试根据玩家客户端语言匹配合适语言文件。
---
## 🔌 PlaceholderAPI 占位符
YuPay 内置多维度 PAPI,占位符标识为:
```text
%yupay_xxx%
```
### 个人维度
```text
%yupay_is_banned%
%yupay_total%
%yupay_total_orders%
%yupay_success_orders%
%yupay_rank%
%yupay_has_paid_in_<time>%
%yupay_recent_amount_gt_<time>_<amount>%
%yupay_orders_in_<time>%
%yupay_success_orders_in_<time>%
%yupay_amount_in_<time>%
%yupay_is_limited%
%yupay_daily_remaining%
%yupay_lang%
%yupay_pay_methods_count%
%yupay_pay_methods_list%
```
### 全服维度
```text
%yupay_server_total%
%yupay_server_total_orders%
%yupay_server_success_orders%
%yupay_server_orders_in_<time>%
%yupay_server_success_orders_in_<time>%
%yupay_server_amount_in_<time>%
```
### 排行榜维度
```text
%yupay_top_<rank>_name%
%yupay_top_<rank>_amount%
%yupay_top<rank>_name%
%yupay_top<rank>_amount%
```
`<rank>` 支持 `1` 到 `10`。
### 卡密维度
```text
%yupay_code_total%
%yupay_code_active%
%yupay_code_<code>_status%
%yupay_code_<code>_amount%
%yupay_code_<code>_economy_type%
%yupay_code_<code>_economy_amount%
%yupay_code_<code>_used%
%yupay_code_<code>_max_uses%
%yupay_code_<code>_expire%
%yupay_code_<code>_expire_formatted%
%yupay_code_<code>_remark%
%yupay_code_<code>_locked%
%yupay_code_<code>_redeemed%
%yupay_code_<code>_can_redeem%
```
### 参数说明
| 参数 | 说明 |
|---|---|
| `<time>` | 毫秒,例如 24 小时 = `86400000` |
| `<amount>` | 金额阈值,单位元 |
| `<rank>` | 排行榜名次,1-10 |
| `<code>` | 卡密字符串,会自动大写匹配 |
金额类占位符统一保留两位小数,例如:
```text
99.50
100.00
```
可用于:
- 计分板
- BossBar
- 菜单 GUI
- 聊天前缀
- 称号系统
- 物品 Lore
- 排行榜展示
- 活动进度展示
---
## 🌍 跨服发奖:适合群组服网络
如果你运营的是多子服网络,YuPay 支持跨服发奖队列。
设计目标是:
> 支付回调可以由任意服务器接收,但奖励由目标服务器通过共享数据库队列领取执行。
支持路由模式:
| 模式 | 说明 |
|---|---|
| `LOCAL` | 当前服务器直接发奖 |
| `SERVER` | 固定交给指定服务器发奖 |
| `PLAYER_ONLINE` | 玩家在哪个子服在线,就由哪个子服发奖 |
| `ANY` | 任意在线子服都可领取发奖任务 |
推荐生产环境使用:
```yaml
database:
type: "mysql"
cross-server:
enabled: true
server-id: "lobby-1"
security:
require-shared-secret: true
shared-secret: "请改成足够长的随机字符串"
```
查看跨服状态:
```bash
/yp crossserver status
/yp crossserver queue
```
---
## 🛡️ 安全能力
YuPay 对支付类插件最重要的安全问题做了完整处理。
| 安全能力 | 说明 |
|---|---|
| 支付回调验签 | 微信 / 支付宝回调都会进行签名验证 |
| 金额校验 | 回调金额必须与订单金额匹配 |
| 幂等处理 | 避免重复回调导致重复发奖 |
| 回调 IP 白名单 | 支持 CIDR 白名单限制来源 |
| 请求体限制 | 防止异常大请求压垮回调服务 |
| 敏感日志脱敏 | 密钥、签名、平台交易号、IP 等可脱敏 |
| 回调流水 | 可记录平台回调处理结果、哈希、状态 |
| 安全审计日志 | API 拒绝、回调异常、退款等敏感行为单独记录 |
| API 权限控制 | 外部插件调用 YuPay API 需要按插件名授权 |
| 事件监听审计 | 可检查哪些插件正在监听 YuPay 资金相关事件 |
| 地图支付锁 | 地图二维码支付期间限制玩家乱丢 / 切换关键物品 |
安全审计命令:
```bash
/yp audit
/yp audit listeners
/yp audit security
```
健康检查命令:
```bash
/yp health
/yp health summary
/yp health full
```
---
## 🧩 给开发者的 API 与 Bukkit 事件
YuPay 提供两种开发者接入方式:
1. **YuPayApi**:适合商城插件、菜单插件、网页充值系统、跨服系统主动创建订单、查询订单、退款、补发奖励。
2. **Bukkit 事件**:适合监听支付流程,在订单创建前拦截、支付完成后联动、退款完成后同步数据。
---
### 1. 在其他插件中依赖 YuPay
如果你的插件需要调用 YuPay API,建议在 `plugin.yml` 中声明依赖:
```yaml
depend: [YuPay]
```
如果只是可选支持 YuPay,可以写:
```yaml
softdepend: [YuPay]
```
Gradle 本地依赖示例:
```gradle
dependencies {
compileOnly files("libs/YuPay.jar")
}
```
将 `YuPay.jar` 放入你插件项目的 `libs/` 目录即可编译。
---
### 2. 获取 YuPayApi 实例
YuPay 会通过 Bukkit `ServicesManager` 注册 `YuPayApi`。
推荐写法:
```java
import org.bukkit.plugin.java.JavaPlugin;
import org.yutay.yupay.api.YuPayApi;
import org.yutay.yupay.api.YuPayProvider;
public final class MyShopPlugin extends JavaPlugin {
private YuPayApi yuPayApi;
@Override
public void onEnable() {
yuPayApi = YuPayProvider.get();
if (yuPayApi == null) {
getLogger().warning("未检测到 YuPay,支付功能将不可用。");
return;
}
getLogger().info("已成功接入 YuPay API。");
}
public YuPayApi getYuPayApi() {
return yuPayApi;
}
}
```
也可以直接通过 Bukkit `ServicesManager` 获取:
```java
import org.bukkit.Bukkit;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.yutay.yupay.api.YuPayApi;
RegisteredServiceProvider<YuPayApi> provider =
Bukkit.getServicesManager().getRegistration(YuPayApi.class);
YuPayApi api = provider == null ? null : provider.getProvider();
```
---
### 3. YuPay API 授权配置
YuPay 默认会检查外部插件是否有权调用 API。
第三方插件创建订单时必须声明 `sourcePlugin`,通常传入自己插件的名字:
```java
.sourcePlugin(getName())
```
然后服主需要在 `config.yml` 中授权:
```yaml
api:
enabled: true
require-plugin-declaration: true
audit-log: true
allowed-plugins:
MyShop:
create-order: true
cancel-order: true
query-order: true
refund-order: false
retry-reward: false
no-reward-order: false
expire-orders: false
```
权限说明:
| 权限项 | 说明 |
|---|---|
| `create-order` | 允许创建支付订单 |
| `cancel-order` | 允许取消待支付订单 |
| `query-order` | 允许查询订单、订单列表、发奖步骤 |
| `refund-order` | 允许通过 API 发起退款 |
| `retry-reward` | 允许通过 API 重试发奖 |
| `no-reward-order` | 允许创建无奖励订单 |
| `expire-orders` | 允许通过 API 主动过期待支付订单 |
如果未授权,API 调用会返回失败结果,并在 YuPay 安全审计中记录拒绝原因。
---
### 4. 创建支付订单
示例:商城插件为玩家创建一笔 30 元的微信订单。
```java
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.yutay.yupay.api.YuPayOrderRequest;
import org.yutay.yupay.api.YuPayOrderResult;
import java.util.LinkedHashMap;
import java.util.Map;
public void createShopOrder(Player player, String shopOrderId) {
if (yuPayApi == null) {
player.sendMessage("§c支付系统暂不可用。");
return;
}
Map<String, Object> metadata = new LinkedHashMap<>();
metadata.put("shopOrderId", shopOrderId);
metadata.put("itemId", "vip_30d");
YuPayOrderRequest request = YuPayOrderRequest
.builder(player.getUniqueId(), player.getName(), 30.00)
.payMethod("wechat")
.sourcePlugin(getName())
.externalOrderId(shopOrderId)
.businessType("SHOP")
.metadata(metadata)
.expireMinutes(15)
.noReward(false)
.build();
yuPayApi.createOrder(request).thenAccept(result -> {
if (!result.isSuccess()) {
Bukkit.getScheduler().runTask(this, () -> {
player.sendMessage("§c订单创建失败:" + result.getMessage());
});
return;
}
Bukkit.getScheduler().runTask(this, () -> {
player.sendMessage("§a订单创建成功!");
player.sendMessage("§7订单号:§f" + result.getOrderId());
player.sendMessage("§7支付方式:§f" + result.getPayMethod());
player.sendMessage("§7金额:§f" + result.getAmount());
player.sendMessage("§e请使用以下二维码链接完成支付:");
player.sendMessage("§b" + result.getQrUrl());
});
}).exceptionally(error -> {
Bukkit.getScheduler().runTask(this, () -> {
player.sendMessage("§c创建订单时发生异常:" + error.getMessage());
});
return null;
});
}
```
> 注意:`createOrder` 返回的是 `CompletableFuture`,回调不一定在 Bukkit 主线程。
> 如果要给玩家发消息、操作背包、执行 Bukkit API,建议使用 `Bukkit.getScheduler().runTask(...)` 切回主线程。
---
### 5. YuPayOrderRequest 参数说明
| 方法 | 说明 |
|---|---|
| `builder(UUID, String, double)` | 创建请求,传入玩家 UUID、玩家名、金额 |
| `payMethod(String)` | 支付方式,例如 `wechat` 或 `alipay`,不填则使用 YuPay 默认支付方式 |
| `orderId(String)` | 自定义 YuPay 订单号,不填则自动生成 |
| `noReward(boolean)` | 是否为无奖励订单 |
| `sourcePlugin(String)` | 调用方插件名,用于 API 授权和安全审计 |
| `externalOrderId(String)` | 外部系统订单号,例如商城订单号 |
| `businessType(String)` | 业务类型,例如 `SHOP`、`VIP`、`REDEEM`、`CUSTOM` |
| `metadataJson(String)` | 自定义 JSON 元数据 |
| `metadata(Map<String, ?>)` | 以 Map 方式传入元数据,YuPay 会转为 JSON |
| `expireMinutes(int)` | 当前订单过期分钟数,`0` 表示使用 YuPay 全局配置 |
---
### 6. YuPayOrderResult 返回值说明
| 方法 | 说明 |
|---|---|
| `isSuccess()` | 是否创建成功 |
| `getOrderId()` | YuPay 订单号 |
| `getPayMethod()` | 实际使用的支付方式 |
| `getAmount()` | 订单金额 |
| `getQrUrl()` | 支付二维码链接 |
| `getMessage()` | 失败原因 |
---
### 7. 查询订单
```java
yuPayApi.getOrder(getName(), orderId).thenAccept(record -> {
if (record == null) {
getLogger().info("订单不存在:" + orderId);
return;
}
getLogger().info("订单号:" + record.getOrderId());
getLogger().info("玩家:" + record.getPlayerName());
getLogger().info("金额:" + record.getAmount());
getLogger().info("支付状态:" + record.getStatus());
getLogger().info("发奖状态:" + record.getRewardStatus());
getLogger().info("支付方式:" + record.getPayMethod());
getLogger().info("外部订单号:" + record.getExternalOrderId());
getLogger().info("业务类型:" + record.getBusinessType());
});
```
常用字段:
| 方法 | 说明 |
|---|---|
| `getOrderId()` | YuPay 订单号 |
| `getPlayerUuid()` | 玩家 UUID |
| `getPlayerName()` | 玩家名 |
| `getAmount()` | 金额 |
| `getStatus()` | 支付状态 |
| `getRewardStatus()` | 发奖状态 |
| `getPayMethod()` | 支付方式 |
| `isNoReward()` | 是否无奖励 |
| `getQrUrl()` | 二维码链接 |
| `getFailReason()` | 失败原因 |
| `getRefundStatus()` | 退款状态 |
| `getRefundAmount()` | 已退款金额 |
| `getSourcePlugin()` | 来源插件 |
| `getExternalOrderId()` | 外部订单号 |
| `getMetadataJson()` | 元数据 JSON |
| `isCreatedByApi()` | 是否由 API 创建 |
| `getBusinessType()` | 业务类型 |
| `getExpireTime()` | 过期时间戳 |
---
### 8. 查询订单列表
```java
yuPayApi.listOrders(getName(), "success", 20, 0).thenAccept(records -> {
for (var record : records) {
getLogger().info(record.getOrderId() + " - " + record.getStatus());
}
});
```
`type` 可用值:
| type | 说明 |
|---|---|
| `pending` | 待支付订单 |
| `cancelled` | 已取消订单 |
| `success` | 支付成功订单 |
| `reward_failed` | 发奖失败订单 |
| `risk` | 风险订单 |
| `abnormal` | 异常订单 |
---
### 9. 取消订单
```java
yuPayApi.cancelOrderDetailed(getName(), orderId, "玩家在商城界面取消支付")
.thenAccept(result -> {
if (result.isSuccess()) {
getLogger().info("订单取消成功:" + orderId);
} else {
getLogger().warning("订单取消失败:" + result.getCode() + " - " + result.getMessage());
}
});
```
也可以取消玩家最近一笔待支付订单:
```java
yuPayApi.cancelLatestPendingOrder(getName(), player.getUniqueId(), "玩家取消最近订单")
.thenAccept(result -> {
player.sendMessage(result.isSuccess() ? "§a已取消最近订单。" : "§c取消失败:" + result.getMessage());
});
```
---
### 10. 发起退款
```java
yuPayApi.refundOrderDetailed(getName(), orderId, 10.00, "商城售后退款")
.thenAccept(result -> {
if (result.isSuccess()) {
getLogger().info("退款已受理:订单=" + result.getOrderId()
+ ", 本次退款=" + result.getRefundAmount()
+ ", 累计退款=" + result.getTotalRefunded()
+ ", 退款状态=" + result.getRefundStatus());
} else {
getLogger().warning("退款失败:" + result.getCode() + " - " + result.getMessage());
}
});
```
退款需要在 YuPay 配置中给调用插件开启:
```yaml
api:
allowed-plugins:
MyShop:
refund-order: true
```
---
### 11. 重试发奖
当订单已经支付成功,但发奖状态为失败时,可以通过 API 重试:
```java
yuPayApi.retryReward(getName(), orderId).thenAccept(result -> {
if (result.isSuccess()) {
getLogger().info("已提交发奖重试:" + orderId);
} else {
getLogger().warning("发奖重试失败:" + result.getCode() + " - " + result.getMessage());
}
});
```
需要授权:
```yaml
api:
allowed-plugins:
MyShop:
retry-reward: true
```
---
### 12. 查询发奖步骤
```java
yuPayApi.listRewardSteps(getName(), orderId).thenAccept(steps -> {
for (var step : steps) {
getLogger().info("步骤:" + step.getStepKey()
+ ", 类型:" + step.getStepType()
+ ", 状态:" + step.getStatus()
+ ", 尝试次数:" + step.getAttempts()
+ ", 错误:" + step.getLastError());
}
});
```
发奖步骤字段:
| 方法 | 说明 |
|---|---|
| `getOrderId()` | 订单号 |
| `getStepKey()` | 步骤键 |
| `getStepType()` | 步骤类型 |
| `getStatus()` | 步骤状态 |
| `getAttempts()` | 尝试次数 |
| `getDetail()` | 步骤详情 |
| `getLastError()` | 最近错误 |
| `getCreatedTime()` | 创建时间 |
| `getUpdatedTime()` | 更新时间 |
---
### 13. 主动过期待支付订单
```java
yuPayApi.expirePendingOrders(getName()).thenAccept(result -> {
getLogger().info("已过期待支付订单数量:" + result.getAffected());
});
```
需要授权:
```yaml
api:
allowed-plugins:
MyShop:
expire-orders: true
```
---
### 14. 支付方式工具方法
```java
String defaultMethod = yuPayApi.getDefaultPayMethod();
boolean wechatAvailable = yuPayApi.isPayMethodAvailable("wechat");
boolean alipayAvailable = yuPayApi.isPayMethodAvailable("alipay");
String orderId = yuPayApi.generateOrderId(player.getUniqueId(), false);
```
如果第三方插件想自己展示二维码图片,也可以使用:
```java
BufferedImage image = yuPayApi.createPaymentQrImage(qrUrl, 256);
```
免费版可能会自动在二维码图中加入 YuPay 品牌标识,可通过:
```java
boolean required = yuPayApi.isFreeEditionBrandingRequired();
String adLink = yuPayApi.getFreeEditionAdLink();
```
判断是否需要展示品牌信息。
---
## Bukkit 事件监听
YuPay 的事件位于:
```java
org.yutay.yupay.event
```
使用方式与普通 Bukkit 事件完全一致。
---
### 1. 注册监听器
```java
import org.bukkit.plugin.java.JavaPlugin;
public final class MyPlugin extends JavaPlugin {
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(new YuPayListener(this), this);
}
}
```
监听器示例:
```java
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.yutay.yupay.event.*;
public final class YuPayListener implements Listener {
private final MyPlugin plugin;
public YuPayListener(MyPlugin plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onPrePayment(PrePaymentEvent event) {
Player player = event.getPlayer();
// 示例:低于 1 元的订单禁止创建
if (event.getAmount() < 1.0) {
event.setCancelled(true);
event.setCancelReason("§c最低赞助金额为 1 元。");
return;
}
// 示例:强制某个世界只能使用支付宝
if ("activity".equalsIgnoreCase(player.getWorld().getName())) {
event.setPayMethod("alipay");
}
// 示例:超过 100 元自动改为链接模式
if (event.getAmount() >= 100.0) {
event.setOutputMode("link");
}
}
@EventHandler
public void onOrderCreated(PaymentOrderCreatedEvent event) {
plugin.getLogger().info("YuPay 订单已创建:"
+ event.getOrderId()
+ ", 玩家=" + event.getPlayerName()
+ ", 金额=" + event.getAmount()
+ ", 支付方式=" + event.getPayMethod()
+ ", 二维码=" + event.getQrCodeUrl());
}
@EventHandler
public void onPaymentCompleted(PaymentCompletedEvent event) {
plugin.getLogger().info("YuPay 支付完成:"
+ event.getPlayerName()
+ " 支付 " + event.getAmountRmb()
+ " 元,订单号:" + event.getOrderId());
Player player = event.getPlayer();
if (player != null) {
player.sendMessage("§a感谢赞助,订单已完成!");
}
// 示例:支付完成后联动其他系统
Bukkit.dispatchCommand(
Bukkit.getConsoleSender(),
"say " + event.getPlayerName() + " 刚刚完成了一笔赞助!"
);
}
@EventHandler
public void onPaymentFailed(PaymentFailedEvent event) {
plugin.getLogger().warning("YuPay 支付失败或取消:"
+ event.getOrderId()
+ ", 玩家=" + event.getPlayerName()
+ ", 原因=" + event.getReason());
}
@EventHandler
public void onRefundCompleted(RefundCompletedEvent event) {
plugin.getLogger().info("YuPay 退款完成:"
+ event.getOrderId()
+ ", 玩家=" + event.getPlayerName()
+ ", 退款金额=" + event.getRefundAmount());
}
@EventHandler
public void onRedeemCodeRedeemed(RedeemCodeRedeemedEvent event) {
plugin.getLogger().info("YuPay 卡密已核销:"
+ event.getCode()
+ ", 玩家=" + event.getPlayerName()
+ ", 支付金额=" + event.getAmountPaid()
+ ", 订单=" + event.getOrderId());
}
@EventHandler
public void onRedeemCodeLifetime(RedeemCodeLifetimeEvent event) {
plugin.getLogger().info("YuPay 卡密状态变化:"
+ event.getCode()
+ ", " + event.getOldStatus()
+ " -> " + event.getNewStatus());
}
@EventHandler
public void onPlayerPointsChanged(PlayerPointsChangedEvent event) {
plugin.getLogger().info("YuPay 经济变化:"
+ event.getPlayerUUID()
+ ", 类型=" + event.getEconomyType()
+ ", 变化=" + event.getDelta()
+ ", 新余额=" + event.getNewBalance());
}
}
```
---
### 2. PrePaymentEvent:支付创建前拦截
`PrePaymentEvent` 在玩家通过 `/donate` 等游戏内命令创建订单前触发,可取消,也可修改部分参数。
> 注意:第三方插件直接调用 `YuPayApi#createOrder` 创建订单时,不会经过玩家命令流程,因此不依赖 `PrePaymentEvent`。API 调用方应自行在创建订单前完成自己的业务校验。
可读取 / 修改:
| 方法 | 说明 |
|---|---|
| `getPlayer()` | 发起支付的玩家 |
| `getAmount()` / `setAmount(double)` | 金额 |
| `getPayMethod()` / `setPayMethod(String)` | 支付方式 |
| `getOrderId()` / `setOrderId(String)` | 订单号 |
| `isNoReward()` / `setNoReward(boolean)` | 是否无奖励 |
| `getOutputMode()` / `setOutputMode(String)` | 输出模式,`link` / `text` / `map` |
| `setCancelled(boolean)` | 是否取消 |
| `setCancelReason(String)` | 取消后提示给玩家的原因 |
示例:禁止某玩家创建赞助订单。
```java
@EventHandler(priority = EventPriority.HIGHEST)
public void onPrePayment(PrePaymentEvent event) {
if (event.getPlayer().getName().equalsIgnoreCase("BadPlayer")) {
event.setCancelled(true);
event.setCancelReason("§c你暂时无法使用赞助功能。");
}
}
```
---
### 3. PaymentOrderCreatedEvent:订单创建成功
当 YuPay 成功向支付平台创建订单,并获得二维码链接后触发。
可读取:
| 方法 | 说明 |
|---|---|
| `getPlayerUUID()` | 玩家 UUID |
| `getPlayerName()` | 玩家名 |
| `getAmount()` | 金额 |
| `getPayMethod()` | 支付方式 |
| `getOrderId()` | 订单号 |
| `getQrCodeUrl()` | 支付二维码链接 |
| `isNoReward()` | 是否无奖励订单 |
示例:
```java
@EventHandler
public void onOrderCreated(PaymentOrderCreatedEvent event) {
plugin.getLogger().info("订单创建成功:" + event.getOrderId());
}
```
---
### 4. PaymentCompletedEvent:支付成功并完成发奖
当支付成功,并且 YuPay 内部奖励逻辑处理完成后触发。
可读取:
| 方法 | 说明 |
|---|---|
| `getPlayerUUID()` | 玩家 UUID |
| `getPlayerName()` | 玩家名 |
| `getAmountRmb()` | 支付金额 |
| `getPointsEarned()` | 获得的经济奖励数量 |
| `getPayMethod()` | 支付方式 |
| `getOrderId()` | 订单号 |
| `isNoReward()` | 是否无奖励 |
| `getTimestamp()` | 事件时间戳 |
| `isPlayerOnline()` | 玩家是否在线 |
| `getPlayer()` | 在线时返回 Player,否则返回 `null` |
示例:支付完成后发全服广播。
```java
@EventHandler
public void onPaymentCompleted(PaymentCompletedEvent event) {
Bukkit.broadcastMessage("§6感谢 §e" + event.getPlayerName()
+ " §6赞助服务器 §e" + event.getAmountRmb() + " 元!");
}
```
---
### 5. PaymentFailedEvent:支付失败或取消
可读取:
| 方法 | 说明 |
|---|---|
| `getPlayerUUID()` | 玩家 UUID |
| `getPlayerName()` | 玩家名 |
| `getOrderId()` | 订单号 |
| `getReason()` | 失败或取消原因 |
| `getPayMethod()` | 支付方式 |
示例:
```java
@EventHandler
public void onPaymentFailed(PaymentFailedEvent event) {
plugin.getLogger().warning("订单失败:" + event.getOrderId()
+ ", 原因:" + event.getReason());
}
```
---
### 6. RefundCompletedEvent:退款完成
当外部支付平台退款成功,并且 YuPay 内部状态已经更新后触发。
可读取:
| 方法 | 说明 |
|---|---|
| `getOperatorUUID()` | 操作者 UUID |
| `getOperatorName()` | 操作者名称 |
| `getOrderId()` | 原支付订单号 |
| `getRefundAmount()` | 本次退款金额 |
| `getOriginalAmount()` | 原订单金额 |
| `getPlayerUUID()` | 被退款玩家 UUID |
| `getPlayerName()` | 被退款玩家名 |
| `isRedeemRefund()` | 是否为卡密相关退款 |
示例:
```java
@EventHandler
public void onRefundCompleted(RefundCompletedEvent event) {
Bukkit.getLogger().info("[YuPay] 订单 " + event.getOrderId()
+ " 已退款 " + event.getRefundAmount() + " 元");
}
```
---
### 7. RedeemCodeRedeemedEvent:卡密核销完成
可读取:
| 方法 | 说明 |
|---|---|
| `getCode()` | 卡密 |
| `getPlayerUUID()` | 玩家 UUID |
| `getPlayerName()` | 玩家名 |
| `getAmountPaid()` | 核销时支付金额,免费卡密为 `0` |
| `getEconomyType()` | 经济类型 |
| `getEconomyAmount()` | 经济奖励数量 |
| `hasCommands()` | 是否绑定命令奖励 |
| `getOrderId()` | 关联订单号,免费卡密可能为空 |
示例:
```java
@EventHandler
public void onRedeem(RedeemCodeRedeemedEvent event) {
plugin.getLogger().info(event.getPlayerName()
+ " 核销了卡密 " + event.getCode());
}
```
---
### 8. RedeemCodeLifetimeEvent:卡密状态变化
当卡密进入 `DEPLETED`、`EXPIRED`、`DISABLED` 等状态时触发。
可读取:
| 方法 | 说明 |
|---|---|
| `getCode()` | 卡密 |
| `getOldStatus()` | 旧状态 |
| `getNewStatus()` | 新状态 |
示例:
```java
@EventHandler
public void onCodeStatusChange(RedeemCodeLifetimeEvent event) {
plugin.getLogger().info("卡密状态变化:"
+ event.getCode()
+ " " + event.getOldStatus()
+ " -> " + event.getNewStatus());
}
```
---
### 9. PlayerPointsChangedEvent:YuPay 经济变动
当 YuPay 内部发放 Vault 或 PlayerPoints 经济奖励后触发。
可读取:
| 方法 | 说明 |
|---|---|
| `getPlayerUUID()` | 玩家 UUID |
| `getEconomyType()` | 经济类型,通常为 `vault` 或 `playerpoints` |
| `getDelta()` | 本次变化数量 |
| `getNewBalance()` | 新余额,未知时为 `-1` |
示例:
```java
@EventHandler
public void onEconomyChanged(PlayerPointsChangedEvent event) {
plugin.getLogger().info("玩家 " + event.getPlayerUUID()
+ " 的 " + event.getEconomyType()
+ " 变化:" + event.getDelta());
}
```
---
## API 与事件使用建议
- 如果你要“主动创建支付订单”,使用 `YuPayApi#createOrder`。
- 如果你要“监听玩家通过 YuPay 命令发起支付”,使用 `PrePaymentEvent`。
- 如果你要“支付完成后同步商城订单状态”,监听 `PaymentCompletedEvent`。
- 如果你要“玩家取消或订单失败后回滚业务状态”,监听 `PaymentFailedEvent`。
- 如果你要“退款后撤销外部权益”,监听 `RefundCompletedEvent`。
- 如果你要“卡密核销后同步外部系统”,监听 `RedeemCodeRedeemedEvent`。
- `CompletableFuture` 回调中如需操作 Bukkit API,请切回主线程。
- 不建议在事件监听器里执行耗时数据库或网络请求;如有耗时逻辑,请自行异步处理。
[/MD]
[MD]
---
## ⚙️ 环境要求
| 项目 | 要求 |
|---|---|
| 服务端 | Spigot / Paper / 兼容 Bukkit API 的分支 |
| Minecraft 版本 | 1.8+ 主流版本,具体以实际服务端测试为准 |
| Java | Java 8 或更高 |
| 可选前置 | Vault、PlayerPoints、PlaceholderAPI、ProtocolLib |
### 可选前置说明
| 插件 | 用途 |
|---|---|
| Vault | 发放 Vault 经济奖励 |
| PlayerPoints | 发放点券奖励 |
| PlaceholderAPI | 启用 PAPI 占位符 |
| ProtocolLib | 地图二维码支付锁与更安全的地图交互控制 |
---
## 🚀 两分钟上手
1. 将 `YuPay.jar` 放入服务器 `plugins/` 目录
2. 启动服务器,等待自动生成配置文件
3. 打开 `plugins/YuPay/config.yml`
4. 配置微信支付或支付宝参数
5. 将支付平台后台的异步通知地址配置为你的公网回调地址
6. 放行回调端口,默认 `8080`
7. 执行重载:
```bash
/yp reload
```
8. 运行健康检查:
```bash
/yp health full
```
9. 使用小额订单测试:
```bash
/donate 0.01
```
确认支付成功、回调正常、奖励到账后,即可正式使用。
---
## 🔧 常用配置速查
### 支付核心
```yaml
pay:
default-method: "wechat"
order-subject: "赞助-{player}-{amount}元"
min-amount: 0.01
max-amount: -1
max-daily-amount: -1
```
### 待支付订单自动过期
```yaml
pay:
pending-expire:
enabled: true
minutes: 15
check-interval-seconds: 60
batch-size: 100
```
### 回调服务器
```yaml
callback:
port: 8080
host: "0.0.0.0"
io-threads: 2
worker-threads: 8
max-body-size: 65536
allowed-ips: []
access-log: false
```
### 数据库
```yaml
database:
type: "sqlite"
```
或使用 MySQL:
```yaml
database:
type: "mysql"
mysql:
host: "localhost"
port: 3306
database: "minecraft"
username: "root"
password: "password"
```
### 二维码展示
```yaml
qrcode:
default-output-mode: "map"
text-qr-size: 12
text-black-char: "██"
```
可选输出模式:
| 模式 | 说明 |
|---|---|
| `link` | 发送可点击支付链接 |
| `text` | 发送聊天栏字符二维码 |
| `map` | 发送背包地图二维码 |
---
## 🎮 常用命令
> 以下命令按推荐别名展示。实际别名可在 `config.yml` 的 `commands` 节点中自定义。
### 玩家命令
| 命令 | 功能 |
|---|---|
| `/donate <金额> [支付方式] [输出模式] [-n]` | 发起赞助 |
| `/ytop` | 查看赞助排行榜 |
| `/ytotal [玩家|all]` | 查询赞助总额 |
| `/yp history [页数]` | 查看自己的赞助历史 |
| `/yp orders [页数]` | 查看自己的订单列表 |
| `/yp order <订单号>` | 查看自己的订单详情 |
| `/yp cancel [订单号]` | 取消待支付订单 |
| `/yp retry [订单号]` | 自助重试补发奖励 |
| `/yp lang <语言>` | 切换语言 |
| `/yp code redeem <卡密>` | 核销卡密 |
| `/yp refund request <订单号> [金额]` | 申请退款 |
### 管理员命令
| 命令 | 功能 |
|---|---|
| `/yp reload` | 重载配置 |
| `/yp config` | 查看核心配置 |
| `/yp set <选项> <值>` | 在线修改配置项 |
| `/yp ban <玩家|all> [原因]` | 禁止赞助 |
| `/yp unban <玩家|all>` | 解除禁止 |
| `/yp convert <玩家> <来源> <目标> <数量>` | Vault / PlayerPoints 互转 |
| `/yp health [summary|full]` | 健康检查 |
| `/yp audit [listeners|security]` | 事件监听 / 安全日志审计 |
| `/yp crossserver [status|queue]` | 查看跨服发奖状态 |
| `/yp order ...` | 订单中心 |
| `/yp code ...` | 卡密管理 |
| `/yp refund ...` | 退款管理 |
---
## 🔐 权限节点
### 玩家权限
| 权限 | 说明 |
|---|---|
| `yupay.command.pay` | 发起赞助 |
| `yupay.command.top` | 查看排行榜 |
| `yupay.command.total` | 查询赞助总额 |
| `yupay.command.history` | 查看历史订单 |
| `yupay.command.orders` | 查看订单列表 |
| `yupay.command.cancel` | 取消待支付订单 |
| `yupay.command.retry` | 自助补发 |
| `yupay.command.code` | 使用卡密入口 |
| `yupay.code.redeem` | 核销卡密 |
| `yupay.command.refund` | 使用退款入口 |
| `yupay.refund.request` | 申请退款 |
### 管理权限
| 权限 | 说明 |
|---|---|
| `yupay.reload` | 重载配置 |
| `yupay.ban` | 黑名单管理 |
| `yupay.convert` | 货币转换 |
| `yupay.audit` | 审计 |
| `yupay.order` | 订单管理 |
| `yupay.health` | 健康检查 |
| `yupay.crossserver` | 跨服发奖状态 |
| `yupay.code.admin` | 卡密管理 |
| `yupay.refund` | 退款管理 |
| `yupay.admin` | 管理员总权限 |
---
## 🗄️ 数据表
YuPay 会自动创建并维护所需数据表。
| 表 | 用途 |
|---|---|
| `yu_payments` | 支付订单 |
| `yu_total_levels` | 累计奖励档位记录 |
| `yu_banned_players` | 赞助黑名单 |
| `yu_player_lang` | 玩家语言偏好 |
| `yu_redeem_codes` | 卡密主表 |
| `yu_redeem_logs` | 卡密核销日志 |
| `yu_refund_logs` | 退款操作日志 |
| `yu_refund_requests` | 退款审核请求 |
| `yu_refunds` | 平台退款流水 |
| `yu_reward_steps` | 发奖步骤流水 |
| `yu_payment_callbacks` | 支付 / 退款回调流水 |
| `yu_cross_server_rewards` | 跨服发奖队列 |
| `yu_schema_migrations` | 数据库迁移记录 |
支持自定义表名前缀,适合多插件共用数据库场景。
---
## 🧯 常见问题
### 支付成功但奖励没到账
先让玩家执行:
```bash
/yp retry
```
管理员可继续排查:
```bash
/yp order info <订单号>
/yp order steps <订单号>
/yp order callbacks <订单号>
```
重点检查:
- 订单是否为 `SUCCESS`
- 发奖状态是否为 `FAILED`
- Vault / PlayerPoints 是否正常加载
- 命令奖励中是否有错误命令
- LuckPerms、CMI、EssentialsX 等插件是否可用
---
### 玩家取消订单后又付款了怎么办?
YuPay 会以支付平台成功回调为准进行幂等处理。
管理员可以查看异常订单:
```bash
/yp order abnormal
/yp order risk
```
并根据实际情况处理:
```bash
/yp order resolve <订单号> reward
/yp order resolve <订单号> refund
/yp order resolve <订单号> ignore
/yp order resolve <订单号> hold
```
---
### 回调服务器启动失败
检查:
- 端口是否被占用
- 防火墙是否放行
- 云服务器安全组是否放行
- 支付平台后台 notify-url 是否填写正确
- 是否使用了内网 IP 或 localhost
可执行:
```bash
/yp health full
```
快速查看问题。
---
### 地图二维码无法使用
检查:
- 是否安装 ProtocolLib
- ProtocolLib 版本是否兼容当前服务端
- `qrcode.default-output-mode` 是否为 `map`
- 玩家背包是否有空位
如果 ProtocolLib 不可用,建议临时切换:
```yaml
qrcode:
default-output-mode: "link"
```
---
### 微信 / 支付宝配置失败
建议先执行:
```bash
/yp health full
```
重点检查:
- 微信商户号、AppID、证书序列号
- 微信 API v3 密钥
- 微信平台公钥 / 商户私钥路径
- 支付宝 app-id
- 支付宝商户私钥
- 支付宝公钥或证书模式配置
- notify-url 是否仍为示例地址
---
## 🏆 为什么腐竹会需要 YuPay?
因为它解决的是服务器运营最麻烦的几件事:
- 玩家付款后,奖励能不能自动到账?
- 付款成功但没到账,能不能查清楚?
- 玩家取消订单后又付款,怎么处理?
- 退款有没有审核流程?
- 多个子服如何统一收款和发奖?
- 排行榜和赞助数据怎么展示?
- 商城插件能不能接入支付?
- 出问题时,能不能快速定位?
YuPay 不只是“收钱插件”,而是一套围绕 Minecraft 服务器赞助场景设计的完整支付运营系统。
---
## 💬 交流与支持
插件交流群:**1080918424**
遇到问题时,建议带上:
- 控制台日志
- `config.yml` 关键配置截图
- `/yp health full` 输出
- 订单号
- 支付渠道
- 是否使用 MySQL / 跨服模式
这样更容易快速定位问题。
---
## 💛 支持作者
爱发电:
https://ifdian.net/item/012b6c1c4a0911f18d1b52540025c377
如果 YuPay 帮你节省了对账、发奖、退款和排查问题的时间,欢迎支持作者继续维护。
---
# 一句话总结
**YuPay 把 Minecraft 服务器赞助从“手动收款 + 手动发奖 + 手动对账”,升级成“官方支付 + 自动发奖 + 完整审计 + 可扩展 API”的现代化运营系统。**
把繁琐的赞助流程交给 YuPay,腐竹就能把时间留给真正重要的事:
**做好服务器,服务好玩家。**
[/MD]