المفاتيح السرية تبقى على خادمك
لا تضع sk_live_… أو sk_test_… في JavaScript المتصفح أو تطبيق الهاتف أو مستودع عام. المتصفح يتحدث مع خادمك فقط، وخادمك هو من يستدعي Shernova بالمفتاح السري.
التدفق الكامل
المستخدم لا يثبّت أي تطبيق. خادمك ينشئ الجلسة، وصفحتك تعرض رقم البوابة، والمكالمة نفسها تثبت الملكية. لا يُعتبر الرقم موثّقًا إلا عندما يكون статус الجلسة القادم من API (أو من Webhook موقّع) هو verified أو completed.
1. إنشاء جلسة
أرسل رقم الهاتف بصيغة E.164 مع client_request_id ثابت حتى تكون إعادة المحاولة آمنة (idempotent). ضع المفتاح السري في متغير بيئة على الخادم.
const API = 'https://api.shernova.com';
export async function createVerification(phoneNumber, requestId) {
const res = await fetch(API + '/v1/verifications', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.SHERNOVA_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
phone_number: phoneNumber,
client_request_id: requestId,
}),
});
const data = await res.json();
if (!res.ok) throw Object.assign(new Error(data.message), { data });
return data;
}import os, requests
def create_verification(phone_number: str, request_id: str) -> dict:
response = requests.post(
"https://api.shernova.com/v1/verifications",
headers={"Authorization": f"Bearer {os.environ['SHERNOVA_SECRET_KEY']}"},
json={"phone_number": phone_number, "client_request_id": request_id},
timeout=20,
)
response.raise_for_status()
return response.json()<?php
function create_verification(string $phone, string $requestId): array {
$ch = curl_init('https://api.shernova.com/v1/verifications');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('SHERNOVA_SECRET_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'phone_number' => $phone,
'client_request_id' => $requestId,
]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) throw new RuntimeException((string) $body);
return json_decode((string) $body, true);
}2. التعامل الصحيح مع queued وwaiting
يكون gateway_phone_number مساويًا لـ null أثناء queued لأن خط التحقق غير متاح بعد. لا تعرض رقمًا وهميًا ولا تطلب من المستخدم الاتصال قبل أن يصبح status === "waiting". مثال استجابة جلسة جاهزة:
{
"session_id": "8f14e45f-ea3c-4f3f-a1c2-4d7c8b6a9f01",
"status": "waiting",
"phone_number": "+201012345678",
"gateway_phone_number": "+201001234567",
"qr_code_data": "tel:+201001234567",
"environment": "live",
"expires_at": "2026-10-21T12:05:00.000Z"
}- استعلم كل 2–4 ثوانٍ أثناء
queuedأوwaiting. - عندما يصبح
waitingاعرض الرقم كرابطtel:واطلب رنة واحدة ثم إغلاق. - إذا انتهت الجلسة أنشئ جلسة جديدة؛ لا يمكن إحياء جلسة منتهية.
- اسمح للمستخدم بإلغاء جلسة queued/waiting عند تغيير رأيه لتحرير الخط.
qr_code_data هو tel: URI لنفس رقم البوابة. ارسمه كـ QR بجانب الرقم حتى يمسحه مستخدم الديسكتوب بكاميرا الهاتف الذي سيجري المكالمة — دون كتابة. يكون null أثناء queued وبعد خروج الجلسة من waiting.
3. الاستعلام والإلغاء
export async function getVerification(sessionId) {
const res = await fetch(API + '/v1/verifications/' + encodeURIComponent(sessionId), {
headers: { Authorization: 'Bearer ' + process.env.SHERNOVA_SECRET_KEY },
});
return res.json();
}
export async function cancelVerification(sessionId) {
const res = await fetch(API + '/v1/verifications/' + encodeURIComponent(sessionId), {
method: 'DELETE',
headers: { Authorization: 'Bearer ' + process.env.SHERNOVA_SECRET_KEY },
});
return res.json();
}curl "https://api.shernova.com/v1/verifications/SESSION_UUID" \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY"
curl -X DELETE "https://api.shernova.com/v1/verifications/SESSION_UUID" \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY"4. اعتبر verified نجاحًا
في التنفيذ الحالي، verified تعني أن رقم المتصل طابق الجلسة وأنه آمن للقبول. أما completed فتعني أن الجلسة سُلّمت أيضًا إلى Webhook. لا تنتظر completed عند الاستعلام: اقبل verified فورًا واجعل تحديث قاعدة بياناتك idempotent.
| الحالة | المعنى | ما يجب أن يفعله موقعك |
|---|---|---|
| queued | لا يوجد خط متاح؛ رقم البوابة null | استمر في الاستعلام واعرض رسالة انتظار |
| waiting | تم تخصيص خط؛ اعرض رقم البوابة | اطلب مكالمة فائتة واحدة |
| verified | المكالمة طابقت؛ تم إثبات الملكية | اقبل التحقق واحفظ النتيجة |
| completed | تم تسليم Webhook | تعامل معها كـ verified (idempotent) |
| expired | انتهت نافذة الاتصال | اعرض محاولة جديدة |
| failed / rejected | تعذّر التحقق من المكالمة | اعرض إعادة المحاولة |
| cancelled | أُلغي بواسطتك أو بواسطة المستخدم | اعرض محاولة جديدة |
5. Webhooks هي الخيار الأفضل في الإنتاج
تزيل Webhooks حاجة الاستعلام المستمر. تحقّق من توقيع X-Shernova-Signature (HMAC) على الجسم الخام قبل تحليل JSON، ثم عالج الحدث بشكل idempotent. راجع Webhooks لمعرفة الحقول وإعادة المحاولات.
import express from 'express';
import crypto from 'node:crypto';
const app = express();
app.post('/webhooks/shernova', express.raw({ type: 'application/json' }), (req, res) => {
const received = String(req.headers['x-shernova-signature'] || '').replace(/^sha256=/, '');
const expected = crypto
.createHmac('sha256', process.env.SHERNOVA_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(received);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).send('invalid signature');
}
const event = JSON.parse(req.body.toString());
if (event.status === 'verified' || event.status === 'completed') {
// وثّق الهاتف/المستخدم في قاعدة بياناتك.
}
res.sendStatus(200);
});import hmac, hashlib, os
from flask import Flask, request
app = Flask(__name__)
@app.post("/webhooks/shernova")
def shernova_webhook():
raw = request.get_data()
received = request.headers.get("X-Shernova-Signature", "").removeprefix("sha256=")
expected = hmac.new(
os.environ["SHERNOVA_WEBHOOK_SECRET"].encode(), raw, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, received):
return "invalid signature", 401
event = request.get_json(force=True)
if event.get("status") in ("verified", "completed"):
pass # وثّق الرقم في قاعدة بياناتك
return "", 2006. الأخطاء التي يجب أن يعالجها خادمك
| HTTP | رمز الخطأ | المعنى |
|---|---|---|
| 400 | invalid_phone | رقم غير صالح بصيغة E.164 |
| 401 | unauthorized | مفتاح سري مفقود أو غير صالح |
| 403 | phone_verification_required | صاحب حساب Shernova يجب أن يتحقق من هاتفه أولًا |
| 409 | phone_verification_in_progress | توجد جلسة معلّقة أخرى لنفس الرقم |
| 429 | verification_rate_limited | حدود الرقم/IP/الجهاز؛ احترم retry_after_seconds |
| 503 | maintenance_mode | الخدمة متوقفة مؤقتًا؛ اعرض الرسالة ثنائية اللغة |
إعادة المحاولة بنفس client_request_id تُرجع نفس الجلسة بدل إنشاء نسخة مكررة. استخدم UUID من سجل التسجيل/الطلب لديك.
7. قائمة الأمان والسعة
- أبقِ
sk_*على الخادم فقط؛ مرّر الإنشاء/الحالة/الإلغاء عبر خادمك. - حدّد معدل بدء الجلسات لكل مستخدم مصادق عليه، وليس لكل IP فقط.
- اقبل فقط
verifiedأوcompleted؛ لا تثق بأي علامة من المتصفح. - تحقّق من صيغة الرقم وطبّعها قبل الإرسال.
- استخدم HTTPS وتحقق من توقيع Webhook على الجسم الخام.
- لا تنشئ جلسة ثانية لرقم لديه جلسة نشطة؛ أعد استخدامها.
- نظّف الجلسات المتروكة بالإلغاء أو بتركها تنتهي.
8. أمثلة جاهزة للتشغيل
يحتوي المستودع على أمثلة Node.js وPHP وPython بدون مكتبات إضافية في examples/web-integration/. كل خادم يوفّر إنشاء الجلسة والحالة والإلغاء واستقبال Webhook، ويقرأ المفتاح السري من متغير بيئة. ابدأ بملف README في المجلد.
