feat(reviews): add review service and Stripe payment gateway
This commit is contained in:
1 parent
4c8d38e972
commit
6978af0b8d
2 files changed
+130
No files matched your search
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user