├── src ├── IOException.php ├── InputException.php ├── ArgumentException.php ├── LogicException.php ├── RuntimeException.php ├── CartItem.php ├── Order.php └── Client.php ├── .travis.yml ├── tests ├── bootstrap.php └── ClientTest.php ├── phpunit.xml.dist ├── composer.json └── Readme.md /src/IOException.php: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | ./tests/ 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "soukicz/zbozicz", 3 | "description": "Zboží.cz conversion tracking", 4 | "minimum-stability": "stable", 5 | "license": "MIT", 6 | "keywords": [ 7 | "zbozicz", 8 | "seznam", 9 | "zbozi", 10 | "seznamcz" 11 | ], 12 | "config": { 13 | "sort-packages": true 14 | }, 15 | "authors": [ 16 | { 17 | "name": "Petr Soukup", 18 | "email": "soukup@simplia.cz" 19 | } 20 | ], 21 | "require": { 22 | "php": ">=5.6.0", 23 | "ext-curl": "*", 24 | "ext-json": "*", 25 | "guzzlehttp/psr7": "^1.3|^2.0" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "~9.5", 29 | "phpstan/phpstan": "~0.9|~0.10|~0.11" 30 | }, 31 | "suggest": { 32 | "composer/ca-bundle": "Https certificates bundle" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "Soukicz\\Zbozicz\\": "src/" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "Soukicz\\TestZbozicz\\": "tests/" 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/CartItem.php: -------------------------------------------------------------------------------- 1 | id; 24 | } 25 | 26 | /** 27 | * @param string $id 28 | * @return CartItem 29 | */ 30 | public function setId($id) { 31 | $this->id = $id; 32 | return $this; 33 | } 34 | 35 | /** 36 | * @return string 37 | */ 38 | public function getName() { 39 | return $this->name; 40 | } 41 | 42 | /** 43 | * @param string $name 44 | * @return CartItem 45 | */ 46 | public function setName($name) { 47 | $this->name = $name; 48 | return $this; 49 | } 50 | 51 | /** 52 | * @return float|NULL 53 | */ 54 | public function getUnitPrice() { 55 | return $this->unitPrice; 56 | } 57 | 58 | /** 59 | * @param float $unitPrice 60 | * @return CartItem 61 | */ 62 | public function setUnitPrice($unitPrice) { 63 | $this->unitPrice = $unitPrice; 64 | return $this; 65 | } 66 | 67 | /** 68 | * @return int 69 | */ 70 | public function getQuantity() { 71 | return $this->quantity; 72 | } 73 | 74 | /** 75 | * @param int $quantity 76 | * @return CartItem 77 | */ 78 | public function setQuantity($quantity) { 79 | $this->quantity = $quantity; 80 | return $this; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/soukicz/zbozicz.svg?branch=master)](https://travis-ci.org/soukicz/zbozicz) 2 | 3 | # Pokročilé měření konverzí Zboží.cz 4 | 5 | Vychází z https://github.com/seznam/zbozi-konverze, ale přidává lepší možnost integrace do větších systémů. Namespace, asynchronní odesílání objednávek atd. 6 | 7 | 8 | ## Odeslání objednávky 9 | ```php 10 | setEmail('info@example.org') 21 | ->setDeliveryType('PPL') 22 | ->addCartItem((new CartItem) 23 | ->setId('ABC1') 24 | ->setName('NAZEV PRODUKTU') 25 | ->setUnitPrice(1000) 26 | ->setQuantity(2) 27 | ) 28 | ->addCartItem((new CartItem) 29 | ->setId('ABC2') 30 | ->setName('NAZEV PRODUKTU') 31 | ->setUnitPrice(2000) 32 | ); 33 | 34 | $client->sendOrder($order); 35 | ``` 36 | 37 | ## Paralelní odeslání objednávek 38 | Je možné vytvořit si jen PSR-7 request a data následně odeslat například přes Guzzle. Lze tak jednoduše odesílat objednávky hromadně paralelně. 39 | 40 | ```php 41 | geId()] = $client->createRequest($order); 52 | } 53 | 54 | $httpClient = new \GuzzleHttp\Client(); 55 | $pool = new Pool($httpClient, $requests, [ 56 | 'concurrency' => 5, 57 | 'fulfilled' => function (Response $response, $index) { 58 | echo "Order '$index' accepted\n"; 59 | }, 60 | 'rejected' => function ($reason, $index) { 61 | echo "Order '$index' not accepted: " . $reason . "\n"; 62 | }, 63 | ]); 64 | ``` 65 | -------------------------------------------------------------------------------- /tests/ClientTest.php: -------------------------------------------------------------------------------- 1 | setEmail('info@example.org') 14 | ->setDeliveryType('PPL') 15 | ->setDeliveryPrice(30) 16 | ->setOtherCosts(10) 17 | ->addCartItem((new CartItem()) 18 | ->setId('ABC1') 19 | ->setName('Product ABC') 20 | ->setUnitPrice(1000) 21 | ->setQuantity(2) 22 | ) 23 | ->addCartItem((new CartItem) 24 | ->setId('ABC2') 25 | ->setUnitPrice(2000) 26 | ) 27 | ->addCartItem((new CartItem) 28 | ->setId('ABC3') 29 | ->setUnitPrice(0) 30 | ); 31 | 32 | $request = $client->createRequest($order); 33 | $this->assertEquals('POST', $request->getMethod()); 34 | $data = json_decode($request->getBody(), true); 35 | 36 | $this->assertSame([ 37 | 'PRIVATE_KEY' => '6caf7fe67a300047c72496969f637a9c', 38 | 'sandbox' => true, 39 | 'orderId' => 1234, 40 | 'email' => 'info@example.org', 41 | 'deliveryType' => 'PPL', 42 | 'deliveryPrice' => 30, 43 | 'otherCosts' => 10, 44 | 'cart' => [ 45 | [ 46 | 'itemId' => 'ABC1', 47 | 'productName' => 'Product ABC', 48 | 'unitPrice' => 1000, 49 | 'quantity' => 2, 50 | ], 51 | [ 52 | 'itemId' => 'ABC2', 53 | 'unitPrice' => 2000, 54 | 'quantity' => 1, 55 | ], 56 | [ 57 | 'itemId' => 'ABC3', 58 | 'unitPrice' => 0, 59 | 'quantity' => 1, 60 | ] 61 | ] 62 | ], $data); 63 | $this->assertSame([ 64 | 'Host' => ['sandbox.zbozi.cz'], 65 | 'Content-type' => ['application/json'], 66 | ], $request->getHeaders()); 67 | $this->assertEquals('https://sandbox.zbozi.cz/action/1c342b11e6f1fc2c10242127ea2cacc8/conversion/backend', (string)$request->getUri()); 68 | 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Order.php: -------------------------------------------------------------------------------- 1 | id = $id; 27 | } 28 | 29 | /** 30 | * @return string 31 | */ 32 | public function getId() { 33 | return $this->id; 34 | } 35 | 36 | 37 | /** 38 | * @return string 39 | */ 40 | public function getEmail() { 41 | return $this->email; 42 | } 43 | 44 | /** 45 | * @param string $email 46 | * @return Order 47 | */ 48 | public function setEmail($email) { 49 | $this->email = $email; 50 | return $this; 51 | } 52 | 53 | /** 54 | * @return string 55 | */ 56 | public function getDeliveryType() { 57 | return $this->deliveryType; 58 | } 59 | 60 | /** 61 | * @param string $deliveryType 62 | * @return Order 63 | */ 64 | public function setDeliveryType($deliveryType) { 65 | $this->deliveryType = $deliveryType; 66 | return $this; 67 | } 68 | 69 | /** 70 | * @return string 71 | */ 72 | public function getPaymentType() { 73 | return $this->paymentType; 74 | } 75 | 76 | /** 77 | * @param string $paymentType 78 | * @return Order 79 | */ 80 | public function setPaymentType($paymentType) { 81 | $this->paymentType = $paymentType; 82 | return $this; 83 | } 84 | 85 | /** 86 | * @return float 87 | */ 88 | public function getDeliveryPrice() { 89 | return $this->deliveryPrice; 90 | } 91 | 92 | /** 93 | * @param float $deliveryPrice 94 | * @return Order 95 | */ 96 | public function setDeliveryPrice($deliveryPrice) { 97 | $this->deliveryPrice = $deliveryPrice; 98 | return $this; 99 | } 100 | 101 | /** 102 | * @return float 103 | */ 104 | public function getOtherCosts() { 105 | return $this->otherCosts; 106 | } 107 | 108 | /** 109 | * @param float $otherCosts 110 | * @return Order 111 | */ 112 | public function setOtherCosts($otherCosts) { 113 | $this->otherCosts = $otherCosts; 114 | return $this; 115 | } 116 | 117 | /** 118 | * @return CartItem[] 119 | */ 120 | public function getCartItems() { 121 | return $this->cartItems; 122 | } 123 | 124 | /** 125 | * @param CartItem[] $cartItems 126 | * @return Order 127 | */ 128 | public function setCartItems(array $cartItems) { 129 | $this->cartItems = []; 130 | foreach ($cartItems as $cartItem) { 131 | $this->addCartItem($cartItem); 132 | } 133 | return $this; 134 | } 135 | 136 | public function addCartItem(CartItem $cartItem) { 137 | $this->cartItems[] = $cartItem; 138 | return $this; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | shopId = $shopId; 19 | $this->privateKey = $privateKey; 20 | $this->sandbox = $sandbox; 21 | } 22 | 23 | public function isSandbox() { 24 | return (bool)$this->sandbox; 25 | } 26 | 27 | /** 28 | * @param Order $order 29 | * @return Request 30 | */ 31 | public function createRequest(Order $order) { 32 | $errors = $this->validateOrder($order); 33 | if(!empty($errors)) { 34 | throw new InputException($errors[0]); 35 | } 36 | $data = [ 37 | 'PRIVATE_KEY' => $this->privateKey, 38 | 'sandbox' => $this->sandbox, 39 | 'orderId' => $order->getId(), 40 | 'email' => $order->getEmail(), 41 | ]; 42 | 43 | if($order->getDeliveryType()) { 44 | $data['deliveryType'] = $order->getDeliveryType(); 45 | } 46 | 47 | if($order->getDeliveryPrice() !== null) { 48 | $data['deliveryPrice'] = $order->getDeliveryPrice(); 49 | } 50 | 51 | if($order->getPaymentType()) { 52 | $data['paymentType'] = $order->getPaymentType(); 53 | } 54 | 55 | if($order->getOtherCosts() !== null) { 56 | $data['otherCosts'] = $order->getOtherCosts(); 57 | } 58 | 59 | if(!empty($order->getCartItems())) { 60 | $data['cart'] = []; 61 | foreach ($order->getCartItems() as $cartItem) { 62 | $item = []; 63 | if(!empty($cartItem->getId())) { 64 | $item['itemId'] = $cartItem->getId(); 65 | } 66 | if(!empty($cartItem->getName())) { 67 | $item['productName'] = $cartItem->getName(); 68 | } 69 | if($cartItem->getUnitPrice() !== null) { 70 | $item['unitPrice'] = $cartItem->getUnitPrice(); 71 | } 72 | if(!empty($cartItem->getQuantity())) { 73 | $item['quantity'] = $cartItem->getQuantity(); 74 | } 75 | $data['cart'][] = $item; 76 | } 77 | } 78 | 79 | return new Request( 80 | 'POST', 81 | $this->getUrl(), 82 | ['Content-type' => 'application/json'], 83 | json_encode($data) 84 | ); 85 | } 86 | 87 | public function sendOrder(Order $order) { 88 | $request = $this->createRequest($order); 89 | $ch = curl_init(); 90 | 91 | curl_setopt($ch, CURLOPT_URL, $this->getUrl()); 92 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); 93 | curl_setopt($ch, CURLOPT_POST, true); 94 | $headers = []; 95 | foreach ($request->getHeaders() as $name => $lines) { 96 | $headers[$name] = $request->getHeaderLine($name); 97 | } 98 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 99 | curl_setopt($ch, CURLOPT_HEADER, false); 100 | curl_setopt($ch, CURLOPT_POSTFIELDS, (string)$request->getBody()); 101 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 102 | 103 | if(class_exists('Composer\CaBundle\CaBundle')) { 104 | $caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath(); 105 | if(is_dir($caPathOrFile) || (is_link($caPathOrFile) && is_dir(readlink($caPathOrFile)))) { 106 | curl_setopt($ch, CURLOPT_CAPATH, $caPathOrFile); 107 | } else { 108 | curl_setopt($ch, CURLOPT_CAINFO, $caPathOrFile); 109 | } 110 | } 111 | 112 | $result = curl_exec($ch); 113 | 114 | if($result === false) { 115 | throw new IOException('Unable to establish connection to ZboziKonverze service: curl error (' . curl_errno($ch) . ') - ' . curl_error($ch)); 116 | } 117 | 118 | $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 119 | curl_close($ch); 120 | if($httpCode !== 200) { 121 | $data = json_decode($result, true); 122 | if($data && !empty($data['statusMessage'])) { 123 | throw new IOException('Request was not accepted HTTP ' . $httpCode . ': ' . $data['statusMessage']); 124 | } 125 | throw new IOException('Request was not accepted (HTTP ' . $httpCode . ')'); 126 | } 127 | } 128 | 129 | protected function getUrl() { 130 | $url = 'https://' . ($this->sandbox ? 'sandbox.zbozi.cz' : 'www.zbozi.cz'); 131 | 132 | return $url . '/action/' . $this->shopId . '/conversion/backend'; 133 | } 134 | 135 | /** 136 | * @param Order $order 137 | * @return array 138 | */ 139 | public function validateOrder(Order $order) { 140 | $errors = []; 141 | if(empty($order->getId())) { 142 | $errors[] = 'Missing order code'; 143 | } 144 | return $errors; 145 | } 146 | } 147 | --------------------------------------------------------------------------------