Table of Contents
7.14 兑换福利【未授权拦截通过,待登录态联调】
POST /tieup/api/v1/benefit-redemptions
鉴权:需要 Authorization: Bearer <access_token>。
请求头:
Idempotency-Key: benefit_10001_20260623143000_x7f9k2
实物福利请求体示例:
{
"benefit_id": 30001,
"receiver_name": "张三",
"receiver_phone": "13800000000",
"detail_address": "上海市黄浦区详细地址",
"email_snapshot": "fan@example.com"
}
虚拟福利请求体示例:
{
"benefit_id": 30002,
"email_snapshot": "fan@example.com"
}
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
benefit_id |
integer | 是 | 要兑换的福利 ID |
address_id |
integer | 否 | 收货地址 ID;实物福利可传,虚拟福利不需要 |
receiver_name |
string | 否 | 收货人姓名;实物福利未传 address_id 时必填 |
receiver_phone |
string | 否 | 收货人手机号;实物福利未传 address_id 时必填,必须符合手机号格式 |
detail_address |
string | 否 | 详细地址;实物福利未传 address_id 时必填 |
email_snapshot |
string | 否 | 邮箱快照;need_email=1 的福利必填,必须是单个合法邮箱;与收货地址要求独立 |
响应字段:
| 字段 | 类型 | 说明 |
|---|---|---|
redemption_id |
integer | 福利兑换记录 ID |
redemption_no |
string | 福利兑换编号 |
benefit_id |
integer | 福利 ID |
benefit_title |
string | 福利标题 |
benefit_type |
integer/null | 福利类型:1=实物,2=虚拟 |
redemption_status |
integer | 福利兑换状态:1=待发放,2=已发放,3=已取消,4=已退款 |
candy_amount_snapshot |
string | 兑换糖果快照;达标福利固定为 0.00 |
address_snapshot |
object/null | 兑换时保存的收货地址快照,虚拟福利为空;新直传地址场景包含 receiver_name、receiver_phone、detail_address,使用 address_id 或历史数据时可能包含省市区等扩展字段 |
address_snapshot.receiver_name |
string | 收货人姓名 |
address_snapshot.receiver_phone |
string | 收货人手机号 |
address_snapshot.detail_address |
string | 详细地址 |
email_snapshot |
string | 兑换时保存的邮箱快照;未填写时为空字符串 |
condition_type_snapshot |
string | 兑换时福利条件类型快照 |
condition_value_snapshot |
integer | 兑换时福利条件目标值快照 |
progress_value_snapshot |
integer | 兑换时粉丝历史累计进度快照 |
progress_snapshot |
object | 兑换时完整进度快照 |
progress_snapshot.condition_type |
string | 兑换时福利条件类型 |
progress_snapshot.current_value |
integer | 兑换时粉丝历史累计进度值 |
progress_snapshot.target_value |
integer | 兑换时福利条件目标值 |
progress_snapshot.percent |
integer | 兑换时进度百分比,最大 100 |
progress_snapshot.remaining_value |
integer | 兑换时距离达标还差的数值 |
progress_snapshot.remaining_text |
string/null | 兑换时未达标提示文案;已达标时为空 |
progress_snapshot.is_redeemable |
integer | 兑换时是否可兑换:1=是,2=否 |
progress_snapshot.is_redeemed |
integer | 兑换时是否已兑换:1=是,2=否 |
fulfillment_remark |
string/null | 偶像发放备注,待发放时为空 |
响应示例:
{
"code": 200,
"message": "成功",
"data": {
"redemption_id": 60001,
"redemption_no": "BR202606170001",
"benefit_id": 30001,
"benefit_title": "亲笔签名照",
"benefit_type": 1,
"fan_user_id": 10001,
"artist_id": 20001,
"candy_amount_snapshot": "0.00",
"address_snapshot": {
"receiver_name": "张三",
"receiver_phone": "13800000000",
"detail_address": "上海市黄浦区详细地址"
},
"email_snapshot": "fan@example.com",
"fulfillment_remark": null,
"condition_type_snapshot": "subscription_months",
"condition_value_snapshot": 5,
"progress_value_snapshot": 5,
"progress_snapshot": {
"condition_type": "subscription_months",
"current_value": 5,
"target_value": 5,
"percent": 100,
"remaining_value": 0,
"remaining_text": null,
"is_redeemable": 1,
"is_redeemed": 2
},
"status": 1,
"redemption_status": 1,
"redeemed_at": "2026-06-23 14:30:00",
"fulfilled_at": null
}
}
业务规则:
- 达标后粉丝可任意时间兑换,兑换本身不扣糖果;
candy_spent只是达标条件之一。 - 不管福利什么时候创建,达标判断都按粉丝历史上对该偶像的全部有效累计数据计算。例如今天新增“订阅满 5 个月”,粉丝过去已累计订阅 5 个月,则立即可兑换。
- 订阅按累计有效订阅天数每 30 天折算 1 个月;提问按未退款有效提问数;视频通话按已完成实际分钟;糖果消耗按未取消/未退款的历史有效消耗统计。
- 实物福利必须提供收货地址,服务端支持
address_id读取当前用户启用地址,也支持请求体直接传receiver_name、receiver_phone、detail_address,最终保存兑换时地址快照。 -
address_id只用于兑换时读取用户地址,查询接口以address_snapshot为准;用户后续修改或删除地址簿不会影响历史兑换快照,程序也不会把快照反向恢复成用户地址簿记录。 - 需要邮箱的福利必须提供
email_snapshot,邮箱要求与收货地址要求独立;实物福利可以同时要求地址和邮箱,虚拟福利也可以要求邮箱。 - 兑换时锁定
tieup_benefit行并检查库存;同一fan_user_id + benefit_id或同一fan_user_id + Idempotency-Key不允许重复创建兑换记录,数据库唯一索引作为并发兜底。 - 偶像自行兑现福利,程序只记录待发放/已发放状态、地址快照和发放备注。