支付与结算安全
本页描述 LotteryBox 1.0.4 当前静态源码中的状态流。它用于解释失败时应查看什么,不是对任意经济插件故障、JVM 崩溃、断电或磁盘损坏的无条件保证。
事务状态流
玩家交互
-> 校验箱体、模式、非空奖池、每日限制、支付方式和价格快照
-> 玩家级 token 进入处理中,阻止同一流程重复进入
-> DIRECT 原后端扣款,或 KEY 校验并在提交点消耗一把钥匙
-> 冻结唯一抽奖结果
-> 启动动画并提交一次抽奖
-> 发奖、播报、清理 token 和会话
任一提交前失败
-> 回滚已预占次数
-> 已扣款则按原后端、原货币、原金额退款
-> 已消耗钥匙则退回钥匙
-> 清理 token 和会话
防重复入口
每个玩家使用操作 token 标记付款或抽奖中状态。快速连点、重复 GUI 点击或旧异步回调不能直接开启第二条同类流程;玩家会收到 drawing-in-progress 或购买处理中提示。
token 不是数据库事务,也不能保护插件进程以外的系统。真实服仍需验证经济后端的幂等性、回调线程和异常契约。
扣款前复核
DIRECT 点击支付按钮后,不会只信任打开 GUI 时的数据,而会重新检查:
- 箱子仍存在且身份未变化;
- 当前仍是 DIRECT;
- 奖池仍有可解析奖品;
- 每日/额外次数仍能提交;
- 支付方式仍属于当前箱且后端可用;
- 当前实际价格仍与玩家看到的快照一致。
若管理员在 GUI 打开后改价、移除货币或切换模式,操作以 payment-selection-expired 取消;不会直接按新价格扣款。零价格不调用后端扣款。
抽奖结果与 GUI
奖品在动画开始前冻结。关闭动画 GUI:
- 不重新抽取;
- 不取消已提交结算;
- 不应重复发奖。
动画期间玩家退出时,冻结奖品走抽奖箱位置掉落路径。玩家在线但背包已满时,奖品走脚边掉落路径并使用 inventory-full-drop 提示。管理员应确认箱子位置允许生成物品,且没有清理插件立即移除掉落物。
KEY 消耗和退还
- 错误箱子的钥匙不消耗;
- 正确钥匙在 DIRECT 与 KEY 两种模式都能使用;
- 只有奖池、每日限制和奖品解析通过后才消耗一把;
- 动画启动失败时退还该钥匙;
- 背包满时退回钥匙走安全掉落路径,并显示
key-inventory-full-drop。
购买钥匙与使用钥匙是两步:购买成功只发钥匙,不计每日抽奖次数;真正使用时才进入抽奖次数提交。
退款
扣款成功但抽奖无法提交时,退款保持:
- 原经济后端;
- 原货币 ID;
- 原金额;
- 原玩家。
成功时使用 refund-success。失败时使用 refund-failed,同时写入严重级别控制台日志。自动退款失败后不能只让玩家重试,否则可能产生重复扣款或重复补发。
建议人工对账记录:
- 玩家 UUID 与名称;
- 操作时间和箱子世界/坐标;
- DIRECT 还是 KEY;
- 支付 ID、经济后端和金额;
- 扣款与退款日志;
- 是否消耗次数、钥匙,是否已经生成奖品;
- 管理员最终补款、退款或补发决定。
Folia 与阻塞边界
经济、数据库、网络和文件 I/O 不得让 Region 线程同步等待;Region 回调也不得通过 Future.get() 等方式等待另一个 Region。正确路径是携带 UUID、金额、货币 ID、箱坐标和奖品等快照进入异步处理,再通过 Entity/Region 调度回写实时状态。
运维检查
- 用小额测试货币演练余额不足、后端断开、改价、删货币和玩家离线。
- 搜索
refund-failed、drawing-in-progress、payment-failed、purchase-failed日志与消息。 - 核对每日次数、额外次数、钥匙数量、最终奖品和经济流水,而不只核对聊天提示。
- 备份箱数据和次数数据;升级服务端核心或经济插件后重新验证失败路径。