نظرة عامة
بعد تنفيذ طلبات بطاقات الهدايا، احصل على رموز البطاقات من API وسلّمها للعملاء بأمان. رموز البطاقات ذات قيمة مالية ويجب التعامل معها بعناية فائقة.أمر بالغ الأهمية: رموز البطاقات بيانات حساسة قابلة للاستبدال بالمال. شفّرها دائماً عند التخزين والإرسال.
مرجع API
GET /v3/gift-cards/check-order/{orderId}
استرجاع تفاصيل البطاقة المنفَّذة
هيكل بيانات البطاقة
تحتوي الطلبات المنفَّذة على بطاقات بحقلين:{
"cards": [
{
"value": "XXXX-XXXX-XXXX-XXXX", // Primary credential (PIN/code)
"serial": "123456789" // Secondary credential (serial number)
}
]
}
value وserial حساسان ويجب حمايتهما.مساعدات التشفير
const crypto = require('crypto');
// Encryption configuration
const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // Must be 32 bytes
const ALGORITHM = 'aes-256-gcm';
function encryptCardData(text) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(
ALGORITHM,
Buffer.from(ENCRYPTION_KEY, 'hex'),
iv
);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return {
iv: iv.toString('hex'),
encryptedData: encrypted,
authTag: authTag.toString('hex')
};
}
function decryptCardData(iv, encryptedData, authTag) {
const decipher = crypto.createDecipheriv(
ALGORITHM,
Buffer.from(ENCRYPTION_KEY, 'hex'),
Buffer.from(iv, 'hex')
);
decipher.setAuthTag(Buffer.from(authTag, 'hex'));
let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// Usage
const encrypted = encryptCardData('XXXX-XXXX-XXXX-XXXX');
const decrypted = decryptCardData(encrypted.iv, encrypted.encryptedData, encrypted.authTag);
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
import base64
# Encryption key (32 bytes)
ENCRYPTION_KEY = bytes.fromhex(os.getenv('ENCRYPTION_KEY'))
def encrypt_card_data(text):
aesgcm = AESGCM(ENCRYPTION_KEY)
nonce = os.urandom(12)
encrypted = aesgcm.encrypt(nonce, text.encode('utf-8'), None)
return {
'nonce': base64.b64encode(nonce).decode('utf-8'),
'encryptedData': base64.b64encode(encrypted).decode('utf-8')
}
def decrypt_card_data(nonce_b64, encrypted_data_b64):
aesgcm = AESGCM(ENCRYPTION_KEY)
nonce = base64.b64decode(nonce_b64)
encrypted = base64.b64decode(encrypted_data_b64)
decrypted = aesgcm.decrypt(nonce, encrypted, None)
return decrypted.decode('utf-8')
# Usage
encrypted = encrypt_card_data('XXXX-XXXX-XXXX-XXXX')
decrypted = decrypt_card_data(encrypted['nonce'], encrypted['encryptedData'])
<?php
define('ENCRYPTION_KEY', getenv('ENCRYPTION_KEY')); // 32 bytes hex
define('CIPHER_ALGO', 'aes-256-gcm');
function encryptCardData($text) {
$key = hex2bin(ENCRYPTION_KEY);
$iv = random_bytes(16);
$tag = '';
$encrypted = openssl_encrypt(
$text, CIPHER_ALGO, $key, OPENSSL_RAW_DATA, $iv, $tag, '', 16
);
return [
'iv' => bin2hex($iv),
'encryptedData' => bin2hex($encrypted),
'authTag' => bin2hex($tag)
];
}
function decryptCardData($ivHex, $encryptedHex, $authTagHex) {
$key = hex2bin(ENCRYPTION_KEY);
$iv = hex2bin($ivHex);
$encrypted = hex2bin($encryptedHex);
$authTag = hex2bin($authTagHex);
$decrypted = openssl_decrypt(
$encrypted, CIPHER_ALGO, $key, OPENSSL_RAW_DATA, $iv, $authTag
);
if ($decrypted === false) {
throw new Exception('Decryption failed');
}
return $decrypted;
}
// Usage
$encrypted = encryptCardData('XXXX-XXXX-XXXX-XXXX');
$decrypted = decryptCardData($encrypted['iv'], $encrypted['encryptedData'], $encrypted['authTag']);
?>
التخزين الآمن
شفّر رموز البطاقات دائماً قبل تخزينها في قاعدة البيانات:const crypto = require('crypto');
const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // Must be 32 bytes
const ALGORITHM = 'aes-256-gcm';
function encryptCardData(cardData) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(
ALGORITHM,
Buffer.from(ENCRYPTION_KEY, 'hex'),
iv
);
const plaintext = JSON.stringify(cardData);
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return {
iv: iv.toString('hex'),
encryptedData: encrypted,
authTag: authTag.toString('hex')
};
}
function decryptCardData(iv, encryptedData, authTag) {
const decipher = crypto.createDecipheriv(
ALGORITHM,
Buffer.from(ENCRYPTION_KEY, 'hex'),
Buffer.from(iv, 'hex')
);
decipher.setAuthTag(Buffer.from(authTag, 'hex'));
let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return JSON.parse(decrypted);
}
// Store encrypted cards
async function storeOrderCards(orderId, cards) {
const encryptedCards = cards.map(card => ({
...encryptCardData(card),
cardType: card.type
}));
await db.orders.updateOne(
{ orderId },
{
$set: {
encryptedCards,
status: 'FULFILLED',
fulfilledAt: new Date()
}
}
);
}
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
import base64
import json
ENCRYPTION_KEY = bytes.fromhex(os.getenv('ENCRYPTION_KEY'))
def encrypt_card_data(card_data):
aesgcm = AESGCM(ENCRYPTION_KEY)
nonce = os.urandom(12)
plaintext = json.dumps(card_data).encode('utf-8')
encrypted = aesgcm.encrypt(nonce, plaintext, None)
return {
'nonce': base64.b64encode(nonce).decode('utf-8'),
'encryptedData': base64.b64encode(encrypted).decode('utf-8')
}
def decrypt_card_data(nonce_b64, encrypted_data_b64):
aesgcm = AESGCM(ENCRYPTION_KEY)
nonce = base64.b64decode(nonce_b64)
encrypted = base64.b64decode(encrypted_data_b64)
decrypted = aesgcm.decrypt(nonce, encrypted, None)
return json.loads(decrypted.decode('utf-8'))
def store_order_cards(order_id, cards):
encrypted_cards = [encrypt_card_data(card) for card in cards]
db.orders.update_one(
{'orderId': order_id},
{
'$set': {
'encryptedCards': encrypted_cards,
'status': 'FULFILLED',
'fulfilledAt': datetime.now()
}
}
)
<?php
define('ENCRYPTION_KEY', getenv('ENCRYPTION_KEY'));
define('CIPHER_ALGO', 'aes-256-gcm');
function encryptCardData($cardData) {
$key = hex2bin(ENCRYPTION_KEY);
$iv = random_bytes(16);
$tag = '';
$plaintext = json_encode($cardData);
$encrypted = openssl_encrypt(
$plaintext,
CIPHER_ALGO,
$key,
OPENSSL_RAW_DATA,
$iv,
$tag,
'',
16
);
return [
'iv' => bin2hex($iv),
'encryptedData' => bin2hex($encrypted),
'authTag' => bin2hex($tag)
];
}
function decryptCardData($ivHex, $encryptedHex, $authTagHex) {
$key = hex2bin(ENCRYPTION_KEY);
$iv = hex2bin($ivHex);
$encrypted = hex2bin($encryptedHex);
$authTag = hex2bin($authTagHex);
$decrypted = openssl_decrypt(
$encrypted,
CIPHER_ALGO,
$key,
OPENSSL_RAW_DATA,
$iv,
$authTag
);
if ($decrypted === false) {
throw new Exception('Decryption failed');
}
return json_decode($decrypted, true);
}
?>
طرق التسليم
1. التسليم عبر البريد الإلكتروني
async function deliverCardsViaEmail(userId, orderId, cards) {
const user = await db.users.findOne({ _id: userId });
// Build email content with cards
const cardList = cards.map((card, index) => `
Card ${index + 1}:
Code: ${card.value}
Serial: ${card.serial}
`).join('\n\n');
await sendEmail({
to: user.email,
subject: 'Your Gift Card Order',
html: `
<h2>Your gift cards are ready!</h2>
<p>Order ID: ${orderId}</p>
<pre>${cardList}</pre>
<p><strong>Important:</strong> Keep these codes secure and don't share them.</p>
`
});
// Mark as delivered
await db.orders.updateOne(
{ orderId },
{ $set: { 'encryptedCards.$[].deliveredAt': new Date() } }
);
console.log(`Delivered ${cards.length} cards to ${user.email}`);
}
2. العرض داخل التطبيق
// API endpoint to display cards in-app
app.get('/api/orders/:orderId/cards', authenticateUser, async (req, res) => {
const { orderId } = req.params;
const userId = req.user.id;
// Verify ownership
const order = await db.orders.findOne({ orderId, userId });
if (!order) {
return res.status(404).json({ error: 'Order not found' });
}
if (order.status !== 'FULFILLED') {
return res.status(400).json({ error: 'Order not fulfilled yet' });
}
// Decrypt and return cards
const cards = await getOrderCards(orderId);
res.json({
orderId,
cards: cards.map((card, index) => ({
cardNumber: index + 1,
code: card.value,
serial: card.serial
}))
});
});
3. التحميل الآمن
async function generateSecureCardFile(orderId, cards) {
const content = cards.map((card, index) => `
Gift Card ${index + 1}
Code: ${card.value}
Serial: ${card.serial}
--------------------
`).join('\n');
// Encrypt the file content
const encrypted = encryptCardData(content);
// Store temporarily with unique token
const token = crypto.randomBytes(32).toString('hex');
await db.downloadTokens.insertOne({
token,
orderId,
encrypted,
expiresAt: new Date(Date.now() + 5 * 60 * 1000) // 5 minutes
});
return token;
}
// Download endpoint
app.get('/api/download/:token', async (req, res) => {
const { token } = req.params;
const download = await db.downloadTokens.findOne({ token });
if (!download || download.expiresAt < new Date()) {
return res.status(404).json({ error: 'Download link expired' });
}
const content = decryptCardData(
download.encrypted.iv,
download.encrypted.encryptedData,
download.encrypted.authTag
);
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Content-Disposition', `attachment; filename="cards-${download.orderId}.txt"`);
res.send(content);
// Delete token after use
await db.downloadTokens.deleteOne({ token });
});
تسجيل التدقيق
سجّل أحداث التسليم دون كشف بيانات البطاقة:async function logCardDelivery(orderId, userId, method, cardCount) {
await db.auditLogs.insertOne({
event: 'CARDS_DELIVERED',
orderId,
userId,
method, // 'email', 'in-app', 'download'
cardCount,
timestamp: new Date(),
ipAddress: req.ip,
userAgent: req.headers['user-agent']
});
console.log(`[AUDIT] Delivered ${cardCount} cards for order ${orderId} via ${method}`);
}
// ❌ Never log actual card codes
// console.log('Card code:', card.value); // Don't do this
سير التسليم الكامل
async function completeOrderDelivery(orderId, userId) {
// 1. Check order is fulfilled
const orderStatus = await checkOrderStatus(orderId);
if (orderStatus.status !== 'FULFILLED' && orderStatus.status !== 'PARTIALLY_FILLED') {
throw new Error('Order not ready for delivery');
}
// 2. Store encrypted cards
await storeOrderCards(orderId, userId, orderStatus.cards);
// 3. Deliver to customer
await deliverCardsViaEmail(userId, orderId, orderStatus.cards);
// 4. Log delivery
await logCardDelivery(orderId, userId, 'email', orderStatus.cards.length);
console.log(`✅ Order ${orderId} delivery complete`);
}
def complete_order_delivery(order_id, user_id):
# 1. Check order is fulfilled
order_status = check_order_status(order_id)
if order_status['status'] not in ['FULFILLED', 'PARTIALLY_FILLED']:
raise ValueError('Order not ready for delivery')
# 2. Store encrypted cards
store_order_cards(order_id, user_id, order_status['cards'])
# 3. Deliver to customer
deliver_cards_via_email(user_id, order_id, order_status['cards'])
# 4. Log delivery
log_card_delivery(order_id, user_id, 'email', len(order_status['cards']))
print(f"✅ Order {order_id} delivery complete")
<?php
function completeOrderDelivery($orderId, $userId, $apiKey) {
// 1. Check order is fulfilled
$orderStatus = checkOrderStatus($orderId, $apiKey);
if (!in_array($orderStatus['status'], ['FULFILLED', 'PARTIALLY_FILLED'])) {
throw new Exception('Order not ready for delivery');
}
// 2. Store encrypted cards
storeOrderCards($orderId, $userId, $orderStatus['cards']);
// 3. Deliver to customer
deliverCardsViaEmail($userId, $orderId, $orderStatus['cards']);
// 4. Log delivery
logCardDelivery($orderId, $userId, 'email', count($orderStatus['cards']));
error_log("✅ Order {$orderId} delivery complete");
}
?>
قائمة التحقق الأمنية
1
توليد مفتاح تشفير
أنشئ مفتاح تشفير آمن بطول 32 بايت وخزّنه في متغيرات البيئة
2
التشفير قبل التخزين
شفّر دائماً بيانات البطاقة قبل الإدراج في قاعدة البيانات
3
التحقق من ملكية المستخدم
تأكد أن المستخدم يملك الطلب قبل عرض البطاقة
4
استخدام HTTPS
تأكد من أن جميع طرق التسليم تستخدم نقلاً مشفراً
5
تسجيل التدقيق
سجّل أحداث التسليم دون تضمين الرموز الفعلية
6
الحذف الآمن
نفّذ سياسة حذف البطاقات إذا اقتضت اللوائح ذلك
أفضل الممارسات
التشفير في حالة السكون
شفّر دائماً رموز البطاقة قبل تخزينها في قاعدة البيانات
HTTPS فقط
لا ترسل بطاقات عبر اتصالات غير مشفرة
التحقق من الملكية
تحقق أن المستخدم يملك الطلب قبل عرض البطاقة
لا سجلات نصية
لا تسجّل رموز البطاقة كنص عادي في أي مكان
الحذف الآمن
احذف البطاقات بشكل آمن بعد استرجاع العميل لها إذا اقتضت السياسة ذلك
سجل التدقيق
سجّل أحداث التسليم دون تضمين البيانات الحساسة
الخطوات التالية
نظرة عامة
العودة إلى نظرة عامة على التكامل
تتبع الحالة
استعلام حالة الطلب
أفضل ممارسات الأمان
إرشادات الأمان العامة
تقديم الطلبات
تقديم طلبات بطاقات الهدايا
تحميل الكتالوج
جلب المنتجات المتاحة
مرجع API
التوثيق الكامل للـ endpoint

