Files
wordpress-theme-aksrt/inc/classes/class-aksrt-sms-provider-custom.php
T

170 lines
6.4 KiB
PHP

<?php
/**
* AKSRT SMS Provider — 自定义 HTTP 接口
*
* 支持用户自定义短信发送 HTTP 接口。
* 可配置请求 URL、方法、鉴权方式、Header、Body 字段映射和成功判定规则。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
class AKSRT_SMS_Provider_Custom implements AKSRT_SMS_Provider {
public function get_key(): string {
return 'custom';
}
public function get_label(): string {
return __('自定义接口', 'aksrt');
}
public function validate_config(array $config): array {
$missing = [];
if (empty($config['sms_custom_url'])) $missing[] = 'sms_custom_url';
return $missing;
}
public function send_code(string $phone, string $code, array $config): array {
$url = (string) ($config['sms_custom_url'] ?? '');
$method = strtoupper((string) ($config['sms_custom_method'] ?? 'POST'));
$auth_type = (string) ($config['sms_custom_auth_type'] ?? 'header');
$auth_key = (string) ($config['sms_custom_auth_key'] ?? '');
$auth_value = (string) ($config['sms_custom_auth_value'] ?? '');
$headers_raw = (string) ($config['sms_custom_headers'] ?? '');
$body_tpl = (string) ($config['sms_custom_body'] ?? '');
$success_rule = (string) ($config['sms_custom_success_rule'] ?? '');
if ($url === '') {
return ['success' => false, 'message' => __('自定义接口 URL 未配置', 'aksrt'), 'transport' => 'custom_http'];
}
// 替换模板变量
$replacements = [
'{phone}' => $phone,
'{code}' => $code,
'{time}' => (string) time(),
];
$body = str_replace(array_keys($replacements), array_values($replacements), $body_tpl);
// 解析自定义 headers
$headers = ['Accept' => 'application/json'];
if ($headers_raw !== '') {
foreach (explode("\n", $headers_raw) as $line) {
$line = trim($line);
if ($line === '') continue;
$parts = explode(':', $line, 2);
if (count($parts) === 2) {
$headers[trim($parts[0])] = trim(str_replace(array_keys($replacements), array_values($replacements), $parts[1]));
}
}
}
// 鉴权
if ($auth_key !== '' && $auth_value !== '') {
$replaced_value = str_replace(array_keys($replacements), array_values($replacements), $auth_value);
if ($auth_type === 'header') {
$headers[$auth_key] = $replaced_value;
}
}
$args = [
'timeout' => 10,
'headers' => $headers,
];
if (in_array($method, ['POST', 'PUT', 'PATCH'], true)) {
$args['body'] = $body;
$result = wp_remote_request($url, array_merge($args, ['method' => $method]));
} else {
// GET — append body as query params
$url_with_params = add_query_arg(json_decode($body, true) ?: [], $url);
$result = wp_remote_get($url_with_params, $args);
}
if (is_wp_error($result)) {
$this->log('error', $phone, $result->get_error_message());
return ['success' => false, 'message' => $result->get_error_message(), 'transport' => 'custom_http'];
}
$status_code = wp_remote_retrieve_response_code($result);
$response_body = wp_remote_retrieve_body($result);
// 判定成功规则
if ($success_rule !== '') {
$matched = $this->check_success_rule($success_rule, $status_code, $response_body);
if ($matched) {
$this->log('sent', $phone, "status={$status_code}");
return ['success' => true, 'message' => __('验证码已发送', 'aksrt'), 'transport' => 'custom_http'];
}
$this->log('error', $phone, "status={$status_code}, rule not matched");
return ['success' => false, 'message' => sprintf(__('自定义接口返回异常 (HTTP %d)', 'aksrt'), $status_code), 'transport' => 'custom_http'];
}
// 默认:HTTP 2xx 视为成功
if ($status_code >= 200 && $status_code < 300) {
$this->log('sent', $phone, "status={$status_code}");
return ['success' => true, 'message' => __('验证码已发送', 'aksrt'), 'transport' => 'custom_http'];
}
$this->log('error', $phone, "status={$status_code}");
return ['success' => false, 'message' => sprintf(__('自定义接口返回 HTTP %d', 'aksrt'), $status_code), 'transport' => 'custom_http'];
}
public function get_status(array $config): array {
$missing = $this->validate_config($config);
return [
'provider' => $this->get_key(),
'configured' => empty($missing),
'available' => !empty($config['sms_enabled']) && empty($missing),
'verified' => !empty($config['sms_custom_verified']),
'transport' => 'custom_http',
'missing_fields' => $missing,
];
}
/**
* 校验成功规则
*
* 支持格式:
* - "status:200" — HTTP 状态码匹配
* - "json:code=0" — JSON 响应中字段匹配
* - "contains:success" — 响应体包含字符串
*/
private function check_success_rule(string $rule, int $status_code, string $body): bool {
$parts = explode(':', $rule, 2);
if (count($parts) < 2) return false;
$type = trim($parts[0]);
$value = trim($parts[1]);
switch ($type) {
case 'status':
return (string) $status_code === $value;
case 'json':
$json = json_decode($body, true);
if (!is_array($json)) return false;
$kv = explode('=', $value, 2);
if (count($kv) !== 2) return false;
$key = trim($kv[0]);
$expected = trim($kv[1]);
return (string) ($json[$key] ?? '') === $expected;
case 'contains':
return strpos($body, $value) !== false;
default:
return false;
}
}
private function log(string $level, string $phone, string $message): void {
if (!defined('WP_DEBUG') || !WP_DEBUG) return;
$masked = substr($phone, 0, 3) . '****' . substr($phone, -4);
error_log(sprintf('[AKSRT SMS] [custom] [%s] Phone: %s — %s', $level, $masked, $message));
}
}