Files
HickLab3D_Dashboard/app/Http/Controllers/EtsyController.php
T
hermes d4928789c4
Deploy HickLab3D Dashboard / Deploy to production (push) Failing after 23s
Add HL3D branding, Etsy Live/Draft paging and Projects lifecycle
Replace the old touchmark/PNG identity with the supplied lockup and
monogram, segmented role-aware navigation, Live/Draft listing filters
with generation-based cache invalidation, and admin Projects covering
immutable file revisions, listing live pointers, and exact-decimal
filament/packaging/shipping costing. Additive schema only; no Etsy
publication. Isolated clone tests: 301 tests, 1162 assertions.
2026-11-08 12:50:56 +00:00

119 lines
4.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Http\Controllers;
use App\Models\EtsyConnection;
use App\Services\Etsy;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
use Throwable;
class EtsyController extends Controller
{
/** @var list<string> */
private const UI_STATES = ['active', 'draft'];
private const PER_PAGE = 20;
public function index(Request $request, Etsy $etsy): View
{
abort_unless($request->user()->role === 'admin', 403);
$connection = EtsyConnection::first();
$receipts = [];
$listings = [];
$listingCount = 0;
$receiptsError = null;
$listingsError = null;
$stateError = null;
$pageError = null;
$rawState = $request->query('state', 'active');
$state = is_string($rawState) ? strtolower($rawState) : '';
if (! in_array($state, self::UI_STATES, true)) {
$stateError = 'This is not a supported listing state. Select Live or Drafts.';
$state = 'active';
}
$rawPage = $request->query('page', '1');
if (! is_string($rawPage) || ! ctype_digit($rawPage) || strlen($rawPage) > 5 || (int) $rawPage < 1 || (int) $rawPage > 10000) {
$pageError = 'This is not a valid page number. Use pages 1–10000.';
$page = 1;
} else {
$page = (int) $rawPage;
}
if ($connection && $etsy->configured()) {
try {
$receipts = $etsy->receipts($connection)['results'] ?? [];
} catch (Throwable $exception) {
report($exception);
$receiptsError = 'Order data is temporarily unavailable. Reconnect the shop if this continues.';
}
if (! $stateError && ! $pageError) {
try {
$listingPage = $etsy->listListings($connection, $state, self::PER_PAGE, (max(1, $page) - 1) * self::PER_PAGE);
$listings = $listingPage['results'];
$listingCount = $listingPage['count'];
} catch (Throwable $exception) {
report($exception);
$listingsError = 'Listing data is temporarily unavailable. Reconnect the shop if this continues.';
}
}
}
return view('etsy', [
'connection' => $connection,
'receipts' => $receipts,
'listings' => $listings,
'listingSnapshot' => $listingPage ?? null,
'listingCount' => $listingCount,
'state' => $state,
'page' => $page,
'perPage' => self::PER_PAGE,
'receiptsError' => $receiptsError,
'listingsError' => $listingsError,
'stateError' => $stateError,
'pageError' => $pageError,
'configured' => $etsy->configured(),
]);
}
public function connect(Request $request, Etsy $etsy): RedirectResponse
{
abort_unless($request->user()->role === 'admin', 403);
if (! $etsy->configured()) {
return back()->withErrors(['etsy' => 'Configure the Etsy app key and shared secret first.']);
}
$state = bin2hex(random_bytes(24));
$verifier = rtrim(strtr(base64_encode(random_bytes(48)), '+/', '-_'), '=');
$request->session()->put('etsy_oauth', ['state' => $state, 'verifier' => $verifier, 'started' => time()]);
return redirect()->away($etsy->authorizationUrl($state, $verifier));
}
public function callback(Request $request, Etsy $etsy): RedirectResponse
{
abort_unless($request->user()->role === 'admin', 403);
$pending = $request->session()->pull('etsy_oauth');
if (! is_array($pending) || time() - ($pending['started'] ?? 0) > 600
|| ! is_string($request->query('state'))
|| ! hash_equals($pending['state'] ?? '', $request->query('state'))
|| ! is_string($request->query('code'))) {
return redirect()->route('etsy.index')->withErrors(['etsy' => 'Etsy connection expired or was not verified. Please try again.']);
}
try {
$connection = $etsy->connect($request->query('code'), $pending['verifier']);
return redirect()->route('etsy.index')->with('status', $connection->shop_name.' connected to Etsy.');
} catch (Throwable $exception) {
report($exception);
return redirect()->route('etsy.index')->withErrors(['etsy' => 'Etsy rejected the connection. Check the app credentials and callback URL.']);
}
}
}