اربط متجرك، وأكّد الدفع من السيرفر.
كل مفتاح API خاص بتاجر واحد. استعمله من سيرفر متجرك فقط، ولا تضعه أبدًا في تطبيق الزبون أو صفحة ويب.
البداية بثلاث خطوات
- من لوحة التاجر: اربط حساب شام كاش بالـ QR.
- أصدر مفتاح API من «مفاتيح API»، واحفظه في متغيّرات بيئة سيرفرك.
- اضبط عنوان Webhook، واحفظ سر التوقيع.
١إنشاء طلب دفع
المبلغ نص عشري حتى منزلتين، والعملات: SYP وUSD وEUR. لا تأخذ المبلغ من متصفح الزبون؛ احسبه من منتجاتك في السيرفر.
const response = await fetch(`${process.env.SHAMPAY_BASE_URL}/v1/payments`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SHAMPAY_API_KEY}`,
'Idempotency-Key': 'checkout-order-104-attempt-1',
'Content-Type': 'application/json'
},
body: JSON.stringify({
reference: 'order-104',
title: 'اشتراك شهر',
amount: '15.00',
currency: 'USD',
returnUrl: 'https://store.example/orders/104' // optional
})
});
const { data, error } = await response.json();
if (!response.ok) throw new Error(error.code);
// Store data.id against your own order BEFORE redirecting.
// Redirect the customer to data.checkoutUrl.curl -X POST "$SHAMPAY_BASE_URL/v1/payments" \
-H "Authorization: Bearer $SHAMPAY_API_KEY" \
-H "Idempotency-Key: checkout-order-104-attempt-1" \
-H "Content-Type: application/json" \
-d '{"reference":"order-104","title":"اشتراك شهر","amount":"15.00","currency":"USD","returnUrl":"https://store.example/orders/104"}'$ch = curl_init(getenv('SHAMPAY_BASE_URL') . '/v1/payments');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('SHAMPAY_API_KEY'),
'Idempotency-Key: checkout-order-104-attempt-1',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'reference' => 'order-104', 'title' => 'اشتراك شهر',
'amount' => '15.00', 'currency' => 'USD',
'returnUrl' => 'https://store.example/orders/104',
], JSON_UNESCAPED_UNICODE),
]);
$res = json_decode(curl_exec($ch), true);
// Store $res['data']['id'], then redirect to $res['data']['checkoutUrl'].أعد استخدام نفس Idempotency-Key عند فشل الشبكة أو إعادة المحاولة. نفس المفتاح ونفس البيانات يرجعان نفس الطلب، والبيانات المختلفة تُرفض. المرجع فريد داخل حساب التاجر. صلاحية الطلب ٣٠ دقيقة.
returnUrl اختياري (HTTPS): بعد تأكيد الدفع ترجع صفحة الدفع الزبون إليه تلقائيًا خلال خمس ثوانٍ. لا تعتبر وصول الزبون إليه دليل دفع؛ اعتمد على Webhook.
٢الاستعلام عن الطلب
{
"data": {
"id": "...",
"reference": "order-104",
"amount": "15.00",
"currency": "USD",
"paymentNote": "482913",
"status": "pending",
"received": "0.00",
"remaining": "15.00",
"overpaid": "0.00",
"payerName": null,
"expiresAt": 1790000000000,
"checkoutUrl": "https://YOUR-HOST/pay/..."
}
}الحالات: pending وpaid وexpired. أوقات API بالميلي ثانية منذ Unix.
٣استقبال Webhook وتفعيل الخدمة
اضبط عنوان HTTPS من لوحة التاجر واحفظ سر التوقيع. نرسل payment.paid فقط بعد تأكيد الدفع. التوقيع هو HMAC-SHA256 للقيمة timestamp.rawBody، في الترويسات x-shampay-timestamp وx-shampay-signature وx-shampay-event.
import crypto from 'node:crypto';
function verifyShamPay(rawBody, headers, secret) {
const ts = headers['x-shampay-timestamp'];
const received = headers['x-shampay-signature'] || '';
if (!/^\d+$/.test(ts || '')) throw new Error('Missing timestamp');
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300)
throw new Error('Expired signature');
const expected = 'v1=' + crypto.createHmac('sha256', secret)
.update(`${ts}.${rawBody}`).digest('hex');
const a = Buffer.from(received), b = Buffer.from(expected);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b))
throw new Error('Invalid signature');
const event = JSON.parse(rawBody);
if (event.id !== headers['x-shampay-event'])
throw new Error('Event ID mismatch');
return event;
}
// In one database transaction in YOUR store:
// 1. Find your order by event.data.id / reference.
// 2. Compare expected amount and currency with your stored order.
// 3. Insert event.id into a table with UNIQUE(event_id).
// 4. Mark the order paid and grant access exactly once.
// Duplicate event: return HTTP 200 without granting access again.
// Commit durable work before returning HTTP 200.استخدم جسم الطلب الخام قبل إعادة ترميز JSON. مدة قبول التوقيع المقترحة خمس دقائق مع ساعة سيرفر مضبوطة. الإرسال يتكرر حتى ٨ محاولات بتأخير متزايد؛ قد يصل نفس الحدث أكثر من مرة. بعد الفشل يظهر في لوحة التاجر لإعادة الجدولة.
٤تأكيد الدفع
يتأكد الطلب تلقائيًا عندما يدفع الزبون المبلغ نفسه بالضبط وبالعملة نفسها خلال مهلة الطلب، ويكتب رمز الطلب paymentNote (ستة أرقام) في ملاحظة التحويل. الحوالة بدون الرمز لا تُعتمد تلقائيًا. عندها تصبح حالته paid ويصل إشعار payment.paid لمتجرك.
دفعة ناقصة أو زائدة
إذا دفع الزبون أقل من المطلوب، يبقى الطلب pending وتظهر له صفحة الدفع الباقي ليكمله. الحقل received يعرض ما وصل، وremaining ما تبقى. باقي المبلغ يُقبل فقط من نفس الحساب الذي دفع الجزء الأول، وpayerName يعرض اسم المرسل كما يظهر في شام كاش. إذا دفع أكثر، يصبح الطلب paid ويعرض overpaid قيمة الزيادة التي يجب إرجاعها للزبون.
payment.paid. الدفعة الناقصة لا ترسل إشعارًا.٥الاشتراكات والأخطاء
| HTTP | المعنى |
|---|---|
| 400 | حقول غير صالحة أو فشل تحقق |
| 401 | مفتاح غير صالح أو ملغى |
| 403 | اشتراك موقوف أو منتهٍ، أو الحد الشهري ممتلئ |
| 404 | طلب غير موجود ضمن حسابك |
| 409 | تعارض مرجع أو مفتاح تكرار أو حالة الطلب |
| 429 | طلبات كثيرة؛ انتظر وأعد المحاولة بتأخير |
{ "error": { "code": "...", "message": "...", "requestId": "..." } }استخدم code في برنامجك، وmessage للعرض.
sham-pay · API v1 · منصة مستقلة لا تتبع لشام كاش