feat(sms): align provider and verification flow
This commit is contained in:
1 parent
d0a3972ca7
commit
5091da6a60
20 files changed
+489
-994
No files matched your search
@@ -389,71 +389,42 @@ class SchemaUsers {
|
||||
'sms' => [
|
||||
'key' => 'sms',
|
||||
'title' => '短信接口',
|
||||
'version' => '1.0.0',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [
|
||||
['key' => 'provider', 'title' => '供应商配置', 'fields' => [
|
||||
['key' => 'sms_enabled', 'label' => '启用短信', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'sms_provider', 'label' => '短信服务商', 'type' => 'select', 'default' => 'aliyun', 'dependsOn' => ['key' => 'sms_enabled', 'value' => true], 'options' => [
|
||||
['label' => '阿里云短信', 'value' => 'aliyun'],
|
||||
['key' => 'provider', 'title' => '短信接口', 'fields' => [
|
||||
['key' => 'sms_sdk', 'label' => '设置短信接口', 'type' => 'select', 'default' => 'null', 'options' => [
|
||||
['label' => '不使用', 'value' => 'null'],
|
||||
['label' => '阿里云短信', 'value' => 'ali'],
|
||||
['label' => '腾讯云短信', 'value' => 'tencent'],
|
||||
['label' => '华为云短信', 'value' => 'huawei'],
|
||||
['label' => '自定义接口', 'value' => 'custom'],
|
||||
['label' => '短信宝', 'value' => 'smsbao'],
|
||||
]],
|
||||
['key' => 'sms_number_limit_ch', 'label' => '仅发国内', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'sms_enabled', 'value' => true], 'desc' => '开启后仅支持发送国内短信,关闭后可发送港澳台及国际短信'],
|
||||
['key' => 'sms_access_key', 'label' => 'AccessKey / SecretId / AppKey', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
['key' => 'sms_access_secret', 'label' => 'AccessSecret / SecretKey / AppSecret', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
['key' => 'sms_number_limit_ch', 'label' => '限制国内手机号', 'type' => 'switch', 'default' => true, 'desc' => '开启后只允许发送到中国大陆手机号'],
|
||||
['key' => 'sms_test_phone', 'label' => '测试手机号', 'type' => 'input', 'default' => '', 'ui_only' => true, 'help' => '保存接口配置后,发送固定验证码 888888 测试短信'],
|
||||
['key' => 'sms_test_send', 'label' => '发送测试短信', 'type' => 'button', 'buttonType' => 'primary', 'buttonText' => '发送测试短信', 'ui_only' => true],
|
||||
]],
|
||||
['key' => 'aliyun', 'title' => '阿里云配置', 'fields' => [
|
||||
['key' => 'sms_ali_api_type', 'label' => '接口类型', 'type' => 'radio', 'default' => 'sms', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'aliyun'], 'options' => [
|
||||
['key' => 'aliyun', 'title' => '阿里云', 'fields' => [
|
||||
['key' => 'sms_ali_api_type', 'label' => '产品', 'type' => 'radio', 'default' => 'sms', 'options' => [
|
||||
['label' => '短信服务', 'value' => 'sms'],
|
||||
['label' => '号码认证', 'value' => 'verify'],
|
||||
]],
|
||||
['key' => 'sms_region_id', 'label' => 'RegionId', 'type' => 'input', 'default' => 'cn-hangzhou', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'aliyun']],
|
||||
['label' => '号码认证:短信认证服务', 'value' => 'verify'],
|
||||
], 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'ali']],
|
||||
['key' => 'sms_ali_keyid', 'label' => 'AccessKey Id', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'ali']],
|
||||
['key' => 'sms_ali_keysecret', 'label' => 'AccessKey Secret', 'type' => 'input', 'inputType' => 'password', 'secret' => true, 'default' => '', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'ali']],
|
||||
['key' => 'sms_ali_sign_name', 'label' => '签名', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'ali']],
|
||||
['key' => 'sms_ali_template_code', 'label' => '模板 CODE', 'type' => 'input', 'default' => '', 'dependsOn' => ['all' => [['key' => 'sms_sdk', 'value' => 'ali'], ['key' => 'sms_ali_api_type', 'not' => true, 'value' => 'verify']]], 'help' => '短信服务需要填写。号码认证产品固定使用平台模板'],
|
||||
['key' => 'sms_region_id', 'label' => 'RegionId', 'type' => 'input', 'default' => 'cn-hangzhou', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'ali']],
|
||||
]],
|
||||
['key' => 'tencent', 'title' => '腾讯云配置', 'fields' => [
|
||||
['key' => 'sms_tencent_app_id', 'label' => 'AppID', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'tencent']],
|
||||
['key' => 'sms_tencent_app_key', 'label' => 'AppKey', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'tencent']],
|
||||
['key' => 'sms_tencent_secret_id', 'label' => 'SecretId', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'tencent']],
|
||||
['key' => 'sms_tencent_secret_key', 'label' => 'SecretKey', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'tencent']],
|
||||
['key' => 'sms_tencent_region', 'label' => '地域', 'type' => 'input', 'default' => 'ap-guangzhou', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'tencent']],
|
||||
['key' => 'tencent', 'title' => '腾讯云', 'fields' => [
|
||||
['key' => 'sms_tencent_app_id', 'label' => 'SDK AppID', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'tencent']],
|
||||
['key' => 'sms_tencent_app_key', 'label' => 'App Key', 'type' => 'input', 'inputType' => 'password', 'secret' => true, 'default' => '', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'tencent']],
|
||||
['key' => 'sms_tencent_sign_name', 'label' => '签名', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'tencent']],
|
||||
['key' => 'sms_tencent_template_id', 'label' => '模板 ID', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'tencent'], 'help' => '模板包含验证码和有效分钟数两个参数'],
|
||||
]],
|
||||
['key' => 'huawei', 'title' => '华为云配置', 'fields' => [
|
||||
['key' => 'sms_huawei_sender', 'label' => '通道号 (sender)', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'huawei']],
|
||||
['key' => 'sms_huawei_signature', 'label' => '短信签名', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'huawei']],
|
||||
['key' => 'sms_huawei_endpoint', 'label' => 'API Endpoint', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'huawei']],
|
||||
]],
|
||||
['key' => 'custom', 'title' => '自定义接口配置', 'fields' => [
|
||||
['key' => 'sms_custom_url', 'label' => '请求 URL', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'custom']],
|
||||
['key' => 'sms_custom_method', 'label' => '请求方法', 'type' => 'select', 'default' => 'POST', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'custom'], 'options' => [
|
||||
['label' => 'POST', 'value' => 'POST'],
|
||||
['label' => 'GET', 'value' => 'GET'],
|
||||
['label' => 'PUT', 'value' => 'PUT'],
|
||||
]],
|
||||
['key' => 'sms_custom_auth_type', 'label' => '鉴权方式', 'type' => 'select', 'default' => 'header', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'custom'], 'options' => [
|
||||
['label' => 'Header', 'value' => 'header'],
|
||||
]],
|
||||
['key' => 'sms_custom_auth_key', 'label' => '鉴权 Header 名', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'custom']],
|
||||
['key' => 'sms_custom_auth_value', 'label' => '鉴权 Header 值', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'custom']],
|
||||
['key' => 'sms_custom_headers', 'label' => '自定义 Headers', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'custom']],
|
||||
['key' => 'sms_custom_body', 'label' => '请求 Body', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'custom']],
|
||||
['key' => 'sms_custom_success_rule', 'label' => '成功判定规则', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_provider', 'value' => 'custom']],
|
||||
]],
|
||||
['key' => 'template', 'title' => '短信模板', 'fields' => [
|
||||
['key' => 'sms_sign_name', 'label' => '短信签名', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
['key' => 'sms_template_code', 'label' => '验证码模板ID', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
['key' => 'sms_code_length', 'label' => '验证码长度', 'type' => 'number', 'default' => 6, 'min' => 4, 'max' => 8, 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
['key' => 'sms_code_expire', 'label' => '验证码有效期(秒)', 'type' => 'number', 'default' => 300, 'min' => 60, 'max' => 600, 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
]],
|
||||
['key' => 'rate_limit', 'title' => '限流策略', 'fields' => [
|
||||
['key' => 'sms_rate_limit', 'label' => '发送间隔(秒)', 'type' => 'number', 'default' => 60, 'min' => 10, 'max' => 300, 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
['key' => 'sms_daily_limit', 'label' => '每日发送上限', 'type' => 'number', 'default' => 10, 'min' => 1, 'max' => 100, 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
]],
|
||||
['key' => 'triggers', 'title' => '通知场景', 'fields' => [
|
||||
['key' => 'sms_notify_bind', 'label' => '绑定手机验证', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
['key' => 'sms_notify_login', 'label' => '短信登录', 'type' => 'switch', 'default' => false, 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
['key' => 'sms_notify_register', 'label' => '注册验证', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
['key' => 'sms_notify_reset_password', 'label' => '找回密码', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'sms_enabled', 'value' => true]],
|
||||
['key' => 'smsbao', 'title' => '短信宝', 'fields' => [
|
||||
['key' => 'sms_smsbao_username', 'label' => '用户名', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'smsbao']],
|
||||
['key' => 'sms_smsbao_password', 'label' => '密码', 'type' => 'input', 'inputType' => 'password', 'secret' => true, 'default' => '', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'smsbao']],
|
||||
['key' => 'sms_smsbao_api_key', 'label' => 'ApiKey', 'type' => 'input', 'inputType' => 'password', 'secret' => true, 'default' => '', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'smsbao'], 'help' => '可选;填写后以 ApiKey 认证,可与密码任选其一'],
|
||||
['key' => 'sms_smsbao_template', 'label' => '模板内容', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'sms_sdk', 'value' => 'smsbao'], 'help' => '必须包含 {code},有效时间变量为 {time}'],
|
||||
]],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -232,19 +232,11 @@ class UserController extends ControllerBase {
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
|
||||
// SMS provider status
|
||||
register_rest_route(self::API_NAMESPACE, '/sms/status', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'get_sms_status'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
|
||||
// SMS test send (admin only)
|
||||
register_rest_route(self::API_NAMESPACE, '/sms/test', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'test_sms_send'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'test_sms_send'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
|
||||
// WeChat notification status
|
||||
register_rest_route(self::API_NAMESPACE, '/wechat/status', [
|
||||
|
||||
@@ -72,31 +72,33 @@ trait UserControllerAuthPhoneOperations {
|
||||
return 'aksrt_auth_code_' . md5($scene . '|' . strtolower(trim($target)));
|
||||
}
|
||||
|
||||
private function auth_store_email_code(string $scene, string $target, string $code): void {
|
||||
private function auth_store_code(string $scene, string $target, string $code, int $ttl = 10 * MINUTE_IN_SECONDS): void {
|
||||
set_transient($this->auth_code_key($scene, $target), [
|
||||
'hash' => wp_hash_password($code),
|
||||
'attempts' => 0,
|
||||
], 10 * MINUTE_IN_SECONDS);
|
||||
'expires' => time() + $ttl,
|
||||
], $ttl);
|
||||
}
|
||||
|
||||
private function auth_consume_code(string $scene, string $target, string $code, string $channel): bool {
|
||||
if ($channel === 'phone') {
|
||||
if (!class_exists('\AKSRT\Sms\SMS')) {
|
||||
return false;
|
||||
}
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->verify_code($target, $code);
|
||||
return !empty($result['success']);
|
||||
}
|
||||
|
||||
private function auth_consume_code(string $scene, string $target, string $code): bool {
|
||||
$key = $this->auth_code_key($scene, $target);
|
||||
$stored = get_transient($key);
|
||||
if (!is_array($stored) || empty($stored['hash'])) {
|
||||
return false;
|
||||
}
|
||||
$remaining = (int) ($stored['expires'] ?? 0) - time();
|
||||
if ($remaining <= 0) {
|
||||
delete_transient($key);
|
||||
return false;
|
||||
}
|
||||
$attempts = (int) ($stored['attempts'] ?? 0) + 1;
|
||||
if ($attempts > 6 || !wp_check_password($code, (string) $stored['hash'])) {
|
||||
if ($attempts > 6) {
|
||||
delete_transient($key);
|
||||
return false;
|
||||
}
|
||||
if (!wp_check_password($code, (string) $stored['hash'])) {
|
||||
$stored['attempts'] = $attempts;
|
||||
set_transient($key, $stored, 10 * MINUTE_IN_SECONDS);
|
||||
set_transient($key, $stored, $remaining);
|
||||
return false;
|
||||
}
|
||||
delete_transient($key);
|
||||
@@ -104,7 +106,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
}
|
||||
|
||||
private function auth_channel(string $target, string $requested = ''): string {
|
||||
if ($requested === 'phone' || preg_match('/^1[3-9]\d{9}$/', $target)) {
|
||||
if ($requested === 'phone' || (class_exists('\\AKSRT\\Sms\\SMS') && \AKSRT\Sms\SMS::is_phonenumber($target))) {
|
||||
return 'phone';
|
||||
}
|
||||
return 'email';
|
||||
@@ -162,7 +164,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
$enabled = $scene === 'login' ? \AKSRT\Settings\Settings::passwordless_login_enabled() : ($scene === 'register' ? \AKSRT\Settings\Settings::signup_verification_enabled() : true);
|
||||
if (!$enabled || ($type !== 'email_phone' && $type !== $channel)) return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
||||
}
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !preg_match('/^1[3-9]\d{9}$/', $target))) {
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !\AKSRT\Sms\SMS::is_phonenumber($target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
||||
}
|
||||
if ($this->auth_rate_limited('code:' . $scene, $target, 5, HOUR_IN_SECONDS)) {
|
||||
@@ -183,10 +185,12 @@ trait UserControllerAuthPhoneOperations {
|
||||
if (!class_exists('\AKSRT\Sms\SMS')) {
|
||||
return $this->error(__('短信服务不可用', 'aksrt'), 503);
|
||||
}
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->send_code($target);
|
||||
$code = (string) random_int(100000, 999999);
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->send_code($target, $code);
|
||||
if (empty($result['success'])) {
|
||||
return $this->error((string) ($result['message'] ?? __('验证码发送失败', 'aksrt')));
|
||||
}
|
||||
$this->auth_store_code($scene, $target, $code, 30 * MINUTE_IN_SECONDS);
|
||||
return $this->success(['message' => $generic, 'countdown' => (int) ($result['remaining_time'] ?? 60)]);
|
||||
}
|
||||
|
||||
@@ -215,7 +219,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
}
|
||||
return $this->error(__('验证码发送失败,请稍后再试', 'aksrt'), 500);
|
||||
}
|
||||
$this->auth_store_email_code($scene, $target, $code);
|
||||
$this->auth_store_code($scene, $target, $code);
|
||||
return $this->success(['message' => $generic, 'countdown' => 60]);
|
||||
|
||||
}
|
||||
@@ -239,7 +243,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
) {
|
||||
return $this->error(__('当前登录方式已关闭', 'aksrt'), 403);
|
||||
}
|
||||
if ($target === '' || (preg_match('/^1[3-9]\d{9}$/', $target) && empty($payload['code']) && class_exists('\AKSRT\Settings\Settings') && !\AKSRT\Settings\Settings::signin_phone_enabled())) {
|
||||
if ($target === '' || (\AKSRT\Sms\SMS::is_phonenumber($target) && empty($payload['code']) && class_exists('\AKSRT\Settings\Settings') && !\AKSRT\Settings\Settings::signin_phone_enabled())) {
|
||||
return $this->error(__('账号或密码错误', 'aksrt'), 401);
|
||||
}
|
||||
if ($this->auth_rate_limited('login', $target)) {
|
||||
@@ -261,7 +265,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
||||
}
|
||||
$user = $this->auth_find_user($target, $channel);
|
||||
if (!$user || !$this->auth_consume_code('login', $target, sanitize_text_field((string) $payload['code']), $channel)) {
|
||||
if (!$user || !$this->auth_consume_code('login', $target, sanitize_text_field((string) $payload['code']))) {
|
||||
return $this->error(__('账号或验证码错误', 'aksrt'), 401);
|
||||
}
|
||||
if ($oauth_data) {
|
||||
@@ -348,7 +352,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
if (!$this->auth_channel_allowed($configured, $channel)) {
|
||||
return $this->error(__('当前注册方式不可用', 'aksrt'), 403);
|
||||
}
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !preg_match('/^1[3-9]\d{9}$/', $target))) {
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !\AKSRT\Sms\SMS::is_phonenumber($target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
||||
}
|
||||
$captcha_error = $this->auth_require_captcha('register', $payload);
|
||||
@@ -357,7 +361,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
}
|
||||
$verification_completed = false;
|
||||
if ($signup_verification) {
|
||||
if (!$this->auth_consume_code('register', $target, sanitize_text_field((string) ($payload['code'] ?? '')), $channel)) {
|
||||
if (!$this->auth_consume_code('register', $target, sanitize_text_field((string) ($payload['code'] ?? '')))) {
|
||||
return $this->error(__('验证码错误或已过期', 'aksrt'));
|
||||
}
|
||||
$verification_completed = true;
|
||||
@@ -426,7 +430,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
||||
}
|
||||
}
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !preg_match('/^1[3-9]\d{9}$/', $target))) {
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !\AKSRT\Sms\SMS::is_phonenumber($target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
||||
}
|
||||
if ($this->auth_rate_limited('reset-password', $target, 8, HOUR_IN_SECONDS)) {
|
||||
@@ -446,7 +450,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
if ($password_check !== true) {
|
||||
return $this->error((string) $password_check);
|
||||
}
|
||||
if (!$user || !$this->auth_consume_code('resetpassword', $target, sanitize_text_field((string) ($payload['code'] ?? '')), $channel)) {
|
||||
if (!$user || !$this->auth_consume_code('resetpassword', $target, sanitize_text_field((string) ($payload['code'] ?? '')))) {
|
||||
return $this->error(__('账号或验证码错误', 'aksrt'));
|
||||
}
|
||||
wp_set_password($password, (int) $user->ID);
|
||||
@@ -459,16 +463,19 @@ trait UserControllerAuthPhoneOperations {
|
||||
return $this->error(__('安全验证失败', 'aksrt'), 403);
|
||||
}
|
||||
$phone = sanitize_text_field((string) ($payload['phone'] ?? ''));
|
||||
if (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
|
||||
if (!\AKSRT\Sms\SMS::is_phonenumber($phone)) {
|
||||
return $this->error(__('手机号格式不正确', 'aksrt'));
|
||||
}
|
||||
$captcha_error = $this->auth_require_captcha('bind_phone', $payload);
|
||||
if ($captcha_error) return $captcha_error;
|
||||
if (!class_exists('\AKSRT\Sms\SMS')) return $this->error(__('短信服务不可用', 'aksrt'), 503);
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->send_code($phone);
|
||||
return !empty($result['success'])
|
||||
? $this->success(['message' => __('验证码已发送', 'aksrt'), 'countdown' => (int) ($result['remaining_time'] ?? 60)])
|
||||
: $this->error((string) ($result['message'] ?? __('发送失败', 'aksrt')));
|
||||
$code = (string) random_int(100000, 999999);
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->send_code($phone, $code);
|
||||
if (empty($result['success'])) {
|
||||
return $this->error((string) ($result['message'] ?? __('发送失败', 'aksrt')));
|
||||
}
|
||||
$this->auth_store_code('bind_phone', $phone, $code, 30 * MINUTE_IN_SECONDS);
|
||||
return $this->success(['message' => __('验证码短信已发送', 'aksrt'), 'countdown' => (int) ($result['remaining_time'] ?? 60)]);
|
||||
}
|
||||
|
||||
public function user_bind_phone(\WP_REST_Request $request): \WP_REST_Response {
|
||||
@@ -476,8 +483,25 @@ trait UserControllerAuthPhoneOperations {
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) return $this->error(__('安全验证失败', 'aksrt'), 403);
|
||||
$captcha_error = $this->auth_require_captcha('bind_phone', $payload);
|
||||
if ($captcha_error) return $captcha_error;
|
||||
$phone = sanitize_text_field((string) ($payload['phone'] ?? ''));
|
||||
$code = sanitize_text_field((string) ($payload['code'] ?? ''));
|
||||
if (!$this->auth_consume_code('bind_phone', $phone, $code)) {
|
||||
return $this->error(__('验证码错误或已过期', 'aksrt'));
|
||||
}
|
||||
if (!class_exists('\AKSRT\Auth\Service')) return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
||||
$result = \AKSRT\Auth\Service::bind_phone($payload);
|
||||
$result = \AKSRT\Auth\Service::bind_phone($payload, true);
|
||||
return !empty($result['success']) ? $this->success(['message' => $result['message'] ?? __('绑定成功', 'aksrt')]) : $this->error((string) ($result['error'] ?? __('绑定失败', 'aksrt')));
|
||||
}
|
||||
|
||||
public function test_sms_send(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$params = $this->get_json_params($request);
|
||||
$phone = sanitize_text_field((string) ($params['phone'] ?? ''));
|
||||
if (!class_exists('\\AKSRT\\Sms\\SMS')) {
|
||||
return $this->error(__('短信服务不可用', 'aksrt'), 503);
|
||||
}
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->test_send($phone);
|
||||
return !empty($result['success'])
|
||||
? $this->success(['message' => __('测试短信已发送', 'aksrt')])
|
||||
: $this->error((string) ($result['message'] ?? __('测试短信发送失败', 'aksrt')));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user