170 lines
6.4 KiB
PHP
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));
|
|
}
|
|
}
|