393 lines
16 KiB
PHP
393 lines
16 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
|
use Symfony\Component\Routing\Router;
|
|
|
|
require_once 'class.PayPal.php';
|
|
|
|
/**
|
|
* Dependencies: `composer require paypal/rest-api-sdk-php=^1.14`
|
|
* Example config: $cfg['Modules']['payments']['PayPal'] = [
|
|
* 'clientID' => 'client id',
|
|
* 'secret' => 'client secret',
|
|
* webProfileID - create temporary (3 hours): symfony kupshop:paypal-create-web-profile
|
|
* or permanent profile: symfony kupshop:paypal-create-web-profile --permanent
|
|
* optional image path as an argument: symfony kupshop:paypal-create-web-profile --permanent templates/images/logo.png
|
|
* 'webProfileID' => 'web profile id',
|
|
* 'mode' => 'sandbox' OR 'live',
|
|
* 'enableLog' => false,
|
|
* ].
|
|
*/
|
|
class PayPalPlus extends PayPal
|
|
{
|
|
public static $name = 'PayPal Plus platební brána';
|
|
|
|
protected $templateCart = 'payment.PayPalPlus.cart.tpl';
|
|
protected $templateOrderView = 'payment.PayPalPlus.orderView.tpl';
|
|
protected $templateInit = 'payment.PayPalPlus.init.tpl';
|
|
|
|
public $class = 'PayPalPlus';
|
|
|
|
/** Vytáhne existující, nebo vytvoří novou platbu */
|
|
public function getPayment()
|
|
{
|
|
$cart = ServiceContainer::getService(\KupShop\OrderingBundle\Cart::class);
|
|
// Preferuj ID z objednavky
|
|
if (isset($this->order) && ($this->order->getData('paypalplus')['id'] ?? false)) {
|
|
$payment = $this->getPayPalPayment($this->getApiContext(), $this->order->getData('paypalplus')['id']);
|
|
} elseif ($cart->getData('paypalplus') && $cart->getData('paypalplus')['id']) {
|
|
$payment = $this->getPayPalPayment($this->getApiContext(), $cart->getData('paypalplus')['id']);
|
|
} else {
|
|
$payment = $this->createPayPalPayment();
|
|
$cart->setData('paypalplus', ['id' => $payment->getId(), 'token' => $payment->getToken()]);
|
|
}
|
|
|
|
return $payment;
|
|
}
|
|
|
|
public function getPaymentUrl()
|
|
{
|
|
if (empty($this->order)) {
|
|
throw new \KupShop\OrderingBundle\Exception\PaymentException(translate('sameDevicePayment', 'payment'));
|
|
}
|
|
|
|
return path('kupshop_content_orders_order',
|
|
['id' => $this->order->id, 'cf' => $this->order->getSecurityCode(), 'status' => 1, 'immediate_pay' => 1]);
|
|
}
|
|
|
|
/** Pošle patch request pokud není platba aktualizovana - nema security code */
|
|
public function makeSurePaymentIsUpdated()
|
|
{
|
|
$payment = $this->getPayment();
|
|
if (empty($payment->getTransactions()[0]->getCustom())) {
|
|
return $this->updatePayment();
|
|
}
|
|
|
|
return $payment;
|
|
}
|
|
|
|
/** Vytvoří payment s dummy daty - protože se musí tvořit už v košíku */
|
|
protected function createPayPalPayment($useOriginalCurrency = true, $totalPrice = 0): PayPal\Api\Payment
|
|
{
|
|
$apiContext = $this->getApiContext();
|
|
$dbcfg = Settings::getDefault();
|
|
if ($this->order && $remainingPayment = roundPrice($this->order->getRemainingPayment())->asFloat() <= 0) {
|
|
$this->success(translate('paymentSuccess', 'payment'));
|
|
}
|
|
|
|
$payer = new \PayPal\Api\Payer();
|
|
$payer->setPaymentMethod('paypal');
|
|
|
|
$amount = new \PayPal\Api\Amount();
|
|
$amount->setCurrency('EUR')->setTotal('2.00');
|
|
|
|
$item1 = new \PayPal\Api\Item();
|
|
$item1->setName('Payment for order '.($this->order ? $this->order->order_no : '').' on '.$dbcfg->shop_firm_name)
|
|
->setCurrency('EUR')
|
|
->setQuantity(1)
|
|
->setPrice($totalPrice);
|
|
|
|
$itemList = new \PayPal\Api\ItemList();
|
|
$itemList->setItems([$item1]);
|
|
|
|
$transaction = new \PayPal\Api\Transaction();
|
|
$transaction->setAmount($amount)
|
|
->setDescription('Payment for order')
|
|
->setInvoiceNumber('123456')
|
|
->setItemList($itemList);
|
|
|
|
$redirectUrls = new \PayPal\Api\RedirectUrls();
|
|
$redirectUrls->setReturnUrl(
|
|
path('kupshop_ordering_payment_legacypayment',
|
|
array_merge([
|
|
'step' => 2,
|
|
'class' => $this->class,
|
|
], []),
|
|
Router::ABSOLUTE_URL)
|
|
)->setCancelUrl(
|
|
path('kupshop_ordering_payment_legacypayment',
|
|
array_merge([
|
|
'step' => 4,
|
|
'class' => $this->class,
|
|
], []),
|
|
Router::ABSOLUTE_URL)
|
|
);
|
|
|
|
$payment = new \PayPal\Api\Payment();
|
|
$payment->setIntent('sale')
|
|
->setPayer($payer)
|
|
->setTransactions([$transaction])
|
|
->setRedirectUrls($redirectUrls);
|
|
|
|
try {
|
|
$payment->create($apiContext);
|
|
} catch (PayPal\Exception\PayPalConnectionException $e) {
|
|
$data = $e->getData();
|
|
if ($data) {
|
|
$data = json_decode($data, true);
|
|
}
|
|
|
|
if (($data['name'] ?? false) === 'VALIDATION_ERROR') {
|
|
$this->error(translate('PayPalConnectionException', 'paypal'));
|
|
ServiceContainer::getService('logger')->error('PayPal exception: '.print_r($e->getData(), true),
|
|
['file' => __FILE__, 'line' => __LINE__]);
|
|
} else {
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
return $payment;
|
|
}
|
|
|
|
/** Funkce pro patch paymentu reálnými daty */
|
|
public function updatePayment(): PayPal\Api\Payment
|
|
{
|
|
$payment = $this->getPayment();
|
|
|
|
if ($useOriginalCurrency = in_array($this->order->currency, $this->allowedCurrencies)) {
|
|
$totalPrice = roundPrice($this->order->getRemainingPayment())->asFloat();
|
|
} else {
|
|
$totalPrice = $this->order->convertPriceToEUR(
|
|
roundPrice($this->order->getRemainingPayment())->asFloat()
|
|
);
|
|
}
|
|
|
|
$patchAmount = new \PayPal\Api\Patch();
|
|
$patchAddress = new \PayPal\Api\Patch();
|
|
$patchPayer = new \PayPal\Api\Patch();
|
|
$patchItems = new \PayPal\Api\Patch();
|
|
$patchCustom = new \PayPal\Api\Patch();
|
|
$patchInvoiceNo = new \PayPal\Api\Patch();
|
|
|
|
$amount = new \PayPal\Api\Amount();
|
|
$amount->setCurrency($useOriginalCurrency ? ($this->order ? $this->order->currency : '') : 'EUR')
|
|
->setTotal($totalPrice);
|
|
|
|
$patchAmount->setOp('replace')
|
|
->setPath('/transactions/0/amount')
|
|
->setValue($amount);
|
|
|
|
$payerDeliveryAddress = new \PayPal\Api\ShippingAddress();
|
|
$payerDeliveryAddress->setState($this->order->delivery_state ?? '')
|
|
->setCountryCode($this->order->delivery_country ?? '')
|
|
->setCity($this->order->delivery_city ?? '')
|
|
->setLine1($this->order->delivery_street ?? '')
|
|
->setLine2($this->order->delivery_custom_address ?? '')
|
|
->setPostalCode($this->order->delivery_zip ?? '')
|
|
->setRecipientName($this->order->delivery_name.' '.$this->order->delivery_surname);
|
|
|
|
$patchAddress->setOp('add')
|
|
->setPath('/transactions/0/item_list/shipping_address')
|
|
->setValue($payerDeliveryAddress);
|
|
|
|
$payerInfoAddress = new \PayPal\Api\Address();
|
|
$payerInfoAddress->setState($this->order->invoice_state ?? '')
|
|
->setCountryCode($this->order->invoice_country ?? '')
|
|
->setCity($this->order->invoice_city ?? '')
|
|
->setLine1($this->order->invoice_street ?? '')
|
|
->setLine2($this->order->invoice_custom_address ?? '')
|
|
->setPostalCode($this->order->invoice_zip ?? '');
|
|
|
|
$payerInfo = new \PayPal\Api\PayerInfo();
|
|
$payerInfo->setEmail($this->order ? $this->order->getUserEmail() : '')
|
|
->setBillingAddress($payerInfoAddress)
|
|
->setFirstName($this->order->invoice_name ?? '')
|
|
->setLastName($this->order->invoice_surname ?? '');
|
|
|
|
$patchPayer->setOp('add')
|
|
->setPath('/payer/payer_info')
|
|
->setValue($payerInfo);
|
|
|
|
$items = [];
|
|
$itemList = new \PayPal\Api\ItemList();
|
|
|
|
foreach ($this->order->fetchItems() as $orderItem) {
|
|
$item = new \PayPal\Api\Item();
|
|
$item->setName($orderItem['descr'])
|
|
->setCurrency($useOriginalCurrency ? $this->order->currency ?? '' : 'EUR')
|
|
->setQuantity($orderItem['pieces'])
|
|
->setPrice($orderItem['piece_price']['value_with_vat']->asFloat());
|
|
$items[] = $item;
|
|
}
|
|
$itemList->setItems($items);
|
|
|
|
$patchItems->setOp('replace')
|
|
->setPath('/transactions/0/item_list')
|
|
->setValue($itemList);
|
|
|
|
$patchCustom->setOp('add')
|
|
->setPath('/transactions/0/custom')
|
|
->setValue($this->order->getSecurityCode());
|
|
|
|
$patchInvoiceNo->setOp('add')
|
|
->setPath('/transactions/0/invoice_number')
|
|
->setValue($this->order->order_no);
|
|
|
|
$patchRequest = new \PayPal\Api\PatchRequest();
|
|
$patchRequest->setPatches([
|
|
$patchAddress,
|
|
$patchAmount,
|
|
$patchPayer,
|
|
$patchItems,
|
|
$patchCustom,
|
|
$patchInvoiceNo,
|
|
]);
|
|
|
|
try {
|
|
$payment->update($patchRequest, $this->getApiContext());
|
|
} catch (Exception $ex) {
|
|
ServiceContainer::getService('logger')->error('PayPal exception: '.$ex->getMessage(), ['file' => __FILE__, 'line' => __LINE__]);
|
|
|
|
if ($ex instanceof \PayPal\Exception\PayPalConnectionException) {
|
|
ServiceContainer::getService('logger')->error('PayPal exception: '.print_r($ex->getData(), true),
|
|
['file' => __FILE__, 'line' => __LINE__]);
|
|
}
|
|
|
|
$this->error('storno');
|
|
}
|
|
|
|
return $payment;
|
|
}
|
|
|
|
public function processStep_1()
|
|
{
|
|
// $this->error('');
|
|
}
|
|
|
|
public function processStep_2()
|
|
{
|
|
ServiceContainer::getService('logger')->error('PayPal response: '.print_r([$_GET, $_POST, $_SERVER], true),
|
|
['file' => __FILE__, 'line' => __LINE__]);
|
|
$payPalPaymentID = getVal('paymentId', null, false);
|
|
$apiContext = $this->getApiContext();
|
|
$payment = $this->getPayPalPayment($apiContext, $payPalPaymentID);
|
|
$error = false;
|
|
if ($payment) {
|
|
$this->orderId = $this->selectSQL('orders',
|
|
['order_no' => $payment->getTransactions()[0]->getInvoiceNumber()],
|
|
['id'])->fetchOne();
|
|
$this->order = new Order();
|
|
$this->order->createFromDB($this->orderId);
|
|
if ($this->order->getSecurityCode() == $payment->getTransactions()[0]->getCustom()) {
|
|
// Při platbě paypal umožňuje změnit adresu doručení....
|
|
|
|
// $address = $payment->getTransactions()[0]->getItemList()->getShippingAddress()->toArray();
|
|
// $address = $payment->toArray();
|
|
// if (isset($address['payer']['payer_info']['first_name']) && isset($address['payer']['payer_info']['last_name'])) {
|
|
// // $this->order->updateSQL('orders', [
|
|
// // 'delivery_name' => $address['payer']['payer_info']['first_name'],
|
|
// // 'delivery_surname' => $address['payer']['payer_info']['last_name'],
|
|
// // 'delivery_street' => $address['payer']['payer_info']['shipping_address']['line1'],
|
|
// // 'delivery_city' => $address['payer']['payer_info']['shipping_address']['city'],
|
|
// // 'delivery_zip' => $address['payer']['payer_info']['shipping_address']['postal_code'],
|
|
// // 'delivery_country' => $address['payer']['payer_info']['shipping_address']['country_code'],
|
|
// // 'delivery_phone' => $address['payer']['payer_info']['phone'] ?? '',
|
|
// // 'delivery_state' => $address['payer']['payer_info']['shipping_address']['state'],
|
|
// // ], ['id' => $this->orderId]);
|
|
//
|
|
// $name = explode(' ', $address['recipient_name']);
|
|
// $firstname = $name[0] ?? '';
|
|
// unset($name[0]);
|
|
// $lastname = implode(' ', $name);
|
|
// $this->order->updateSQL('orders', [
|
|
// 'delivery_name' => $firstname,
|
|
// 'delivery_surname' => $lastname ?? '',
|
|
// 'delivery_street' => $address['line1'],
|
|
// 'delivery_city' => $address['city'],
|
|
// 'delivery_zip' => $address['postal_code'],
|
|
// 'delivery_country' => $address['country_code'],
|
|
// 'delivery_phone' => $address['payer']['payer_info']['phone'] ?? '',
|
|
// 'delivery_state' => $address['payer']['payer_info']['shipping_address']['state'],
|
|
// ], ['id' => $this->orderId]);
|
|
// }
|
|
|
|
$this->createPayment(
|
|
$payment->getId(),
|
|
$payment->getTransactions()[0]->getAmount()->getTotal(),
|
|
['paymentClass' => self::class]
|
|
);
|
|
$this->processPayment($payment);
|
|
|
|
$paymentInstructions = $payment->getPaymentInstruction();
|
|
if ($paymentInstructions) {
|
|
$paypalData = $this->order->getData('paypalplus');
|
|
$paypalData['payment_instruction'] = $paymentInstructions->toArray();
|
|
$this->order->setData('paypalplus', $paypalData);
|
|
}
|
|
|
|
$this->step(3, 'wait', ['paymentId' => $payment->getId()]);
|
|
} else {
|
|
$error = true;
|
|
}
|
|
} else {
|
|
$error = true;
|
|
}
|
|
|
|
if ($error) {
|
|
$this->error(translate('payment_status_check_error', 'payment'));
|
|
}
|
|
}
|
|
|
|
public function processStep_3()
|
|
{
|
|
$apiContext = $this->getApiContext();
|
|
$payPalPaymentID = getVal('paymentId', null, false);
|
|
if (!$payPalPaymentID) {
|
|
$kupshopPayment = $this->getPendingPayment();
|
|
if ($kupshopPayment) {
|
|
// use already created payment
|
|
$payPalPaymentID = $kupshopPayment['decoded_data']->session;
|
|
} else {
|
|
$this->error('storno');
|
|
}
|
|
}
|
|
$this->processPayment($this->getPayPalPayment($apiContext, $payPalPaymentID));
|
|
}
|
|
|
|
public function processStep_4()
|
|
{
|
|
$token = getVal('token', null, false);
|
|
if (!$token) {
|
|
$this->error('storno');
|
|
}
|
|
$payPalId = sqlQueryBuilder()
|
|
->select("id, json_extract(note_admin, '$.paypalplus.id') as paypalID")
|
|
->from('orders')
|
|
->where("json_extract(note_admin, '$.paypalplus.token') = :token")
|
|
->setParameter('token', $token)
|
|
->execute()
|
|
->fetchAssociative();
|
|
|
|
if (!$payPalId) {
|
|
$this->error('storno');
|
|
}
|
|
$this->orderId = $payPalId['id'];
|
|
$this->order = new \Order();
|
|
$this->order->createFromDB($this->orderId);
|
|
$this->error(translate('payment_storno', 'payment'));
|
|
}
|
|
|
|
public static function getSettingsConfiguration(): array
|
|
{
|
|
return [
|
|
'fields' => [
|
|
'clientID' => [
|
|
'title' => 'clientID',
|
|
'type' => 'text',
|
|
],
|
|
'secret' => [
|
|
'title' => 'secret',
|
|
'type' => 'text',
|
|
],
|
|
'mode' => [
|
|
'title' => 'Režim',
|
|
'type' => 'select',
|
|
'options' => ['live' => 'Produkční režim', 'sandbox' => 'Testovaci režim'],
|
|
],
|
|
],
|
|
];
|
|
}
|
|
}
|