feat(reviews): add review service and Stripe payment gateway

This commit is contained in:
dualfroz committed 2026-08-25 09:42:54 +02:00
1 parent 4c8d38e972
commit 6978af0b8d
2 files changed
+130

No files matched your search

+65
View File
@@ -0,0 +1,65 @@
<?php
namespace Bazaar\Services;
use Bazaar\Core\Database;
use Bazaar\Repositories\ReviewRepository;
final class ReviewService
{
public const eligible = 'eligible';
public const notPurchased = 'notPurchased';
public const alreadyReviewed = 'alreadyReviewed';
public function __construct(
private readonly Database $db,
private readonly ReviewRepository $reviews
) {
}
public function hasPurchased(int $userId, int $productId): bool
{
return (int) $this->db->scalar(
"SELECT COUNT(*)
FROM orders o
JOIN orderItems oi ON oi.orderId = o.id
WHERE o.userId = ? AND oi.productId = ? AND o.status IN ('paid', 'fulfilled')",
[$userId, $productId]
) > 0;
}
public function eligibility(int $userId, int $productId): string
{
if (!$this->hasPurchased($userId, $productId)) {
return self::notPurchased;
}
if ($this->reviews->existsForUser($productId, $userId)) {
return self::alreadyReviewed;
}
return self::eligible;
}
public function canReview(int $userId, int $productId): bool
{
return $this->eligibility($userId, $productId) === self::eligible;
}
public function submit(int $userId, int $productId, int $rating, string $title, string $body): int
{
if (!$this->canReview($userId, $productId)) {
throw new \RuntimeException('This account is not eligible to review this product.');
}
$rating = max(1, min(5, $rating));
return $this->reviews->create($productId, $userId, $rating, trim($title), trim($body));
}
public function approve(int $reviewId): void
{
$this->reviews->setStatus($reviewId, 'approved');
}
public function reject(int $reviewId): void
{
$this->reviews->setStatus($reviewId, 'rejected');
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace Bazaar\Services;
use Stripe\StripeClient;
use Stripe\Webhook;
final class StripeGateway
{
private StripeClient $client;
public function __construct(
private readonly string $secretKey,
private readonly string $webhookSecret,
private readonly string $currency
) {
$this->client = new StripeClient($this->secretKey);
}
public function createCheckoutSession(int $orderId, array $lines, string $successUrl, string $cancelUrl): array
{
$lineItems = [];
foreach ($lines as $line) {
$lineItems[] = [
'quantity' => $line['quantity'],
'price_data' => [
'currency' => $this->currency,
'unit_amount' => (int) $line['product']['priceCents'],
'product_data' => [
'name' => $line['product']['name'],
],
],
];
}
$session = $this->client->checkout->sessions->create([
'mode' => 'payment',
'line_items' => $lineItems,
'success_url' => $successUrl,
'cancel_url' => $cancelUrl,
'client_reference_id' => (string) $orderId,
'metadata' => ['orderId' => (string) $orderId],
]);
return ['id' => $session->id, 'url' => $session->url];
}
public function retrieveSession(string $sessionId): array
{
$session = $this->client->checkout->sessions->retrieve($sessionId);
return [
'paymentStatus' => $session->payment_status,
'orderId' => isset($session->metadata->orderId) ? (int) $session->metadata->orderId : null,
];
}
public function parseWebhook(string $payload, string $signature): array
{
$event = Webhook::constructEvent($payload, $signature, $this->webhookSecret);
return [
'type' => $event->type,
'session' => $event->data->object,
];
}
}