diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f21777..e41029a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # CHANGELOG +## 1.1.0 (2014-07-24) +* add Shopping Cart Protocol 3.0 http://xsolla.github.io/en/shopingcart3.html + ## 1.0.4 (2014-06-04) * fix incorrect sign code for Shopping Cart Protocol 2.0 * add `$reasonCode` and `$reasonDescription` optional arguments to `PaymentStorageInterface::cancel` diff --git a/example/ipn_shopping_cart_3.php b/example/ipn_shopping_cart_3.php new file mode 100644 index 0000000..9f040a8 --- /dev/null +++ b/example/ipn_shopping_cart_3.php @@ -0,0 +1,57 @@ +getV1(); + } + + public function getAdditionalUserFields(User $user) + { + return array(); + } +} + +$userStorage = new UsersDemoStorage; +$paymentStorage = new PaymentShoppingCart3DemoStorage; + +$demoProject = new Project( + '4783',//demo project id + 'key'//demo project secret key +); +$protocolBuilder = new ProtocolFactory($demoProject); +$protocol = $protocolBuilder->getShoppingCart3Protocol($userStorage, $paymentStorage); + +$request = Request::createFromGlobals(); +$response = $protocol->run($request); +$response->send(); diff --git a/resources/mysql/shopping_cart3.sql b/resources/mysql/shopping_cart3.sql new file mode 100644 index 0000000..a2d5fd8 --- /dev/null +++ b/resources/mysql/shopping_cart3.sql @@ -0,0 +1,25 @@ +CREATE TABLE `xsolla_standard_user` ( + `v1` VARCHAR(255) NOT NULL, + `v2` VARCHAR(200) NULL DEFAULT NULL, + `v3` VARCHAR(100) NULL DEFAULT NULL, + PRIMARY KEY (`v1`)) + ENGINE = InnoDB + DEFAULT CHARACTER SET = utf8 + COLLATE = utf8_general_ci; + +CREATE TABLE `xsolla_shoppingcart3_invoice` ( + `id` INT NOT NULL AUTO_INCREMENT, + `v1` VARCHAR(255) NOT NULL, + `id_xsolla` INT UNSIGNED NOT NULL, + `timestamp_ipn` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `timestamp_xsolla_ipn` TIMESTAMP NOT NULL, + `payment_amount` DECIMAL(12,2) UNSIGNED NOT NULL, + `payment_currency` CHAR(3) NOT NULL, + `is_dry_run` TINYINT(1) NOT NULL, + `is_canceled` TINYINT(1) NOT NULL DEFAULT 0, + `timestamp_canceled` TIMESTAMP NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `id_xsolla_UNIQUE` (`id_xsolla` ASC)) + ENGINE = InnoDB + DEFAULT CHARACTER SET = utf8 + COLLATE = utf8_general_ci; diff --git a/src/Invoice.php b/src/Invoice.php index 4116cec..e2dc68b 100644 --- a/src/Invoice.php +++ b/src/Invoice.php @@ -8,13 +8,23 @@ class Invoice protected $amount; protected $currency; protected $id; + protected $amountShoppingCart3; + protected $currencyShoppingCart3; - public function __construct($virtualCurrencyAmount = null, $amount = null, $currency = null, $id = null) - { + public function __construct( + $virtualCurrencyAmount = null, + $amount = null, + $currency = null, + $id = null, + $amountShoppingCart3 = null, + $currencyShoppingCart3 = null + ) { $this->amount = $amount; $this->virtualCurrencyAmount = $virtualCurrencyAmount; $this->currency = $currency; $this->id = $id; + $this->amountShoppingCart3 = $amountShoppingCart3; + $this->currencyShoppingCart3 = $currencyShoppingCart3; } public function getVirtualCurrencyAmount() @@ -64,4 +74,27 @@ public function setAmount($sum) return $this; } + + public function setAmountShoppingCart3($amountShoppingCart3) + { + $this->amountShoppingCart3 = $amountShoppingCart3; + return $this; + } + + public function getAmountShoppingCart3() + { + return $this->amountShoppingCart3; + } + + public function setCurrencyShoppingCart3($currencyShoppingCart3) + { + $this->currencyShoppingCart3 = $currencyShoppingCart3; + return $this; + } + + public function getCurrencyShoppingCart3() + { + return $this->currencyShoppingCart3; + } + } diff --git a/src/PaymentPage/UrlBuilder.php b/src/PaymentPage/UrlBuilder.php index 8b3434b..f551c39 100644 --- a/src/PaymentPage/UrlBuilder.php +++ b/src/PaymentPage/UrlBuilder.php @@ -70,7 +70,8 @@ public function setInvoice(Invoice $invoice, $lockForUser = true, $hideFromUser $this->setParameter('out', $invoice->getVirtualCurrencyAmount(), $lockForUser, $hideFromUser); $this->setParameter('currency', $invoice->getCurrency(), $lockForUser, $hideFromUser); $this->setParameter('sum', $invoice->getAmount(), $lockForUser, $hideFromUser); - + $this->setParameter('payment_amount', $invoice->getAmountShoppingCart3(), $lockForUser, $hideFromUser); + $this->setParameter('payment_currency', $invoice->getCurrencyShoppingCart3(), $lockForUser, $hideFromUser); return $this; } diff --git a/src/Protocol/Command/Cancel.php b/src/Protocol/Command/Cancel.php index f766a89..137e3a1 100644 --- a/src/Protocol/Command/Cancel.php +++ b/src/Protocol/Command/Cancel.php @@ -40,7 +40,7 @@ public function process(Request $request) public function checkSign(Request $request) { - return $this->generateSign($request, array('command', 'id')) == $request->query->get('md5'); + return $this->generateSign($request, array('command', 'id')) === $request->query->get('md5'); } public function getRequiredParams() diff --git a/src/Protocol/Command/Check.php b/src/Protocol/Command/Check.php index b90dcb7..48a1e9e 100644 --- a/src/Protocol/Command/Check.php +++ b/src/Protocol/Command/Check.php @@ -41,7 +41,7 @@ public function process(Request $request) public function checkSign(Request $request) { - return $this->generateSign($request, array('command', 'v1')) == $request->query->get('md5'); + return $this->generateSign($request, array('command', 'v1')) === $request->query->get('md5'); } public function getRequiredParams() diff --git a/src/Protocol/Command/CheckShoppingCart3.php b/src/Protocol/Command/CheckShoppingCart3.php new file mode 100644 index 0000000..c69ca56 --- /dev/null +++ b/src/Protocol/Command/CheckShoppingCart3.php @@ -0,0 +1,19 @@ +userStorage = $protocol->getUserStorage(); + $this->project = $protocol->getProject(); + } + + public function checkSign(Request $request) + { + return $this->generateSign($request, array('command', 'v1', 'foreignInvoice')) === $request->query->get('md5'); + } +} \ No newline at end of file diff --git a/src/Protocol/Command/PayShoppingCart.php b/src/Protocol/Command/PayShoppingCart.php index 49b348e..5ceb88a 100644 --- a/src/Protocol/Command/PayShoppingCart.php +++ b/src/Protocol/Command/PayShoppingCart.php @@ -57,7 +57,7 @@ public function checkSign(Request $request) { $actualSign = $this->generateSign($request, array('v1', 'amount', 'currency', 'id')); - return $actualSign == $request->query->get('sign'); + return $actualSign === $request->query->get('sign'); } public function getCommentFieldName() diff --git a/src/Protocol/Command/PayShoppingCart3.php b/src/Protocol/Command/PayShoppingCart3.php new file mode 100644 index 0000000..720cfe5 --- /dev/null +++ b/src/Protocol/Command/PayShoppingCart3.php @@ -0,0 +1,62 @@ +userStorage = $protocol->getUserStorage(); + $this->project = $protocol->getProject(); + $this->paymentStorage = $protocol->getPaymentShoppingCart3Storage(); + } + + public function checkSign(Request $request) + { + return $this->generateSign($request, array('command', 'v1', 'foreignInvoice', 'id')) === $request->query->get('md5'); + } + + public function process(Request $request) + { + $user = $this->createUser($request); + if (!$this->userStorage->isUserExists($user)) { + return array( + 'result' => self::CODE_INVALID_ORDER_DETAILS, + self::COMMENT_FIELD_NAME => 'User not found' + ); + } + $datetime = $this->getDateTimeXsolla('Y-m-d H:i:s', $request->query->get('date')); + $id = $this->paymentStorage->pay( + $request->query->get('id'), + $request->query->get('payment_amount'), + $request->query->get('payment_currency'), + $user, + $datetime, + (bool) $request->query->get('dry_run') + ); + return array( + 'result' => self::CODE_SUCCESS, + self::COMMENT_FIELD_NAME => '', + 'id_shop' => $id + ); + } + + public function getRequiredParams() + { + return array('command', 'md5', 'id', 'v1', 'date', 'payment_amount', 'payment_currency'); + } +} \ No newline at end of file diff --git a/src/Protocol/Command/PayStandard.php b/src/Protocol/Command/PayStandard.php index c398e20..59f4f25 100644 --- a/src/Protocol/Command/PayStandard.php +++ b/src/Protocol/Command/PayStandard.php @@ -58,6 +58,6 @@ public function process(Request $request) public function checkSign(Request $request) { - return $this->generateSign($request, array('command', 'v1', 'id')) == $request->query->get('md5'); + return $this->generateSign($request, array('command', 'v1', 'id')) === $request->query->get('md5'); } } diff --git a/src/Protocol/CommandFactory/ShoppingCart3Factory.php b/src/Protocol/CommandFactory/ShoppingCart3Factory.php new file mode 100644 index 0000000..6f5da0d --- /dev/null +++ b/src/Protocol/CommandFactory/ShoppingCart3Factory.php @@ -0,0 +1,36 @@ +getPaymentShoppingCart3Storage()); + default: + throw new WrongCommandException(sprintf( + 'Wrong command: "%s". Available commands for Standard protocol are: "%s".', + $commandName, + join('", "', $protocol->getProtocolCommands()) + )); + } + } +} \ No newline at end of file diff --git a/src/Protocol/ProtocolFactory.php b/src/Protocol/ProtocolFactory.php index 6785265..65a824b 100644 --- a/src/Protocol/ProtocolFactory.php +++ b/src/Protocol/ProtocolFactory.php @@ -2,8 +2,10 @@ namespace Xsolla\SDK\Protocol; +use Xsolla\SDK\Protocol\CommandFactory\ShoppingCart3Factory; use Xsolla\SDK\Protocol\CommandFactory\ShoppingCartFactory; use Xsolla\SDK\Protocol\CommandFactory\StandardFactory; +use Xsolla\SDK\Protocol\Storage\PaymentShoppingCart3StorageInterface; use Xsolla\SDK\Protocol\Storage\PaymentShoppingCartStorageInterface; use Xsolla\SDK\Protocol\Storage\PaymentStandardStorageInterface; use Xsolla\SDK\Protocol\Storage\UserStorageInterface; @@ -20,6 +22,11 @@ public function __construct($project, $ipChecker = null) $this->ipChecker = $ipChecker; } + /** + * @param PaymentShoppingCartStorageInterface $paymentStorage + * @return ShoppingCart + * @see http://xsolla.github.io/en/cash.html + */ public function getShoppingCartProtocol(PaymentShoppingCartStorageInterface $paymentStorage) { return new ShoppingCart( @@ -31,6 +38,12 @@ public function getShoppingCartProtocol(PaymentShoppingCartStorageInterface $pay ); } + /** + * @param UserStorageInterface $userStorage + * @param PaymentStandardStorageInterface $paymentStorage + * @return Standard + * @see http://xsolla.github.io/en/currency.html + */ public function getStandardProtocol(UserStorageInterface $userStorage, PaymentStandardStorageInterface $paymentStorage) { return new Standard( @@ -42,4 +55,22 @@ public function getStandardProtocol(UserStorageInterface $userStorage, PaymentSt $this->ipChecker ); } + + /** + * @param UserStorageInterface $userStorage + * @param PaymentShoppingCart3StorageInterface $paymentStorage + * @return ShoppingCart3 + * @see http://xsolla.github.io/en/shopingcart3.html + */ + public function getShoppingCart3Protocol(UserStorageInterface $userStorage, PaymentShoppingCart3StorageInterface $paymentStorage) + { + return new ShoppingCart3( + $this->project, + new XmlResponseBuilder($this->enableVersionHeader), + new ShoppingCart3Factory(), + $userStorage, + $paymentStorage, + $this->ipChecker + ); + } } diff --git a/src/Protocol/ShoppingCart3.php b/src/Protocol/ShoppingCart3.php new file mode 100644 index 0000000..abe8283 --- /dev/null +++ b/src/Protocol/ShoppingCart3.php @@ -0,0 +1,83 @@ +commandFactory = $commandFactory; + $this->userStorage = $userStorage; + $this->paymentShoppingCart3Storage = $paymentShoppingCart3Storage; + } + + /** + * @return array + */ + public function getProtocolCommands() + { + return array('check', 'pay', 'cancel'); + } + + /** + * @return \Xsolla\SDK\Protocol\Storage\PaymentShoppingCart3StorageInterface + */ + public function getPaymentShoppingCart3Storage() + { + return $this->paymentShoppingCart3Storage; + } + + /** + * @return \Xsolla\SDK\Protocol\Storage\UserStorageInterface + */ + public function getUserStorage() + { + return $this->userStorage; + } + + /** + * @param $message + * @return array + */ + public function getResponseForWrongCommand($message) + { + return array( + 'result' => StandardCommand::CODE_INVALID_REQUEST, + StandardCommand::COMMENT_FIELD_NAME => $message + ); + } +} \ No newline at end of file diff --git a/src/Protocol/Storage/PaymentShoppingCart3StorageInterface.php b/src/Protocol/Storage/PaymentShoppingCart3StorageInterface.php new file mode 100644 index 0000000..a909ecf --- /dev/null +++ b/src/Protocol/Storage/PaymentShoppingCart3StorageInterface.php @@ -0,0 +1,19 @@ +insertPay($xsollaPaymentId, $paymentAmount, $paymentCurrency, $user, $date, $dryRun); + if ($id > 0) { + return $id; + } + return $this->selectPayId($xsollaPaymentId, $paymentAmount, $paymentCurrency, $user); + } + + protected function insertPay($xsollaPaymentId, $paymentAmount, $paymentCurrency, User $user, \DateTime $date, $dryRun) + { + $insert = $this->pdo->prepare( + "INSERT INTO `xsolla_shoppingcart3_invoice` + (`v1`, `id_xsolla`, `payment_amount`, `payment_currency`, `timestamp_xsolla_ipn`, `is_dry_run`) + VALUES (:v1, :id_xsolla, :payment_amount, :payment_currency, :timestamp_xsolla_ipn, :is_dry_run)" + ); + $insert->bindValue(':v1', $user->getV1()); + $insert->bindValue(':id_xsolla', $xsollaPaymentId, \PDO::PARAM_INT); + $insert->bindValue(':payment_amount', $paymentAmount); + $insert->bindValue(':payment_currency', $paymentCurrency, \PDO::PARAM_STR); + $insert->bindValue(':timestamp_xsolla_ipn', $date->format('Y-m-d H:i:s'), \PDO::PARAM_STR); + $insert->bindValue(':is_dry_run', $dryRun, \PDO::PARAM_BOOL); + $insert->execute(); + + return $this->pdo->lastInsertId(); + } + + protected function selectPayId($xsollaPaymentId, $paymentAmount, $paymentCurrency, User $user) + { + $select = $this->pdo->prepare( + "SELECT id, v1, `payment_amount`, `payment_currency`, timestamp_xsolla_ipn, is_dry_run + FROM `xsolla_standard_invoice` + WHERE id_xsolla = :id_xsolla + "); + $select->bindValue(':id_xsolla', $xsollaPaymentId, \PDO::PARAM_INT); + $select->execute(); + $result = $select->fetch(\PDO::FETCH_ASSOC); + if ($result === false) { + throw new \Exception('Temporary error.'); + } + $diffMessage = ''; + if ($result['v1'] != $user->getV1()) { + $diffMessage .= sprintf(' and v1=%s (must be "%s")', $result['v1'], $user->getV1()); + } + if ($result['payment_amount'] != $paymentAmount) { + $diffMessage .= sprintf(' and payment_amount=%0.2f (must be %0.2f)', $result['payment_amount'], $paymentAmount); + } + if ($result['payment_currency'] != $paymentCurrency) { + $diffMessage .= sprintf(' and payment_currency=%s (must be %s)', $result['payment_currency'], $paymentCurrency); + } + if ($diffMessage !== '') { + throw new UnprocessableRequestException(sprintf( + 'Found payment with xsollaPaymentId=%s%s.', + $xsollaPaymentId, + $diffMessage + )); + } + + return $result['id']; + } + + protected function getTable() + { + return self::TABLE; + } +} \ No newline at end of file diff --git a/tests/PaymentPage/UrlBuilderTest.php b/tests/PaymentPage/UrlBuilderTest.php index fe30916..3f24f58 100644 --- a/tests/PaymentPage/UrlBuilderTest.php +++ b/tests/PaymentPage/UrlBuilderTest.php @@ -40,6 +40,8 @@ class UrlBuilderTest extends \PHPUnit_Framework_TestCase protected $fullUrl = 'https://secure.xsolla.com/paystation2/?local=EN&country=US&limit=14&v1=user_v1&v2=user_v2&v3=user_v3&email=email%40example.com&userip=user_userIp&phone=user_phone&out=1.11¤cy=EUR&sum=0.1&project=7096&hidden=limit&signparams=allowSubscription%2Ccurrency%2Cemail%2Cfastcheckout%2Cid_package%2Climit%2Cout%2Cphone%2Cproject%2Csignparams%2Csum%2Ctheme%2Cuserip%2Cv0%2Cv2%2Cv3&sign=a56aaed28a810577e075f4d665031d30'; + protected $fullUrlShoppingCart3 = 'https://secure.xsolla.com/paystation2/?local=EN&country=US&limit=14&v1=user_v1&v2=user_v2&v3=user_v3&email=email%40example.com&userip=user_userIp&phone=user_phone&payment_amount=0.1&payment_currency=EUR&project=7096&hidden=limit&signparams=allowSubscription%2Ccurrency%2Cemail%2Cfastcheckout%2Cid_package%2Climit%2Cout%2Cpayment_amount%2Cpayment_currency%2Cphone%2Cproject%2Csignparams%2Ctheme%2Cuserip%2Cv0%2Cv2%2Cv3&sign=1d2513e200bc7c94cc949304029af0ad'; + public function setUp() { $this->user = new User('user_v1'); @@ -117,4 +119,22 @@ public function testFluentInterface() ->getUrl(); $this->assertSame($this->fullUrl, $url); } + + public function testShoppingCart3() + { + $this->invoice->setAmountShoppingCart3(0.1); + $this->invoice->setCurrencyShoppingCart3('EUR'); + $this->invoice->setVirtualCurrencyAmount(null); + $this->invoice->setCurrency(null); + $url = $this->urlBuilder->clear() + ->setLocale('EN') + ->setCountry('US') + ->setParameter('limit', 14, true, true) + ->setUser($this->user, true, false) + ->setInvoice($this->invoice, true, false) + ->unlockParameterForUser('v1') + ->lockParameterForUser('limit') + ->getUrl(); + $this->assertSame($this->fullUrlShoppingCart3, $url); + } } diff --git a/tests/Protocol/ShoppingCart3ProtocolFullTest.php b/tests/Protocol/ShoppingCart3ProtocolFullTest.php new file mode 100644 index 0000000..6b63df2 --- /dev/null +++ b/tests/Protocol/ShoppingCart3ProtocolFullTest.php @@ -0,0 +1,257 @@ +userStorageMock = $this->getMock('Xsolla\SDK\Protocol\Storage\UserStorageInterface', array(), array(), '', false); + $this->paymentStorageMock = $this->getMock('Xsolla\SDK\Protocol\Storage\PaymentShoppingCart3StorageInterface', array(), array(), '', false); + $this->addCancelHandler($this->paymentStorageMock); + $this->addPayHandler($this->paymentStorageMock); + $this->addCheckHandler($this->userStorageMock); + $this->protocol = $this->protocolBuilder->getShoppingCart3Protocol($this->userStorageMock, $this->paymentStorageMock); + } + + /** + * @dataProvider checkProvider + */ + public function testCheck(array $params, $expectedXml) + { + $this->protocolTest($params, $expectedXml); + } + + public function checkProvider() + { + return array( + array( + array( + 'command' => 'check', + 'v1' => self::CHECK_V1_ANY_EXCEPTION, + 'md5' => '22a0964fb976608f25f2d55ee44c01d3' + ), + '' . PHP_EOL . + '1Any exception' . PHP_EOL + ), + array( + array( + 'command' => 'check', + 'v1' => self::CHECK_V1_NOT_EXISTS, + 'md5' => '7c7fce6806ae451419dede822033dd9e' + ), + '' . PHP_EOL . + '7' . PHP_EOL + ), + array( + array( + 'command' => 'check', + 'v1' => self::CHECK_V1_SUCCESS, + 'md5' => 'a3561b90df78828133eb285e36965419' + ), + '' . PHP_EOL . + '0value1value2' . PHP_EOL + ), + ); + } + + public function noCommandProvider() + { + return array( + array( + array( + 'id' => '100', + 'sign' => 'bc763ff5a8665c7c4c5fa0d8eba75ac8' + ), + '' . PHP_EOL . + '4' . + 'No command in request. Available commands are: "check", "pay", "cancel".' . + '' . PHP_EOL + ) + ); + } + + public function wrongCommandProvider() + { + return array( + array( + array( + 'command' => 'not_exist', + ), + '' . PHP_EOL . + '4' . + 'Wrong command: "not_exist". Available commands for Standard protocol are: "check", "pay", "cancel".' . + '' . PHP_EOL + ) + ); + } + + public function ipCheckerProvider() + { + return array( + array( + array( + 'command' => 'pay', + ), + '' . PHP_EOL . + '7' . + 'IP whitelist doesn\'t contain client IP address' . + '' . PHP_EOL + ) + ); + } + + public function payProvider() + { + return array( + array( + array( + 'command' => 'pay' + ), + '' . PHP_EOL . + '4' . + 'Invalid request format. Missed parameters: md5, id, v1, date, payment_amount, payment_currency' . + '' . PHP_EOL + ), + array( + array( + 'command' => 'pay', + 'md5' => '11111ff5a8661171111111d8e1171111', + 'id' => self::PAY_ID_SUCCESS, + 'v1' => 'demo', + 'payment_amount' => '100.20', + 'payment_currency' => 'EUR', + 'date' => '2014-02-19 20:07:08' + ), + '' . PHP_EOL . + '3Invalid md5 signature' . PHP_EOL + ), + array( + array( + 'command' => 'pay', + 'md5' => '151783427f854e7c61e54fae5361cacb', + 'id' => self::PAY_ID_SUCCESS, + 'v1' => 'demo', + 'payment_amount' => '100.20', + 'payment_currency' => 'EUR', + 'date' => 'DATE' + ), + '' . PHP_EOL . + '4' . + 'Datetime string DATE could not be converted to DateTime object from format \'Y-m-d H:i:s\'' . + '' . PHP_EOL + ), + array( + array( + 'command' => 'pay', + 'md5' => 'b03894e48c0dfa6a9adda90739ca986c', + 'id' => self::PAY_ID_ANY_EXCEPTION, + 'v1' => 'demo', + 'payment_amount' => '100.20', + 'payment_currency' => 'EUR', + 'date' => '2014-02-19 20:07:08' + ), + '' . PHP_EOL . + '1Any exception' . PHP_EOL + ), + array( + array( + 'command' => 'pay', + 'md5' => '151783427f854e7c61e54fae5361cacb', + 'id' => self::PAY_ID_SUCCESS, + 'v1' => 'demo', + 'payment_amount' => '100.20', + 'payment_currency' => 'EUR', + 'date' => '2014-02-19 20:07:08' + ), + '' . PHP_EOL . + '0' . self::PAY_SHOP_ID . '' . PHP_EOL + ), + array( + array( + 'command' => 'pay', + 'md5' => '6d2228aaf135ea197aaaecf2d1260f13', + 'id' => self::ZERO, + 'v1' => 'demo', + 'payment_amount' => '100.20', + 'payment_currency' => 'EUR', + 'date' => '2014-02-19 20:07:08' + ), + '' . PHP_EOL . + '0' . self::PAY_SHOP_ID . '' . PHP_EOL + ), + array( + array( + 'command' => 'pay', + 'v1' => self::CHECK_V1_NOT_EXISTS, + 'md5' => '19d8988edfae51f90a1c78180d099a17', + 'id' => self::PAY_ID_SUCCESS, + 'payment_amount' => '100.20', + 'payment_currency' => 'EUR', + 'date' => '2014-02-19 20:07:08' + ), + '' . PHP_EOL . + '2User not found' . PHP_EOL + ), + ); + } + + public function addCheckHandler(UserStorageInterface $userStorageMock) + { + $userStorageMock->expects($this->any()) + ->method('isUserExists') + ->with($this->isInstanceOf('Xsolla\SDK\User')) + ->will($this->returnCallback(array($this, 'isUserExistsCallback'))); + $userStorageMock->expects($this->any()) + ->method('getAdditionalUserFields') + ->with($this->isInstanceOf('Xsolla\SDK\User')) + ->will($this->returnValue(array( + 'parameter1' => 'value1', + 'parameter2' => 'value2', + ))); + } + + public function isUserExistsCallback(User $user) + { + switch ($user->getV1()) { + case self::CHECK_V1_ANY_EXCEPTION: + throw new \Exception('Any exception'); + case self::CHECK_V1_NOT_EXISTS: + return false; + default: + return true; + } + } + + public function addPayHandler(PaymentShoppingCart3StorageInterface $paymentStorageMock) + { + $paymentStorageMock->expects($this->any()) + ->method('pay') + ->will($this->returnCallback(array($this, 'payCallback'))); + } + + public function payCallback($invoiceId) + { + if ($invoiceId == self::PAY_ID_ANY_EXCEPTION) { + throw new \Exception('Any exception'); + } else { + return self::PAY_SHOP_ID; + } + } +} diff --git a/tests/Protocol/StandardProtocolFullTest.php b/tests/Protocol/StandardProtocolFullTest.php index dc7408b..ddaef7c 100644 --- a/tests/Protocol/StandardProtocolFullTest.php +++ b/tests/Protocol/StandardProtocolFullTest.php @@ -191,6 +191,18 @@ public function payProvider() '' . PHP_EOL . '0' . self::PAY_SHOP_ID . '' . PHP_EOL ), + array( + array( + 'command' => 'pay', + 'v1' => self::CHECK_V1_NOT_EXISTS, + 'md5' => '19d8988edfae51f90a1c78180d099a17', + 'id' => self::PAY_ID_SUCCESS, + 'sum' => '100.20', + 'date' => '2014-02-19 20:07:08' + ), + '' . PHP_EOL . + '2User not found' . PHP_EOL + ), ); } diff --git a/tests/Protocol/Storage/Pdo/PaymentShoppingCart3StorageTest.php b/tests/Protocol/Storage/Pdo/PaymentShoppingCart3StorageTest.php new file mode 100644 index 0000000..394e0d8 --- /dev/null +++ b/tests/Protocol/Storage/Pdo/PaymentShoppingCart3StorageTest.php @@ -0,0 +1,126 @@ +paymentStorage = new PaymentShoppingCart3Storage($this->pdoMock); + $this->datetimeObj = \DateTime::createFromFormat('Y-m-d H:i:s', '2013-03-25 18:48:22', $this->xsollaTimeZone); + } + + protected function setUpPayPdoMock() + { + $this->pdoMock->expects($this->at(0)) + ->method('prepare') + ->with($this->anything()) + ->will($this->returnValue($this->insertMock)); + } + + protected function setUpPayInsertMock() + { + $this->insertMock->expects($this->exactly(6)) + ->method('bindValue') + ->with($this->anything(), $this->anything()); + $this->insertMock->expects($this->at(6)) + ->method('execute'); + } + + public function testPaySuccess() + { + $this->setUpPayInsertMock(); + + $this->pdoMock->expects($this->at(0)) + ->method('prepare') + ->with($this->anything()) + ->will($this->returnValue($this->insertMock)); + $this->pdoMock->expects($this->at(1)) + ->method('lastInsertId') + ->will($this->returnValue(555)); + + $this->assertEquals(555, $this->paymentStorage->pay(10, 10, 'EUR', $this->userMock, $this->datetimeObj, false)); + } + + public function testPayExists() + { + $this->setUpSelectMock(); + $this->selectMock->expects($this->once()) + ->method('fetch') + ->with($this->equalTo(\PDO::FETCH_ASSOC)) + ->will($this->returnValue(array('id' => self::INVOICE_ID, 'v1' => $this->userMock->getV1(), 'payment_amount' => self::INVOICE_AMOUNT, 'payment_currency' => self::INVOICE_CURRENCY))); + + $this->setUpPayInsertMock(); + $this->pdoMock->expects($this->at(1)) + ->method('lastInsertId') + ->will($this->returnValue(0)); + + $this->setUpPayPdoMock(); + $this->pdoMock->expects($this->at(2)) + ->method('prepare') + ->with($this->anything()) + ->will($this->returnValue($this->selectMock)); + + $this->assertEquals(self::INVOICE_ID, $this->paymentStorage->pay(self::INVOICE_ID, self::INVOICE_AMOUNT, self::INVOICE_CURRENCY, $this->userMock, $this->datetimeObj, false)); + } + + /** + * @dataProvider payErrorProvider + */ + public function testPayError($result, $exceptionDesc) + { + $this->setUpSelectMock(); + $this->selectMock->expects($this->once()) + ->method('fetch') + ->with($this->equalTo(\PDO::FETCH_ASSOC)) + ->will($this->returnValue($result)); + + $this->setUpPayInsertMock(); + + $this->setUpPayPdoMock(); + $this->pdoMock->expects($this->at(1)) + ->method('lastInsertId') + ->will($this->returnValue(0)); + $this->pdoMock->expects($this->at(2)) + ->method('prepare') + ->with($this->anything()) + ->will($this->returnValue($this->selectMock)); + + $this->setExpectedException($exceptionDesc[0], $exceptionDesc[1]); + $this->paymentStorage->pay($result['id'], self::INVOICE_AMOUNT, self::INVOICE_CURRENCY, $this->userMock, $this->datetimeObj, false); + } + + public function payErrorProvider() + { + return array( + array( + array('id' => self::INVOICE_ID, 'v1' => 'demo', 'payment_amount' => 0, 'payment_currency' => 'USD'), + array('Xsolla\SDK\Exception\UnprocessableRequestException', 'Found payment with xsollaPaymentId=101 and payment_amount=0.00 (must be 100.20) and payment_currency=USD (must be EUR).') + ), + array( + array('id' => self::INVOICE_ID, 'v1' => 'demo1', 'payment_amount' => 100.20, 'payment_currency' => 'EUR'), + array('Xsolla\SDK\Exception\UnprocessableRequestException', 'Found payment with xsollaPaymentId=101 and v1=demo1 (must be "demo").') + ), + array( + array('id' => self::INVOICE_ID, 'v1' => 'demo1', 'payment_amount' => 0, 'payment_currency' => 'EUR'), + array('Xsolla\SDK\Exception\UnprocessableRequestException', 'Found payment with xsollaPaymentId=101 and v1=demo1 (must be "demo") and payment_amount=0.00 (must be 100.20).') + ), + array( + false, + array('Exception', 'Temporary error.') + ) + ); + } +}