'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'], ], ], ]; } }