الدفع بالترخيص
كيف يبيع الموقع ترخيصيه: صفحة الدفع، وصفحتا Stripe وPayPal، وصفحة الدفع التجريبية، والخطاف (webhook)، والإيصال ومفتاح الترخيص، وكيف تغيّر العروض أو تبيع عبر متجر خارجي.
لحزمة الحزمة الكاملة
من الترخيص إلى الإيصال
- في صفحة الأسعار يضغط المشتري احصل على ترخيص المشروع الواحد أو احصل على ترخيص الاستوديو. يعرض ملخص الشراء الترخيص وما يغطيه والإجمالي، ويفتح المتابعة إلى الدفع الصفحة
/checkout?license=singleأو?license=studio. - تطلب صفحة الدفع بريدًا إلكترونيًا واسمًا كاملًا ووسيلة دفع، بطاقة أو PayPal، والموافقة على شروط الترخيص. يرسلها المتابعة إلى الدفع إلى واجهة API، التي تكتب الطلب (برقم مرجعي مثل
NS-7KQ2-M9XD) بسعرها هي: فالمتصفح لا يرسل سعرًا أبدًا. - يدفع المشتري على صفحة Stripe أو PayPal نفسها، أو على صفحة الدفع التجريبية في الموقع عندما لا تملك الوسيلة مفاتيح. لا تُكتب بيانات البطاقة أبدًا على موقعك.
- الصفحة التي يعود إليها المشتري،
/checkout/success، تطلب من واجهة API التحقق من الدفع لدى المزوّد، ثم تكرر ذلك كل ثلاث ثوان، حتى خمس مرات، ما دام الدفع قيد المعالجة. وبعد أن تتحقق منه واجهة API يصبح الطلب مدفوعًا، ويعرض الإيصال مفتاح الترخيص (مثلNSK-4F7Q-2M9X-HB3T-8KRD) مع انسخ مفتاح الترخيص، ويُرسل الإيصال بالبريد إلى المشتري.
لا يقرر الموقع أبدًا أن طلبًا مدفوع. لا يصبح الطلب مدفوعًا إلا بخطاف Stripe الموقَّع، أو بجلسة Stripe التي تقرؤها واجهة API عند عودة المشتري، أو بتحصيل PayPal الذي تجريه واجهة API عند تلك العودة. وواحد منها فقط يُصدر المفتاح والبريد، مهما وصل منها. أما المشتري الذي يغادر صفحة الدفع فيصل إلى /checkout/cancelled، التي تقول إنه لم يؤخذ أي مال وتقدّم أعد المحاولة.
وسائل الدفع
| الطريقة | تُضبط في ملف .env الخاص بواجهة API | كيف يعمل |
|---|---|---|
| بطاقة، عبر Stripe | STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET | Stripe Checkout بوضع الدفع. يسافر السعر مع كل جلسة، فلا يلزم إنشاء شيء في Stripe أولًا. يعيد Stripe المشتري إلى /checkout/success أو /checkout/cancelled. |
| PayPal | PAYPAL_CLIENT_ID وPAYPAL_CLIENT_SECRET (تطبيق PayPal REST) وPAYPAL_ENV (sandbox افتراضيًا، أو live) | صفحة PayPal نفسها. تحصّل واجهة API الدفعة المعتمدة عندما يعود المشتري إلى /checkout/success. |
| صفحة الدفع التجريبية | لا شيء: تقبل أي وسيلة لا تملك مفاتيح | صفحة الموقع /checkout/test، مع ادفع وألغِ الدفع. لا تحصّل من أحد شيئًا. |
- تذهب كل وسيلة إلى مزوّدها بمجرد ضبط مفاتيحه، وإلى صفحة الدفع التجريبية إلى ذلك الحين، فيستطيع التثبيت الجديد إتمام عملية شراء فورًا.
- ما دامت لا وسيلة تحرّك أموالًا حقيقية (لا مفاتيح، أو مفتاح Stripe من نوع
sk_test_، أو PayPal sandbox)، تقول صفحة الدفع إنها في وضع الاختبار. ومع مفتاح Stripe للاختبار تذكر بطاقة الاختبار:4242 4242 4242 4242وأي تاريخ مستقبلي وأي CVC. - استخدم مفاتيح الاختبار أولًا: فهي لا تحرّك أي مال. عند بدء واجهة API تسجّل لكل مزوّد ما إذا كان يقبل مدفوعات اختبار أو مدفوعات حقيقية.
- مفاتيح Stripe هي نفسها التي تستخدمها فوترة الاشتراكات في لوحة التحكم.
خطاف Stripe (webhook)
تتشارك طلبات التراخيص واشتراكات مساحات العمل خطاف Stripe واحدًا. قبل الإطلاق، سجّله في Stripe على العنوان العام لواجهة API وضع سره التوقيعي في STRIPE_WEBHOOK_SECRET:
https://<your-api>/api/billing/webhooks/stripeللتراخيص، تتصرف واجهة API عند checkout.session.completed (الطلب مدفوع) وcheckout.session.expired (الطلب فاشل). وتميّز جلسة الترخيص عن جلسة الاشتراك من البيانات الوصفية للجلسة kind: license. لا يحتاج PayPal إلى خطاف: يتم التحصيل عندما يعود المشتري.
عند ضبط STRIPE_SECRET_KEY وترك STRIPE_WEBHOOK_SECRET فارغًا، يذكر سجل واجهة API ذلك عند البدء ويُرفض كل تسليم بالرمز 400 مع ذكر المتغير. عندئذ لا يُدفع طلب البطاقة إلا عندما يعود المشتري إلى صفحة الإيصال.
على جهازك لا يستطيع Stripe الوصول إلى واجهة API، ومع ذلك يكتمل الدفع التجريبي: تسأل صفحة العودة واجهة API، فتقرأ الجلسة من Stripe. اضبط WEBSITE_URL على عنوان الموقع، لأن كل رابط عودة تعطيه واجهة API لـ Stripe وPayPal يُبنى منه، بلغة المشتري.
حالات الطلب
| الحالة | المعنى | ما تقوله صفحة الإيصال |
|---|---|---|
pending | كُتب، لم يُدفع بعد. | «نتحقق من دفعتك.»، ثم «دفعتك ما زالت قيد المعالجة.» مع تحقق مرة أخرى. |
paid | تم التحقق منه لدى المزوّد؛ وصدر مفتاح الترخيص. | «شكرًا لك. أصبح ترخيص الاستوديو لك.» (مع الترخيص المشترى)، وتفاصيل الطلب والمفتاح. |
failed | انتهت جلسة Stripe، أو رفض PayPal التحصيل، أو تعذّر فتح صفحة المزوّد. | «لم تتم الدفعة.» مع أعد المحاولة. |
cancelled | غادر المشتري صفحة الدفع. | «تم إلغاء هذا الطلب.» |
لا يعرض الإيصال مفتاح الترخيص إلا لطلب مدفوع، ويميّز الطلب التجريبي بعبارة «دفعة تجريبية: لم تنتقل أي أموال.» وإذا أبلغ خطاف Stripe لاحقًا عن دفعة لطلب ملغى أو فاشل (مشترٍ عاد ودفع في تبويب آخر)، يصبح ذلك الطلب مدفوعًا.
الحدود والأمان
- لا يُقرأ الطلب إلا بمعرّفه، وهو 32 حرفًا عشوائيًا لا يحمله إلا روابط عودة المشتري وبريد الإيصال. ولا توجد قائمة طلبات يتصفحها الزائر.
- يحمل كل إرسال رمزه الخاص، فالضغط على المتابعة إلى الدفع مرتين يفتح الطلب نفسه وصفحة الدفع نفسها، ولا يحدث تحصيل ثانٍ أبدًا.
- يستطيع كل عنوان بدء عشر عمليات دفع في الساعة؛ وتقول التالية «محاولات كثيرة من هذا العنوان. أعد المحاولة بعد ساعة.» خلف خادم وكيل عكسي، اضبط
TRUST_PROXY=1في ملف.envالخاص بواجهة API، وإلا يتشارك كل المشترين حصة الوكيل. - لا تُضاف أي ضريبة. جهّز الضريبة لبلدك قبل قبول مدفوعات حقيقية؛ أما رسوم Stripe وPayPal فمنفصلة.
بريد الإيصال
يذكر بريد الطلب المدفوع الرقم المرجعي للطلب والترخيص والمبلغ ومفتاح الترخيص ورابطًا يعود إلى الإيصال. يُرسل عبر SMTP_HOST وبقية إعدادات SMTP_*؛ وبدونها يُكتب في سجل واجهة API. صياغة البريد بالإنجليزية في اللغتين، في back-end/src/modules/licenses/services/license-checkout.service.ts؛ أما رابط الإيصال فيُفتح باللغة التي أتم بها المشتري الدفع.
غيّر التراخيص وأسعارها
يُحفظ السعر في مكانين، ويجب أن يتطابقا:
| الملف | ما الذي يقرره |
|---|---|
back-end/src/modules/licenses/constants/license-offers.ts | ما تحصّله واجهة API بالسنتات (19900 و39900، بالدولار الأمريكي)، والاسم على صفحة المزوّد وفي الإيصال. |
website/src/config/licenses.ts | ما يطبعه الموقع، وعدد المنتجات النهائية التي يغطيها كل ترخيص، والبطاقة الموصى بها، والأسطر التي تسردها كل بطاقة (كلماتها تحت PricingPage.licenses في website/messages/pricing/en.json وar.json). |
السعر الذي يتغير في الموقع وحده يغيّر ما تعرضه الصفحة فقط: ويظل المشتري يدفع مبلغ واجهة API. معرّفا الترخيصين، single وstudio، مكتوبان في الملفين كليهما، طلب الدفع في واجهة API ورسائل الموقع، فإضافة ترخيص ثالث تغيير في كل منهما.
البيع عبر متجر خارجي
اضبط NEXT_PUBLIC_PURCHASE_URL_SINGLE وNEXT_PUBLIC_PURCHASE_URL_STUDIO على صفحة دفع المتجر لكل ترخيص وابنِ الموقع من جديد (مع Docker Compose: PURCHASE_URL_SINGLE وPURCHASE_URL_STUDIO في ملف .env بجوار docker-compose.yml). عندئذ يفتح المتابعة إلى الدفع ذلك العنوان بدل /checkout. والترخيص الذي يُترك فارغًا يحتفظ بصفحة الدفع الخاصة بالموقع.
من دون واجهة API
عندما يعمل الموقع على بياناته التجريبية المدمجة، يُفحص الدفع ويُجاب عنه في المتصفح: يفتح الطلب صفحة الدفع التجريبية الخاصة بالموقع، ويُصدر ادفع مفتاح ترخيص في الإيصال، وتُحفظ الطلبات في ذلك المتصفح وحده. لا يُحصَّل شيء ولا يُرسل شيء.
في وضع العرض التجريبي
مُضمَّن مع مشترياتك. سجّل الدخول لقراءته، أو افتحه في ملف التنزيل.
كيف تعمل صفحة الدفع في عرض تجريبي عام.
المسارات خلفه
| المسار | وظيفته |
|---|---|
GET /api/licenses/checkout/options | الوسائل المعروضة، والمزوّد الذي يتولى كلًا منها (stripe أو paypal أو simulator) وtestMode. |
POST /api/licenses/checkout | license (single أو studio) وemail وname وmethod (card أو paypal) وlocale (en أو ar) وclientToken. يعيد معرّف الطلب وصفحة الدفع؛ ويعيد 429 عند تجاوز الحد، و503 payment_unavailable عندما يتعذّر فتح صفحة المزوّد. |
GET /api/licenses/orders/:id | الطلب، مع مفتاح ترخيصه بعد الدفع. |
POST /api/licenses/orders/:id/confirm | يسأل مزوّد الدفع هل تمت الدفعة. |
POST /api/licenses/orders/:id/cancel | غادر المشتري صفحة الدفع. |
POST /api/licenses/orders/:id/simulate | pay أو cancel في صفحة الدفع التجريبية، لطلب تولّته صفحة الدفع التجريبية. |
كلها عامة: معرّف الطلب هو ما يثبت أن القارئ هو مشتريه. والمعرّف الذي ليس 32 خانة سداسية عشرية، أو الذي لا يطابق أي طلب، يعيد 404 order_not_found.