Guide de sécurité
Bonnes pratiques pour sécuriser votre intégration PayerScan Payment Gateway.
Sécurité de l'API Key
Ne jamais exposer votre API Key
Votre API Key authentifie les requêtes vers l'API PayerScan. Elle ne doit jamais être visible par les utilisateurs finaux.
// ❌ INCORRECT - Appeler l'API depuis le frontend (API Key exposée à tous)
fetch('https://api.payerscan.com/payment/crypto', {
headers: { 'x-api-key': 'EQnhBYpknGAP...' } // API Key exposée !
})
// ✅ CORRECT - Appeler l'API depuis le serveur backend
// Le frontend appelle votre API interne
fetch('/api/create-payment', { body: { amount: 100 } })
// Votre backend appelle PayerScan (API Key sécurisée sur votre serveur)
app.post('/api/create-payment', async (req, res) => {
const result = await fetch('https://api.payerscan.com/payment/crypto', {
headers: { 'x-api-key': process.env.PAYERSCAN_API_KEY }
})
res.json(result)
})
Stocker l'API Key en toute sécurité
# ✅ Utiliser des variables d'environnement
PAYERSCAN_API_KEY=EQnhBYpknGAP...
# ❌ Ne pas coder en dur dans le code
const API_KEY = 'EQnhBYpknGAP...' # NE FAITES PAS CELA
Remarque : Ne commitez jamais d'API Keys dans le contrôle de version (Git). Ajoutez
.envà votre fichier.gitignore.
Rotation périodique de l'API Key
- Changez l'API Key si vous suspectez qu'elle a été compromise
- Allez dans la gestion de la boutique → Générer une nouvelle API Key
- Mettez immédiatement à jour la nouvelle API Key sur votre serveur
- L'ancienne API Key sera révoquée immédiatement après la génération d'une nouvelle
Sécurité des Webhooks
Utiliser HTTPS
Utilisez toujours HTTPS pour votre callback_url afin de chiffrer les données en transit et prévenir les attaques man-in-the-middle.
// ✅ CORRECT - Utiliser HTTPS
callback_url: 'https://your-server.com/webhook/payment'
// ❌ INCORRECT - Utiliser HTTP (données non chiffrées, vulnérable à l'interception)
callback_url: 'http://your-server.com/webhook/payment'
Vérifier les requêtes Webhook
Vérifiez toujours que les requêtes webhook proviennent réellement de PayerScan :
app.post('/webhook/payment', express.json(), async (req, res) => {
try {
const { merchant_id, api_key, trans_id, request_id, status, amount, transaction_hash } = req.body
// 1. Vérifier merchant_id (completed et expired)
if (merchant_id !== process.env.PAYERSCAN_MERCHANT_ID) {
return res.status(401).json({ error: 'Invalid merchant' })
}
// 2. Vérifier api_key (completed uniquement — expired n'inclut pas api_key)
if (status === 'completed' && api_key !== process.env.PAYERSCAN_API_KEY) {
return res.status(401).json({ error: 'Invalid API Key' })
}
// 3. Trouver la commande par request_id (si fourni) ou trans_id
let order = null
if (request_id) {
order = await db.orders.findOne({ request_id })
}
if (!order) {
order = await db.orders.findOne({ trans_id })
}
if (!order) {
return res.status(404).json({ error: 'Order not found' })
}
// 4. Traiter uniquement les commandes dans un état modifiable (idempotent)
const processableStatuses = ['waiting', 'pending', 'processing']
if (!processableStatuses.includes(order.status)) {
return res.status(200).json({ message: 'Already processed' })
}
// 5. Vérifier que le montant correspond (completed uniquement — anti-falsification)
if (status === 'completed') {
if (parseFloat(amount) !== parseFloat(order.amount)) {
return res.status(400).json({ error: 'Amount mismatch' })
}
}
// 6. Vérifier que transaction_hash n'a pas déjà été utilisé (anti-rejeu)
if (status === 'completed') {
const existingTx = await db.orders.findOne({ transaction_hash })
if (existingTx) {
return res.status(409).json({ error: 'Transaction hash already used' })
}
}
// 7. Mise à jour atomique — inclut le filtre de statut pour prévenir les conditions de course
if (status === 'completed') {
const result = await db.orders.update(
{ trans_id, status: { $in: processableStatuses } },
{ status: 'completed', transaction_hash }
)
if (result.modifiedCount === 0) {
return res.status(200).json({ message: 'Already processed' })
}
} else if (status === 'expired') {
const result = await db.orders.update(
{ trans_id, status: { $in: processableStatuses } },
{ status: 'expired' }
)
if (result.modifiedCount === 0) {
return res.status(200).json({ message: 'Already processed' })
}
}
res.status(200).json({ success: true })
} catch (error) {
console.error('Webhook processing error:', error)
res.status(500).json({ error: 'Internal server error' })
}
})
Traitement idempotent
Les webhooks peuvent être réessayés jusqu'à 5 fois. Assurez-vous que le traitement multiple est sûr :
// ✅ CORRECT - Traiter uniquement les commandes dans un état modifiable
const processableStatuses = ['waiting', 'pending', 'processing']
if (!processableStatuses.includes(order.status)) {
return res.status(200).json({ message: 'Already processed' })
}
// ❌ INCORRECT - Pas de vérification, pourrait créditer le solde plusieurs fois
await addBalance(user, amount) // Si le webhook est envoyé deux fois → crédité deux fois !
Sécurité des données
Ne pas journaliser les données sensibles
// ❌ INCORRECT - Journaliser l'API Key
console.log('Request with API Key:', req.headers['x-api-key'])
// ✅ CORRECT - Ne journaliser que le nécessaire
console.log('Payment created:', { trans_id, amount, status })
Valider les entrées
PayerScan valide les entrées côté serveur, mais vous devriez également valider de votre côté :
// Valider le montant (doit être positif, max 1 000 000 USD)
const amount = parseFloat(req.body.amount)
if (isNaN(amount) || amount <= 0 || amount > 1000000) {
return res.status(400).json({ error: 'Invalid amount' })
}
// Valider le format de callback_url
if (req.body.callback_url) {
try {
const url = new URL(req.body.callback_url)
if (url.protocol !== 'https:') {
console.warn('callback_url devrait utiliser HTTPS pour la sécurité')
}
} catch {
return res.status(400).json({ error: 'Invalid callback_url' })
}
}
Protections côté serveur PayerScan
PayerScan implémente plusieurs couches de sécurité côté serveur :
- Protection SSRF : Le système valide toutes les valeurs
callback_urlavant d'envoyer les webhooks. Les IP internes/privées et les URL suspectes sont automatiquement bloquées. - Limitation de débit : Les endpoints API sont protégés par des limites de débit burst et soutenues pour prévenir les abus. Voir Limites de débit pour plus de détails.
- Validation des entrées : Tous les paramètres de requête sont validés à l'aide de schémas stricts (plage de montant, format d'URL, limites de longueur de chaîne).
- Timeout Webhook : Les callbacks webhook ont un timeout de 10 secondes. Si votre serveur ne répond pas dans les 10 secondes, la tentative est marquée comme échouée et sera réessayée.
Liste de vérification de sécurité
Avant la mise en production
- API Key stockée dans les variables d'environnement (pas codée en dur)
- API Key non exposée dans le code frontend/client
- Fichier
.envajouté au.gitignore - Endpoint webhook utilise HTTPS
- Webhook vérifie
merchant_idpour completed et expired - Webhook vérifie
api_keypour les webhooks completed - Webhook trouve la commande par
request_idoutrans_id - Le traitement des webhooks est idempotent (pas de traitement en double)
- Le webhook vérifie que le
amountcorrespond au montant de la commande - Le webhook vérifie que
transaction_hashn'a pas déjà été utilisé (anti-rejeu) - Le webhook utilise une mise à jour atomique avec filtre de statut (anti-concurrence)
- Aucune donnée sensible enregistrée (API Key, clés privées)
- Validation des entrées pour les montants, URLs
Maintenance continue
- Examiner régulièrement les journaux d'accès du endpoint webhook
- Surveiller les requêtes webhook non autorisées ou suspectes
- Rotation de l'API Key périodiquement ou en cas de compromission
- Mettre à jour les dépendances avec les correctifs de sécurité
- Tester que le endpoint webhook répond en moins de 10 secondes
Réponse aux incidents de sécurité
Si l'API Key est compromise
- Immédiatement allez dans la gestion de la boutique → Générer une nouvelle API Key
- Mettez à jour la nouvelle API Key sur tous les serveurs
- Examinez les journaux API récents pour les requêtes non autorisées
- Contactez le support si des transactions suspectes sont trouvées
Si des webhooks frauduleux sont détectés
- Vérifiez que votre gestionnaire de webhook vérifie
merchant_idetapi_key - Vérifiez les logs pour les incohérences de
amountou les tentatives detransaction_hashen double - Bloquez l'IP source (si identifiable)
- Ajoutez une liste blanche d'IP si possible
- Contactez le support pour signaler l'incident
Contacter le support
Pour les problèmes de sécurité urgents, contactez-nous immédiatement :
- Email : payerscan@gmail.com