108 lines
3.3 KiB
Go
108 lines
3.3 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"gestion/db"
|
|
"gestion/services"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// IPNWebhook - POST /api/v1/webhooks/nowpayments
|
|
// Reçoit les callbacks de NowPayments lors des changements de statut
|
|
func IPNWebhook(c *gin.Context) {
|
|
database := c.MustGet("database").(*db.Database)
|
|
np, ok := c.MustGet("nowpayments").(*services.NowPaymentsClient)
|
|
if !ok || np == nil {
|
|
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "paiement crypto non configuré"})
|
|
return
|
|
}
|
|
|
|
body, err := io.ReadAll(c.Request.Body)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "impossible de lire le corps"})
|
|
return
|
|
}
|
|
|
|
sig := c.GetHeader("x-nowpayments-sig")
|
|
if !np.VerifyIPN(body, sig) {
|
|
log.Printf("[IPN] signature invalide")
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "signature invalide"})
|
|
return
|
|
}
|
|
|
|
var payload services.IPNPayload
|
|
if err := json.Unmarshal(body, &payload); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "payload invalide"})
|
|
return
|
|
}
|
|
|
|
payment, err := database.GetCryptoPaymentByNowPaymentID(payload.PaymentID.String())
|
|
if err != nil || payment == nil {
|
|
log.Printf("[IPN] paiement introuvable: %s", payload.PaymentID.String())
|
|
// 200 pour éviter les retries NowPayments
|
|
c.JSON(http.StatusOK, gin.H{"ok": true})
|
|
return
|
|
}
|
|
|
|
payAmount, _ := payload.PayAmount.Float64()
|
|
if err := database.UpdateCryptoPaymentStatus(payment.ID, payload.PaymentStatus, payAmount); err != nil {
|
|
log.Printf("[IPN] erreur mise à jour paiement %d: %v", payment.ID, err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur base de données"})
|
|
return
|
|
}
|
|
|
|
switch payload.PaymentStatus {
|
|
case "finished", "confirmed":
|
|
if err := database.ActivateCryptoCommand(payment.CommandID); err != nil {
|
|
log.Printf("[IPN] erreur activation commande %d: %v", payment.CommandID, err)
|
|
} else {
|
|
log.Printf("[IPN] commande %d activée (paiement %s confirmé)", payment.CommandID, payload.PaymentID.String())
|
|
}
|
|
case "failed", "expired":
|
|
if err := database.CancelCryptoCommand(payment.CommandID); err != nil {
|
|
log.Printf("[IPN] erreur annulation commande %d: %v", payment.CommandID, err)
|
|
} else {
|
|
log.Printf("[IPN] commande %d annulée (paiement %s)", payment.CommandID, payload.PaymentStatus)
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"ok": true})
|
|
}
|
|
|
|
// GetCommandPaymentStatus - GET /api/v1/commands/:id/payment-status
|
|
// Retourne le statut du paiement crypto d'une commande (polling côté client)
|
|
func GetCommandPaymentStatus(c *gin.Context) {
|
|
database := c.MustGet("database").(*db.Database)
|
|
|
|
commandID, err := strconv.Atoi(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "id de commande invalide"})
|
|
return
|
|
}
|
|
|
|
payment, err := database.GetCryptoPaymentByCommandID(commandID)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur base de données"})
|
|
return
|
|
}
|
|
if payment == nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "aucun paiement crypto pour cette commande"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"command_id": payment.CommandID,
|
|
"payment_status": payment.Status,
|
|
"pay_address": payment.PayAddress,
|
|
"pay_amount": payment.PayAmount,
|
|
"pay_currency": payment.PayCurrency,
|
|
"price_amount": payment.PriceAmount,
|
|
"price_currency": payment.PriceCurrency,
|
|
})
|
|
}
|