<?php
namespace App\Controller\Api;
use App\Entity\WEB3\NFT;
use App\Entity\User\User;
use App\Entity\WEB3\TX;
use App\Entity\WEB3\Wallet;
use App\Service\NFT\QRCodeGenerator;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
class ApiWalletController extends AbstractController
{
private Security $security;
private EntityManagerInterface $em;
private QRCodeGenerator $qr;
public function __construct(Security $security, EntityManagerInterface $em, QRCodeGenerator $qr)
{
$this->security = $security;
$this->em = $em;
$this->qr = $qr;
}
#[Route('/web3api/wallet/history', name: 'web3api_wallet_history', methods: ['GET'])]
public function walletHistory(Request $request): JsonResponse
{
// 1. Get the currently logged-in user
$user = $this->security->getUser();
if (!$user) {
return new JsonResponse(['error' => 'User is not logged in'], 403);
}
// 2. Get pagination, sorting, and search parameters
$page = $request->query->getInt('page', 1);
$limit = $request->query->getInt('limit', 20);
$sortField = $request->query->get('sortField', 'id');
$sortOrder = $request->query->get('sortOrder', 'desc');
$search = $request->query->get('search', null);
// 3. Build query for TXs for this user
$qb = $this->em->createQueryBuilder();
$qb->select('tx')
->from(TX::class, 'tx')
->where('tx.userId = :userId')
->setParameter('userId', $user->getId())
->orderBy('tx.' . $sortField, $sortOrder)
->setFirstResult(($page - 1) * $limit)
->setMaxResults($limit);
if ($search) {
$qb->andWhere(
$qb->expr()->orX(
$qb->expr()->like('tx.txHash', ':search'),
$qb->expr()->like('tx.action', ':search')
)
)->setParameter('search', '%' . $search . '%');
}
$txs = $qb->getQuery()->getResult();
$countQb = clone $qb;
$countQb->select('COUNT(tx.id)');
$countQb->setFirstResult(null)->setMaxResults(null);
$total = (int) $countQb->getQuery()->getSingleScalarResult();
$txArray = [];
foreach ($txs as $tx) {
$txArray[] = [
'id' => $tx->getId(),
'action' => $tx->getAction(),
'txHash' => $tx->getTxHash(),
'txStatus' => $tx->getTxStatus(),
'createdAt' => $tx->getCreatedAt()?->format('Y-m-d H:i:s'),
'error' => $tx->getError(),
];
}
return $this->json([
'data' => $txArray,
'meta' => [
'total' => $total,
'page' => $page,
'limit' => $limit,
'pages' => ceil($total / $limit),
],
]);
}
#[Route('/web3api/wallet/nft', name: 'web3api_wallet_nft', methods: ['GET'])]
public function walletNFT(): JsonResponse
{
// 1) Auth check (your pattern)
$user = $this->security->getUser();
if (!$user) {
return new JsonResponse(['error' => 'User is not logged in'], 403);
}
$nfts = $this->em->getRepository(NFT::class)->findBy(['user' => $user]);
if (empty($nfts)) {
return new JsonResponse(['message' => 'No NFTs found for this user'], 404);
}
$nftData = [];
foreach ($nfts as $nft) {
$collection = $nft->getCollection();
$brand = $collection?->getBrand();
$brandUser = $brand?->getUser();
$token = $collection?->getCashbackToken();
$nftData[] = [
'collection' => $collection ? [
'collectionId' => $collection->getId(),
'name' => $collection->getName(),
'baseUri' => $collection->getIpfsBaseUri(),
'contractAddress' => $collection->getContractAddress(),
] : null,
'brand' => $brand ? [
'name' => $brand->getName(),
'logo' => $brand->getLogo(),
'user' => $brandUser?->getUsername(),
'verified' => method_exists($brand, 'isVerified') ? $brand->isVerified() : null,
] : null,
'cashbackToken' => $token ? [
'name' => $token->getName(),
'symbol' => $token->getSymbol(),
'contractAddress' => $token->getContractAddress(),
'decimals' => $token->getDecimals(),
'logo' => $token->getLogo(),
] : null,
'tokenId' => $nft->getTokenId(),
'id' => $nft->getId(),
'ipfs' => $nft->getIpfs(),
'isClaimed' => $nft->isIsClaimed(),
'claimStatus' => $nft->isClaimStatus(),
'claimedAt' => $nft->getClaimedAt()?->format(\DateTimeInterface::ATOM),
'claimTx' => $nft->getClaimTx(),
'priceUSD' => $nft->getPrice(),
'tokens' => $nft->getTokens(),
'type' => $nft->getType() ? ucfirst(strtolower($nft->getType())) : null,
];
}
return new JsonResponse(['nfts' => $nftData]);
}
/**
* GET /web3api/wallet/addressqr
*
* Authenticated. If 'address' omitted, uses the user's first wallet address.
*
* Query:
* - address?: string
* - theme?: light|dark (default light)
* - size?: int [128..2048] (default 512)
* - margin?: int [0..64] (default 8)
* - logo?: 0|1 (default 1)
* - ecl?: high|medium|quartile|low (default high)
*
* Returns: image/png
*/
#[Route('/web3api/wallet/addressqr', name: 'addressqr', methods: ['GET'])]
public function addressQr(Request $request): Response
{
// 1) Auth check
$user = $this->security->getUser();
if (!$user) {
return $this->json(['success' => false, 'error' => 'User is not logged in'], 403);
}
// 2) Re-attach user via injected EM (no getDoctrine in Symfony 6)
$managedUser = $this->em->getReference(User::class, $user->getId());
// 3) Query wallets (don’t rely on collection; avoids detached issues)
$walletRepo = $this->em->getRepository(Wallet::class);
$wallets = $walletRepo->findBy(['user' => $managedUser], ['id' => 'ASC'], 1);
if (!$wallets || count($wallets) === 0) {
return $this->json(['success' => false, 'error' => 'User wallet not found'], 404);
}
$userWallet = $wallets[0];
$userAddress = $userWallet->getWalletAddress();
if (!$userAddress) {
return $this->json(['success' => false, 'error' => 'Wallet address not set'], 404);
}
$address = trim((string)$request->query->get('address', ''));
if ($address === '')
{
}
else
{
$userAddress = $address;
}
// --- QR options ---
$theme = strtolower((string)$request->query->get('theme', 'light'));
$logoOn = (int)$request->query->get('logo', 1) === 1;
$size = max(128, min(2048, (int)$request->query->get('size', 512)));
$margin = max(0, min(64, (int)$request->query->get('margin', 8)));
$ecl = (string)$request->query->get('ecl', 'high');
$projectDir = $this->getParameter('kernel.project_dir');
$logoPath = $projectDir . '/public_html/qr-logo.png';
$logoPathDark = $projectDir . '/public_html/qr-logo-dark.png';
if ($theme === 'dark') {
$fg = [255, 255, 255];
$bg = [18, 18, 18];
$logo = $logoOn ? (is_file($logoPathDark) ? $logoPathDark : (is_file($logoPath) ? $logoPath : null)) : null;
} else {
$fg = [0, 0, 0];
$bg = [255, 255, 255];
$logo = $logoOn ? (is_file($logoPath) ? $logoPath : (is_file($logoPathDark) ? $logoPathDark : null)) : null;
}
// Build QR with service (use $userAddress, not $address)
$png = $this->qr->generateToString(
data: $userAddress,
logoPath: $logo,
size: $size,
margin: $margin,
errorCorrection: $ecl,
foregroundRgb: $fg,
backgroundRgb: $bg
);
return new Response($png, 200, [
'Content-Type' => 'image/png',
'Cache-Control' => 'private, max-age=60',
]);
}
/**
* GET /web3api/wallet/addressqr
*
* Authenticated. If 'address' omitted, uses the user's first wallet address.
*
* Query:
* - address?: string
* - theme?: light|dark (default light)
* - size?: int [128..2048] (default 512)
* - margin?: int [0..64] (default 8)
* - logo?: 0|1 (default 1)
* - ecl?: high|medium|quartile|low (default high)
*
* Returns: image/png
*/
#[Route('/papi/wallet/addressqr', name: 'addressqr', methods: ['GET'])]
public function publicAddressQr(Request $request): Response
{
$address = trim((string)$request->query->get('address', ''));
if ($address === '')
{
return $this->json(['success' => false, 'error' => 'Wallet address not set'], 404);
}
else
{
$userAddress = $address;
}
// --- QR options ---
$theme = strtolower((string)$request->query->get('theme', 'light'));
$logoOn = (int)$request->query->get('logo', 1) === 1;
$size = max(128, min(2048, (int)$request->query->get('size', 512)));
$margin = max(0, min(64, (int)$request->query->get('margin', 8)));
$ecl = (string)$request->query->get('ecl', 'high');
$projectDir = $this->getParameter('kernel.project_dir');
$logoPath = $projectDir . '/public_html/qr-logo.png';
$logoPathDark = $projectDir . '/public_html/qr-logo-dark.png';
if ($theme === 'dark') {
$fg = [255, 255, 255];
$bg = [18, 18, 18];
$logo = $logoOn ? (is_file($logoPathDark) ? $logoPathDark : (is_file($logoPath) ? $logoPath : null)) : null;
} else {
$fg = [0, 0, 0];
$bg = [255, 255, 255];
$logo = $logoOn ? (is_file($logoPath) ? $logoPath : (is_file($logoPathDark) ? $logoPathDark : null)) : null;
}
// Build QR with service (use $userAddress, not $address)
$png = $this->qr->generateToString(
data: $userAddress,
logoPath: $logo,
size: $size,
margin: $margin,
errorCorrection: $ecl,
foregroundRgb: $fg,
backgroundRgb: $bg
);
return new Response($png, 200, [
'Content-Type' => 'image/png',
'Cache-Control' => 'private, max-age=60',
]);
}
}