KedaSMS
從帳戶準備到用量策略設定,按步驟完成簡訊整合。
選擇下方語言按鈕即可切換到對應的在地化文件。
控制台、前端代理與後端服務分別部署,請在網路策略中放行以下位址。
https://www.kedasms.net
控制台與官網入口(可達SMS 前端)https://www.kedasms.net/api
前端代理入口(/api/*),負責接收加密請求並轉發至後端https://www.kedasms.net/docs
線上接入指引與 API 文件註冊並驗證信箱,控制台才會生成 API Key。
透過前端代理提交 AES-GCM 加密請求,系統會預凍結餘額並在通道接收後自動扣費。
POST https://www.kedasms.net/api/sms/send Content-Type: application/json { "apiKey": "your-api-key", "timestamp": 1712123456, "iv": "base64-iv==", "cipherText": "base64-ciphertext==" }
{ "code": "0", "message": "成功", "data": { "taskId": "20240408-0001", "message": "短信任务创建成功", "debitedAmount": 0.056, "currency": "USD" } }
回應會返回任務 ID 以及本次扣費金額,便於核對。
兩個介面均返回 JSON,可直接嵌入落地頁或內部看板。
透過每日簡訊和花費上限確保預算合規。
PUT https://backup.kedasms.net/api/external/accounts/limits Content-Type: application/json { "apiKey": "your-api-key", "dailySmsCap": 5000, "dailySpendCap": 200, "alertThreshold": 0.8, "blockOnLimit": true }
該限制即時生效,API 與控制台任務都會遵循。
查询短信发送状态,支持实时状态更新。
GET https://backup.kedasms.net/api/sms/status?msgid=msg_abc123def456 { "success": true, "data": { "msgid": "msg_abc123def456", "status": "DELIVERED", "recipient": "+8613800138000", "sender": "KedaSMS", "message": "您的验证码为 123456", "sent_time": "2024-01-01T12:00:00Z", "delivered_time": "2024-01-01T12:00:05Z" }, "code": 200 }
接收短信状态变更的实时通知,支持状态回调和链接点击回调。
POST https://backup.kedasms.net/api/sms/callback/KEDA Content-Type: application/json { "msgid": "msg_abc123def456", "status": "DELIVERED", "recipient": "+8613800138000", "sender": "KedaSMS", "sent_time": "2024-01-01T12:00:00Z", "delivered_time": "2024-01-01T12:00:05Z", "cost": "0.05" }
使用相同的加密結構呼叫 /api/shortlinks,可產生可追蹤的專屬短網址。
POST https://www.kedasms.net/api/shortlinks Content-Type: application/json { "apiKey": "your-api-key", "timestamp": 1712123456, "iv": "base64-iv==", "cipherText": "base64-ciphertext==" }
以下範例會先加密業務載荷並透過 HTTPS POST 到前端代理,同時將 timestamp 作為 AAD。
import base64 import json import os import time import requests from Crypto.Cipher import AES api_key = "your-api-key" encryption_key = base64.b64decode("YOUR_ENCRYPTION_KEY_BASE64") payload = { "countryCode": "CN", "phoneNumbers": ["13800138000"], "content": "您的验证码为 123456", "senderId": "KedaSMS", "trackClick": True, "callbackUrl": "https://example.com/webhook/sms" } iv = os.urandom(12) timestamp = int(time.time()) cipher = AES.new(encryption_key, AES.MODE_GCM, nonce=iv) cipher.update(timestamp.to_bytes(8, "big")) ciphertext, tag = cipher.encrypt_and_digest(json.dumps(payload).encode("utf-8")) body = { "apiKey": api_key, "timestamp": timestamp, "iv": base64.b64encode(iv).decode(), "cipherText": base64.b64encode(ciphertext + tag).decode() } response = requests.post( "https://www.kedasms.net/api/sms/send", json=body, timeout=10 ) response.raise_for_status() print(response.json())
SecretKeySpec keySpec = new SecretKeySpec(Base64.getDecoder().decode("YOUR_ENCRYPTION_KEY_BASE64"), "AES"); byte[] iv = SecureRandom.getInstanceStrong().generateSeed(12); long timestamp = Instant.now().getEpochSecond(); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); GCMParameterSpec spec = new GCMParameterSpec(128, iv); cipher.init(Cipher.ENCRYPT_MODE, keySpec, spec); cipher.update(ByteBuffer.allocate(Long.BYTES).putLong(timestamp).array()); ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(payload); byte[] encrypted = cipher.doFinal(json.getBytes(StandardCharsets.UTF_8)); Map<String, Object> body = Map.of( "apiKey", "your-api-key", "timestamp", timestamp, "iv", Base64.getEncoder().encodeToString(iv), "cipherText", Base64.getEncoder().encodeToString(encrypted) ); String requestBody = objectMapper.writeValueAsString(body); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://www.kedasms.net/api/sms/send")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(requestBody)) .build(); HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body());
package main import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/base64" "encoding/binary" "encoding/json" "log" "net/http" "time" ) func main() { apiKey := "your-api-key" payload := map[string]any{ "countryCode": "CN", "phoneNumbers": []string{"13800138000"}, "content": "您的验证码为 123456", "senderId": "KedaSMS", "trackClick": true, "callbackUrl": "https://example.com/webhook/sms", } key, err := base64.StdEncoding.DecodeString("YOUR_ENCRYPTION_KEY_BASE64") if err != nil { log.Fatal(err) } iv := make([]byte, 12) if _, err := rand.Read(iv); err != nil { log.Fatal(err) } block, err := aes.NewCipher(key) if err != nil { log.Fatal(err) } gcm, err := cipher.NewGCM(block) if err != nil { log.Fatal(err) } timestamp := time.Now().Unix() aad := make([]byte, 8) binary.BigEndian.PutUint64(aad, uint64(timestamp)) plaintext, err := json.Marshal(payload) if err != nil { log.Fatal(err) } ct := gcm.Seal(nil, iv, plaintext, aad) body := map[string]any{ "apiKey": apiKey, "timestamp": timestamp, "iv": base64.StdEncoding.EncodeToString(iv), "cipherText": base64.StdEncoding.EncodeToString(ct), } bodyBytes, err := json.Marshal(body) if err != nil { log.Fatal(err) } req, err := http.NewRequest(http.MethodPost, "https://www.kedasms.net/api/sms/send", bytes.NewReader(bodyBytes)) if err != nil { log.Fatal(err) } req.Header.Set("Content-Type", "application/json") client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() if resp.StatusCode >= 400 { log.Fatalf("unexpected status: %s", resp.Status) } }
<?php $apiKey = 'your-api-key'; $encryptionKey = base64_decode('YOUR_ENCRYPTION_KEY_BASE64'); $payload = [ 'countryCode' => 'CN', 'phoneNumbers' => ['13800138000'], 'content' => '您的验证码为 123456', 'senderId' => 'KedaSMS', 'trackClick' => true, 'callbackUrl' => 'https://example.com/webhook/sms', ]; $iv = random_bytes(12); $timestamp = time(); $aad = pack('N2', $timestamp >> 32, $timestamp & 0xffffffff); $ciphertext = openssl_encrypt( json_encode($payload, JSON_UNESCAPED_UNICODE), 'aes-256-gcm', $encryptionKey, OPENSSL_RAW_DATA, $iv, $tag, $aad ); if ($ciphertext === false) { throw new RuntimeException('encryption failed'); } $body = [ 'apiKey' => $apiKey, 'timestamp' => $timestamp, 'iv' => base64_encode($iv), 'cipherText' => base64_encode($ciphertext . $tag), ]; $ch = curl_init('https://www.kedasms.net/api/sms/send'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode($body), CURLOPT_TIMEOUT => 10, ]); $response = curl_exec($ch); if ($response === false) { throw new RuntimeException(curl_error($ch)); } $httpStatus = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); curl_close($ch); if ($httpStatus >= 400) { throw new RuntimeException('unexpected status: ' . $httpStatus); } echo $response;
請為每次請求產生新的 12 位元組 IV,並確保 timestamp 使用 UTC Unix 秒且與伺服器時間誤差不超過 5 分鐘。
按照以下最佳實踐保持通道穩定。