Files
kupshop/bundles/KupShop/PayPalCheckoutBundle/Controller/PayPalCheckoutController.php
2025-08-02 16:30:27 +02:00

194 lines
7.4 KiB
PHP

<?php
namespace KupShop\PayPalCheckoutBundle\Controller;
use KupShop\KupShopBundle\Context\CountryContext;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Util\Logging\SentryLogger;
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Core\ProductionEnvironment;
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
use PayPalCheckoutSdk\Orders\OrdersGetRequest;
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;
/**
* !!!
* composer require paypal/paypal-checkout-sdk.
*/
class PayPalCheckoutController extends AbstractController
{
/** @var CurrencyContext */
protected $currencyContext;
/** @var CountryContext */
protected $countryContext;
/** @var SentryLogger */
protected $logger;
/** @var \PayPalCheckout */
protected $payPalCheckout;
public function __construct(
CurrencyContext $currencyContext,
CountryContext $countryContext,
SentryLogger $logger,
) {
$this->currencyContext = $currencyContext;
$this->countryContext = $countryContext;
$this->logger = $logger;
\Payment::includeClass('PayPalCheckout');
$this->payPalCheckout = new \PayPalCheckout();
$this->payPalCheckout->loadConfig();
}
protected function createClient(): PayPalHttpClient
{
$config = $this->payPalCheckout->config;
if (($config['mode'] ?? 'sandbox') === 'sandbox') {
$environment = new SandboxEnvironment($config['clientID'], $config['secret']);
} else {
$environment = new ProductionEnvironment($config['clientID'], $config['secret']);
}
return new PayPalHttpClient($environment);
}
/**
* @Route("/paypal-checkout/create-paypal-order/")
*
* @return Response
*/
public function createPayPalOrder(Request $request, \Cart $cart)
{
$cart->load();
$deliveryTypeID = $this->payPalCheckout->config['delivery_type_id'];
$cart->setTransport($deliveryTypeID);
// set street, email and country so cart does not break in case paypal fails
$cart->updateAddresses(
['street' => 'street', 'email' => 'email@email.com'],
['country' => $this->countryContext->getActiveId()]
);
$step = 'delivery';
$cart->findNextStep($step);
$cart->actualStep = $step;
$cart->save();
$cart->createFromDB(); // TODO: accept also hidden delivery type
try {
$inStoreError = $cart->checkCartItems();
} catch (\Exception $e) {
$inStoreError = $e->getMessage();
}
if (!empty($inStoreError)) {
$response = ['error' => $inStoreError];
return new JsonResponse($response);
}
$price = number_format($cart->totalPricePay->asFloat(), 2, '.', '');
$data = [
'intent' => 'CAPTURE',
'purchase_units' => [
[
'amount' => [
'currency_code' => $this->currencyContext->getActiveId(),
'value' => $price,
],
],
],
];
$request = new OrdersCreateRequest();
$request->prefer('return=representation');
$request->body = $data;
$response = $this->createClient()->execute($request);
return new JsonResponse($response);
}
/**
* @Route("/paypal-checkout/capture-paypal-order/{paypalOrderID}/")
*
* @return Response
*/
public function capturePayPalOrder(string $paypalOrderID, Request $request, \Cart $cart)
{
$client = $this->createClient();
$cart->load();
$cart->createFromDB();
$orderGetResponse = $client->execute(new OrdersGetRequest($paypalOrderID));
if (($orderGetResponse->result->status ?? false) === 'APPROVED') {
$address = $orderGetResponse->result->purchase_units[0]->shipping->address;
$tmpLine1 = explode(' ', $address->address_line_1);
$house = count($tmpLine1) > 1 ? array_pop($tmpLine1) : '';
$street = implode(' ', $tmpLine1);
$invoice = [
'email' => $orderGetResponse->result->payer->email_address,
'gender' => 'male',
'name' => $orderGetResponse->result->payer->name->given_name,
'surname' => $orderGetResponse->result->payer->name->surname,
'phone' => $orderGetResponse->result->payer->phone->phone_number ?? '',
'zip' => $address->postal_code,
'city' => $address->admin_area_2,
'street' => $street,
'house' => $house,
'country' => $address->country_code,
];
$invoice['phone'] = empty($invoice['phone']) ? '123456789' : $invoice['phone'];
$delivery = $invoice;
$delivery['street'] = $address->address_line_1;
unset($delivery['email'], $delivery['gender']);
$cart->updateAddresses($invoice, $delivery);
$order = $cart->submitOrder();
if (!$order instanceof \Order) {
$this->logger->captureMessage('PayPal Checkout: Failed to create kupshop order for PayPal order '.$paypalOrderID.': submitOrder() result '.json_encode($order));
addUserMessage('Failed to create order', 'danger');
redirection(createScriptURL(['s' => 'cart']));
}
// create kupshop order payment
$price = \Decimal::create($orderGetResponse->result->purchase_units[0]->amount->value);
$this->payPalCheckout->setOrder($order);
$this->payPalCheckout->createPayment($orderGetResponse->result->id, $price, [
'PayPalCheckoutController' => true,
'result' => $orderGetResponse,
]);
// capture paypal order
$captureResponse = $client->execute(new OrdersCaptureRequest($paypalOrderID));
if (($captureResponse->result->status ?? false) === 'COMPLETED') {
// capture succeeded
if (!$this->payPalCheckout->setStatus(\Payment::STATUS_FINISHED, $orderGetResponse->result->id)) {
$this->logger->captureMessage('setStatus failed!');
throw new \Exception('Set status failed');
}
// redirect to order detail
$this->payPalCheckout->success(translate('paymentSuccess', 'payment'));
}
// payment storno
if (!$this->payPalCheckout->setStatus(\Payment::STATUS_STORNO, $orderGetResponse->result->id)) {
$this->logger->captureMessage('setStatus failed!');
throw new \Exception('Set status failed');
}
// redirect to order detail
$this->payPalCheckout->error(translate('payment_storno', 'payment'));
}
$this->logger->captureMessage('PayPal Checkout: PayPal order '.$paypalOrderID.' not APPROVED!');
addUserMessage('Failed to create order', 'danger');
redirection(createScriptURL(['s' => 'cart']));
}
}