☰
支付风控规则设计:名单频次阈值关系与灰度监控误杀排查
2026/10/1 13:46:25 网站建设 项目流程

支付风控规则这个东西,外行听着像玄学,内行做起来全是脏活累活。我带过几支风控小组,最常见的画面是:产品经理拿着一条客诉冲进来说"用户正常付款被拦了,赶紧把规则关掉",而风控同学打开后台发现这条规则上线才三天,拦了八千笔,其中七千多笔确实有问题,剩下那几百笔才是误伤。关还是不关?这个判断力,就是支付风控规则这门手艺的核心。这篇内容不打算讲什么高大上的模型架构,就聊规则本身——从一笔请求进来到决策返回,中间那几百毫秒里到底发生了什么,规则怎么设计、阈值怎么定、上线之后怎么盯、误杀之后怎么查。适合刚接手风控策略的同学、做支付业务的研发、以及被风控拦过却又想搞明白为什么的产品同学。看完你至少能自己动手配一条不丢人的规则,并且知道它上线之后会怎么咬人。

1. 支付风控规则在整条链路上的介入位置

1.1 风控不是支付那一下才开始的

新手最容易犯的错,是把支付风控规则理解成"支付接口前面那道闸机"。实际上闸机只是最后一道,前面还有好几道门。一个完整的支付链路里,风控至少在这些节点会介入:注册环节判断是不是批量养号、登录环节判断是不是盗号、绑卡环节判断这张卡是不是和几十个账号关联、下单环节判断商品和收货地址有没有异常、支付环节判断这次付款的资金流向和频次、以及支付成功之后的退款和提现环节。

为什么要把规则铺得这么散?因为支付的本质是资金转移,一旦钱出去了,追回来的成本极高。行业里有个粗略的说法,事前拦截一笔欺诈的成本是几毛钱,事中人工审核是几块钱,事后追款是几十块甚至收不回来。所以规则前置是第一原则,能在注册时拦掉的账号,就不要留到支付时才发现。我见过一些团队把所有资源都堆在支付那一瞬间,结果就是规则越堆越多、响应时间越来越长、误杀越来越重,最后变成"宁可错杀一千"的粗暴状态,用户体验和业务量一起掉。

提示:如果你刚接手一条存量风控链路,先别急着加规则,先把现有规则按介入节点列一张表,标清楚每条规则在哪个节点生效、拦了什么、拦了多少。这一步能帮你快速看出规则分布是不是畸形。

1.2 规则要回答的三个本质问题

不管规则写得多花哨,它归根到底只在回答三个问题:这次操作是不是账号本人在做、是不是本人真实意愿、这笔钱本身干不干净。这三个问题对应的风险类型完全不同,处理手段也不一样。

"是不是本人"对应的是账号盗用类风险。判断依据通常是设备指纹是否更换、登录地是否突变、操作习惯是否偏离历史。这类规则的特点是宁可敏感一点,因为盗号损失是直接的,而且用户会主动来找你,误杀了打个电话就能解。

"是不是本人意愿"对应的是被诱导类风险,比如用户被骗着去付款、被诱导充值。这类最难做,因为从系统视角看,所有行为都是用户自己点的,没有任何技术异常。规则能抓的往往是间接信号:收款方是不是新注册、是不是短时间内收到大量来自不同用户的付款、金额是不是有聚集特征。

"钱干不干净"对应的是资金链风险,涉及洗钱、跑分、赌博充值这类。这类看的不是单笔,而是整体资金网络的形态:账号之间有没有环形转账、收款账号是不是快进快出、资金留存时间是不是特别短。

把这三个问题分开之后,你会发现很多规则冲突迎刃而解。比如一条规则为了抓盗号把设备变更判成高风险,另一条规则为了抓骗局把新设备直接拒绝,两条规则叠在一起就会误伤大量换手机的正常用户。给每条规则标注它服务的是哪个问题、对应的调性是什么,是规则治理的第一步。

2. 四类规则骨架:名单、频次、阈值、关系

2.1 名单类规则:上手最快,腐烂也最快

名单类规则就是黑名单、白名单、灰名单。黑名单直接拒绝,白名单直接放过,灰名单转人工或者要求二次验证。这类规则写起来最简单,一条 if 就完事,但它是最容易腐坏的一类。

我见过一个团队的黑名单跑了一年多没清理,里面躺着三十多万条记录,其中大量是当年某个临时活动误伤进来的正常用户。这些人后来一直用不了支付,投诉到客服,客服也不知道为什么。更麻烦的是黑名单的写入往往没有严格的准入标准,某个运营同学觉得这个人可疑就加进去,加完就没人管了。

比较健康的做法是给名单加上时间属性和来源属性。每条记录都要有加入时间、加入原因、加入人、过期时间。永久黑名单只留给证据确凿的场景,比如确认的欺诈账号、司法协查名单。其余全部设定有效期,到期自动降级或者进入复核队列。

注意:白名单是最危险的东西。一旦业务方发现"加白名单就能绕过风控",各种关系户就会涌进来。白名单必须走审批,并且定期审查存续必要性,我习惯每隔一个季度就把白名单全量导出,逐条问"这条还需要吗"。

2.2 频次类规则:滑动窗口的坑比想象中多

频次类规则是风控的主力,比如"同一设备一分钟内发起超过5笔支付""同一张卡一小时内被超过3个账号使用""同一IP十分钟内注册超过10个账号"。听起来简单,实现起来全是细节。

第一个坑是窗口的定义。固定窗口会带来边界问题:如果限制是"每分钟5笔",用户在 00:59 发5笔、01:01 又发5笔,固定窗口会认为两分钟各5笔都合规,但实际上两秒钟内发生了10笔。所以支付场景基本都要用滑动窗口,用 Redis 的有序集合或者 Flink 的状态窗口来实现,按事件时间戳精确计数。

第二个坑是维度的选择。同一个"10分钟内5笔"的限制,按设备算、按账号算、按银行卡算、按IP算,抓到的风险完全不同。盗号团伙通常换账号不换设备,跑分团伙通常换设备不换卡,薅羊毛的通常换一切但集中在同一个收货地址。所以实际生产中,一条频次规则往往是多个维度同时计数,任一维度超限即触发。

第三个坑是计数的时间基准。用客户端上报的时间还是服务端接收的时间?必须是服务端时间。客户端时间可以被篡改,而且存在时区和时钟漂移问题。我见过一个案例,攻击者把设备时间往后调,导致所有请求的服务端时间戳看起来都落在同一毫秒,反而绕过了滑动窗口的判断逻辑。

-- 计算某设备在滑动一小时内的独立账号数,用于团伙聚集判断 SELECT device_id, COUNT(DISTINCT uid) AS uid_cnt_1h FROM dwd_payment_event WHERE event_time >= NOW() - INTERVAL '1' HOUR GROUP BY device_id HAVING COUNT(DISTINCT uid) >= 3;

2.3 阈值类规则:抓的是金额和比例的异常形态

阈值类规则看的是单次操作的数值特征,比如金额异常、折扣比例异常、余额变动异常。这类规则的关键不是"多大算大",而是"相对于谁算大"。

同样是支付五千块,对于一个日均消费三百的用户是异常,对于一个每月固定交房租的用户是正常。所以阈值类规则几乎都要带个性化基线。常见的做法是给每个用户算一个历史金额的分位数,比如取过去九十天支付金额的 P95 作为基线,超过基线三倍才触发。这样既能抓住突发的异常大额,又不会因为用户换了消费场景就误伤。

还有一类阈值规则针对的是比例而非绝对值,比如"优惠抵扣金额占订单金额比例超过90%""退款金额超过原订单金额"。这类规则对付的是薅羊毛和退款欺诈,数值型阈值在这种场景下比频次更有效,因为攻击者可以控制频次但很难控制比例。

2.4 关系类规则:从单点判断升级到团伙识别

前面三类规则看的都是单个账号、单台设备、单张卡。但真实的欺诈很少是孤立的,背后几乎都是一个团伙,共享设备、共享银行卡、共享收款账号、共享收货地址。关系类规则就是把账号、设备、卡、手机号、地址这些实体当成图的节点,把它们之间的关联当成边,然后在这个图上找异常结构。

最基础的关系规则是"共用"判断:同一个设备登录过几个账号、同一张卡被几个账号绑定、同一个收货地址收到过多少个不同账号的订单。进阶一点的是找环,比如 A 转账给 B,B 转账给 C,C 又转回 A,这种环形资金流是很典型的洗钱特征。再进阶就是社区发现,用图算法把明显抱团的节点聚成一个簇,整簇一起处理,效率比一条条规则去匹配高得多。

关系类规则的计算成本明显高于前三类,通常不会放在实时链路上,而是离线跑出结果,把高风险的关系标签写回用户画像,实时规则再去读这些标签。这是一种典型的"离线算关系、在线用标签"的分工。

3. 特征字段与算子:一份能直接落地的规则配置结构

3.1 特征字段的四个来源

写规则之前得先想清楚规则能读哪些数据。我把风控规则的特征字段分成四类来源。

第一类是请求自带字段,也就是这次支付请求里直接带着的东西:金额、币种、商户号、商品类目、支付方式、收款账号。这类字段最实时、最准确,但信息量有限,单看这些几乎判断不出什么。

第二类是实时聚合字段,需要风控系统自己算,比如设备近一小时支付笔数、账号近十分钟登录失败次数、收款方近一天收款笔数。这类字段是规则的主力,计算逻辑通常放在流处理任务里,结果写进低延迟的存储供规则引擎读取。

第三类是画像标签字段,属于离线产出,比如账号注册天数、历史成功支付笔数、历史投诉次数、设备是否被标记为模拟器。这类字段更新频率低但信息密度高,是判断"这个账号是不是新号"这类问题的关键依据。

第四类是外部数据字段,比如银行卡 BIN 对应的发卡行和卡种、手机号归属地、IP 归属地。这类数据通常通过第三方服务或者本地库获取,用于做交叉验证,比如"账号注册地和本次支付IP归属地相隔三个省"就是一个值得关注的信号。

这四类字段在规则里的用法差异很大,配置时最好在字段命名上做区分,比如实时聚合用agg_前缀,画像标签用tag_前缀,一眼就能看出这条规则依赖的是哪类数据,排查问题时也能快速定位是哪个数据源出了岔子。

3.2 算子设计:为什么坚持原子算子而不是随便写脚本

我见过两种极端的规则引擎实现。一种是允许策略同学直接写 Java 或 Python 脚本,灵活度拉满,但结果是规则不可读、不可审计、性能不可控,同一条逻辑十个人写出十种。另一种是只给下拉框,只能选"大于""小于""等于",安全但表达力太弱,稍微复杂的逻辑就得开发介入。

比较舒服的做法是原子算子加组合。原子算子是一组固定的、语义明确的判断函数,比如时间窗口计数、分位数比较、集合包含、字符串相似度、地理位置距离。策略同学通过组合这些算子来表达逻辑,既能覆盖大多数场景,又保证了每条规则的行为可预测、可审计。

规则配置本身建议用 JSON 或 YAML 这种结构化格式,配合版本管理。好处是规则变更可以被 review、可以回滚、可以 diff,出了事故能快速定位是哪次改动引入的。这一点在多人协作的时候尤其重要,我经历过一次线上事故,最后查出来是某条规则的阈值被人从500改成了50,没有走审批,没有任何记录,靠着一层层翻日志才找到。

{ "rule_id": "R_PAY_0017", "rule_name": "同设备短时多账号聚集支付", "scene": "payment_checkout", "version": "v3", "priority": 300, "window": "10m", "conditions": { "and": [ { "field": "agg_device_distinct_uid_1h", "op": "gte", "value": 3 }, { "field": "order_amount", "op": "between", "value": [100, 5000] }, { "field": "tag_user_reg_days", "op": "lt", "value": 7 }, { "field": "device_is_emulator", "op": "eq", "value": false } ] }, "action": { "decision": "review", "risk_level": "medium", "ttl": "7d" }, "owner": "risk_team_a", "created_at": "2024-06-01" }

上面这段配置里,priority决定规则的执行顺序,window声明时间窗口,conditions是条件树,action是命中后的动作。把动作和条件分开,是为了支持"影子模式"——只记录命中不实际拦截,这个后面会详细讲。

3.3 规则优先级与命中顺序的设计

规则一多,冲突就来了。五条规则同时命中,有的说拒绝,有的说通过,听谁的?这时候优先级就是唯一的裁判。我的习惯是按动作的严重程度倒序排:直接拒绝的最高,要求二次验证的次之,转人工审核再次,打标观察最低。同一级别里再按规则的精确度排,证据越硬的越靠前。

顺序为什么重要?因为有些规则一旦命中,后面的规则根本不需要再算,直接短路返回,这能省下大量计算时间。比如一条确定性的黑名单规则命中,就没必要再跑二十条实时聚合规则了。但也有些场景需要全量计算,比如风控报表需要统计每条规则的独立命中量,这时候就得把短路逻辑关掉。

提示:短路优化和全量统计是有冲突的。我的做法是设置一个采样开关,主链路走短路保证低延迟,同时按1%的采样率跑全量计算,用采样数据来评估每条规则的独立贡献,两边都不耽误。

4. 阈值怎么定:从历史分布里切,而不是拍脑袋

4.1 先把分布画出来再说话

新手定阈值最常见的动作是:看看昨天线上数据,觉得"5笔好像差不多",然后写个5。这个动作的问题在于,你根本不知道那个字段的真实分布长什么样。可能95%的用户这个字段是0到1,5%是2到3,而攻击者是50起步,那你定5还是定8其实都无所谓,都有效。但如果分布是连续渐变的,那定5和定8的差别可能就是几万笔误杀。

所以定阈值的第一步永远是拉分布。把过去三十天的该字段值全部捞出来,按正常用户和确认风险用户两个群体分别画直方图或者累计分布曲线。你会看到两种典型形态:一种是两个群体有明显间隔,中间有一块空白区,那阈值就取在空白区里,怎么取都行;另一种是两个群体重叠严重,怎么切都会误伤,这种字段本身就不适合做单点阈值,得换字段或者组合字段。

-- 分别看正常与风险群体的分布,找阈值切点 SELECT is_fraud, approx_percentile(amount, 0.5) AS p50, approx_percentile(amount, 0.9) AS p90, approx_percentile(amount, 0.99) AS p99, MAX(amount) AS max_amount FROM dws_payment_feature WHERE dt BETWEEN '2024-05-01' AND '2024-05-31' GROUP BY is_fraud;

4.2 用分位数和区分度指标确定初值

有了分布,接下来是选切点。常用两个参考量:分位数和区分度。

分位数的作用是控制影响面。如果你希望这条规则最多影响0.5%的正常用户,那就去看正常群体分布的P99.5,把阈值定在那附近。这样从统计意义上,误伤率上限就有了个大概的锚。注意这里是"大概",因为分布会漂移,今天合理的阈值过两个月可能就不合理了。

区分度指标衡量的是这个切点把两个群体分开的能力。直观理解就是,切点以下有多少是正常用户、多少是风险用户;切点以上反过来。理想情况下,我们希望切点以上风险用户的浓度远高于切点以下。这个浓度差越大,规则的性价比越高。定阈值的时候可以在多个候选切点上算一遍,选浓度差最大的那个。

定完初值还有一个必须做的事:小流量验证。哪怕统计学上算得很漂亮,真实流量里总会有你没想到的情况。所以初版阈值不要直接全量生效,先影子跑几天,看真实命中量和真实误杀反馈,再决定是收紧还是放松。

4.3 阈值上线后必须盯的三条曲线

阈值不是定完就完了,它需要持续被监控。我习惯盯三条曲线。

第一条是命中量曲线。正常情况下它应该是平稳的或者有规律波动的(比如白天高晚上低)。如果某天突然翻倍,要么是有新的攻击手法来了,要么是上游数据出了问题导致字段值整体偏移,两种情况都需要立刻查。

第二条是误杀率曲线。误杀率的获取通常要等客诉,所以有滞后。但可以通过"命中规则的账号在后续七天内的自证行为"来近似估计,比如命中后用户完成了一次人脸验证并且继续正常使用,那就大概率是误杀。这条曲线缓慢上升就是阈值该放松的信号。

第三条是字段分布漂移曲线。监控阈值字段本身的分布变化,如果分布整体右移了,说明用户行为在变化,原来的阈值相对就变严了,误杀会自然上升。这条曲线往往能提前于误杀率曲线发出预警。

5. 误杀投诉的排查链路:一次完整的回溯

5.1 先把决策快照捞出来

排查误杀最忌讳的就是凭印象猜。线上环境千变万化,你必须能看到当时到底发生了什么。所以风控系统必须保存决策快照,快照里至少要包含:请求时间、命中的规则ID和版本、每条命中规则读取到的字段实际值、最终的决策动作、以及本次请求的关键标识(设备号、账号、订单号)。

没有快照的系统,排查起来就是噩梦。我经历过一个早期的系统,只记录了"命中规则X",字段值一个都没存,用户投诉来说自己正常付款被拦,我们只能看着规则X的条件发呆,完全不知道当时是哪个字段踩线了。后来花了两个月补齐快照,才把排查效率提上来。

有了快照,排查的第一步就很简单:拿用户的账号或订单号去捞快照,看一眼最终决策是哪几条规则共同作用出来的。通常情况是,用户看到的是"付款失败",但背后其实是两三条规则叠加命中的结果,单独任何一条都不足以拒绝,叠加起来才拒绝。

5.2 逐条验证"这条规则为什么命中"

捞出快照之后,逐条看每条命中规则的字段值。这里最常发现三类问题。

第一类是字段值本身就不对。比如设备指纹因为用户升级了系统被重新生成,导致系统认为"换设备了"。这类问题属于数据质量问题,不是规则逻辑问题,修的是上游采集。

第二类是字段值对,但规则组合的语义有偏差。比如"新设备"和"新账号"两个条件单独看都合理,但组合起来在大促期间会误伤大量真实新用户,因为大促本来就是拉新的时候。这类问题是规则设计时缺少场景上下文,修的是规则本身,加个条件排除大促场景就行。

第三类是字段值对、规则也对,但阈值偏紧。比如某条频次规则定的是10分钟内3笔,而用户当天在超市连刷了几次小额支付,正好踩线。这类问题修的是阈值。

把这三类分清楚很关键,因为它们的修复方式和责任人完全不同。数据问题找数据团队,逻辑问题找策略同学,阈值问题策略同学自己就能改。

5.3 修复方案:降级、加白、还是拆条件

确认是误杀之后,有三种修复路径,选哪种取决于影响面和紧急程度。

最轻的是加白名单,只对这一个用户放行。优点是见效快、影响面小、风险低;缺点是治标不治本,如果同类误杀有一千个用户,你不可能加一千条白名单。加白只适合个案处理。

中等的是调整规则动作,从"拒绝"降级为"二次验证"或"转人工"。这样既不会直接放走风险,也不会直接挡住用户,把判断权交还给真实的人类行为。这个做法在大促期间特别常用,因为大促时段的误杀代价远高于平时,宁可放宽一点。

最重的是拆条件或者调阈值,改动规则本身。这是根治方案但风险最大,因为改动会影响所有命中这条规则的流量,可能把一部分本该拦住的也放走了。所以必须走灰度,先影子跑,观察新旧版本在同一批流量上的决策差异,确认差异都在预期范围内再切量。

注意:任何一次规则修复都要留下记录,说清楚为什么改、改成什么、影响了多少流量。我见过太多团队改完就忘,三个月后同样的问题再来一次,没人记得当初是怎么处理的。

6. 灰度、监控与规则迭代的日常

6.1 影子模式跑一周再放量

新规则上线前必须先跑影子模式,这一点没有商量余地。影子模式的意思是:规则照常计算,命中日志照常记录,但决策动作不生效,用户端完全无感。这样你可以拿到这条规则在真实流量上的完整表现,包括命中量、命中的人群分布、命中的时间段分布、以及命中的账号后续有没有出现欺诈行为。

为什么至少跑一周?因为用户行为的周期性在一天和一周内都很明显。工作日和周末、白天和深夜、发薪日和月末,支付行为差异巨大。只跑一天你看到的可能是一个特殊时段的表现,据此放量很容易出偏差。一周能覆盖大部分周期,是比较稳妥的观察窗口。

影子模式期间重点看两件事。一是命中量级,如果一天命中几十万笔,那这条规则放量之后会直接把审核队列和客服电话打爆,得先想办法收窄。二是命中人群的质量,抽样看看命中的人里有多少明显是正常用户,如果浓度太低就说明规则区分度不够。

6.2 监控面板上必须有的六个指标

规则上线之后的日常运营,靠的是一个说得清楚的监控面板。我把必须有的指标归成六个。

命中量:按规则、按小时统计的命中次数,用来发现突增突降。

覆盖率:这条规则贡献的决策占总决策的比例,用来衡量规则的重要性,长期覆盖率接近零的规则就是该退休了。

准确率:命中规则的用户中,后续被确认为风险的比例。这个指标获取有滞后,通常用一周或一个月的窗口来算。

误杀率:命中规则的用户中,后续出现自证正常行为的比例,跟准确率互补。

拦截资损:这条规则实际挡下来的金额,用来衡量规则的经济价值。一条准确率高但拦的都是几块钱订单的规则,优先级其实不高。

响应耗时:这条规则给整个决策链路增加的延迟。实时链路对耗时极其敏感,一条算得慢的规则足以拖垮整条链路。

这六个指标放在一个面板上,配合每个规则的负责人,就形成了一个基本的规则治理闭环。每周过一遍,该收紧的收紧,该放松的放松,该下线的下线。

6.3 规则的退休机制

大部分团队只管加规则,不管删规则,结果规则库越滚越大,最后没人敢动。这是我见过最普遍的规则治理问题。规则和人一样需要退休。

我给规则设了三个退休触发条件。第一,连续三个月零命中,或者命中量低到统计上无意义,直接下线。第二,连续三个月准确率低于一个底线,说明这条规则抓的东西已经不构成风险,或者上游数据变了导致它失效,先下线再研究。第三,被新的规则完全覆盖,旧规则的存在只是增加耗时,直接下线。

下线也要走流程,不能直接删。先进观察模式,只记录不生效,跑两周看看有没有什么异常,确认无影响再彻底移除。移除之后的规则配置保留在版本库里,万一后面又要用,直接翻历史版本恢复就行。这套机制跑起来之后,规则库能保持一个相对精简的状态,每条规则都有明确的使命和明确的责任人,出问题也能快速定位。

我在实际操作中的体会是,风控规则这事儿最难的不是写规则,是克制加规则的冲动。每次有新的欺诈案件出现,第一反应总是"再加一条规则吧",但实际上很多时候应该做的是看看已有规则哪里漏了,是阈值松了还是字段缺了。规则数量本身没有意义,能用最少的规则覆盖最多的风险,同时把误杀压在可接受范围内,这才是这门手艺的目标。我见过把规则数从四百条精简到一百二十条、拦截效果反而变好的案例,靠的就是一条条复盘、合并、淘汰。这个过程枯燥且没有成就感,但它比写新规则有价值得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询