تدفق Connect
تحصل الموصّلات على مفتاح دون أن ينسخ صاحب المتجر واحدًا أبدًا. طريقان يؤديان إلى النتيجة نفسها: جولة في المتصفح مع PKCE، أو رمز اقتران يُكتب في الإضافة. يربط كلاهما المفتاح برابط المتجر والتطبيق، ويظهران تحت التكاملات › الاتصالات في Wamia Shop.
نقطة الوصول
| HTTP | URL |
|---|---|
| GET | https://api.wamia.tn/connect/authorize |
| POST | https://api.wamia.tn/connect/token |
| POST | https://api.wamia.tn/connect/pair |
| POST | https://api.wamia.tn/connect/revoke |
التفويض مع PKCE
وجّه متصفح صاحب المتجر إلى GET /connect/authorize مع client_id (رمز تطبيقك)، وredirect_uri بـ https يطابق الأنماط المسجلة، وقائمة scope، وstate من 8 أحرف على الأقل، وcode_challenge (S256) ورابط shop. يسجل صاحب المتجر دخوله، ويراجع الصلاحيات في صفحة الموافقة، ويعود مع code وstate.
بادل الرمز على POST /connect/token مع grant_type: authorization_code وcode_verifier وclient_id وredirect_uri وplugin_version وshop_url. الإجابة هي المفتاح وميزانيته؛ الرمز يُستخدم مرة واحدة وينتهي بعد 60 ثانية، فبادله فور عودة المتصفح.
رمز الاقتران
حين لا تستطيع الإضافة فتح جولة متصفح (تثبيت من سطر الأوامر، لوحات إدارة بلا متصفح)، يولّد صاحب المتجر رمز اقتران في اللوحة — التكاملات › الاتصالات › اقتران إضافة — ويكتبه في الإضافة. ترسله الإضافة إلى POST /connect/pair مع client_id وshop_url وplugin_version وتتلقى إجابة نقطة الرمز نفسها. تعيش الرموز 10 دقائق وتُستهلك عند أول استخدام.
PKCE بلغة TypeScript
import { createHash, randomBytes } from 'node:crypto';
const verifier = randomBytes(48).toString('base64url'); // 43–128 chars, [A-Za-z0-9-._~]
const challenge = createHash('sha256').update(verifier).digest('base64url');
const state = randomBytes(16).toString('hex');
const authorize = new URL('https://api.wamia.tn/connect/authorize');
authorize.searchParams.set('client_id', 'woocommerce');
authorize.searchParams.set('redirect_uri', 'https://shop.example.tn/wp-admin/admin.php?page=wamia');
authorize.searchParams.set('response_type', 'code');
authorize.searchParams.set('code_challenge', challenge);
authorize.searchParams.set('code_challenge_method', 'S256');
authorize.searchParams.set('scope', 'offers.manage catalog.media inventory.adjust orders.read orders.fulfil webhooks.manage');
authorize.searchParams.set('state', state);
authorize.searchParams.set('shop', 'https://shop.example.tn');
// Send the shop owner's browser to authorize.toString(); on return, verify state and exchange:
const token = await fetch('https://api.wamia.tn/connect/token', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
grant_type: 'authorization_code', code, code_verifier: verifier, client_id: 'woocommerce',
redirect_uri, plugin_version: '1.2.0', shop_url: 'https://shop.example.tn',
}),
}).then((r) => r.json());
// → { api_key: 'wamia_…', key_prefix, key_id, connection_id, organization_id, scopes[], expires_at,
// rate_limit: { reads_per_minute, writes_per_minute, bulk_per_minute } }
// Store api_key encrypted at rest: it is shown once. Rotate before expires_at (365 days).الاقتران عبر curl
curl -sS https://api.wamia.tn/connect/pair \
-H 'content-type: application/json' \
--data '{ "code": "K7P2M9QX", "client_id": "prestashop", "shop_url": "https://shop.example.tn", "plugin_version": "1.0.0" }'
# → { "api_key": "wamia_…", "key_prefix": "…", "connection_id": "…", "organization_id": "…", "scopes": [ … ], "expires_at": "…", "rate_limit": { … } }POST /connect/token
| الاسم | المعنى |
|---|---|
| api_key | السر، يُعرض مرة واحدة. بادئة wamia_؛ خزّنه مشفّرًا. |
| key_prefix / key_id | معرّفات آمنة للتسجيل والعرض في شاشة إعداداتك. |
| connection_id | الاتصال الذي يراه المتجر في اللوحة. |
| organization_id | المتجر؛ كل مورد تلمسه يخصه. |
| scopes | ما مُنح — قد يكون أقل مما طلبت. |
| expires_at | 365 يومًا من الإصدار؛ دوّر قبلها. |
| rate_limit | reads_per_minute وwrites_per_minute وbulk_per_minute لهذا النوع من المفاتيح. |
دورة حياة المفتاح
الربط
ترتبط مفاتيح الموصّلات بـ shop_url والتطبيق. يُرفض النداء من مضيف آخر بـ UNAUTHENTICATED؛ تحتاج نسخة الاختبار من المتجر إلى اتصالها الخاص.
التدوير
استدعِ apiRotateKey قبل expires_at بثلاثين يومًا (تذكّر اللوحة المتجر أيضًا). تحمل الإجابة المفتاح الجديد؛ يبقى القديم صالحًا 24 ساعة كي لا يفقد نشر نصف منجز الطلبات.
الحد الأدنى للإصدار
يستطيع فريق واميا رفع الحد الأدنى لإصدار موصّل. تتلقى الإضافات الأقدم CONNECTOR_UPGRADE_REQUIRED (HTTP 426) مع extensions.minSupportedVersion في كل نداء حتى تُحدَّث؛ أظهر لافتة التحديث وأوقف المجدولات بدل إعادة المحاولة.
الإلغاء والفصل
يستطيع المتجر إلغاء اتصال من اللوحة؛ وتستطيع الإضافة استدعاء POST /connect/revoke بالمفتاح. بعد أيهما يجيب كل نداء بـ CONNECTION_REVOKED. على الإضافة حينها مسح المفتاح وإيقاف المجدولات والاحتفاظ بالبيانات المحلية — لا تحذف واميا قوائم متجر عند الفصل أبدًا.