Dev / controllers /wallet_controller.go
Mhamdans17
fix: hide QRIS pending topups from admin verification panel
27a7c82
Raw
History Blame Contribute Delete
8.39 kB
package controllers
import (
"fmt"
"net/http"
"service-warungpos-go/config"
"service-warungpos-go/models"
"service-warungpos-go/services"
"github.com/gin-gonic/gin"
)
// ==================== MERCHANT ENDPOINTS ====================
// GetWalletDetail mengembalikan saldo dompet dan riwayat mutasi perusahaan
func GetWalletDetail(c *gin.Context) {
userVal, _ := c.Get("user")
user := userVal.(*models.User)
var company models.Company
if err := config.DB.First(&company, *user.CompanyID).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"detail": "Company tidak ditemukan"})
return
}
var history []models.WalletTransaction
if err := config.DB.Where("company_id = ?", company.ID).Order("created_at desc").Limit(50).Find(&history).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mengambil riwayat dompet"})
return
}
c.JSON(http.StatusOK, gin.H{
"balance": company.WalletBalance,
"limit": company.WalletLimit,
"history": history,
})
}
type TopupManualRequest struct {
Amount float64 `json:"amount" binding:"required,gt=0"`
ProofURL string `json:"proof_url" binding:"required"`
}
type TopupQrisRequest struct {
Amount float64 `json:"amount" binding:"required,gt=0"`
}
// RequestTopupManual mengajukan penambahan saldo (menunggu persetujuan admin)
func RequestTopupManual(c *gin.Context) {
userVal, _ := c.Get("user")
user := userVal.(*models.User)
var req TopupManualRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
return
}
topup := models.TopupRequest{
CompanyID: *user.CompanyID,
Amount: req.Amount,
ProofURL: req.ProofURL,
Status: "pending",
}
if err := config.DB.Create(&topup).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mengajukan top-up"})
return
}
c.JSON(http.StatusCreated, gin.H{"message": "Pengajuan top-up berhasil dibuat, menunggu persetujuan admin", "data": topup})
}
// RequestTopupQris mengajukan penambahan saldo menggunakan QRIS Tripay
func RequestTopupQris(c *gin.Context) {
userVal, _ := c.Get("user")
user := userVal.(*models.User)
var req TopupQrisRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
return
}
// 1. Buat record TopupRequest dengan status pending
topup := models.TopupRequest{
CompanyID: *user.CompanyID,
Amount: req.Amount,
ProofURL: "QRIS TRIPAY", // Tandai bahwa ini via QRIS
Status: "pending",
}
if err := config.DB.Create(&topup).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal membuat request topup"})
return
}
merchantRef := fmt.Sprintf("TOPUP-%d", topup.ID)
var company models.Company
config.DB.First(&company, *user.CompanyID)
tripayItems := []services.TripayItem{
{
SKU: "TOPUP",
Name: "Top-up Saldo Dompet",
Price: int(req.Amount),
Quantity: 1,
},
}
// 2. Request QRIS ke Tripay
tripayRes, err := services.CreateTripayTransaction(
merchantRef,
int(req.Amount),
"QRIS", // Paksa QRIS
user.Name,
user.Email,
user.Phone,
tripayItems,
*user.CompanyID,
)
if err != nil {
// Batalkan topup request jika gagal
topup.Status = "rejected"
config.DB.Save(&topup)
c.JSON(http.StatusBadRequest, gin.H{"detail": fmt.Sprintf("Gagal generate QRIS Tripay: %v", err)})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "Berhasil generate QRIS Top-up",
"topup_id": topup.ID,
"qr_url": tripayRes.Data.QrURL,
"checkout_url": tripayRes.Data.CheckoutURL,
"amount": tripayRes.Data.Amount,
"expired_time": tripayRes.Data.ExpiredTime,
})
}
// ==================== SUPER ADMIN ENDPOINTS ====================
// AdminGetPendingTopups melihat semua pengajuan topup yang menunggu (kecuali QRIS)
func AdminGetPendingTopups(c *gin.Context) {
var topups []models.TopupRequest
if err := config.DB.Preload("Company").Where("status = ? AND proof_url != ?", "pending", "QRIS TRIPAY").Order("created_at asc").Find(&topups).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mengambil data top-up"})
return
}
c.JSON(http.StatusOK, topups)
}
// AdminApproveTopup menyetujui pengajuan topup dan menambah saldo dompet
func AdminApproveTopup(c *gin.Context) {
topupID := c.Param("id")
tx := config.DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
var topup models.TopupRequest
if err := tx.First(&topup, topupID).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusNotFound, gin.H{"detail": "Request Top-up tidak ditemukan"})
return
}
if topup.Status != "pending" {
tx.Rollback()
c.JSON(http.StatusBadRequest, gin.H{"detail": "Status top-up sudah diproses sebelumnya"})
return
}
topup.Status = "approved"
if err := tx.Save(&topup).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal update status topup"})
return
}
// Tambahkan saldo ke perusahaan
var company models.Company
if err := tx.First(&company, topup.CompanyID).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusNotFound, gin.H{"detail": "Company tidak ditemukan"})
return
}
company.WalletBalance += topup.Amount
if err := tx.Save(&company).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal update saldo perusahaan"})
return
}
// Catat di log transaksi dompet
walletTx := models.WalletTransaction{
CompanyID: company.ID,
Amount: topup.Amount,
Type: "topup_manual",
RefID: fmt.Sprintf("TOPUP-%d", topup.ID),
Desc: "Top-up Saldo Manual (Disetujui Admin)",
}
if err := tx.Create(&walletTx).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mencatat log dompet"})
return
}
if err := tx.Commit().Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menyimpan perubahan"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Top-up berhasil disetujui", "new_balance": company.WalletBalance})
}
// AdminRejectTopup menolak pengajuan topup
func AdminRejectTopup(c *gin.Context) {
topupID := c.Param("id")
var topup models.TopupRequest
if err := config.DB.First(&topup, topupID).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"detail": "Request Top-up tidak ditemukan"})
return
}
if topup.Status != "pending" {
c.JSON(http.StatusBadRequest, gin.H{"detail": "Status top-up sudah diproses sebelumnya"})
return
}
topup.Status = "rejected"
if err := config.DB.Save(&topup).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menolak topup"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Top-up berhasil ditolak"})
}
type AdminAdjustWalletRequest struct {
Amount float64 `json:"amount" binding:"required"`
}
// AdminAdjustWallet (Untuk keperluan testing) menyesuaikan saldo tanpa topup
func AdminAdjustWallet(c *gin.Context) {
companyID := c.Param("id")
var req AdminAdjustWalletRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
return
}
tx := config.DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
var company models.Company
if err := tx.First(&company, companyID).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusNotFound, gin.H{"detail": "Company tidak ditemukan"})
return
}
company.WalletBalance += req.Amount
if err := tx.Save(&company).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal update saldo perusahaan"})
return
}
walletTx := models.WalletTransaction{
CompanyID: company.ID,
Amount: req.Amount,
Type: "admin_adjustment",
RefID: "ADMIN",
Desc: "Penyesuaian saldo manual oleh admin",
}
if err := tx.Create(&walletTx).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mencatat log dompet"})
return
}
if err := tx.Commit().Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menyimpan perubahan"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Saldo berhasil disesuaikan", "new_balance": company.WalletBalance})
}