diff --git a/.travis.yml b/.travis.yml index 3a8da30999..56f22e0a71 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,6 +70,7 @@ script: - cd ../SettingsBundle; ../../../../bin/phpspec run -fpretty --verbose - cd ../PromotionsBundle; ../../../../bin/phpspec run -fpretty --verbose - cd ../PaymentsBundle; ../../../../bin/phpspec run -fpretty --verbose + - cd ../PayumBundle; ../../../../bin/phpspec run -fpretty --verbose notifications: email: "travis-ci@sylius.org" diff --git a/app/AppKernel.php b/app/AppKernel.php index aa81c8ac94..e50c8a966f 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -39,6 +39,7 @@ class AppKernel extends Kernel new Sylius\Bundle\TaxationBundle\SyliusTaxationBundle(), new Sylius\Bundle\ShippingBundle\SyliusShippingBundle(), new Sylius\Bundle\PaymentsBundle\SyliusPaymentsBundle(), + new Sylius\Bundle\PayumBundle\SyliusPayumBundle(), new Sylius\Bundle\PromotionsBundle\SyliusPromotionsBundle(), new Sylius\Bundle\AddressingBundle\SyliusAddressingBundle(), new Sylius\Bundle\InventoryBundle\SyliusInventoryBundle(), @@ -71,6 +72,7 @@ class AppKernel extends Kernel new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(), new JMS\TranslationBundle\JMSTranslationBundle(), new Knp\Bundle\SnappyBundle\KnpSnappyBundle(), + new Payum\Bundle\PayumBundle\PayumBundle(), ); if (in_array($this->getEnvironment(), array('dev'))) { diff --git a/app/config/config.yml b/app/config/config.yml index 4c8942aeab..3aa43e125a 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -2,6 +2,7 @@ imports: - { resource: parameters.yml } - { resource: security.yml } - { resource: sylius.yml } + - { resource: payum.yml } framework: translator: { fallback: %sylius.locale% } @@ -129,4 +130,4 @@ knp_snappy: image: enabled: true binary: /usr/bin/wkhtmltoimage - options: [] + options: [] \ No newline at end of file diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist index 908745347d..6fc6b6f203 100644 --- a/app/config/parameters.yml.dist +++ b/app/config/parameters.yml.dist @@ -17,3 +17,11 @@ parameters: sylius.currency: EUR sylius.cache: file_system + + paypal.express_checkout.username: EDITME + paypal.express_checkout.password: EDITME + paypal.express_checkout.signature: EDITME + paypal.express_checkout.sandbox: true + + stripe.secret_key: EDITME + stripe.test_mode: true \ No newline at end of file diff --git a/app/config/payum.yml b/app/config/payum.yml new file mode 100644 index 0000000000..3bd2e77f03 --- /dev/null +++ b/app/config/payum.yml @@ -0,0 +1,60 @@ +payum: + security: + token_storage: + Sylius\Bundle\PayumBundle\Model\PaymentSecurityToken: + doctrine: + driver: orm + + contexts: + paypal_express_checkout: + paypal_express_checkout_nvp: + api: + options: + username: %paypal.express_checkout.username% + password: %paypal.express_checkout.password% + signature: %paypal.express_checkout.signature% + sandbox: %paypal.express_checkout.sandbox% + actions: + - sylius.payum.action.capture_order_with_paypal + - sylius.payum.action.order_status + + storages: + Sylius\Bundle\CoreBundle\Model\Order: + doctrine: + driver: orm + Sylius\Bundle\PaymentsBundle\Model\Payment: + doctrine: + driver: orm + + stripe: + omnipay: + type: Stripe + options: + apiKey: %stripe.secret_key% + testMode: %stripe.test_mode% + actions: + - sylius.payum.action.capture_order_with_stripe + - sylius.payum.action.obtain_credit_card + - sylius.payum.action.order_status + + storages: + Sylius\Bundle\CoreBundle\Model\Order: + doctrine: + driver: orm + Sylius\Bundle\PaymentsBundle\Model\Payment: + doctrine: + driver: orm + + dummy: + custom: + actions: + - sylius.payum.action.capture_order_with_dummy + - sylius.payum.action.dummy_order_status + + storages: + Sylius\Bundle\CoreBundle\Model\Order: + doctrine: + driver: orm + Sylius\Bundle\PaymentsBundle\Model\Payment: + doctrine: + driver: orm \ No newline at end of file diff --git a/app/config/routing.yml b/app/config/routing.yml index ff28e08a03..e0bf074933 100644 --- a/app/config/routing.yml +++ b/app/config/routing.yml @@ -4,6 +4,9 @@ sylius_installer: sylius_web: resource: @SyliusWebBundle/Resources/config/routing/main.yml +payum_capture: + resource: "@PayumBundle/Resources/config/routing/capture.xml" + fos_user_security: resource: "@FOSUserBundle/Resources/config/routing/security.xml" diff --git a/app/config/sylius.yml b/app/config/sylius.yml index ceb6b5c7f8..d46e56167c 100644 --- a/app/config/sylius.yml +++ b/app/config/sylius.yml @@ -72,6 +72,9 @@ sylius_payments: dummy: Test stripe: Stripe +sylius_payum: + driver: doctrine/orm + sylius_addressing: classes: address: diff --git a/composer.json b/composer.json index edf7e70e6d..e1917c56d0 100644 --- a/composer.json +++ b/composer.json @@ -1,3 +1,4 @@ + { "name": "sylius/sylius", "type": "project", @@ -38,7 +39,6 @@ "liip/doctrine-cache-bundle": "*", "liip/imagine-bundle": "~0.9", "mathiasverraes/money": "*@dev", - "omnipay/omnipay": "*@dev", "sensio/distribution-bundle": "2.3.*", "stof/doctrine-extensions-bundle": "1.1.*", "symfony/assetic-bundle": "2.3.*", @@ -47,7 +47,12 @@ "symfony/swiftmailer-bundle": "2.3.*", "symfony/symfony": "~2.3", "twig/extensions": "1.0.*", - "white-october/pagerfanta-bundle": "1.0.*@dev" + "white-october/pagerfanta-bundle": "1.0.*@dev", + "omnipay/omnipay": "1.0.*", + "payum/payum": "0.6.*@dev", + "payum/payum-bundle": "0.6.*@dev", + "payum/paypal-express-checkout-nvp": "0.6.*@dev", + "payum/omnipay-bridge": "0.6.*@dev" }, "require-dev": { "behat/behat": "2.4.*@stable", diff --git a/composer.lock b/composer.lock index c85adf4d55..2453f972ee 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "44206bb63f0af4d9594c308f6dcbb0cc", + "hash": "275dc038a243748ecf2b11bfb9340c59", "packages": [ { "name": "athari/yalinqo", @@ -53,12 +53,12 @@ "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "40db0c96985aab2822edbc4848b3bd2429e02670" + "reference": "v1.1.2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/40db0c96985aab2822edbc4848b3bd2429e02670", - "reference": "40db0c96985aab2822edbc4848b3bd2429e02670", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/v1.1.2", + "reference": "v1.1.2", "shasum": "" }, "require": { @@ -773,17 +773,17 @@ "source": { "type": "git", "url": "https://github.com/FriendsOfSymfony/FOSRest.git", - "reference": "9a84df8220553e2ae3163b8325a3c4c2e9792426" + "reference": "0.8.0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfSymfony/FOSRest/zipball/9a84df8220553e2ae3163b8325a3c4c2e9792426", - "reference": "9a84df8220553e2ae3163b8325a3c4c2e9792426", + "url": "https://api.github.com/repos/FriendsOfSymfony/FOSRest/zipball/0.8.0", + "reference": "0.8.0", "shasum": "" }, "require": { "php": ">=5.3.2", - "symfony/http-foundation": "~2.0" + "symfony/http-foundation": ">=2.0,<3.0" }, "type": "library", "extra": { @@ -1644,7 +1644,7 @@ ], "authors": [ { - "name": "Johannes M. Schmitt", + "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com", "homepage": "http://jmsyst.com", "role": "Developer of wrapped JMSSerializerBundle" @@ -2159,6 +2159,54 @@ ], "time": "2013-07-19 00:03:27" }, + { + "name": "kriswallsmith/buzz", + "version": "v0.10", + "source": { + "type": "git", + "url": "https://github.com/kriswallsmith/Buzz.git", + "reference": "759432d66387e3433d3b06cb6f773b97225b9b17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kriswallsmith/Buzz/zipball/759432d66387e3433d3b06cb6f773b97225b9b17", + "reference": "759432d66387e3433d3b06cb6f773b97225b9b17", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "suggest": { + "ext-curl": "*" + }, + "type": "library", + "autoload": { + "psr-0": { + "Buzz": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Lightweight HTTP client", + "homepage": "https://github.com/kriswallsmith/Buzz", + "keywords": [ + "curl", + "http client" + ], + "time": "2013-05-19 03:41:15" + }, { "name": "liip/doctrine-cache-bundle", "version": "1.0.3", @@ -2231,7 +2279,7 @@ "symfony/options-resolver": ">=2.0.16,~2.0" }, "require-dev": { - "symfony/yaml": ">=2.0.16,~2.0", + "symfony/yaml": ">=2.0.16.0,>=2.0,<3.0", "twig/twig": ">=1.0,<2.0-dev" }, "suggest": { @@ -2383,13 +2431,13 @@ "version": "v0.9.1", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "b1cc9ce676b4350b23d0fafc8244d08eee2fe287" + "url": "https://github.com/nikic/PHP-Parser", + "reference": "v0.9.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/b1cc9ce676b4350b23d0fafc8244d08eee2fe287", - "reference": "b1cc9ce676b4350b23d0fafc8244d08eee2fe287", + "url": "https://github.com/nikic/PHP-Parser/archive/v0.9.1.zip", + "reference": "v0.9.1", "shasum": "" }, "require": { @@ -2415,20 +2463,20 @@ "parser", "php" ], - "time": "2012-04-23 22:52:11" + "time": "2012-04-23 15:52:11" }, { "name": "omnipay/omnipay", - "version": "dev-master", + "version": "v1.0.4", "source": { "type": "git", "url": "https://github.com/adrianmacneil/omnipay.git", - "reference": "4de6c429661af33ce6323d9a58f60b4e82fcf35d" + "reference": "4f5e82232161939c0b14f5921c092ed77980d450" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/adrianmacneil/omnipay/zipball/4de6c429661af33ce6323d9a58f60b4e82fcf35d", - "reference": "4de6c429661af33ce6323d9a58f60b4e82fcf35d", + "url": "https://api.github.com/repos/adrianmacneil/omnipay/zipball/4f5e82232161939c0b14f5921c092ed77980d450", + "reference": "4f5e82232161939c0b14f5921c092ed77980d450", "shasum": "" }, "require": { @@ -2445,11 +2493,6 @@ "twig/twig": "~1.12" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-0": { "Omnipay": "src/" @@ -2486,7 +2529,6 @@ "merchant", "migs", "mollie", - "multisafepay", "netaxept", "netbanx", "pay", @@ -2503,11 +2545,10 @@ "stripe", "tala", "tala-payments", - "targetpay", "twocheckout", "worldpay" ], - "time": "2013-10-10 23:03:59" + "time": "2013-09-20 23:40:41" }, { "name": "pagerfanta/pagerfanta", @@ -2571,6 +2612,267 @@ ], "time": "2013-09-23 08:06:55" }, + { + "name": "payum/omnipay-bridge", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Payum/OmnipayBridge.git", + "reference": "da2a1c061efe27ecc8006bc8c7e7e7ba7a17e763" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Payum/OmnipayBridge/zipball/da2a1c061efe27ecc8006bc8c7e7e7ba7a17e763", + "reference": "da2a1c061efe27ecc8006bc8c7e7e7ba7a17e763", + "shasum": "" + }, + "require": { + "omnipay/omnipay": "1.0.*", + "payum/payum": "0.6.x-dev", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.6-dev" + } + }, + "autoload": { + "psr-0": { + "Payum\\Bridge\\Omnipay": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kotlyar Maksim", + "email": "kotlyar.maksim@gmail.com" + } + ], + "description": "This bridge allows you to use omnipay gateways but in payum like way.", + "homepage": "https://github.com/Payum/OmnipayBridge", + "keywords": [ + "omnipay", + "payment" + ], + "time": "2013-07-31 10:48:07" + }, + { + "name": "payum/paypal-express-checkout-nvp", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Payum/PaypalExpressCheckoutNvp.git", + "reference": "7aaaf140b5348a9676feb87881df5893b84b06f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Payum/PaypalExpressCheckoutNvp/zipball/7aaaf140b5348a9676feb87881df5893b84b06f9", + "reference": "7aaaf140b5348a9676feb87881df5893b84b06f9", + "shasum": "" + }, + "require": { + "kriswallsmith/buzz": "*", + "payum/payum": "0.6.x-dev", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/mongodb-odm": "~1.0@dev", + "doctrine/orm": ">=2.3,<2.4-dev", + "ext-curl": "*", + "ext-pdo_sqlite": "*", + "fp/testo": "dev-master", + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.6-dev" + } + }, + "autoload": { + "psr-0": { + "Payum\\Paypal\\ExpressCheckout\\Nvp": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kotlyar Maksim", + "email": "kotlyar.maksim@gmail.com" + } + ], + "description": "Paypal express checkout", + "keywords": [ + "digital goods", + "express checkout", + "payment", + "paypal", + "recurring payment" + ], + "time": "2013-10-10 20:10:49" + }, + { + "name": "payum/payum", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Payum/Payum.git", + "reference": "57b553f6dac1e4a2499f3f79797447476f9d9542" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Payum/Payum/zipball/57b553f6dac1e4a2499f3f79797447476f9d9542", + "reference": "57b553f6dac1e4a2499f3f79797447476f9d9542", + "shasum": "" + }, + "require": { + "kriswallsmith/buzz": "*", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/mongodb": "1.0.*@dev", + "doctrine/mongodb-odm": "1.0.*@dev", + "doctrine/orm": "2.*", + "ext-curl": "*", + "ext-pdo_sqlite": "*", + "fp/testo": "dev-master", + "phpunit/phpunit": "3.7.*", + "psr/log": "~1.0" + }, + "suggest": { + "doctrine/mongodb-odm": "If you want to store models to mongo doctrin2 ODM", + "doctrine/orm": "If you want to store models to database using doctrin2 ORM", + "monolog/monolog": "In case you want to use logger", + "payum/authorize-net-aim": "If you want to use Authorize.Net AIM payment gateway", + "payum/be2bill": "If you want to use be2bill payment gateway", + "payum/omnipay-bridge": "If you want to use omnipay's gateways", + "payum/paypal-express-checkout-nvp": "If you want to use paypal express checkout, digital goods or recurring payments", + "payum/paypal-ipn": "If you want to use paypal instant payment notifications(Paypal IPN)", + "payum/paypal-pro-checkout-nvp": "If you want to use paypal pro checkout" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.6-dev" + } + }, + "autoload": { + "psr-0": { + "Payum": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kotlyar Maksim", + "email": "kotlyar.maksim@gmail.com" + } + ], + "description": "Payment lib", + "keywords": [ + "authorize.net", + "be2bill", + "omnipay gateways", + "payment", + "paypal", + "paypal digital goods", + "paypal ipn", + "paypal recurring payments" + ], + "time": "2013-10-14 18:47:40" + }, + { + "name": "payum/payum-bundle", + "version": "dev-master", + "target-dir": "Payum/Bundle/PayumBundle", + "source": { + "type": "git", + "url": "https://github.com/Payum/PayumBundle.git", + "reference": "7f4708009c256b2dcc5dea378e8c003e61c3c09c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Payum/PayumBundle/zipball/7f4708009c256b2dcc5dea378e8c003e61c3c09c", + "reference": "7f4708009c256b2dcc5dea378e8c003e61c3c09c", + "shasum": "" + }, + "require": { + "payum/payum": "0.6.x-dev", + "php": ">=5.3.2", + "symfony/framework-bundle": "2.*" + }, + "require-dev": { + "doctrine/orm": "*", + "fp/testo": "@dev", + "payum/authorize-net-aim": "0.6.x-dev", + "payum/be2bill": "0.6.x-dev", + "payum/omnipay-bridge": "0.6.x-dev", + "payum/payex": "0.6.x-dev", + "payum/paypal-express-checkout-nvp": "0.6.x-dev", + "payum/paypal-pro-checkout-nvp": "0.6.x-dev", + "phpunit/phpunit": "*" + }, + "suggest": { + "payum/authorize-net-aim": "If you want to use authorize.net gateway", + "payum/be2bill": "If you want to use be2bill gateway", + "payum/omnipay-bridge": "If you want to use omnipay provided gateways", + "payum/payex": "If you want to use payex gateway", + "payum/paypal-express-checkout-nvp": "If you want to use paypal express checkout nvp gateway", + "payum/paypal-pro-checkout-nvp": "If you want to use paypal pro checkout nvp gateway" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "0.6-dev" + } + }, + "autoload": { + "psr-0": { + "Payum\\Bundle\\PayumBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kotlyar Maksim", + "email": "kotlyar.maksim@gmail.com" + }, + { + "name": "Community", + "homepage": "https://github.com/Payum/PayumBundle/graphs/contributors" + } + ], + "description": "The payment bundle integrates payum libs into symfony2 framework", + "homepage": "https://github.com/Payum/PayumBundle", + "keywords": [ + "authorize.net", + "be2bill", + "omnipay", + "payex", + "payment", + "paypal", + "paypal express checkout", + "paypal pro checkout", + "paypal recurring payment" + ], + "time": "2013-09-30 20:51:39" + }, { "name": "phpcollection/phpcollection", "version": "0.2.0", @@ -2675,13 +2977,13 @@ "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + "url": "https://github.com/php-fig/log", + "reference": "1.0.0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "url": "https://github.com/php-fig/log/archive/1.0.0.zip", + "reference": "1.0.0", "shasum": "" }, "type": "library", @@ -3864,12 +4166,12 @@ "source": { "type": "git", "url": "https://github.com/phpspec/php-diff.git", - "reference": "8d82ac415225fac373a4073ba14b1fe286aa2312" + "reference": "v1.0.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/php-diff/zipball/8d82ac415225fac373a4073ba14b1fe286aa2312", - "reference": "8d82ac415225fac373a4073ba14b1fe286aa2312", + "url": "https://api.github.com/repos/phpspec/php-diff/zipball/v1.0.1", + "reference": "v1.0.1", "shasum": "" }, "type": "library", @@ -4028,8 +4330,11 @@ "knplabs/knp-gaufrette-bundle": 20, "knplabs/knp-snappy-bundle": 20, "mathiasverraes/money": 20, - "omnipay/omnipay": 20, "white-october/pagerfanta-bundle": 20, + "payum/payum": 20, + "payum/payum-bundle": 20, + "payum/paypal-express-checkout-nvp": 20, + "payum/omnipay-bridge": 20, "behat/behat": 0, "phpspec/phpspec": 20 }, diff --git a/features/frontend/checkout_finalize.feature b/features/frontend/checkout_finalize.feature index a350ecceb3..bbfcef9302 100644 --- a/features/frontend/checkout_finalize.feature +++ b/features/frontend/checkout_finalize.feature @@ -20,8 +20,8 @@ Feature: Checkout finalization | zone | name | | UK | DHL Express | And the following payment methods exist: - | name | gateway | enabled | - | Credit Card | stripe | yes | + | name | gateway | enabled | + | Dummy | dummy | yes | Scenario: Placing the order Given I am logged in user @@ -31,7 +31,7 @@ Feature: Checkout finalization And I press "Continue" And I select the "DHL Express" radio button And I press "Continue" - And I select the "Credit Card" radio button + And I select the "Dummy" radio button And I press "Continue" When I click "Place order" Then I should be on the store homepage diff --git a/features/frontend/checkout_security.feature b/features/frontend/checkout_security.feature index 2d9f72ada7..3669a95bd1 100644 --- a/features/frontend/checkout_security.feature +++ b/features/frontend/checkout_security.feature @@ -24,8 +24,8 @@ Feature: Checkout security | zone | name | | UK | DHL Express | And the following payment methods exist: - | name | gateway | enabled | - | Credit Card | stripe | yes | + | name | gateway | enabled | + | Dummy | dummy | yes | And I added product "PHP Top" to cart And I go to the checkout start page @@ -62,7 +62,7 @@ Feature: Checkout security And I press "Continue" And I select the "DHL Express" radio button And I press "Continue" - And I select the "Credit Card" radio button + And I select the "Dummy" radio button And I press "Continue" And I click "Place order" Then I should be on the store homepage diff --git a/features/frontend/checkout_taxation.feature b/features/frontend/checkout_taxation.feature index df01df1c9b..931bfe9046 100644 --- a/features/frontend/checkout_taxation.feature +++ b/features/frontend/checkout_taxation.feature @@ -26,8 +26,8 @@ Feature: Checkout taxation | zone | name | | UK | DHL Express | And the following payment methods exist: - | name | gateway | enabled | - | Credit Card | stripe | yes | + | name | gateway | enabled | + | Dummy | dummy | yes | And I am logged in user And I added product "PHP Top" to cart And I go to the checkout start page @@ -37,7 +37,7 @@ Feature: Checkout taxation And I press "Continue" And I select the "DHL Express" radio button And I press "Continue" - And I select the "Credit Card" radio button + And I select the "Dummy" radio button When I press "Continue" Then I should be on the checkout finalize step And "Tax total: €37.50" should appear on the page diff --git a/src/Sylius/Bundle/CoreBundle/Checkout/CheckoutProcessScenario.php b/src/Sylius/Bundle/CoreBundle/Checkout/CheckoutProcessScenario.php index 505c48bbe6..6b88f0d5e5 100644 --- a/src/Sylius/Bundle/CoreBundle/Checkout/CheckoutProcessScenario.php +++ b/src/Sylius/Bundle/CoreBundle/Checkout/CheckoutProcessScenario.php @@ -52,6 +52,7 @@ class CheckoutProcessScenario implements ProcessScenarioInterface ->add('shipping', 'sylius_checkout_shipping') ->add('payment', 'sylius_checkout_payment') ->add('finalize', 'sylius_checkout_finalize') + ->add('purchase', 'sylius_checkout_purchase') ; $builder diff --git a/src/Sylius/Bundle/CoreBundle/Checkout/Step/FinalizeStep.php b/src/Sylius/Bundle/CoreBundle/Checkout/Step/FinalizeStep.php index 278f7994b7..a33b6f32d8 100644 --- a/src/Sylius/Bundle/CoreBundle/Checkout/Step/FinalizeStep.php +++ b/src/Sylius/Bundle/CoreBundle/Checkout/Step/FinalizeStep.php @@ -13,6 +13,7 @@ namespace Sylius\Bundle\CoreBundle\Checkout\Step; use Sylius\Bundle\CoreBundle\Checkout\SyliusCheckoutEvents; use Sylius\Bundle\CoreBundle\Model\OrderInterface; +use Sylius\Bundle\CoreBundle\Model\Order; use Sylius\Bundle\FlowBundle\Process\Context\ProcessContextInterface; use Sylius\Bundle\SalesBundle\SyliusOrderEvents; @@ -45,10 +46,6 @@ class FinalizeStep extends CheckoutStep $order->setUser($this->getUser()); $this->completeOrder($order); - $this->getCartProvider()->abandonCart(); - - $translator = $this->get('translator'); - $this->get('session')->getFlashBag()->add('success', $translator->trans('sylius.checkout.success', array(), 'flashes')); return $this->complete(); } diff --git a/src/Sylius/Bundle/CoreBundle/DataFixtures/ORM/LoadPaymentMethodsData.php b/src/Sylius/Bundle/CoreBundle/DataFixtures/ORM/LoadPaymentMethodsData.php index f3fa4a6d6e..f821c71bdc 100644 --- a/src/Sylius/Bundle/CoreBundle/DataFixtures/ORM/LoadPaymentMethodsData.php +++ b/src/Sylius/Bundle/CoreBundle/DataFixtures/ORM/LoadPaymentMethodsData.php @@ -25,9 +25,9 @@ class LoadPaymentMethodsData extends DataFixture */ public function load(ObjectManager $manager) { - $manager->persist($this->createPaymentMethod('Test Credit Card', 'dummy')); - $manager->persist($this->createPaymentMethod('Stripe', 'dummy')); - $manager->persist($this->createPaymentMethod('Disabled Payment method', 'dummy'), false); + $manager->persist($this->createPaymentMethod('Dummy', 'dummy')); + $manager->persist($this->createPaymentMethod('Paypal Express Checkout', 'paypal_express_checkout')); + $manager->persist($this->createPaymentMethod('Stripe', 'stripe')); $manager->flush(); } diff --git a/src/Sylius/Bundle/CoreBundle/spec/Sylius/Bundle/CoreBundle/Checkout/CheckoutProcessScenarioSpec.php b/src/Sylius/Bundle/CoreBundle/spec/Sylius/Bundle/CoreBundle/Checkout/CheckoutProcessScenarioSpec.php index ba13d52f87..0d38c7272d 100644 --- a/src/Sylius/Bundle/CoreBundle/spec/Sylius/Bundle/CoreBundle/Checkout/CheckoutProcessScenarioSpec.php +++ b/src/Sylius/Bundle/CoreBundle/spec/Sylius/Bundle/CoreBundle/Checkout/CheckoutProcessScenarioSpec.php @@ -50,6 +50,7 @@ class CheckoutProcessScenarioSpec extends ObjectBehavior $builder->add('shipping', 'sylius_checkout_shipping')->willReturn($builder)->shouldBeCalled(); $builder->add('payment', 'sylius_checkout_payment')->willReturn($builder)->shouldBeCalled(); $builder->add('finalize', 'sylius_checkout_finalize')->willReturn($builder)->shouldBeCalled(); + $builder->add("purchase", "sylius_checkout_purchase")->willReturn($builder)->shouldBeCalled(); $builder->setDisplayRoute('sylius_checkout_display')->willReturn($builder)->shouldBeCalled(); $builder->setForwardRoute('sylius_checkout_forward')->willReturn($builder)->shouldBeCalled(); diff --git a/src/Sylius/Bundle/PaymentsBundle/Model/Payment.php b/src/Sylius/Bundle/PaymentsBundle/Model/Payment.php index 67fd01303e..03d4e73a85 100644 --- a/src/Sylius/Bundle/PaymentsBundle/Model/Payment.php +++ b/src/Sylius/Bundle/PaymentsBundle/Model/Payment.php @@ -83,15 +83,21 @@ class Payment implements PaymentInterface */ protected $updatedAt; + /** + * @var array + */ + protected $details; + /** * Constructor. */ public function __construct() { $this->amount = 0; - $this->state = PaymentInterface::STATE_PENDING; + $this->state = PaymentInterface::STATE_NEW; $this->logs = new ArrayCollection(); $this->createdAt = new \DateTime('now'); + $this->details = array(); } /** @@ -275,4 +281,20 @@ class Payment implements PaymentInterface return $this; } + + /** + * @param array $details + */ + public function setDetails(array $details) + { + $this->details = $details; + } + + /** + * @return array + */ + public function getDetails() + { + return $this->details; + } } diff --git a/src/Sylius/Bundle/PaymentsBundle/Model/PaymentInterface.php b/src/Sylius/Bundle/PaymentsBundle/Model/PaymentInterface.php index 12a4c163c3..1deb36ad87 100644 --- a/src/Sylius/Bundle/PaymentsBundle/Model/PaymentInterface.php +++ b/src/Sylius/Bundle/PaymentsBundle/Model/PaymentInterface.php @@ -27,6 +27,8 @@ interface PaymentInterface extends TimestampableInterface const STATE_FAILED = 'failed'; const STATE_VOID = 'void'; const STATE_COMPLETED = 'completed'; + const STATE_NEW = 'new'; + const STATE_UNKNOWN = 'unknown'; /** * Get payment method associated with this payment. @@ -127,4 +129,14 @@ interface PaymentInterface extends TimestampableInterface * @param PaymentLogInterface $log */ public function removeLog(PaymentLogInterface $log); + + /** + * @param array $details + */ + public function setDetails(array $details); + + /** + * @return array + */ + public function getDetails(); } diff --git a/src/Sylius/Bundle/PaymentsBundle/Resources/config/doctrine/model/Payment.orm.xml b/src/Sylius/Bundle/PaymentsBundle/Resources/config/doctrine/model/Payment.orm.xml index a8a179bff1..5ac2ab103b 100644 --- a/src/Sylius/Bundle/PaymentsBundle/Resources/config/doctrine/model/Payment.orm.xml +++ b/src/Sylius/Bundle/PaymentsBundle/Resources/config/doctrine/model/Payment.orm.xml @@ -26,6 +26,7 @@ + diff --git a/src/Sylius/Bundle/PaymentsBundle/spec/Sylius/Bundle/PaymentsBundle/Model/PaymentSpec.php b/src/Sylius/Bundle/PaymentsBundle/spec/Sylius/Bundle/PaymentsBundle/Model/PaymentSpec.php index b8b3923ff6..a767b99494 100644 --- a/src/Sylius/Bundle/PaymentsBundle/spec/Sylius/Bundle/PaymentsBundle/Model/PaymentSpec.php +++ b/src/Sylius/Bundle/PaymentsBundle/spec/Sylius/Bundle/PaymentsBundle/Model/PaymentSpec.php @@ -94,9 +94,9 @@ class PaymentSpec extends ObjectBehavior $this->getAmount()->shouldReturn(4999); } - function it_has_pending_state_by_default() + function it_has_new_state_by_default() { - $this->getState()->shouldReturn(PaymentInterface::STATE_PENDING); + $this->getState()->shouldReturn(PaymentInterface::STATE_NEW); } function its_state_is_mutable() diff --git a/src/Sylius/Bundle/PayumBundle/.gitignore b/src/Sylius/Bundle/PayumBundle/.gitignore new file mode 100644 index 0000000000..46aed33710 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/.gitignore @@ -0,0 +1,5 @@ +vendor/ +bin/ + +composer.phar +composer.lock diff --git a/src/Sylius/Bundle/PayumBundle/Checkout/Step/PurchaseStep.php b/src/Sylius/Bundle/PayumBundle/Checkout/Step/PurchaseStep.php new file mode 100644 index 0000000000..46de8c44be --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Checkout/Step/PurchaseStep.php @@ -0,0 +1,120 @@ +getCurrentCart(); + + $captureToken = $this->getTokenFactory()->createCaptureToken( + $order->getPayment()->getMethod()->getGateway(), + $order, + 'sylius_checkout_forward', + array('stepName' => $this->getName()) + ); + + return new RedirectResponse($captureToken->getTargetUrl()); + } + + /** + * {@inheritdoc} + */ + public function forwardAction(ProcessContextInterface $context) + { + $token = $this->getHttpRequestVerifier()->verify($this->getRequest()); + $this->getHttpRequestVerifier()->invalidate($token); + $this->getCartProvider()->abandonCart(); + + $payment = $this->getPayum()->getPayment($token->getPaymentName()); + + $status = new StatusRequest($token); + $payment->execute($status); + + /** @var OrderInterface $order */ + $order = $status->getModel(); + + if (!$order instanceof OrderInterface) { + throw new \RuntimeException(sprintf('Expected order to be set as model but it is %s', get_class($order))); + } + + $order->getPayment()->setState($status->getStatus()); + + if ($status->isSuccess()) { + $type = 'success'; + $msg = 'sylius.checkout.success'; + } elseif ($status->isPending()) { + $type = 'notice'; + $msg = 'sylius.checkout.pending'; + } elseif ($status->isCanceled()) { + $type = 'notice'; + $msg = 'sylius.checkout.canceled'; + } elseif ($status->isExpired()) { + $type = 'notice'; + $msg = 'sylius.checkout.expired'; + } elseif ($status->isSuspended()) { + $type = 'notice'; + $msg = 'sylius.checkout.suspended'; + } elseif ($status->isFailed()) { + $type = 'error'; + $msg = 'sylius.checkout.failed'; + } else { + $type = 'error'; + $msg = 'sylius.checkout.unknown'; + } + + //TODO: an event here + + $this->getDoctrine()->getManager()->flush(); + + $this->get('session')->getFlashBag()->add($type, $this->get('translator')->trans($msg, array(), 'flashes')); + + return $this->complete(); + } + + /** + * @return RegistryInterface + */ + protected function getPayum() + { + return $this->get('payum'); + } + + /** + * @return TokenFactory + */ + protected function getTokenFactory() + { + return $this->get('payum.security.token_factory'); + } + + /** + * @return HttpRequestVerifierInterface + */ + protected function getHttpRequestVerifier() + { + return $this->get('payum.security.http_request_verifier'); + } +} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/DependencyInjection/Configuration.php b/src/Sylius/Bundle/PayumBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000000..5e103aa3b9 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/DependencyInjection/Configuration.php @@ -0,0 +1,60 @@ +root('sylius_payum'); + + $rootNode + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('driver')->cannotBeOverwritten()->isRequired()->cannotBeEmpty()->end() + ->end() + ; + + $this->addClassesSection($rootNode); + + return $treeBuilder; + } + + /** + * Adds `classes` section. + * + * @param ArrayNodeDefinition $node + */ + private function addClassesSection(ArrayNodeDefinition $node) + { + $node + ->children() + ->arrayNode('classes') + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('payment_security_token') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('model')->defaultValue('Sylius\\Bundle\\PayumBundle\\Model\\PaymentSecurityToken')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } +} diff --git a/src/Sylius/Bundle/PayumBundle/DependencyInjection/SyliusPayumExtension.php b/src/Sylius/Bundle/PayumBundle/DependencyInjection/SyliusPayumExtension.php new file mode 100644 index 0000000000..e5437c9c63 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/DependencyInjection/SyliusPayumExtension.php @@ -0,0 +1,73 @@ +processConfiguration($configuration, $config); + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + + $driver = $config['driver']; + + if (!in_array($driver, SyliusPayumBundle::getSupportedDrivers())) { + throw new \InvalidArgumentException(sprintf('Driver "%s" is unsupported for SyliusPayumBundle', $driver)); + } + + $loader->load(sprintf('driver/%s.xml', $driver)); + + $container->setParameter('sylius_payum.driver', $driver); + $container->setParameter('sylius_payum.driver.'.$driver, true); + + $loader->load('services.xml'); + + $classes = $config['classes']; + + $this->mapClassParameters($classes, $container); + + if ($container->hasParameter('sylius.config.classes')) { + $classes = array_merge($classes, $container->getParameter('sylius.config.classes')); + } + + $container->setParameter('sylius.config.classes', $classes); + } + + /** + * Remap class parameters. + * + * @param array $classes + * @param ContainerBuilder $container + */ + protected function mapClassParameters(array $classes, ContainerBuilder $container) + { + foreach ($classes as $model => $serviceClasses) { + foreach ($serviceClasses as $service => $class) { + $service = $service === 'form' ? 'form.type' : $service; + $container->setParameter(sprintf('sylius.%s.%s.class', $service, $model), $class); + } + } + } +} diff --git a/src/Sylius/Bundle/PayumBundle/Model/PaymentSecurityToken.php b/src/Sylius/Bundle/PayumBundle/Model/PaymentSecurityToken.php new file mode 100644 index 0000000000..6079b7aedb --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Model/PaymentSecurityToken.php @@ -0,0 +1,18 @@ +supports($request)) { + throw RequestNotSupportedException::createActionNotSupported($this, $request); + } + + /** @var OrderInterface $order */ + $order = $request->getModel(); + + $paymentDetails = $order->getPayment()->getDetails(); + if (empty($paymentDetails)) { + $order->getPayment()->setDetails(array( + 'captured' => true, + )); + } + } + + /** + * {@inheritDoc} + */ + public function supports($request) + { + return + $request instanceof CaptureRequest && + $request->getModel() instanceof OrderInterface + ; + } +} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/Payum/Action/CaptureOrderWithPaypalAction.php b/src/Sylius/Bundle/PayumBundle/Payum/Action/CaptureOrderWithPaypalAction.php new file mode 100644 index 0000000000..f0343d0b08 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Payum/Action/CaptureOrderWithPaypalAction.php @@ -0,0 +1,81 @@ +supports($request)) { + throw RequestNotSupportedException::createActionNotSupported($this, $request); + } + + /** @var OrderInterface $order */ + $order = $request->getModel(); + + $paymentDetails = $order->getPayment()->getDetails(); + if (empty($paymentDetails)) { + $paymentDetails['RETURNURL'] = $request->getToken()->getTargetUrl(); + $paymentDetails['CANCELURL'] = $request->getToken()->getTargetUrl(); + $paymentDetails['INVNUM'] = $order->getNumber(); + + $paymentDetails['PAYMENTREQUEST_0_CURRENCYCODE'] = $order->getCurrency(); + $paymentDetails['PAYMENTREQUEST_0_AMT'] = number_format(($order->getTotal() + $order->getShippingTotal()) / 100, 2); + $paymentDetails['PAYMENTREQUEST_0_ITEMAMT'] = number_format($order->getItemsTotal() / 100, 2); + $paymentDetails['PAYMENTREQUEST_0_TAXAMT'] = number_format($order->getTaxTotal() / 100, 2); + $paymentDetails['PAYMENTREQUEST_0_SHIPPINGAMT'] = number_format($order->getShippingTotal() / 100, 2); + + $m = 0; + foreach ($order->getItems() as $item) { + $paymentDetails['L_PAYMENTREQUEST_0_AMT'.$m] = number_format($item->getTotal() / 100, 2); + $paymentDetails['L_PAYMENTREQUEST_0_QTY'.$m] = $item->getQuantity(); + + $m++; + } + } + + // TODO: find a way to simply the next logic + + $paymentDetails = ArrayObject::ensureArrayObject($paymentDetails); + + try { + $this->payment->execute(new CaptureRequest($paymentDetails)); + $order->getPayment()->setDetails((array) $paymentDetails); + } catch (\Exception $e) { + $order->getPayment()->setDetails((array) $paymentDetails); + + throw $e; + } + } + + /** + * {@inheritDoc} + */ + public function supports($request) + { + return + $request instanceof SecuredCaptureRequest && + $request->getModel() instanceof OrderInterface + ; + } +} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/Payum/Action/CaptureOrderWithStripeAction.php b/src/Sylius/Bundle/PayumBundle/Payum/Action/CaptureOrderWithStripeAction.php new file mode 100644 index 0000000000..104e465183 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Payum/Action/CaptureOrderWithStripeAction.php @@ -0,0 +1,80 @@ +supports($request)) { + throw RequestNotSupportedException::createActionNotSupported($this, $request); + } + + /** @var OrderInterface $order */ + $order = $request->getModel(); + + $paymentDetails = $order->getPayment()->getDetails(); + if (empty($paymentDetails)) { + $this->payment->execute($obtainCreditCardRequest = new ObtainCreditCardRequest($order)); + + $paymentDetails = array( + 'card' => array( + 'number' => $obtainCreditCardRequest->getCreditCard()->getNumber(), + 'expiryMonth' => $obtainCreditCardRequest->getCreditCard()->getExpiryMonth(), + 'expiryYear' => $obtainCreditCardRequest->getCreditCard()->getExpiryYear(), + 'cvv' => $obtainCreditCardRequest->getCreditCard()->getSecurityCode() + ), + 'amount' => number_format($order->getTotal() / 100, 2), + 'currency' => $order->getCurrency(), + ); + } + + // TODO: find a way to simply the next logic + + $paymentDetails = ArrayObject::ensureArrayObject($paymentDetails); + + try { + $this->payment->execute(new CaptureRequest($paymentDetails)); + + unset($paymentDetails['card']); + $order->getPayment()->setDetails((array) $paymentDetails); + } catch (\Exception $e) { + unset($paymentDetails['card']); + $order->getPayment()->setDetails((array) $paymentDetails); + + throw $e; + } + } + + /** + * {@inheritdoc} + */ + public function supports($request) + { + return + $request instanceof SecuredCaptureRequest && + $request->getModel() instanceof OrderInterface + ; + } +} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/Payum/Action/DummyOrderStatusAction.php b/src/Sylius/Bundle/PayumBundle/Payum/Action/DummyOrderStatusAction.php new file mode 100644 index 0000000000..75a51d5ea9 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Payum/Action/DummyOrderStatusAction.php @@ -0,0 +1,60 @@ +supports($request)) { + throw RequestNotSupportedException::createActionNotSupported($this, $request); + } + + /** @var Order $order */ + $order = $request->getModel(); + + $paymentDetails = $order->getPayment()->getDetails(); + if (empty($paymentDetails)) { + $request->markNew(); + + return; + } + + if (isset($paymentDetails['captured'])) { + $request->markSuccess(); + + return; + } + + $request->markUnknown(); + } + + /** + * {@inheritDoc} + */ + public function supports($request) + { + return + $request instanceof StatusRequestInterface && + $request->getModel() instanceof Order + ; + } +} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/Payum/Action/ObtainCreditCardAction.php b/src/Sylius/Bundle/PayumBundle/Payum/Action/ObtainCreditCardAction.php new file mode 100644 index 0000000000..e08e66cb57 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Payum/Action/ObtainCreditCardAction.php @@ -0,0 +1,102 @@ +formFactory = $formFactory; + $this->templating = $templating; + } + + /** + * @param Request $request + */ + public function setRequest(Request $request = null) + { + $this->httpRequest = $request; + } + + /** + * {@inheritDoc} + */ + public function execute($request) + { + /** @var $request ObtainCreditCardRequest */ + if (!$this->supports($request)) { + throw RequestNotSupportedException::createActionNotSupported($this, $request); + } + if (!$this->httpRequest) { + throw new LogicException('The action can be run only when http request is set.'); + } + + $form = $this->createCreditCardForm(); + $form->handleRequest($this->httpRequest); + if ($form->isValid()) { + $request->setCreditCard($form->getData()); + + return; + } + + throw new ResponseInteractiveRequest(new Response( + $this->templating->render('SyliusPayumBundle::Payum\Action\obtainCreditCard.html.twig', array( + 'form' => $form->createView() + )) + )); + } + + /** + * {@inheritDoc} + */ + public function supports($request) + { + return $request instanceof ObtainCreditCardRequest; + } + + /** + * @return \Symfony\Component\Form\Form + */ + protected function createCreditCardForm() + { + return $this->formFactory->create('sylius_credit_card'); + } +} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/Payum/Action/OrderStatusAction.php b/src/Sylius/Bundle/PayumBundle/Payum/Action/OrderStatusAction.php new file mode 100644 index 0000000000..02c518e824 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Payum/Action/OrderStatusAction.php @@ -0,0 +1,55 @@ +supports($request)) { + throw RequestNotSupportedException::createActionNotSupported($this, $request); + } + + /** @var OrderInterface $order */ + $order = $request->getModel(); + + if ($order->getPayment()->getDetails()) { + $request->setModel($order->getPayment()->getDetails()); + + $this->payment->execute($request); + + $request->setModel($order); + } else { + $request->markNew(); + } + } + + /** + * {@inheritDoc} + */ + public function supports($request) + { + return + $request instanceof StatusRequestInterface && + $request->getModel() instanceof OrderInterface + ; + } +} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/Payum/Request/ObtainCreditCardRequest.php b/src/Sylius/Bundle/PayumBundle/Payum/Request/ObtainCreditCardRequest.php new file mode 100644 index 0000000000..a5e0ab9a6e --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Payum/Request/ObtainCreditCardRequest.php @@ -0,0 +1,60 @@ +order = $order; + } + + /** + * @return OrderInterface + */ + public function getOrder() + { + return $this->order; + } + + /** + * @param CreditCardInterface $creditCard + */ + public function setCreditCard(CreditCardInterface $creditCard) + { + $this->creditCard = $creditCard; + } + + /** + * @return CreditCardInterface + */ + public function getCreditCard() + { + return $this->creditCard; + } +} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/Payum/Request/StatusRequest.php b/src/Sylius/Bundle/PayumBundle/Payum/Request/StatusRequest.php new file mode 100644 index 0000000000..d822638759 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Payum/Request/StatusRequest.php @@ -0,0 +1,146 @@ +status = PaymentInterface::STATE_NEW; + } + + /** + * {@inheritdoc} + */ + public function isNew() + { + return $this->status === PaymentInterface::STATE_NEW; + } + + /** + * {@inheritdoc} + */ + public function markSuccess() + { + $this->status = PaymentInterface::STATE_COMPLETED; + } + + /** + * {@inheritdoc} + */ + public function isSuccess() + { + return $this->status === PaymentInterface::STATE_COMPLETED; + } + + /** + * {@inheritdoc} + */ + public function markSuspended() + { + $this->status = PaymentInterface::STATE_VOID; + } + + /** + * {@inheritdoc} + */ + public function isSuspended() + { + return $this->status === PaymentInterface::STATE_VOID; + } + + /** + * {@inheritdoc} + */ + public function markExpired() + { + $this->status = PaymentInterface::STATE_FAILED; + } + + /** + * {@inheritdoc} + */ + public function isExpired() + { + return $this->status === PaymentInterface::STATE_FAILED; + } + + /** + * {@inheritdoc} + */ + public function markCanceled() + { + $this->status = PaymentInterface::STATE_VOID; + } + + /** + * {@inheritdoc} + */ + public function isCanceled() + { + return $this->status === PaymentInterface::STATE_VOID; + } + + /** + * {@inheritdoc} + */ + public function markPending() + { + $this->status = PaymentInterface::STATE_PENDING; + } + + /** + * {@inheritdoc} + */ + public function isPending() + { + return $this->status === PaymentInterface::STATE_PENDING; + } + + /** + * {@inheritdoc} + */ + public function markFailed() + { + $this->status = PaymentInterface::STATE_FAILED; + } + + /** + * {@inheritdoc} + */ + public function isFailed() + { + return $this->status === PaymentInterface::STATE_FAILED; + } + + /** + * {@inheritdoc} + */ + public function markUnknown() + { + $this->status = PaymentInterface::STATE_UNKNOWN; + } + + /** + * {@inheritdoc} + */ + public function isUnknown() + { + return $this->status === PaymentInterface::STATE_UNKNOWN; + } +} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/README.md b/src/Sylius/Bundle/PayumBundle/README.md new file mode 100644 index 0000000000..4e4459eb0d --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/README.md @@ -0,0 +1,4 @@ +SyliusPayumBundle +================= + +Provide payum integration for sylius. \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/Resources/config/doctrine/model/PaymentSecurityToken.orm.xml b/src/Sylius/Bundle/PayumBundle/Resources/config/doctrine/model/PaymentSecurityToken.orm.xml new file mode 100644 index 0000000000..536d30c67b --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Resources/config/doctrine/model/PaymentSecurityToken.orm.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/Resources/config/driver/doctrine/orm.xml b/src/Sylius/Bundle/PayumBundle/Resources/config/driver/doctrine/orm.xml new file mode 100644 index 0000000000..5bea6f9aea --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Resources/config/driver/doctrine/orm.xml @@ -0,0 +1,39 @@ + + + + + + + + Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository + + + + + + + + + + + + + diff --git a/src/Sylius/Bundle/PayumBundle/Resources/config/services.xml b/src/Sylius/Bundle/PayumBundle/Resources/config/services.xml new file mode 100644 index 0000000000..421f202eba --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Resources/config/services.xml @@ -0,0 +1,52 @@ + + + + + + + + Sylius\Bundle\PayumBundle\Checkout\Step\PurchaseStep + Sylius\Bundle\PayumBundle\Payum\Action\CaptureOrderWithPaypalAction + Sylius\Bundle\PayumBundle\Payum\Action\CaptureOrderWithDummyAction + Sylius\Bundle\PayumBundle\Payum\Action\CaptureOrderWithStripeAction + Sylius\Bundle\PayumBundle\Payum\Action\OrderStatusAction + Sylius\Bundle\PayumBundle\Payum\Action\DummyOrderStatusAction + Sylius\Bundle\PayumBundle\Payum\Action\ObtainCreditCardAction + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sylius/Bundle/PayumBundle/Resources/meta/LICENSE b/src/Sylius/Bundle/PayumBundle/Resources/meta/LICENSE new file mode 100644 index 0000000000..653d37087d --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Resources/meta/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2013 Paweł Jędrzejewski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/Sylius/Bundle/PayumBundle/Resources/views/Payum/Action/obtainCreditCard.html.twig b/src/Sylius/Bundle/PayumBundle/Resources/views/Payum/Action/obtainCreditCard.html.twig new file mode 100644 index 0000000000..1e55f6c0f5 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/Resources/views/Payum/Action/obtainCreditCard.html.twig @@ -0,0 +1,24 @@ +{% extends 'SyliusWebBundle:Frontend:layout.html.twig' %} + +{% block content %} + + +
+ + {{ form_errors(form) }} + +
+ {{ form_widget(form) }} +
+ + {{ form_widget(form._token) }} + +
+ {{ 'sylius.checkout.back'|trans }}   + +
+ +
+{% endblock %} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/SyliusPayumBundle.php b/src/Sylius/Bundle/PayumBundle/SyliusPayumBundle.php new file mode 100644 index 0000000000..287e62862a --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/SyliusPayumBundle.php @@ -0,0 +1,51 @@ + 'sylius.model.payment_security_token.class', + ); + + $container->addCompilerPass(new ResolveDoctrineTargetEntitiesPass('sylius_payum', $interfaces)); + + $mappings = array( + realpath(__DIR__ . '/Resources/config/doctrine/model') => 'Sylius\Bundle\PayumBundle\Model', + ); + + $container->addCompilerPass(DoctrineOrmMappingsPass::createXmlMappingDriver($mappings, array('doctrine.orm.entity_manager'), 'sylius_payum.driver.doctrine/orm')); + } +} diff --git a/src/Sylius/Bundle/PayumBundle/composer.json b/src/Sylius/Bundle/PayumBundle/composer.json new file mode 100644 index 0000000000..1785cff00e --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/composer.json @@ -0,0 +1,40 @@ +{ + "name": "sylius/payum-bundle", + "type": "symfony-bundle", + "description": "Payum integration for Symfony2 e-commerce applications.", + "keywords": ["payments", "payment", "payum"], + "homepage": "http://sylius.com", + "license": "MIT", + "authors": [ + { + "name": "Kotlyar Maksim", + "email": "kotlyar.maksim@gmail.com", + }, + { + "name": "Community contributions", + "homepage": "https://github.com/Sylius/SyliusPaymentsBundle/contributors" + } + ], + "require": { + "php": ">=5.3.3", + + "sylius/sales-bundle": "1.0.*@dev", + "sylius/core-bundle": "1.0.*@dev", + "sylius/payments-bundle": "1.0.*@dev", + "sylius/resource-bundle": "1.0.*@dev", + "payum/payum-bundle": "0.6.*@dev", + "payum/payum": "0.6.*@dev" + }, + "config": { + "bin-dir": "bin" + }, + "autoload": { + "psr-0": { "Sylius\\Bundle\\PayumBundle": "" } + }, + "target-dir": "Sylius/Bundle/PayumBundle", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Model/PaymentSecurityTokenSpec.php b/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Model/PaymentSecurityTokenSpec.php new file mode 100644 index 0000000000..3b63fdfbc7 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Model/PaymentSecurityTokenSpec.php @@ -0,0 +1,19 @@ +shouldHaveType('Sylius\Bundle\PayumBundle\Model\PaymentSecurityToken'); + } + + function it_extends_payum_token() + { + $this->shouldHaveType('Payum\Model\Token'); + } +} diff --git a/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Payum/Action/OrderStatusActionSpec.php b/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Payum/Action/OrderStatusActionSpec.php new file mode 100644 index 0000000000..050f8ab40a --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Payum/Action/OrderStatusActionSpec.php @@ -0,0 +1,103 @@ +shouldHaveType('Sylius\Bundle\PayumBundle\Payum\Action\OrderStatusAction'); + } + + function it_extends_payum_payment_aware_action() + { + $this->shouldHaveType('Payum\Action\PaymentAwareAction'); + } + + /** + * @param Sylius\Bundle\CoreBundle\Model\OrderInterface $order + * @param Payum\Request\StatusRequestInterface $statusRequest + */ + function it_should_support_status_request_with_order_model($order, $statusRequest) + { + $statusRequest->getModel()->willReturn($order); + + $this->supports($statusRequest)->shouldReturn(true); + } + + /** + * @param Payum\Request\StatusRequestInterface $statusRequest + */ + function it_should_not_support_status_request_with_no_order_model($statusRequest) + { + $statusRequest->getModel()->willReturn('foo'); + + $this->supports($statusRequest)->shouldReturn(false); + } + + function it_should_not_support_any_no_status_requests() + { + $this->supports('foo')->shouldReturn(false); + } + + function it_throws_exception_if_executing_not_supported_request() + { + $notSupportedRequest = 'foo'; + + $this + ->shouldThrow('Payum\Exception\RequestNotSupportedException') + ->duringExecute($notSupportedRequest) + ; + } + + /** + * @param Sylius\Bundle\CoreBundle\Model\OrderInterface $order + * @param Sylius\Bundle\PaymentsBundle\Model\PaymentInterface $payment + * @param Payum\Request\StatusRequestInterface $statusRequest + */ + function it_should_mark_new_if_order_have_empty_payment_details($order, $payment, $statusRequest) + { + $statusRequest->getModel()->willReturn($order); + $statusRequest->markNew()->shouldBeCalled(); + + $order->getPayment()->willReturn($payment); + $payment->getDetails()->willReturn(array()); + + $this->execute($statusRequest); + } + + /** + * @param Sylius\Bundle\CoreBundle\Model\OrderInterface $order + * @param Sylius\Bundle\PaymentsBundle\Model\PaymentInterface $payment + * @param Payum\Request\StatusRequestInterface $statusRequest + * @param Payum\PaymentInterface $payment + */ + function it_should_do_status_subrequest_with_payment_details_as_model($order, $payment, $statusRequest, $payment) + { + $details = array('foo' => 'foo', 'bar' => 'baz'); + + $statusRequest->getModel()->willReturn($order); + $statusRequest->setModel($details)->shouldBeCalled(); + $statusRequest->setModel($order)->shouldBeCalled(); + + $payment->execute($statusRequest)->shouldBeCalled(); + + $order->getPayment()->willReturn($payment); + $payment->getDetails()->willReturn($details); + + $this->setPayment($payment); + $this->execute($statusRequest); + } +} diff --git a/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Payum/Request/ObtainCreditCardRequestSpec.php b/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Payum/Request/ObtainCreditCardRequestSpec.php new file mode 100644 index 0000000000..f291dcc2f3 --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Payum/Request/ObtainCreditCardRequestSpec.php @@ -0,0 +1,48 @@ +beConstructedWith($order); + } + + function it_is_initializable() + { + $this->shouldHaveType('Sylius\Bundle\PayumBundle\Payum\Request\ObtainCreditCardRequest'); + } + + /** + * @param Sylius\Bundle\CoreBundle\Model\OrderInterface $order + */ + function it_should_allow_get_order_set_in_constructor($order) + { + $this->getOrder()->shouldReturn($order); + } + + /** + * @param Sylius\Bundle\PaymentsBundle\Model\CreditCardInterface $creditCard + */ + function it_should_allow_get_credit_card_set_before($creditCard) + { + $this->setCreditCard($creditCard); + + $this->getCreditCard()->shouldReturn($creditCard); + } +} \ No newline at end of file diff --git a/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Payum/Request/StatusRequestSpec.php b/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Payum/Request/StatusRequestSpec.php new file mode 100644 index 0000000000..7b5984030a --- /dev/null +++ b/src/Sylius/Bundle/PayumBundle/spec/Sylius/Bundle/PayumBundle/Payum/Request/StatusRequestSpec.php @@ -0,0 +1,146 @@ +beConstructedWith(new \stdClass); + } + + function it_is_initializable() + { + $this->shouldHaveType('Sylius\Bundle\PayumBundle\Payum\Request\StatusRequest'); + } + + function it_extends_base_status_class() + { + $this->shouldHaveType('Payum\Request\BaseStatusRequest'); + } + + function it_is_new_when_marked_as_new() + { + $this->markNew(); + + $this->isNew()->shouldReturn(true); + } + + function it_is_return_new_status_when_marked_as_new() + { + $this->markNew(); + + $this->getStatus()->shouldReturn(PaymentInterface::STATE_NEW); + } + + function it_is_success_when_marked_as_success() + { + $this->markSuccess(); + + $this->isSuccess()->shouldReturn(true); + } + + function it_is_return_completed_status_when_marked_as_success() + { + $this->markSuccess(); + + $this->getStatus()->shouldReturn(PaymentInterface::STATE_COMPLETED); + } + + function it_is_pending_when_marked_as_pending() + { + $this->markPending(); + + $this->isPending()->shouldReturn(true); + } + + function it_is_return_pending_status_when_marked_as_pending() + { + $this->markPending(); + + $this->getStatus()->shouldReturn(PaymentInterface::STATE_PENDING); + } + + function it_is_failed_when_marked_as_failed() + { + $this->markFailed(); + + $this->isFailed()->shouldReturn(true); + } + + function it_is_return_failed_status_when_marked_as_failed() + { + $this->markFailed(); + + $this->getStatus()->shouldReturn(PaymentInterface::STATE_FAILED); + } + + function it_is_canceled_when_marked_as_canceled() + { + $this->markCanceled(); + + $this->isCanceled()->shouldReturn(true); + } + + function it_is_return_void_status_when_marked_as_canceled() + { + $this->markCanceled(); + + $this->getStatus()->shouldReturn(PaymentInterface::STATE_VOID); + } + + function it_is_suspended_when_marked_as_suspended() + { + $this->markSuspended(); + + $this->isSuspended()->shouldReturn(true); + } + + function it_is_return_void_status_when_marked_as_suspended() + { + $this->markSuspended(); + + $this->getStatus()->shouldReturn(PaymentInterface::STATE_VOID); + } + + function it_is_expired_when_marked_as_expired() + { + $this->markExpired(); + + $this->isExpired()->shouldReturn(true); + } + + function it_is_return_failed_status_when_marked_as_expired() + { + $this->markExpired(); + + $this->getStatus()->shouldReturn(PaymentInterface::STATE_FAILED); + } + + function it_is_unknown_when_marked_as_unknown() + { + $this->markUnknown(); + + $this->isUnknown()->shouldReturn(true); + } + + function it_is_return_unknown_status_when_marked_as_unknown() + { + $this->markUnknown(); + + $this->getStatus()->shouldReturn(PaymentInterface::STATE_UNKNOWN); + } +} \ No newline at end of file