feat(affiliate): attribution modes, settlement hardening, trusted proxies, QR and poster
- 归因模式可配置(first_touch/last_touch)、停佣开关、懒结算与佣金基数降级复查 - aksrt_get_client_ip 接入 aksrt_trusted_proxies 信任代理名单,防 CDN 头伪造 - 收益页 Canvas 二维码 + 750x1000 推广海报(本地打包 qrcode.min.js,无 CDN) - 契约测试固化归因/海报/信任代理链路
This commit is contained in:
@@ -570,6 +570,15 @@ add_action('admin_notices', static function () {
|
||||
// ============================================================
|
||||
|
||||
function aksrt_get_client_ip(): string {
|
||||
$remote = isset($_SERVER['REMOTE_ADDR']) ? (string) $_SERVER['REMOTE_ADDR'] : '';
|
||||
|
||||
// 信任代理名单:过滤返回非空 CIDR/IP 列表时,仅当 REMOTE_ADDR 命中名单
|
||||
// 才采纳 CDN / 反代头;否则客户端可伪造这些头绕过 IP 维度的风控。
|
||||
$trusted = apply_filters('aksrt_trusted_proxies', []);
|
||||
if (is_array($trusted) && $trusted !== [] && $remote !== '' && !aksrt_ip_in_list($remote, $trusted)) {
|
||||
return $remote;
|
||||
}
|
||||
|
||||
// Prefer CDN / reverse-proxy headers (Cloudflare first), then generic proxies.
|
||||
$keys = [
|
||||
'HTTP_CF_CONNECTING_IP',
|
||||
@@ -601,6 +610,51 @@ function aksrt_get_client_ip(): string {
|
||||
return isset($_SERVER['REMOTE_ADDR']) ? (string) $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
|
||||
}
|
||||
|
||||
/**
|
||||
* IP 是否命中精确 IP 或 CIDR 名单(IPv4 与 IPv6)。
|
||||
*
|
||||
* @param string $ip 待判定 IP。
|
||||
* @param array $list 精确 IP / CIDR 段列表。
|
||||
*/
|
||||
function aksrt_ip_in_list(string $ip, array $list): bool {
|
||||
foreach ($list as $entry) {
|
||||
$entry = trim((string) $entry);
|
||||
if ($entry === '') {
|
||||
continue;
|
||||
}
|
||||
if (!str_contains($entry, '/')) {
|
||||
if (strcasecmp($entry, $ip) === 0) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
[$subnet, $prefix] = explode('/', $entry, 2);
|
||||
$prefix = (int) $prefix;
|
||||
$ip_binary = @inet_pton($ip);
|
||||
$subnet_binary = @inet_pton($subnet);
|
||||
if ($ip_binary === false || $subnet_binary === false || strlen($ip_binary) !== strlen($subnet_binary)) {
|
||||
continue;
|
||||
}
|
||||
$max_bits = strlen($ip_binary) * 8;
|
||||
if ($prefix < 0 || $prefix > $max_bits) {
|
||||
continue;
|
||||
}
|
||||
$full_bytes = intdiv($prefix, 8);
|
||||
$remain_bits = $prefix % 8;
|
||||
if ($full_bytes > 0 && substr($ip_binary, 0, $full_bytes) !== substr($subnet_binary, 0, $full_bytes)) {
|
||||
continue;
|
||||
}
|
||||
if ($remain_bits > 0) {
|
||||
$mask = chr((0xFF << (8 - $remain_bits)) & 0xFF);
|
||||
if ((substr($ip_binary, $full_bytes, 1) & $mask) !== (substr($subnet_binary, $full_bytes, 1) & $mask)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Native wp-comments-post + any path that omits comment_author_IP.
|
||||
// Ensures comment_author_IP (and thus aksrt_comment_addr) is the real client IP behind CDN.
|
||||
add_filter('pre_comment_user_ip', static function ($ip) {
|
||||
|
||||
Reference in New Issue
Block a user