<?php
namespace App\Service;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
class PayPalService
{
private $httpClient;
private $clientId;
private $secret;
private $mode;
private $baseUrl;
public function __construct(
HttpClientInterface $httpClient,
string $paypalClientId,
string $paypalSecret,
string $paypalMode
) {
$this->httpClient = $httpClient;
$this->clientId = $paypalClientId;
$this->secret = $paypalSecret;
$this->mode = strtolower(trim($paypalMode));
// URL de base selon le mode
$this->baseUrl = $this->mode === 'live'
? 'https://api-m.paypal.com'
: 'https://api-m.sandbox.paypal.com';
}
/**
* Obtenir un token d'accès PayPal
*/
public function getAccessToken(): string
{
try {
$response = $this->httpClient->request('POST', $this->baseUrl . '/v1/oauth2/token', [
'auth_basic' => [$this->clientId, $this->secret],
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
],
'body' => [
'grant_type' => 'client_credentials',
],
]);
$statusCode = $response->getStatusCode();
$content = $response->getContent(false);
$data = json_decode($content, true);
if ($statusCode >= 400) {
throw new \RuntimeException('PayPal OAuth error (' . $statusCode . '): ' . $content);
}
if (!is_array($data) || !isset($data['access_token'])) {
throw new \RuntimeException('PayPal OAuth response missing access_token: ' . $content);
}
return (string) $data['access_token'];
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('PayPal OAuth connection error: ' . $e->getMessage(), 0, $e);
}
}
/**
* Créer une commande PayPal
*/
public function createOrder(
float $amount,
string $currency = 'EUR',
string $orderId = null,
?string $returnUrl = null,
?string $cancelUrl = null
): array
{
$accessToken = $this->getAccessToken();
try {
$applicationContext = [
'brand_name' => 'Aromaforest',
'locale' => 'fr-FR',
'landing_page' => 'NO_PREFERENCE',
'shipping_preference' => 'NO_SHIPPING',
'user_action' => 'PAY_NOW',
];
if ($returnUrl) {
$applicationContext['return_url'] = $returnUrl;
}
if ($cancelUrl) {
$applicationContext['cancel_url'] = $cancelUrl;
}
$response = $this->httpClient->request('POST', $this->baseUrl . '/v2/checkout/orders', [
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $accessToken,
],
'json' => [
'intent' => 'CAPTURE',
'purchase_units' => [[
'reference_id' => $orderId ?? uniqid('ORDER_'),
'amount' => [
'currency_code' => $currency,
'value' => number_format($amount, 2, '.', ''),
],
]],
'application_context' => $applicationContext,
],
]);
$statusCode = $response->getStatusCode();
$content = $response->getContent(false);
$data = json_decode($content, true);
if ($statusCode >= 400) {
throw new \RuntimeException('PayPal createOrder error (' . $statusCode . '): ' . $content);
}
if (!is_array($data) || !isset($data['id'])) {
throw new \RuntimeException('PayPal createOrder response missing id: ' . $content);
}
return $data;
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('PayPal createOrder connection error: ' . $e->getMessage(), 0, $e);
}
}
/**
* Capturer le paiement d'une commande PayPal
*/
public function captureOrder(string $orderId): array
{
$accessToken = $this->getAccessToken();
try {
$response = $this->httpClient->request(
'POST',
$this->baseUrl . '/v2/checkout/orders/' . $orderId . '/capture',
[
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $accessToken,
],
]
);
$statusCode = $response->getStatusCode();
$content = $response->getContent(false);
$data = json_decode($content, true);
if ($statusCode >= 400) {
throw new \RuntimeException('PayPal captureOrder error (' . $statusCode . '): ' . $content);
}
if (!is_array($data)) {
throw new \RuntimeException('PayPal captureOrder invalid response: ' . $content);
}
return $data;
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('PayPal captureOrder connection error: ' . $e->getMessage(), 0, $e);
}
}
/**
* Obtenir les détails d'une commande PayPal
*/
public function getOrderDetails(string $orderId): array
{
$accessToken = $this->getAccessToken();
try {
$response = $this->httpClient->request(
'GET',
$this->baseUrl . '/v2/checkout/orders/' . $orderId,
[
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $accessToken,
],
]
);
$statusCode = $response->getStatusCode();
$content = $response->getContent(false);
$data = json_decode($content, true);
if ($statusCode >= 400) {
throw new \RuntimeException('PayPal getOrderDetails error (' . $statusCode . '): ' . $content);
}
if (!is_array($data)) {
throw new \RuntimeException('PayPal getOrderDetails invalid response: ' . $content);
}
return $data;
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('PayPal getOrderDetails connection error: ' . $e->getMessage(), 0, $e);
}
}
/**
* Obtenir le Client ID pour le frontend
*/
public function getClientId(): string
{
return $this->clientId;
}
/**
* Obtenir le mode (sandbox ou live)
*/
public function getMode(): string
{
return $this->mode;
}
}