بناء محرك تسعير متعدد العملات لموزّع سلع رقمية — 2026
إذا كنت تدير واجهة متجر سلع رقمية B2C وتورّد من FoxReload (أو أي مورّد جملة مُقوَّم بالدولار)، فإن محرك التسعير هو أهم قطعة كود في عملك. إنه يحدد مباشرةً الهامش والقدرة التنافسية ومدى تكرار خسارتك من تقلبات FX. يستعرض هذا الدليل البنية، والحسابات، وتطبيقاً مرجعياً بلغة TypeScript يتعامل مع الحالات الحدّية.
مسار التسعير
المسار سلسلة من التحويلات النقية. كل خطوة قابلة للاختبار بمعزل:
wholesale_price (from GET /api/products/{id_or_slug})
→ × fx_rate_to_customer_currency
→ × (1 + category_markup_pct)
→ × (1 + fx_buffer_pct)
→ round to currency-appropriate increment
→ cache for TTL
سبب اتباع هذا الترتيب بالضبط — تريد تطبيق الهامش على قيمة الجملة بعملة العميل (لا بالدولار)، حتى تبقى نسبة الهامش ثابتة بصرف النظر عن تحركات FX. ويُطبَّق احتياطي FX فوق ذلك لأنه يغطي انزلاق وقت التسوية، لا الهامش.
جلب أسعار الجملة من FoxReload
استدعِ GET /api/products/{id_or_slug} (أو اسرد المنتجات لكل فئة بـ GET /api/products/?category_id_or_slug=...) للحصول على حقل price الحالي. خزّنه مؤقتاً إلى جانب بيانات FX لديك.
curl "https://public-api.foxreload.com/api/products/product_01krgfgww8eth9xvvysd6y7r4j" \
-H "X-API-Key: YOUR_API_KEY"
حقل price هو تكلفة الجملة. اسحبه وفق جدول مزامنة الأسعار لديك (يومياً للمنتجات المستقرة، وكل ساعة للفئات المتقلبة في FX).
تطبيق TypeScript المرجعي
interface PricingInput {
productId: string;
wholesalePrice: number; // from GET /api/products/{id}
customerCurrency: 'RUB' | 'EUR' | 'USD' | 'TRY' | 'INR';
category: 'game-code' | 'gift-card' | 'esim' | 'recharge';
customerSegment: 'retail' | 'vip' | 'wholesale';
}
const CATEGORY_MARKUP: Record<string, number> = {
'game-code': 0.22, // 22% for game top-ups
'gift-card': 0.18, // 18% for gift cards
'esim': 0.35, // 35% for eSIM
'recharge': 0.12, // 12% for mobile recharge
};
const FX_BUFFER: Record<string, number> = {
'USD': 0.005,
'EUR': 0.01,
'RUB': 0.025, // RUB volatile, wider buffer
'TRY': 0.04, // TRY very volatile
'INR': 0.012,
};
const SEGMENT_DISCOUNT: Record<string, number> = {
'retail': 1.0,
'vip': 0.95,
'wholesale': 0.88,
};
async function computeRetailPrice(input: PricingInput): Promise<number> {
const fxRate = await getFxRate('USD', input.customerCurrency);
const wholesaleLocal = input.wholesalePrice * fxRate;
const withMarkup = wholesaleLocal * (1 + CATEGORY_MARKUP[input.category]);
const withBuffer = withMarkup * (1 + FX_BUFFER[input.customerCurrency]);
const withSegment = withBuffer * SEGMENT_DISCOUNT[input.customerSegment];
return roundForCurrency(withSegment, input.customerCurrency);
}
function roundForCurrency(price: number, ccy: string): number {
if (ccy === 'RUB' || ccy === 'INR') return Math.ceil(price); // whole units
if (ccy === 'TRY') return Math.ceil(price * 2) / 2; // 0.50 increments
return Math.ceil(price * 100) / 100; // 0.01 increments
}
اختيار مصدر FX
سعر FX ليس رقماً موضوعياً واحداً — إنه يعتمد على ما تتحوّط ضده. مصادر عملية حسب حالة الاستخدام:
| العملة | المصدر | التحديث |
|---|---|---|
| EUR, GBP, JPY | المرجع اليومي لـ ECB | يومياً 16:00 CET |
| RUB | 0.7 × CBR + 0.3 × مزيج P2P (Bybit، Garantex) | كل ساعة |
| TRY | TCMB + تعديل 1% | كل ساعة |
| ARS | BCRA الرسمي + مزيج blue-chip | كل ساعة |
| INR | مرجع RBI | يومياً |
| USD/USDT | 1:1 (أو الزوج الحي إن لزم) | الزمن الفعلي |
بالنسبة لـ RUB تحديداً، سيُكلّفك CBR الخالص خسارةً أثناء أحداث انخفاض القيمة لأن العملاء يستطيعون شراء USDT على Bybit أسرع من تحديث CBR. ويبقى المزيج قريباً بما يكفي من السوق لتظل تنافسياً دون التخلي عن الهامش.
استراتيجية التخزين المؤقت
مفتاح التخزين المؤقت — (product_id, customer_currency, customer_segment). وTTL — طابِق فاصل تحديث FX لديك، ساعة واحدة للعملات المتقلبة و24 ساعة للمستقرة. استخدم Redis أو ما يعادله بنمط إخلاء ناعم بحيث تقدّم سعراً قديماً قليلاً (5-10 دقائق) بدلاً من إعادة الحساب في كل طلب.
مخطط التخزين للسجل التدقيقي:
CREATE TABLE pricing_decisions (
id BIGSERIAL PRIMARY KEY,
computed_at TIMESTAMPTZ NOT NULL,
product_id TEXT NOT NULL,
customer_currency CHAR(3) NOT NULL,
customer_segment TEXT NOT NULL,
wholesale_price NUMERIC(10,4) NOT NULL,
fx_rate NUMERIC(14,6) NOT NULL,
markup_pct NUMERIC(5,4) NOT NULL,
buffer_pct NUMERIC(5,4) NOT NULL,
final_price NUMERIC(12,2) NOT NULL,
cache_key TEXT NOT NULL,
INDEX idx_product_time (product_id, computed_at DESC)
);
احتفظ بها 90 يوماً. هذه البيانات لا تُقدّر بثمن في نزاعات الاسترداد ("هذا هو السعر الدقيق الذي رآه العميل عند هذا الطابع الزمني") وفي اختبار A/B لقواعد الهامش.
هذا هو المحرك بأكمله. اسحب GET /api/products/ من FoxReload، وأدخِله في المسار أعلاه، وستحصل على تسعير متعدد العملات آمن للهامش في الإنتاج. ابدأ من foxreload.com.
