├── .gitignore
├── tests
├── config.php
├── phpunit.xml
├── unit_tests
│ ├── Exception
│ │ ├── PayplugExceptionTest.php
│ │ └── HttpExceptionTest.php
│ ├── Resource
│ │ ├── PaymentFailureTest.php
│ │ ├── PaymentNotificationTest.php
│ │ ├── PaymentAuthorizationTest.php
│ │ ├── PaymentCardTest.php
│ │ ├── PaymentCustomerTest.php
│ │ ├── PaymentHostedPaymentTest.php
│ │ ├── APIResourceTest.php
│ │ ├── CardTest.php
│ │ ├── AccountingReportTest.php
│ │ ├── OneySimulationResourceTest.php
│ │ └── RefundTest.php
│ ├── PluginTelemetryTest.php
│ ├── Core
│ │ ├── APIRoutesTest.php
│ │ └── HttpClientTest.php
│ ├── NotificationTest.php
│ ├── PayplugTest.php
│ └── OneySimulationTest.php
├── integration_test
│ └── HttpClientTest.php
├── utils.php
└── README.rst
├── lib
├── Payplug
│ ├── Exception
│ │ ├── ForbiddenException.php
│ │ ├── NotFoundException.php
│ │ ├── BadRequestException.php
│ │ ├── NotAllowedException.php
│ │ ├── UnauthorizedException.php
│ │ ├── PayplugServerException.php
│ │ ├── InvalidPaymentException.php
│ │ ├── PHPVersionException.php
│ │ ├── DependencyException.php
│ │ ├── UnprocessableEntityException.php
│ │ ├── ConfigurationException.php
│ │ ├── UndefinedAttributeException.php
│ │ ├── ConfigurationNotSetException.php
│ │ ├── ConnectionException.php
│ │ ├── UnknownAPIResourceException.php
│ │ ├── UnexpectedAPIResponseException.php
│ │ ├── PayplugException.php
│ │ └── HttpException.php
│ ├── Resource
│ │ ├── IAPIResourceFactory.php
│ │ ├── PaymentCard.php
│ │ ├── PaymentCustomer.php
│ │ ├── PaymentBilling.php
│ │ ├── PaymentShipping.php
│ │ ├── PaymentNotification.php
│ │ ├── PaymentAuthorization.php
│ │ ├── PaymentHostedPayment.php
│ │ ├── PaymentPaymentFailure.php
│ │ ├── InstallmentPlanSchedule.php
│ │ ├── IVerifiableAPIResource.php
│ │ ├── Card.php
│ │ ├── OneySimulationResource.php
│ │ ├── AccountingReport.php
│ │ ├── APIResource.php
│ │ ├── Refund.php
│ │ ├── InstallmentPlan.php
│ │ └── Payment.php
│ ├── Card.php
│ ├── OneySimulation.php
│ ├── Notification.php
│ ├── Core
│ │ ├── IHttpRequest.php
│ │ ├── CurlRequest.php
│ │ ├── Config.php
│ │ └── APIRoutes.php
│ ├── AccountingReport.php
│ ├── Refund.php
│ ├── InstallmentPlan.php
│ ├── Payment.php
│ ├── PluginTelemetry.php
│ ├── Payplug.php
│ └── Authentication.php
└── init.php
├── rector.php
├── docs
├── index.rst
├── Makefile
└── conf.py
├── composer.json
├── LICENCE.md
├── CHANGELOG.md
├── .github
└── workflows
│ └── payplug-ci.yml
└── README.rst
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.swp
3 | _build/
4 | .idea/
5 | vendor/
6 | *.iml
7 | *.phar
8 | coverage/
9 |
--------------------------------------------------------------------------------
/tests/config.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ..
5 |
6 | ../lib
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | withPaths([
10 | __DIR__ . '/lib',
11 | ])
12 | ->withPhpVersion(Rector\ValueObject\PhpVersion::PHP_84)
13 | ->withRules([
14 | ExplicitNullableParamTypeRector::class,
15 | CompleteDynamicPropertiesRector::class,
16 | ]);
--------------------------------------------------------------------------------
/lib/Payplug/Resource/IAPIResourceFactory.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
20 | return $object;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/PaymentCustomer.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
20 | return $object;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/PaymentBilling.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
20 | return $object;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/PaymentShipping.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
20 | return $object;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/PaymentNotification.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
20 | return $object;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/PaymentAuthorization.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
20 | return $object;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/PaymentHostedPayment.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
20 | return $object;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/PaymentPaymentFailure.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
20 | return $object;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/unit_tests/Exception/PayplugExceptionTest.php:
--------------------------------------------------------------------------------
1 | getMockForAbstractClass('\PayPlug\Exception\HttpException', array('this_is_a_message', 808));
14 |
15 | $this->assertContains('Mock_HttpException', (string)$exception);
16 | $this->assertContains('this_is_a_message', (string)$exception);
17 | $this->assertContains('808', (string)$exception);
18 | }
19 | }
--------------------------------------------------------------------------------
/lib/Payplug/Card.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
20 | return $object;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/Payplug/Exception/PayplugException.php:
--------------------------------------------------------------------------------
1 | code}]: {$this->message}";
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tests/unit_tests/Resource/PaymentFailureTest.php:
--------------------------------------------------------------------------------
1 | 'card_stolen',
15 | 'message' => 'The card is stolen.'
16 | ));
17 |
18 | $this->assertEquals('card_stolen', $paymentFailure->code);
19 | $this->assertEquals('The card is stolen.', $paymentFailure->message);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/integration_test/HttpClientTest.php:
--------------------------------------------------------------------------------
1 | _configuration = new Payplug('abc','1970-01-01');
19 | }
20 |
21 | public function testAPIRequest()
22 | {
23 | $httpClient = new Core\HttpClient($this->_configuration);
24 | $response = $httpClient->testRemote();
25 | $this->assertEquals(200, $response['httpStatus']);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/Payplug/OneySimulation.php:
--------------------------------------------------------------------------------
1 | 'https://www.example.net/payplug_notification.html',
15 | 'response_code' => 200
16 | ));
17 |
18 | $this->assertEquals('https://www.example.net/payplug_notification.html', $notification->url);
19 | $this->assertEquals(200, $notification->response_code);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/unit_tests/Resource/PaymentAuthorizationTest.php:
--------------------------------------------------------------------------------
1 | 1554896133,
15 | 'expires_at' => 1555459200,
16 | 'authorized_amount' => 4200,
17 | ));
18 |
19 | $this->assertEquals(1554896133, $hostedPayment->authorized_at);
20 | $this->assertEquals(1555459200, $hostedPayment->expires_at);
21 | $this->assertEquals(4200, $hostedPayment->authorized_amount);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/IVerifiableAPIResource.php:
--------------------------------------------------------------------------------
1 | '1234',
15 | 'country' => 'FR',
16 | 'exp_year' => 2022,
17 | 'exp_month' => 12,
18 | 'brand' => 'Visa'
19 | ));
20 |
21 | $this->assertEquals('1234', $card->last4);
22 | $this->assertEquals('FR', $card->country);
23 | $this->assertEquals(2022, $card->exp_year);
24 | $this->assertEquals(12, $card->exp_month);
25 | $this->assertEquals('Visa', $card->brand);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | PayPlug PHP library documentation
2 | =================================
3 |
4 | Please see https://www.payplug.com/docs/api/?php for an up-to-date documentation of the PHP Library.
5 |
6 | The latest version of the library is now available on `GitHub`_.
7 |
8 | .. _github: https://github.com/payplug/payplug-php
9 |
10 | Version Support:
11 | ----------------
12 |
13 | If you are using PHP 5.2, you can still download the 1.1.12 version (`zip`_, `gz`_, `bz2`_).
14 |
15 | .. _zip: https://bitbucket.org/payplug/payplug_php/get/V1.1.2.zip
16 | .. _gz: https://bitbucket.org/payplug/payplug_php/get/V1.1.2.tar.gz
17 | .. _bz2: https://bitbucket.org/payplug/payplug_php/get/V1.1.2.tar.bz2
18 |
19 | The documentation is still available on the `1.1.2 documentation page`_.
20 |
21 | .. _1.1.2 documentation page: http://payplug-php.readthedocs.org/en/v1.1.2/
22 |
23 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "payplug/payplug-php",
3 | "description": "A simple PHP library for PayPlug public API.",
4 | "type": "library",
5 | "homepage": "https://www.payplug.com",
6 | "time": "2015-05-06",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "PayPlug",
11 | "email": "support@payplug.com"
12 | },
13 | {
14 | "name": "Daniel FLOREZ MURILLO",
15 | "email": "dmurillo@payplug.com"
16 | }
17 | ],
18 | "require": {
19 | "php": ">=5.3.0",
20 | "ext-curl": "*",
21 | "ext-openssl": "*"
22 | },
23 | "require-dev": {
24 | "phpunit/phpunit": "5.7.*",
25 | "phpdocumentor/phpdocumentor": "2.*",
26 | "rector/rector": "^1.2"
27 | },
28 | "minimum-stability": "stable",
29 | "autoload": {
30 | "psr-0": {"Payplug\\": "lib/"},
31 | "psr-4": {"Payplug\\": "lib/Payplug"}
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tests/utils.php:
--------------------------------------------------------------------------------
1 | getMethod($methodName);
22 | $method->setAccessible(true);
23 |
24 | return $method->invokeArgs($object, $parameters);
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/lib/Payplug/Notification.php:
--------------------------------------------------------------------------------
1 | getConsistentResource($authentication);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/LICENCE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Payplug
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/tests/unit_tests/Resource/PaymentCustomerTest.php:
--------------------------------------------------------------------------------
1 | 'john.doe@example.com',
15 | 'first_name' => 'John',
16 | 'last_name' => 'Doe',
17 | 'address1' => 'rue abc',
18 | 'address2' => 'cba',
19 | 'postcode' => '12345',
20 | 'country' => 'FR'
21 | ));
22 |
23 | $this->assertEquals('john.doe@example.com', $customer->email);
24 | $this->assertEquals('John', $customer->first_name);
25 | $this->assertEquals('Doe', $customer->last_name);
26 | $this->assertEquals('rue abc', $customer->address1);
27 | $this->assertEquals('cba', $customer->address2);
28 | $this->assertEquals('12345', $customer->postcode);
29 | $this->assertEquals('FR', $customer->country);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/Payplug/Core/IHttpRequest.php:
--------------------------------------------------------------------------------
1 | 'https://www.payplug.com/pay/test/7ZcMGi6KNnVT5H7o9hms9g',
15 | 'notification_url' => 'https://www.payplug.com/?notification',
16 | 'return_url' => 'https://www.payplug.com/?return',
17 | 'cancel_url' => 'https://www.payplug.com/?cancel',
18 | 'paid_at' => 1410437806,
19 | 'notification_answer_code' => 200,
20 | ));
21 |
22 | $this->assertEquals('https://www.payplug.com/pay/test/7ZcMGi6KNnVT5H7o9hms9g', $hostedPayment->payment_url);
23 | $this->assertEquals('https://www.payplug.com/?notification', $hostedPayment->notification_url);
24 | $this->assertEquals('https://www.payplug.com/?return', $hostedPayment->return_url);
25 | $this->assertEquals('https://www.payplug.com/?cancel', $hostedPayment->cancel_url);
26 | $this->assertEquals(1410437806, $hostedPayment->paid_at);
27 | $this->assertEquals(200, $hostedPayment->notification_answer_code);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/unit_tests/Exception/HttpExceptionTest.php:
--------------------------------------------------------------------------------
1 | assertContains('Payplug\Exception\HttpException', (string)$exception);
15 | $this->assertContains('message_content', (string)$exception);
16 | $this->assertContains('http_response_content', (string)$exception);
17 | $this->assertContains('808', (string)$exception);
18 | }
19 |
20 | public function testGetHttpResponse()
21 | {
22 | $exception = new HttpException('message_content', 'http_response_content', 808);
23 | $this->assertEquals('http_response_content', $exception->getHttpResponse());
24 | }
25 |
26 | public function testGetErrorObjectWhenErrorIsJson()
27 | {
28 | $exception = new HttpException('message_content', '{"error": "an_error"}', 808);
29 | $this->assertEquals(array('error' => 'an_error'), $exception->getErrorObject());
30 | }
31 |
32 | public function testGetErrorObjectWhenErrorIsNotJson()
33 | {
34 | $exception = new HttpException('message_content', '{}not_json{}', 808);
35 | $this->assertNull($exception->getErrorObject());
36 | }
37 | }
--------------------------------------------------------------------------------
/lib/Payplug/Core/CurlRequest.php:
--------------------------------------------------------------------------------
1 | _curl = curl_init();
21 | }
22 |
23 | /**
24 | * @inheritDoc
25 | */
26 | public function setopt($option, $value)
27 | {
28 | return curl_setopt($this->_curl, $option, $value);
29 | }
30 |
31 | /**
32 | * @inheritDoc
33 | */
34 | public function getinfo($option)
35 | {
36 | return curl_getinfo($this->_curl, $option);
37 | }
38 |
39 | /**
40 | * @inheritDoc
41 | */
42 | public function exec()
43 | {
44 | return curl_exec($this->_curl);
45 | }
46 |
47 | /**
48 | * @inheritDoc
49 | */
50 | public function close()
51 | {
52 | curl_close($this->_curl);
53 | }
54 |
55 | /**
56 | * @inheritDoc
57 | */
58 | public function error()
59 | {
60 | return curl_error($this->_curl);
61 | }
62 |
63 | /**
64 | * @inheritDoc
65 | */
66 | public function errno()
67 | {
68 | return curl_errno($this->_curl);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/lib/Payplug/Exception/HttpException.php:
--------------------------------------------------------------------------------
1 | _httpResponse = $httpResponse;
24 | parent::__construct($message, $code);
25 | }
26 |
27 | /**
28 | * {@inheritdoc} It also appends the HTTP response to the returned string.
29 | */
30 | public function __toString()
31 | {
32 | return parent::__toString() . "; HTTP Response: {$this->_httpResponse}";
33 | }
34 |
35 | /**
36 | * Get the plain HTTP response which caused the exception.
37 | *
38 | * @return string the HTTP response
39 | */
40 | public function getHttpResponse()
41 | {
42 | return $this->_httpResponse;
43 | }
44 |
45 | /**
46 | * Try to parse the HTTP response as a JSON array and return it.
47 | *
48 | * @return array|null the error array if the HTTP response was a properly formed JSON string, null otherwise.
49 | */
50 | public function getErrorObject()
51 | {
52 | return json_decode($this->_httpResponse, true);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/Card.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
21 | return $object;
22 | }
23 |
24 | /**
25 | * Delete the card.
26 | *
27 | * @param Payplug\Card $card the card or card id
28 | * @param Payplug\Payplug $payplug the client configuration
29 | *
30 | * @return Card the deleted card or null on error
31 | *
32 | * @throws Payplug\Exception\ConfigurationNotSetException
33 | */
34 | public static function deleteCard($card, $payplug = null)
35 | {
36 | if ($payplug === null) {
37 | $payplug = Payplug\Payplug::getDefaultConfiguration();
38 | }
39 |
40 | if ($card instanceof Card) {
41 | $card = $card->id;
42 | }
43 |
44 | $httpClient = new Payplug\Core\HttpClient($payplug);
45 | $response = $httpClient->delete(Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::CARD_RESOURCE, $card));
46 |
47 | return $response;
48 | }
49 |
50 | /**
51 | * Delete the card.
52 | *
53 | * @param Payplug\Payplug $payplug the client configuration
54 | *
55 | * @throws Payplug\Exception\ConfigurationNotSetException
56 | */
57 | public function delete($payplug = null)
58 | {
59 | self::deleteCard($this->id, $payplug);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/Payplug/Core/Config.php:
--------------------------------------------------------------------------------
1 |
25 | * 'curl_version' => 'php5-curl'
26 | *
27 | * means that this program requires curl_version() function to work properly and that it corresponds to php5-curl
28 | * package.
29 | */
30 | public static $REQUIRED_FUNCTIONS = array(
31 | 'json_decode' => 'php5-json',
32 | 'json_encode' => 'php5-json',
33 | 'curl_version' => 'php5-curl'
34 | );
35 | }
36 |
37 | // Check PHP version
38 | if (version_compare(phpversion(), Config::PHP_MIN_VERSION, "<")) {
39 | throw new \RuntimeException('This library requires PHP ' . Config::PHP_MIN_VERSION . ' or newer.');
40 | }
41 |
42 | // Check PHP configuration
43 | foreach(Config::$REQUIRED_FUNCTIONS as $key => $value) {
44 | if (!function_exists($key)) {
45 | throw new Exception\DependencyException('This library requires ' . $value . '.');
46 | }
47 | }
48 |
49 | // Prior to PHP 5.5, CURL_SSLVERSION_TLSv1 and CURL_SSLVERSION_DEFAULT didn't exist. Hence, we have to use a numeric value.
50 | if (!defined('CURL_SSLVERSION_DEFAULT')) {
51 | define('CURL_SSLVERSION_DEFAULT', 0);
52 | }
53 | if (!defined('CURL_SSLVERSION_TLSv1')) {
54 | define('CURL_SSLVERSION_TLSv1', 1);
55 | }
56 |
--------------------------------------------------------------------------------
/tests/README.rst:
--------------------------------------------------------------------------------
1 | Contributing
2 | ====================
3 |
4 | Troubleshooting
5 | ----------------
6 |
7 | PHP 8 CI test is failing:
8 | ::
9 | $ ./tools/phpunit MyTest
10 | PHP Fatal error: Declaration of MyTest::setUp() must be compatible
11 | with PHPUnit\Framework\TestCase::setUp(): void in ...
12 |
13 | To keep compatibility through PHP 5.6 to PHP 8 the setUp method cannot be typed.
14 |
15 | Use a homemade methode instead:
16 | ::
17 | /** @before **/
18 | protected setUpTest() {
19 | ...
20 | }
21 |
22 | How to run the tests
23 | ====================
24 |
25 | Prerequisites:
26 | --------------
27 |
28 | Download composer and update dev dependencies.
29 | ::
30 |
31 | php composer.phar update
32 |
33 | Run the recommended tests:
34 | --------------------------
35 |
36 | It is recommended to launch these tests at least once to ensure this library will work properly on your configuration.
37 | ::
38 |
39 | vendor/phpunit/phpunit/phpunit --group recommended --exclude-group ignore --bootstrap tests/config.php tests
40 |
41 | Run a specific test:
42 | --------------------
43 |
44 | You can run a specific test adding a filter to the previous command.
45 | ::
46 |
47 | vendor/phpunit/phpunit/phpunit --filter CardTest --group unit --exclude-group ignore --bootstrap tests/config.php tests
48 |
49 | Run specific groups of test
50 | ---------------------------
51 |
52 | You can filter tests by groups:
53 | ::
54 |
55 | # Run unit tests
56 | vendor/phpunit/phpunit/phpunit --group unit --exclude-group ignore --bootstrap tests/config.php tests
57 |
58 | # Run functional tests
59 | vendor/phpunit/phpunit/phpunit --group functional --exclude-group ignore --bootstrap tests/config.php tests
60 |
61 | # Run unit tests and functional tests
62 | vendor/phpunit/phpunit/phpunit --group unit,functional --exclude-group ignore --bootstrap tests/config.php tests
63 |
--------------------------------------------------------------------------------
/lib/Payplug/Refund.php:
--------------------------------------------------------------------------------
1 | $installmentPlanId));
37 | return $installmentPlan->abort($payplug);
38 | }
39 |
40 | /**
41 | * Creates an InstallmentPlan.
42 | *
43 | * @param array $data API data for payment creation
44 | * @param Payplug $payplug the client configuration
45 | *
46 | * @return null|Resource\InstallmentPlan the created payment instance
47 | *
48 | * @throws Exception\ConfigurationNotSetException
49 | */
50 | public static function create(array $data, $payplug = null)
51 | {
52 | return Resource\InstallmentPlan::create($data, $payplug);
53 | }
54 | };
55 |
--------------------------------------------------------------------------------
/tests/unit_tests/PluginTelemetryTest.php:
--------------------------------------------------------------------------------
1 | _configuration = new Payplug\Payplug('abc');
20 | $this->_httpClient = new Payplug\Core\HttpClient(new \Payplug\Payplug('abc'));
21 | Payplug\Payplug::setDefaultConfiguration($this->_configuration);
22 |
23 | $this->_requestMock = $this->createMock('Payplug\Core\IHttpRequest');
24 | Payplug\Core\HttpClient::$REQUEST_HANDLER = $this->_requestMock;
25 | }
26 |
27 | /**
28 | * Verify the behavior of send() method in the PluginTelemetry class
29 | * with a mocked API URL that raises an exception
30 | *
31 | * @throws Exception\ConnectionException
32 | * @throws Exception\HttpException
33 | * @throws Exception\UnexpectedAPIResponseException
34 | */
35 | public function testSendWithBadMockedURL()
36 | {
37 | $this->expectException('Payplug\Exception\HttpException');
38 |
39 | // Data to send to the MPDC microservice
40 | $data = array(
41 | 'version' => '4.0.0',
42 | 'php_version' => '8.2.1',
43 | 'name' => 'value',
44 | 'from' => 'save',
45 | 'domains' => array(
46 | array(
47 | 'url' => 'www.mywebsite.com',
48 | 'default' => true
49 | )
50 | ),
51 | 'configurations' => array(
52 | 'name' => 'value'
53 | ),
54 | 'modules' => array(
55 | array(
56 | 'name' => 'value',
57 | 'version' => 'value'
58 | )
59 | )
60 | );
61 | $data = json_encode($data);
62 |
63 | // call send and assert
64 | PluginTelemetry::send($data);
65 | }
66 | }
--------------------------------------------------------------------------------
/lib/Payplug/Resource/OneySimulationResource.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
26 | return $object;
27 | }
28 |
29 | /**
30 | * Initializes the resource.
31 | * This method must be overridden when the resource has objects as attributes.
32 | *
33 | * @param array $attributes the attributes to initialize.
34 | */
35 | protected function initialize(array $attributes)
36 | {
37 | parent::initialize($attributes);
38 |
39 | // initialize Oney Payment Simulation with x3_with_fees operation
40 | if (isset($attributes['x3_with_fees'])) {
41 | $this->x3_with_fees = $attributes['x3_with_fees'];
42 | }
43 |
44 | // initialize Oney Payment Simulation with x4_with_fees operation
45 | if (isset($attributes['x4_with_fees'])) {
46 | $this->x4_with_fees = $attributes['x4_with_fees'];
47 | }
48 | }
49 |
50 | /**
51 | * Get Oney Payment Simulation
52 | *
53 | * @param $sim_data
54 | * @param Payplug\Payplug|null $payplug
55 | * @return array
56 | * @throws Payplug\Exception\ConfigurationNotSetException
57 | * @throws Payplug\Exception\ConnectionException
58 | * @throws Payplug\Exception\HttpException
59 | * @throws Payplug\Exception\UnexpectedAPIResponseException
60 | */
61 | public static function getSimulations($sim_data, $payplug = null)
62 | {
63 | if ($payplug === null) {
64 | $payplug = Payplug\Payplug::getDefaultConfiguration();
65 | }
66 |
67 | $sim_route = Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::ONEY_PAYMENT_SIM_RESOURCE);
68 |
69 | $httpClient = new Payplug\Core\HttpClient($payplug);
70 |
71 | $response = $httpClient->post(
72 | $sim_route,
73 | $sim_data
74 | );
75 |
76 | return $response['httpResponse'];
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | 3.4.0
2 | -----
3 | - **NEW**: Add method to get publishable keys
4 |
5 | 3.3.2
6 | -----
7 | - Fix retrieve payment function when id_payment is not set
8 |
9 | 3.3.1
10 | -----
11 | - Update certificate
12 |
13 |
14 | 3.3.0
15 | -----
16 | - NEW: Add AccountingReport class to handle new API endpoint
17 |
18 | 3.2.0
19 | -----
20 |
21 | - **NEW**: Add Oney Payment Simulations
22 |
23 | 3.1.0
24 | -----
25 |
26 | - **NEW**: Deprecated Payplug::setSecretKey function, use Payplug::init instead
27 | - **NEW**: API version should now be specified explicitly from module
28 | - **NEW**: Improve Payplug setSecretKey with init method
29 | - **NEW**: Excluded configuration API_VERSION versionning, it will be send from the apps nowadays
30 |
31 | 3.0.0
32 | -----
33 |
34 | - **Major**: Replace customer by shipping and billing to be compliant with PSD2.
35 | - **NEW**: Add versionning with new API_VERSION parameter.
36 |
37 |
38 | 2.7.0
39 | -----
40 |
41 | - **NEW**: Add deferred payments.
42 |
43 |
44 | 2.6.0
45 | -----
46 |
47 | - **NEW**: Add installment plans.
48 |
49 |
50 | 2.5.1
51 | -----
52 |
53 | - **Bugfix**: Fix packagist bundle.
54 |
55 |
56 | 2.5.0
57 | -----
58 |
59 | - **NEW**: Remove classes and methods specific to customer
60 | - **NEW**: Add Authentication class for method specific to login and account permissions
61 | - **NEW**: Add a method to patch payment
62 |
63 | 2.4.0
64 | -----
65 |
66 | - **NEW**: Support for __isset magic method in resources. This should bring Twig support.
67 | (Thanks [oservieres](https://github.com/oservieres))
68 | - **NEW**: Add a method to specify custom User-Agent products.
69 |
70 | 2.3.0
71 | -----
72 |
73 | - **NEW**: Support for Customer/Cards. (see official documentation)
74 | - **NEW**: Payment objects can now be aborted.
75 |
76 | ```
77 | $payment->abort();
78 | ```
79 |
80 | - **Breaking change**: Drop *data* key in objects collections (e.g. list of payments/list of refunds):
81 |
82 | ```
83 | $payments = \Payplug\Payment::listPayments();
84 | // $payment = $payments['data'][0]; // BEFORE
85 | $payment = $payments[0]; // NEW BEHAVIOR
86 |
87 | $refunds = \Payplug\Refund::listRefunds($payment);
88 | // $refund = $refunds['data'][0]; // BEFORE
89 | $refund = $refunds[0]; // NEW BEHAVIOR
90 | ```
91 |
92 | - **Breaking change**: Drop deprecated classes *PayPlugException* and *PayPlugServerException* (with two uppercase
93 | letters in **P**ay**P**lug). If you use **P**ay**p**lugException* and **P**ay**p**lugServerException* classes (with one
94 | uppercase letter in Payplug), you have nothing to do.
95 | - **NEW**: This library is now under MIT Licence (Issue #4).
96 |
--------------------------------------------------------------------------------
/.github/workflows/payplug-ci.yml:
--------------------------------------------------------------------------------
1 | name: PHP Composer
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - develop
8 | tags:
9 | - "*"
10 | pull_request:
11 | branches:
12 | - master
13 | - develop
14 |
15 | jobs:
16 | tests_unit:
17 |
18 | runs-on: ubuntu-latest
19 | strategy:
20 | matrix:
21 | php-versions: ['7.3', '7.4', '8.0']
22 | phpunit-versions: ['8.5.15']
23 | include:
24 | - php-versions: '5.6'
25 | phpunit-versions: '5.7.27'
26 | - php-versions: '7.0'
27 | phpunit-versions: '6.5.14'
28 |
29 | name: Php Version ${{matrix.php-versions }} / php Unit ${{ matrix.phpunit-versions }}
30 | steps:
31 | - uses: actions/checkout@v2
32 | - name: Setup PHP with PECL extension
33 | uses: shivammathur/setup-php@v2
34 | with:
35 | php-version: ${{ matrix.php-versions }}
36 | tools: phpunit:${{ matrix.phpunit-versions }}
37 |
38 | - name: php version
39 | run: php -v
40 |
41 | - name: Run test suite
42 | run: phpunit --bootstrap tests/config.php tests --configuration tests/phpunit.xml
43 |
44 | sonarcloud:
45 | runs-on: ubuntu-latest
46 | continue-on-error: true
47 | steps:
48 | - uses: actions/checkout@v3
49 | with:
50 | # Disabling shallow clone is recommended for improving relevancy of reporting
51 | fetch-depth: 0
52 | - name: SonarCloud Scan
53 | uses: sonarsource/sonarcloud-github-action@master
54 | env:
55 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
56 | with:
57 | projectBaseDir: .
58 | args: >
59 | -Dsonar.organization=${{ vars.SONAR_ORGANIZATION }}
60 | -Dsonar.projectKey=github-payplug-payplug-php
61 | -Dsonar.sources=lib/
62 | -Dsonar.test.exclusions=tests/**
63 | -Dsonar.tests=tests/
64 | -Dsonar.verbose=true
65 |
66 | release:
67 |
68 | runs-on: ubuntu-latest
69 | needs: tests_unit
70 | if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
71 | steps:
72 | - uses: actions/checkout@master
73 | with:
74 | token: ${{ secrets.GITHUB_TOKEN}}
75 | - name: Fetch library version
76 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
77 | - name: Apply new version from tag
78 | run: |
79 | echo $RELEASE_VERSION
80 | sed -i "s/LIBRARY_VERSION = '*.*'/LIBRARY_VERSION = '${{ env.RELEASE_VERSION }}'/" lib/Payplug/Core/Config.php
81 | git config user.email actions@github.com
82 | git config user.name "GitHub Actions"
83 | git add .
84 | git commit -m "[Automated Release Action]"
85 | git push origin HEAD:master
86 |
87 |
--------------------------------------------------------------------------------
/tests/unit_tests/Core/APIRoutesTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($expected, $beginRoute);
17 | }
18 |
19 | public function testCreatePaymentRoute()
20 | {
21 | $route = APIRoutes::getRoute(APIRoutes::PAYMENT_RESOURCE);
22 | $expected = '/payments';
23 | $endRoute = substr($route, -strlen($expected));
24 | $this->assertEquals($expected, $endRoute);
25 | }
26 |
27 | public function testRetrievePaymentRoute()
28 | {
29 | $route = APIRoutes::getRoute(APIRoutes::PAYMENT_RESOURCE, 'foo');
30 | $expected = '/payments/foo';
31 | $endRoute = substr($route, -strlen($expected));
32 | $this->assertEquals($expected, $endRoute);
33 | }
34 |
35 | public function testListpaymentspaginationRoute()
36 | {
37 | $pagination = array('perPage' => 5, 'page' => 1);
38 | $route = APIRoutes::getRoute(APIRoutes::PAYMENT_RESOURCE, null, array(), $pagination);
39 | $expected = '/payments?perPage=5&page=1';
40 | $endRoute = substr($route, -strlen($expected));
41 | $this->assertEquals($expected, $endRoute);
42 | }
43 |
44 | public function testCreateRefundRoute()
45 | {
46 | $route = APIRoutes::getRoute(APIRoutes::REFUND_RESOURCE, null, array('PAYMENT_ID' => 'foo'));
47 | $expected = '/payments/foo/refunds';
48 | $endRoute = substr($route, -strlen($expected));
49 | $this->assertEquals($expected, $endRoute);
50 | }
51 |
52 | public function testRetrieveRefundRoute()
53 | {
54 | $route = APIRoutes::getRoute(APIRoutes::REFUND_RESOURCE, 'bar', array('PAYMENT_ID' => 'foo'));
55 | $expected = '/payments/foo/refunds/bar';
56 | $endRoute = substr($route, -strlen($expected));
57 | $this->assertEquals($expected, $endRoute);
58 | }
59 |
60 | public function testListRefundsRoute()
61 | {
62 | $route = APIRoutes::getRoute(APIRoutes::REFUND_RESOURCE, null, array('PAYMENT_ID' => 'foo'));
63 | $expected = '/payments/foo/refunds';
64 | $endRoute = substr($route, -strlen($expected));
65 | $this->assertEquals($expected, $endRoute);
66 | }
67 |
68 | public function testSetApiBaseUrl()
69 | {
70 | APIRoutes::setApiBaseUrl('https://api.payplug.com');
71 | $this->assertEquals('https://api.payplug.com', APIRoutes::$API_BASE_URL);
72 |
73 |
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tests/unit_tests/Resource/APIResourceTest.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
10 | return $object;
11 | }
12 | }
13 |
14 | /**
15 | * @group unit
16 | * @group ci
17 | * @group recommended
18 | */
19 | class APIResourceTest extends \PHPUnit\Framework\TestCase
20 | {
21 | private $_myApiResource = null;
22 |
23 | /**
24 | * @before
25 | */
26 | protected function setUpTest()
27 | {
28 | $this->_myApiResource = APIResourceMock::fromAttributes(array(
29 | 'attr1' => 'val_attr1',
30 | 'attr2' => 'val_attr2'
31 | ));
32 | }
33 |
34 | public function testThrowsExceptionWhenKeyDoesNotExist()
35 | {
36 | $this->expectException('\PayPlug\Exception\UndefinedAttributeException');
37 | $this->_myApiResource->an_undefined_attribute;
38 | }
39 |
40 | public function testIssetReturnsTrueWhenPropertyExists()
41 | {
42 | $this->assertTrue(isset($this->_myApiResource->attr1));
43 | }
44 |
45 | public function testIssetReturnsFalseWhenPropertyDoesNotExist()
46 | {
47 | $this->assertFalse(isset($this->_myApiResource->an_undefined_attribute));
48 | }
49 |
50 | public function testPaymentFromAPIResourceFactory()
51 | {
52 | $attributes = array(
53 | 'id' => 'pay_123',
54 | 'object' => 'payment'
55 | );
56 | $payment = Resource\APIResource::factory($attributes);
57 | $this->assertTrue($payment instanceof Resource\Payment);
58 | $this->assertEquals('pay_123', $payment->id);
59 | }
60 |
61 | public function testRefundFromAPIResourceFactory()
62 | {
63 | $attributes = array(
64 | 'id' => 're_123',
65 | 'object' => 'refund'
66 | );
67 | $refund = Resource\APIResource::factory($attributes);
68 | $this->assertTrue($refund instanceof Resource\Refund);
69 | $this->assertEquals('re_123', $refund->id);
70 | }
71 |
72 | public function testAPIResourceFactoryWhenObjectIsNotDefined()
73 | {
74 | $this->expectException('\PayPlug\Exception\UnknownAPIResourceException');
75 | $attributes = array(
76 | 'id' => 'a_random_object'
77 | );
78 | Resource\APIResource::factory($attributes);
79 | }
80 |
81 | public function testAPIResourceFactoryWhenObjectIsUnknown()
82 | {
83 | $this->expectException('\PayPlug\Exception\UnknownAPIResourceException');
84 | $attributes = array(
85 | 'id' => 'a_random_object',
86 | 'object' => 'an_unknown_object'
87 | );
88 | Resource\APIResource::factory($attributes);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/lib/Payplug/Payment.php:
--------------------------------------------------------------------------------
1 | $paymentId));
37 | return $payment->abort($payplug);
38 | }
39 |
40 | /**
41 | * Captures a Payment.
42 | *
43 | * @param string $paymentId the payment ID
44 | * @param Payplug $payplug the client configuration
45 | *
46 | * @return null|Resource\Payment the captured payment or null on error
47 | *
48 | * @throws Exception\ConfigurationNotSetException
49 | */
50 | public static function capture($paymentId, $payplug = null)
51 | {
52 | $payment = Resource\Payment::fromAttributes(array('id' => $paymentId));
53 | return $payment->capture($payplug);
54 | }
55 |
56 | /**
57 | * Creates a Payment.
58 | *
59 | * @param array $data API data for payment creation
60 | * @param Payplug $payplug the client configuration
61 | *
62 | * @return null|Resource\Payment the created payment instance
63 | *
64 | * @throws Exception\ConfigurationNotSetException
65 | */
66 | public static function create(array $data, $payplug = null)
67 | {
68 | return Resource\Payment::create($data, $payplug);
69 | }
70 |
71 | /**
72 | * List payments.
73 | *
74 | * @param int $perPage number of results per page
75 | * @param int $page the page number
76 | * @param Payplug $payplug the client configuration
77 | *
78 | * @return null|Resource\Payment[] the array of payments
79 | *
80 | * @throws Exception\InvalidPaymentException
81 | * @throws Exception\UnexpectedAPIResponseException
82 | */
83 | public static function listPayments($perPage = null, $page = null, $payplug = null)
84 | {
85 | return Resource\Payment::listPayments($perPage, $page, $payplug);
86 | }
87 | };
--------------------------------------------------------------------------------
/lib/Payplug/Resource/AccountingReport.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
21 | return $object;
22 | }
23 |
24 | /**
25 | * Retrieves an AccountingReport.
26 | *
27 | * @param string $reportId the accounting report ID
28 | * @param Payplug\Payplug $payplug the client configuration
29 | *
30 | * @return null|AccountingReport the retrieved report or null on error
31 | *
32 | * @throws Payplug\Exception\ConfigurationNotSetException
33 | */
34 | public static function retrieve($reportId, $payplug = null)
35 | {
36 | if ($payplug === null) {
37 | $payplug = Payplug\Payplug::getDefaultConfiguration();
38 | }
39 |
40 | $httpClient = new Payplug\Core\HttpClient($payplug);
41 | $response = $httpClient->get(
42 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::ACCOUNTING_REPORT_RESOURCE, $reportId)
43 | );
44 |
45 | return AccountingReport::fromAttributes($response['httpResponse']);
46 | }
47 |
48 | /**
49 | * Creates an AccountingReport.
50 | *
51 | * @param array $data API data for accounting report creation
52 | * @param Payplug\Payplug $payplug the client configuration
53 | *
54 | * @return null|AccountingReport the created accounting report instance
55 | *
56 | * @throws Payplug\Exception\ConfigurationNotSetException
57 | */
58 | public static function create(array $data, $payplug = null)
59 | {
60 | if ($payplug === null) {
61 | $payplug = Payplug\Payplug::getDefaultConfiguration();
62 | }
63 |
64 | $httpClient = new Payplug\Core\HttpClient($payplug);
65 | $response = $httpClient->post(
66 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::ACCOUNTING_REPORT_RESOURCE),
67 | $data
68 | );
69 |
70 | return AccountingReport::fromAttributes($response['httpResponse']);
71 | }
72 |
73 | /**
74 | * Returns an API resource that you can trust.
75 | *
76 | * @param Payplug\Payplug $payplug the client configuration.
77 | *
78 | * @return Payplug\Resource\APIResource The consistent API resource.
79 | *
80 | * @throws Payplug\Exception\UndefinedAttributeException when the local resource is invalid.
81 | */
82 | function getConsistentResource($payplug = null)
83 | {
84 | if (!array_key_exists('id', $this->_attributes)) {
85 | throw new Payplug\Exception\UndefinedAttributeException('The id of the accounting report is not set.');
86 | }
87 |
88 | return AccountingReport::retrieve($this->_attributes['id'], $payplug);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/tests/unit_tests/NotificationTest.php:
--------------------------------------------------------------------------------
1 | _configuration = new \Payplug\Payplug('abc');
24 | Payplug::setDefaultConfiguration($this->_configuration);
25 |
26 | $this->_requestMock = $this->createMock('\Payplug\Core\IHttpRequest');
27 | Core\HttpClient::$REQUEST_HANDLER = $this->_requestMock;
28 | }
29 |
30 | protected function setUpTwice()
31 | {
32 | $this->_configuration = new Payplug('abc','1970-01-01');
33 | Payplug::setDefaultConfiguration($this->_configuration);
34 |
35 | $this->_requestMock = $this->createMock('\Payplug\Core\IHttpRequest');
36 | Core\HttpClient::$REQUEST_HANDLER = $this->_requestMock;
37 | }
38 |
39 | public function testTreatPayment()
40 | {
41 |
42 | $this->_requestMock
43 | ->expects($this->once())
44 | ->method('exec')
45 | ->will($this->returnValue('{ "id": "real_payment", "object": "payment" }'));
46 | $this->_requestMock
47 | ->expects($this->any())
48 | ->method('getinfo')
49 | ->will($this->returnCallback(function($option) {
50 | switch($option) {
51 | case CURLINFO_HTTP_CODE:
52 | return 200;
53 | }
54 | return null;
55 | }));
56 |
57 | $body = '{ "id": "pay_123", "object": "payment" }';
58 | $payment = Notification::treat($body, $this->_configuration);
59 | $this->assertTrue($payment instanceof Resource\Payment);
60 | $this->assertEquals('real_payment', $payment->id);
61 | }
62 |
63 | public function testTreatInstallmentPlan()
64 | {
65 |
66 | $this->_requestMock
67 | ->expects($this->once())
68 | ->method('exec')
69 | ->will($this->returnValue('{ "id": "real_installment_plan", "object": "installment_plan" }'));
70 | $this->_requestMock
71 | ->expects($this->any())
72 | ->method('getinfo')
73 | ->will($this->returnCallback(function($option) {
74 | switch($option) {
75 | case CURLINFO_HTTP_CODE:
76 | return 200;
77 | }
78 | return null;
79 | }));
80 |
81 | $body = '{ "id": "inst_123456", "object": "installment_plan" }';
82 | $installmentPlan = Notification::treat($body, $this->_configuration);
83 | $this->assertTrue($installmentPlan instanceof Resource\InstallmentPlan);
84 | $this->assertEquals('real_installment_plan', $installmentPlan->id);
85 | }
86 |
87 | public function testTreatWhenBodyIsNotValidJSON()
88 | {
89 | $this->expectException('\PayPlug\Exception\UnknownAPIResourceException');
90 |
91 | $this->_requestMock
92 | ->expects($this->never())
93 | ->method('exec');
94 |
95 | $body = 'invalidJSON';
96 | Notification::treat($body, $this->_configuration);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/lib/Payplug/PluginTelemetry.php:
--------------------------------------------------------------------------------
1 | post(
34 | Core\APIRoutes::getServiceRoute(Core\APIRoutes::TELEMETRY_SERVICE),
35 | $data
36 | );
37 | }
38 |
39 | /**
40 | * Validate the $data and return UnprocessableEntityException
41 | *
42 | * @param array $data
43 | * @throws Exception\UnprocessableEntityException
44 | */
45 | public static function validateData(array $data)
46 | {
47 | $requiredFields = array('version', 'php_version', 'name', 'source', 'domains', 'configurations', 'modules');
48 |
49 | foreach ($requiredFields as $field) {
50 | if (!isset($data[$field])) {
51 | throw new Exception\UnprocessableEntityException(
52 | 'The server encountered an error while processing the request. The submitted data could not be processed.',
53 | '{"detail":[{"loc":["body","' . $field . '"],"msg":"field required","type":"value_error.missing"}]}',
54 | 422
55 | );
56 | }
57 | }
58 | if (!is_array($data['domains']) || empty($data['domains']) || !isset($data['domains'][0]['url']) || !isset($data['domains'][0]['default'])) {
59 | throw new Exception\UnprocessableEntityException(
60 | 'The server encountered an error while processing the request. The submitted data could not be processed.',
61 | '{"detail":[{"loc":["body","domains"],"msg":"invalid structure","type":"value_error.invalid_structure"}]}',
62 | 422
63 | );
64 | }
65 |
66 | if (!is_array($data['configurations']) || empty($data['configurations']) || !isset($data['configurations']['name'])) {
67 | throw new Exception\UnprocessableEntityException(
68 | 'The server encountered an error while processing the request. The submitted data could not be processed.',
69 | '{"detail":[{"loc":["body","configurations"],"msg":"invalid structure","type":"value_error.invalid_structure"}]}',
70 | 422
71 | );
72 | }
73 |
74 | if (!is_array($data['modules']) || empty($data['modules']) || !isset($data['modules'][0]['name']) || !isset($data['modules'][0]['version'])) {
75 | throw new Exception\UnprocessableEntityException(
76 | 'The server encountered an error while processing the request. The submitted data could not be processed.',
77 | '{"detail":[{"loc":["body","modules"],"msg":"invalid structure","type":"value_error.invalid_structure"}]}',
78 | 422
79 | );
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/tests/unit_tests/PayplugTest.php:
--------------------------------------------------------------------------------
1 | 'cba',
16 | 'apiVersion' => null
17 | ));
18 |
19 | $configuration = Payplug::getDefaultConfiguration();
20 |
21 | $this->assertEquals('cba', $configuration->getToken());
22 | $this->assertEquals('2019-06-14', $configuration->getApiVersion());
23 | }
24 | public function testCanInitializeConfiguration()
25 | {
26 | Payplug::init(array(
27 | 'secretKey' => 'cba',
28 | 'apiVersion' => '1970-01-01'
29 | ));
30 |
31 | $configuration = Payplug::getDefaultConfiguration();
32 |
33 | $this->assertEquals('cba', $configuration->getToken());
34 | $this->assertEquals('1970-01-01', $configuration->getApiVersion());
35 | }
36 |
37 | public function testDeprecatedCanInitializeConfiguration()
38 | {
39 | Payplug::setSecretKey('cba');
40 |
41 | $configuration = Payplug::getDefaultConfiguration();
42 |
43 | $this->assertEquals('cba', $configuration->getToken());
44 | }
45 |
46 | public function testCannotInitializeConfigurationWhenLiveTokenIsNotAString()
47 | {
48 | $this->expectException('\PayPlug\Exception\ConfigurationException');
49 | Payplug::init(array(
50 | 'secretKey' => true,
51 | 'apiVersion' => '1970-01-01',
52 | ));
53 | }
54 |
55 | public function testDeprecatedCannotInitializeConfigurationWhenLiveTokenIsNotAString()
56 | {
57 | $this->expectException('\PayPlug\Exception\ConfigurationException');
58 | Payplug::setSecretKey(true);
59 | }
60 |
61 | public function testCannotInitializeConfigurationWhenTestTokenIsArray()
62 | {
63 | $this->expectException('\PayPlug\Exception\ConfigurationException');
64 |
65 | Payplug::init(array(
66 | 'secretKey' => array(
67 | 'LIVE_TOKEN' => 'cba'
68 | ),
69 | 'apiVersion' => null,
70 | ));
71 | }
72 |
73 | public function testDeprecatedCannotInitializeConfigurationWhenTestTokenIsArray()
74 | {
75 | $this->expectException('\PayPlug\Exception\ConfigurationException');
76 |
77 | Payplug::setSecretKey(array(
78 | 'LIVE_TOKEN' => 'cba'
79 | ));
80 | }
81 |
82 | public function testCanGetAToken()
83 | {
84 | $configuration = Payplug::init(array('secretKey' => 'cba', 'apiVersion' => null));
85 | $this->assertEquals('cba', $configuration->getToken());
86 | }
87 |
88 | public function testDeprecatedCanGetAToken()
89 | {
90 | $configuration = Payplug::setSecretKey('cba');
91 | $this->assertEquals('cba', $configuration->getToken());
92 | }
93 |
94 | /**
95 | * @runInSeparateProcess so that static default configuration is cleared before the test
96 | */
97 | public function testThrowsExceptionWhenDefaultConfigurationIsNotSet()
98 | {
99 | $this->expectException('\Payplug\Exception\ConfigurationNotSetException');
100 | Payplug::getDefaultConfiguration();
101 | }
102 |
103 | public function testCanSetDefaultConfiguration()
104 | {
105 | $configuration = Payplug::init(array('secretKey' => 'abc', 'apiVersion' => null));
106 | Payplug::setDefaultConfiguration($configuration);
107 | $this->assertEquals($configuration, Payplug::getDefaultConfiguration());
108 | }
109 |
110 | public function testDeprecatedCanSetDefaultConfiguration()
111 | {
112 | $configuration = Payplug::setSecretKey('abc');
113 | Payplug::setDefaultConfiguration($configuration);
114 | $this->assertEquals($configuration, Payplug::getDefaultConfiguration());
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/APIResource.php:
--------------------------------------------------------------------------------
1 | _attributes;
58 | }
59 |
60 | /**
61 | * Sets the attributes of this resource.
62 | *
63 | * @param array $attributes The attributes to set.
64 | */
65 | protected function setAttributes(array $attributes)
66 | {
67 | $this->_attributes = $attributes;
68 | }
69 |
70 | /**
71 | * Reads an API resource property.
72 | *
73 | * @param string $attribute the key of the attribute to get
74 | *
75 | * @return mixed The value of the attribute
76 | *
77 | * @throws Payplug\Exception\UndefinedAttributeException
78 | */
79 | public function __get($attribute)
80 | {
81 | if ($this->__isset($attribute)) {
82 | return $this->_attributes[$attribute];
83 | }
84 |
85 | throw new Payplug\Exception\UndefinedAttributeException('Requested attribute ' . $attribute . ' is undefined.');
86 | }
87 |
88 | /**
89 | * Checks if an API resource property is set
90 | *
91 | * @param string $attribute the key of the attribute to check
92 | *
93 | * @return bool True if the property is set. False otherwise.
94 | */
95 | public function __isset($attribute)
96 | {
97 | return array_key_exists($attribute, $this->_attributes);
98 | }
99 |
100 |
101 | /**
102 | * Sets an API resource property.
103 | *
104 | * @param string $attribute the attribute key
105 | * @param mixed $value the new value of the attribute
106 | */
107 | public function __set($attribute, $value)
108 | {
109 | $this->_attributes[$attribute] = $value;
110 | }
111 |
112 | /**
113 | * Initializes the resource.
114 | * This method must be overridden when the resource has objects as attributes.
115 | *
116 | * @param array $attributes the attributes to initialize.
117 | */
118 | protected function initialize(array $attributes)
119 | {
120 | $this->setAttributes($attributes);
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/lib/Payplug/Core/APIRoutes.php:
--------------------------------------------------------------------------------
1 | $value) {
53 | $route = str_replace('{' . $parameter . '}', $value, $route);
54 | }
55 |
56 | $resourceIdUrl = $resourceId ? '/' . $resourceId : '';
57 |
58 | $query_parameters = '';
59 | if (!empty($query_datas))
60 | $query_parameters = '?' . http_build_query($query_datas);
61 |
62 | if (in_array($route, [self::OAUTH2_TOKEN_RESOURCE, self::OAUTH2_AUTH_RESOURCE]) && false !== strpos(self::$API_BASE_URL, 'https://service.')) {
63 | self::$API_BASE_URL = 'https://hydra--4444.external.gamma.notpayplug.com';
64 | }
65 |
66 | return self::$API_BASE_URL . ($with_version ? '/v' . self::API_VERSION : '') . $route . $resourceIdUrl . $query_parameters;
67 | }
68 |
69 | /**
70 | * Get the route to a specified resource.
71 | *
72 | * @param string $route One of the routes defined above
73 | * @param array $parameters The parameters required by the route.
74 | *
75 | * @return string the full URL to the resource
76 | *
77 | */
78 | public static function getServiceRoute($route, array $parameters = array())
79 | {
80 | return self::$SERVICE_BASE_URL . $route . ($parameters ? '?' . http_build_query($parameters) : '');
81 | }
82 |
83 | /**
84 | * @description set $API_BASE_URL from plugin
85 | * @param $apiBaseUrl
86 | */
87 | public static function setApiBaseUrl($apiBaseUrl)
88 | {
89 | self::$API_BASE_URL = $apiBaseUrl;
90 | }
91 |
92 | /**
93 | * @description set $SERVICE_BASE_URL from plugin
94 | * @param $serviceBaseUrl
95 | */
96 | public static function setServiceBaseUrl($serviceBaseUrl)
97 | {
98 | self::$SERVICE_BASE_URL = $serviceBaseUrl;
99 | }
100 |
101 | /**
102 | * Gets a route that allows to check whether the remote API is up.
103 | *
104 | * @return string the full URL to the test resource
105 | */
106 | public static function getTestRoute()
107 | {
108 | return APIRoutes::$API_BASE_URL . '/test';
109 | }
110 | }
111 |
112 | APIRoutes::$API_BASE_URL = 'https://api.payplug.com';
113 | APIRoutes::$SERVICE_BASE_URL = 'https://retail.service.payplug.com';
114 |
--------------------------------------------------------------------------------
/tests/unit_tests/Resource/CardTest.php:
--------------------------------------------------------------------------------
1 | _configuration = new Payplug\Payplug('abc');
21 | Payplug\Payplug::setDefaultConfiguration($this->_configuration);
22 |
23 | $this->_requestMock = $this->createMock('\Payplug\Core\IHttpRequest');
24 | Payplug\Core\HttpClient::$REQUEST_HANDLER = $this->_requestMock;
25 | }
26 |
27 | protected function setUpTwice()
28 | {
29 | $this->_configuration = new Payplug\Payplug('abc','1970-01-01');
30 | Payplug\Payplug::setDefaultConfiguration($this->_configuration);
31 |
32 | $this->_requestMock = $this->createMock('\Payplug\Core\IHttpRequest');
33 | Payplug\Core\HttpClient::$REQUEST_HANDLER = $this->_requestMock;
34 | }
35 |
36 | public function testCreateCardFromAttributes()
37 | {
38 | $card = Card::fromAttributes(array(
39 | 'id' => 'card_167oJVCpvtR9j8N85LraL2GA',
40 | 'object' => 'card',
41 | 'created_at' => 1431523049,
42 | 'is_live' => false,
43 | 'last4' => '1111',
44 | 'brand' => 'Visa',
45 | 'exp_month' => 5,
46 | 'exp_year' => 2029,
47 | 'customer_id' => 'cus_6ESfofiMiLBjC6',
48 | 'country' => 'France',
49 | 'metadata' => array(
50 | 'a_custom_field' => 'a custom value',
51 | 'another_key' => 'another value'
52 | )
53 | ));
54 |
55 | $this->assertEquals('card_167oJVCpvtR9j8N85LraL2GA', $card->id);
56 | $this->assertEquals('card', $card->object);
57 | $this->assertEquals(1431523049, $card->created_at);
58 | $this->assertEquals(false, $card->is_live);
59 | $this->assertEquals('1111', $card->last4);
60 | $this->assertEquals('Visa', $card->brand);
61 | $this->assertEquals(5, $card->exp_month);
62 | $this->assertEquals(2029, $card->exp_year);
63 | $this->assertEquals('cus_6ESfofiMiLBjC6', $card->customer_id);
64 | $this->assertEquals('France', $card->country);
65 |
66 | $this->assertEquals('a custom value', $card->metadata['a_custom_field']);
67 | $this->assertEquals('another value', $card->metadata['another_key']);
68 | }
69 |
70 | public function testCardDeleteCardObject()
71 | {
72 | function testCardDeleteCardObject_getinfo($option) {
73 | switch($option) {
74 | case CURLINFO_HTTP_CODE:
75 | return 200;
76 | }
77 | return null;
78 | }
79 | $GLOBALS['CURLOPT_URL_DATA'] = null;
80 | function testCardDeleteCardObject_setopt($option, $value = null) {
81 | switch($option) {
82 | case CURLOPT_URL:
83 | $GLOBALS['CURLOPT_URL_DATA'] = $value;
84 | return true;
85 | }
86 | return true;
87 | }
88 |
89 | $this->_requestMock
90 | ->expects($this->once())
91 | ->method('exec')
92 | ->will($this->returnValue('{"status":"ok"}'));
93 |
94 | $this->_requestMock
95 | ->expects($this->any())
96 | ->method('getinfo')
97 | ->will($this->returnCallback(function($option) {
98 | switch($option) {
99 | case CURLINFO_HTTP_CODE:
100 | return 200;
101 | }
102 | return null;
103 | }));
104 | $this->_requestMock
105 | ->expects($this->any())
106 | ->method('setopt')
107 | ->will($this->returnCallback(function($option, $value = null) {
108 | switch($option) {
109 | case CURLOPT_URL:
110 | $GLOBALS['CURLOPT_URL_DATA'] = $value;
111 | return true;
112 | }
113 | return true;
114 | }));
115 |
116 | $card = Card::fromAttributes(array('id' => 'a_card_id'));
117 | $card->delete();
118 |
119 | $this->assertStringEndsWith('a_card_id', $GLOBALS['CURLOPT_URL_DATA']);
120 |
121 | unset($GLOBALS['CURLOPT_URL_DATA']);
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/lib/Payplug/Payplug.php:
--------------------------------------------------------------------------------
1 | _token = $token;
42 |
43 | // if no given version then set a default
44 | $this->_apiVersion = $apiVersion ? $apiVersion : '2019-06-14';
45 | }
46 |
47 |
48 | /**
49 | * Initializes a Authentication and sets it as the new default global authentication.
50 | * It also performs some checks before saving the authentication.
51 | *
52 | *
53 | * Expected array format for argument $authentication :
54 | * $authentication['TOKEN'] = 'YOUR TOKEN'
55 | *
56 | *
57 | * @param string $token the authentication token
58 | *
59 | * @return Payplug the new client authentication
60 | *
61 | * @throws Exception\ConfigurationException
62 | * @deprecated Use Payplug::init(array('secretKey' => 'token', 'apiVersion' => 'version'))
63 | */
64 | public static function setSecretKey($token)
65 | {
66 | if (!is_string($token)) {
67 | throw new Exception\ConfigurationException('Expected string values for the token.');
68 | }
69 |
70 | $clientConfiguration = new Payplug($token);
71 |
72 | self::setDefaultConfiguration($clientConfiguration);
73 |
74 | return $clientConfiguration;
75 | }
76 |
77 | /**
78 | * Initializes a Authentication and sets it as the new default global authentication.
79 | * It also performs some checks before saving the authentication and set the API version
80 | *
81 | * @param array $props
82 | * @return Payplug
83 | * @throws ConfigurationException
84 | */
85 | public static function init($props)
86 | {
87 | $secretKey = isset($props['secretKey']) && $props['secretKey'] ? $props['secretKey'] : null;
88 | $apiVersion = isset($props['apiVersion']) && $props['apiVersion'] ? $props['apiVersion'] : null;
89 |
90 | if (!$secretKey) {
91 | throw new Exception\ConfigurationException('Expected string values for the token.');
92 | }
93 |
94 | $clientConfiguration = new Payplug($secretKey, $apiVersion);
95 |
96 | self::setDefaultConfiguration($clientConfiguration);
97 |
98 | return $clientConfiguration;
99 | }
100 |
101 | /**
102 | * Gets the token corresponding to the mode currently in use (Live token or Test token).
103 | *
104 | * @return string The current token
105 | */
106 | public function getToken()
107 | {
108 | return $this->_token;
109 | }
110 |
111 | /**
112 | * Sets the API Version corresponding to the module currently in use.
113 | *
114 | * @return string
115 | */
116 | public function getApiVersion()
117 | {
118 | return $this->_apiVersion;
119 | }
120 |
121 | /**
122 | * Gets the default global authentication.
123 | *
124 | * @return Payplug The last client authentication
125 | *
126 | * @throws Exception\ConfigurationNotSetException when the global authentication was not set.
127 | */
128 | public static function getDefaultConfiguration()
129 | {
130 | if (self::$_defaultConfiguration === null) {
131 | throw new Exception\ConfigurationNotSetException('Unable to find an authentication.');
132 | }
133 |
134 | return self::$_defaultConfiguration;
135 | }
136 |
137 | /**
138 | * Sets the new default client authentication. This authentication will be used when no authentication is explicitly
139 | * passed to methods.
140 | *
141 | * @param Payplug $defaultConfiguration the new default authentication
142 | */
143 | public static function setDefaultConfiguration($defaultConfiguration)
144 | {
145 | self::$_defaultConfiguration = $defaultConfiguration;
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/Refund.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
21 | return $object;
22 | }
23 |
24 | /**
25 | * Creates a refund on a payment.
26 | *
27 | * @param string|Payment $payment the payment id or the payment object
28 | * @param array $data API data for refund
29 | * @param Payplug\Payplug $payplug the client configuration
30 | *
31 | * @return null|Refund the refund object
32 | * @throws Payplug\Exception\ConfigurationNotSetException
33 | */
34 | public static function create($payment, $data = null, $payplug = null)
35 | {
36 | if ($payplug === null) {
37 | $payplug = Payplug\Payplug::getDefaultConfiguration();
38 | }
39 | if ($payment instanceof Payment) {
40 | $payment = $payment->id;
41 | }
42 |
43 | $httpClient = new Payplug\Core\HttpClient($payplug);
44 | $response = $httpClient->post(
45 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::REFUND_RESOURCE, null, array('PAYMENT_ID' => $payment)),
46 | $data
47 | );
48 |
49 | return Refund::fromAttributes($response['httpResponse']);
50 | }
51 |
52 | /**
53 | * Retrieves a refund object on a payment.
54 | *
55 | * @param string|Payment $payment the payment id or the payment object
56 | * @param string $refundId the refund id
57 | * @param Payplug\Payplug $payplug the client configuration
58 | *
59 | * @return null|Payplug\Resource\APIResource|Refund the refund object
60 | *
61 | * @throws Payplug\Exception\ConfigurationNotSetException
62 | */
63 | public static function retrieve($payment, $refundId, $payplug = null)
64 | {
65 | if ($payplug === null) {
66 | $payplug = Payplug\Payplug::getDefaultConfiguration();
67 | }
68 | if ($payment instanceof Payment) {
69 | $payment = $payment->id;
70 | }
71 |
72 | $httpClient = new Payplug\Core\HttpClient($payplug);
73 | $response = $httpClient->get(
74 | Payplug\Core\APIRoutes::getRoute(
75 | Payplug\Core\APIRoutes::REFUND_RESOURCE, $refundId, array('PAYMENT_ID' => $payment)
76 | )
77 | );
78 |
79 | return Refund::fromAttributes($response['httpResponse']);
80 | }
81 |
82 | /**
83 | * Lists the last refunds of a payment.
84 | *
85 | * @param string|Payment $payment the payment id or the payment object
86 | * @param Payplug\Payplug $payplug the client configuration
87 | *
88 | * @return null|Refund[] an array containing the refunds on success.
89 | *
90 | * @throws Payplug\Exception\ConfigurationNotSetException
91 | * @throws Payplug\Exception\UnexpectedAPIResponseException
92 | */
93 | public static function listRefunds($payment, $payplug = null)
94 | {
95 | if ($payplug === null) {
96 | $payplug = Payplug\Payplug::getDefaultConfiguration();
97 | }
98 | if ($payment instanceof Payment) {
99 | $payment = $payment->id;
100 | }
101 |
102 | $httpClient = new Payplug\Core\HttpClient($payplug);
103 |
104 | $response = $httpClient->get(
105 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::REFUND_RESOURCE, null, array('PAYMENT_ID' => $payment))
106 | );
107 |
108 | if (!array_key_exists('data', $response['httpResponse']) || !is_array($response['httpResponse']['data'])) {
109 | throw new Payplug\Exception\UnexpectedAPIResponseException(
110 | "Expected API response to contain 'data' key referencing an array.",
111 | $response['httpResponse']
112 | );
113 | }
114 |
115 | $refunds = array();
116 | foreach ($response['httpResponse']['data'] as &$refund) {
117 | $refunds[] = Refund::fromAttributes($refund);
118 | }
119 |
120 | return $refunds;
121 | }
122 |
123 | /**
124 | * Returns an API resource that you can trust.
125 | *
126 | * @param Payplug\Payplug $payplug the client configuration.
127 | *
128 | * @return Payplug\Resource\APIResource The consistent API resource.
129 | *
130 | * @throws Payplug\Exception\UndefinedAttributeException when the local resource is invalid.
131 | */
132 | function getConsistentResource($payplug = null)
133 | {
134 | if (!array_key_exists('id', $this->_attributes)) {
135 | throw new Payplug\Exception\UndefinedAttributeException('The id of the refund is not set.');
136 | }
137 | else if (!array_key_exists('payment_id', $this->_attributes)) {
138 | throw new Payplug\Exception\UndefinedAttributeException('The payment_id of the refund is not set.');
139 | }
140 |
141 | return Payplug\Resource\Refund::retrieve($this->_attributes['payment_id'], $this->_attributes['id'], $payplug);
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/tests/unit_tests/OneySimulationTest.php:
--------------------------------------------------------------------------------
1 | _configuration = new Payplug\Payplug('abc');
20 | Payplug\Payplug::setDefaultConfiguration($this->_configuration);
21 |
22 | $this->_requestMock = $this->createMock('\Payplug\Core\IHttpRequest');
23 | Core\HttpClient::$REQUEST_HANDLER = $this->_requestMock;
24 | }
25 |
26 | public function testGetOneyPaymentSimulation()
27 | {
28 | $data = array(
29 | 'amount' => 50500,
30 | 'country' => 'FR',
31 | 'operations' => array(
32 | 'x3_with_fees',
33 | 'x4_with_fees',
34 | ),
35 | );
36 |
37 | $response = array(
38 | 'x3_with_fees' => array(
39 | 'effective_annual_percentage_rate' => 19.25,
40 | 'nominal_annual_percentage_rate' => 17.74,
41 | 'total_cost' => 732,
42 | 'installments' => array(
43 | array (
44 | 'date' => '2020-03-06T01:00:00.000Z',
45 | 'amount' => 16834,
46 | ),
47 | array (
48 | 'date' => '2020-04-06T00:00:00.000Z',
49 | 'amount' => 16833,
50 | ),
51 | ),
52 | 'down_payment_amount' => 17565,
53 | ),
54 | 'x4_with_fees' => array(
55 | 'effective_annual_percentage_rate' => 19.25,
56 | 'nominal_annual_percentage_rate' => 17.74,
57 | 'total_cost' => 732,
58 | 'installments' => array(
59 | array (
60 | 'date' => '2020-03-06T01:00:00.000Z',
61 | 'amount' => 16834,
62 | ),
63 | array (
64 | 'date' => '2020-04-06T00:00:00.000Z',
65 | 'amount' => 16833,
66 | ),
67 | array (
68 | 'date' => '2020-05-06T00:00:00.000Z',
69 | 'amount' => 16833,
70 | ),
71 | ),
72 | 'down_payment_amount' => 17565,
73 | )
74 | );
75 |
76 | $this->_requestMock
77 | ->expects($this->once())
78 | ->method('exec')
79 | ->will($this->returnValue(json_encode($response)));
80 |
81 | $this->_requestMock
82 | ->expects($this->any())
83 | ->method('getinfo')
84 | ->will($this->returnCallback(function($option) {
85 | switch($option) {
86 | case CURLINFO_HTTP_CODE:
87 | return 201;
88 | }
89 | return null;
90 | }));
91 |
92 | $simulations = OneySimulation::getSimulations($data);
93 |
94 | // check 3x with fees
95 | $this->assertArrayHasKey('x3_with_fees', $simulations);
96 | if (isset($simulations['x3_with_fees']) && $simulations['x3_with_fees']) {
97 | $x3_with_fees = $simulations['x3_with_fees'];
98 |
99 | $this->assertEquals(true, is_array($x3_with_fees));
100 | $this->assertEquals(19.25, $x3_with_fees['effective_annual_percentage_rate']);
101 | $this->assertEquals(17.74, $x3_with_fees['nominal_annual_percentage_rate']);
102 | $this->assertEquals(732, $x3_with_fees['total_cost']);
103 |
104 | $installments_x3 = $x3_with_fees['installments'];
105 |
106 | $this->assertEquals(2, count($installments_x3));
107 | $this->assertEquals('2020-03-06T01:00:00.000Z', $installments_x3[0]['date']);
108 | $this->assertEquals(16834, $installments_x3[0]['amount']);
109 | $this->assertEquals('2020-04-06T00:00:00.000Z', $installments_x3[1]['date']);
110 | $this->assertEquals(16833, $installments_x3[1]['amount']);
111 |
112 | $this->assertEquals(17565, $x3_with_fees['down_payment_amount']);
113 | }
114 |
115 | // check 4x with fees
116 | $this->assertArrayHasKey('x4_with_fees', $simulations);
117 | if (isset($simulations['x4_with_fees']) && $simulations['x4_with_fees']) {
118 | $x4_with_fees = $simulations['x4_with_fees'];
119 |
120 | $this->assertEquals(true, is_array($x4_with_fees));
121 | $this->assertEquals(19.25, $x4_with_fees['effective_annual_percentage_rate']);
122 | $this->assertEquals(17.74, $x4_with_fees['nominal_annual_percentage_rate']);
123 | $this->assertEquals(732, $x4_with_fees['total_cost']);
124 |
125 | $installments_x4 = $x4_with_fees['installments'];
126 |
127 | $this->assertEquals(3, count($installments_x4));
128 |
129 | $this->assertEquals('2020-03-06T01:00:00.000Z', $installments_x4[0]['date']);
130 | $this->assertEquals(16834, $installments_x4[0]['amount']);
131 | $this->assertEquals('2020-04-06T00:00:00.000Z', $installments_x4[1]['date']);
132 | $this->assertEquals(16833, $installments_x4[1]['amount']);
133 | $this->assertEquals('2020-05-06T00:00:00.000Z', $installments_x4[2]['date']);
134 | $this->assertEquals(16833, $installments_x4[2]['amount']);
135 |
136 | $this->assertEquals(17565, $x4_with_fees['down_payment_amount']);
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/tests/unit_tests/Resource/AccountingReportTest.php:
--------------------------------------------------------------------------------
1 | _configuration = new Payplug\Payplug('abc');
22 | Payplug\Payplug::setDefaultConfiguration($this->_configuration);
23 |
24 | $this->_requestMock = $this->createMock('\Payplug\Core\IHttpRequest');
25 | Payplug\Core\HttpClient::$REQUEST_HANDLER = $this->_requestMock;
26 | }
27 |
28 | protected function setUpTwice()
29 | {
30 | $this->_configuration = new Payplug\Payplug('abc','1970-01-01');
31 | Payplug\Payplug::setDefaultConfiguration($this->_configuration);
32 |
33 | $this->_requestMock = $this->createMock('\Payplug\Core\IHttpRequest');
34 | Payplug\Core\HttpClient::$REQUEST_HANDLER = $this->_requestMock;
35 | }
36 |
37 | public function testCreateAccountingReportFromAttributes()
38 | {
39 | $report= AccountingReport::fromAttributes(array(
40 | 'start_date' => '2020-01-01',
41 | 'object' => 'accounting_report',
42 | 'notification_url' => 'notification_url',
43 | 'end_date' => '2020-04-30',
44 | 'id' => 'ar_1GKEACvltTVXT5muBd3AQv',
45 | 'file_available_until' => 1588083743,
46 | 'temporary_url' => 'temporary_url'
47 | ));
48 |
49 | $this->assertEquals('ar_1GKEACvltTVXT5muBd3AQv', $report->id);
50 | $this->assertEquals('2020-01-01', $report->start_date);
51 | $this->assertEquals('2020-04-30', $report->end_date);
52 | $this->assertEquals('notification_url', $report->notification_url);
53 | $this->assertEquals('temporary_url', $report->temporary_url);
54 | $this->assertEquals(1588083743, $report->file_available_until);
55 | }
56 |
57 | public function testAccountingReportCreate()
58 | {
59 | $GLOBALS['CURLOPT_POSTFIELDS_DATA'] = null;
60 |
61 | $this->_requestMock
62 | ->expects($this->once())
63 | ->method('exec')
64 | ->will($this->returnValue('{"status":"ok"}'));
65 |
66 | $this->_requestMock
67 | ->expects($this->atLeastOnce())
68 | ->method('setopt')
69 | ->will($this->returnCallback(function($option, $value = null) {
70 | switch($option) {
71 | case CURLOPT_POSTFIELDS:
72 | $GLOBALS['CURLOPT_POSTFIELDS_DATA'] = json_decode($value, true);
73 | return true;
74 | }
75 | return true;
76 | }));
77 | $this->_requestMock
78 | ->expects($this->any())
79 | ->method('getinfo')
80 | ->will($this->returnCallback(function($option) {
81 | switch($option) {
82 | case CURLINFO_HTTP_CODE:
83 | return 200;
84 | }
85 | return null;
86 | }));
87 |
88 | $report = AccountingReport::create(array(
89 | 'start_date' => '2020-01-01',
90 | 'object' => 'accounting_report',
91 | 'notification_url' => 'notification_url',
92 | 'end_date' => '2020-04-30',
93 | 'id' => 'ar_1GKEACvltTVXT5muBd3AQv',
94 | 'file_available_until' => 1588083743,
95 | 'temporary_url' => 'temporary_url'
96 | ));
97 |
98 | $this->assertTrue(is_array($GLOBALS['CURLOPT_POSTFIELDS_DATA']));
99 | $this->assertEquals('ok', $report->status);
100 |
101 | unset($GLOBALS['CURLOPT_POSTFIELDS_DATA']);
102 | }
103 |
104 | public function testAccountingReportRetrieve()
105 | {
106 | $GLOBALS['CURLOPT_URL_DATA'] = null;
107 |
108 | $this->_requestMock
109 | ->expects($this->once())
110 | ->method('exec')
111 | ->will($this->returnValue('{"status":"ok"}'));
112 |
113 | $this->_requestMock
114 | ->expects($this->any())
115 | ->method('getinfo')
116 | ->will($this->returnCallback(function($option) {
117 | switch($option) {
118 | case CURLINFO_HTTP_CODE:
119 | return 200;
120 | }
121 | return null;
122 | }));
123 | $this->_requestMock
124 | ->expects($this->any())
125 | ->method('setopt')
126 | ->will($this->returnCallback(function($option, $value = null) {
127 | switch($option) {
128 | case CURLOPT_URL:
129 | $GLOBALS['CURLOPT_URL_DATA'] = $value;
130 | return true;
131 | }
132 | return true;
133 | }));
134 |
135 | $report = AccountingReport::retrieve('a_report_id');
136 |
137 | $this->assertStringEndsWith('a_report_id', $GLOBALS['CURLOPT_URL_DATA']);
138 | $this->assertEquals('ok', $report->status);
139 |
140 | unset($GLOBALS['CURLOPT_URL_DATA']);
141 | }
142 |
143 | public function testRetrieveConsistentAccountingReportWhenIdIsUndefined()
144 | {
145 | $this->expectException('\PayPlug\Exception\UndefinedAttributeException');
146 |
147 | $report= AccountingReport::fromAttributes(array('this_report' => 'has_no_id'));
148 | $report->getConsistentResource();
149 | }
150 |
151 | public function testRetrieveConsistentAccountingReport()
152 | {
153 | function testRetrieveConsistentAccountingReport_getinfo($option) {
154 | switch($option) {
155 | case CURLINFO_HTTP_CODE:
156 | return 200;
157 | }
158 | return null;
159 | }
160 |
161 | $this->_requestMock
162 | ->expects($this->once())
163 | ->method('exec')
164 | ->will($this->returnValue('{"id": "ar_345"}'));
165 |
166 | $this->_requestMock
167 | ->expects($this->any())
168 | ->method('setopt')
169 | ->will($this->returnValue(true));
170 | $this->_requestMock
171 | ->expects($this->any())
172 | ->method('getinfo')
173 | ->will($this->returnCallback(function($option) {
174 | switch($option) {
175 | case CURLINFO_HTTP_CODE:
176 | return 200;
177 | }
178 | return null;
179 | }));
180 |
181 | $report1 = AccountingReport::fromAttributes(array('id' => 'ar_123'));
182 | $report2 = $report1->getConsistentResource($this->_configuration);
183 |
184 | $this->assertEquals('ar_123', $report1->id);
185 | $this->assertEquals('ar_345', $report2->id);
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/InstallmentPlan.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
21 | return $object;
22 | }
23 |
24 | /**
25 | * Initializes the resource.
26 | * This method must be overridden when the resource has objects as attributes.
27 | *
28 | * @param array $attributes the attributes to initialize.
29 | */
30 | protected function initialize(array $attributes)
31 | {
32 | parent::initialize($attributes);
33 |
34 | /*
35 | * @deprecated No longer use by API, use billing and shipping instead
36 | */
37 | if (isset($attributes['customer'])) {
38 | $this->customer = PaymentCustomer::fromAttributes($attributes['customer']);
39 | }
40 | if (isset($attributes['billing'])) {
41 | $this->billing = PaymentBilling::fromAttributes($attributes['billing']);
42 | }
43 | if (isset($attributes['shipping'])) {
44 | $this->shipping = PaymentShipping::fromAttributes($attributes['shipping']);
45 | }
46 | if (isset($attributes['failure'])) {
47 | $this->failure = PaymentPaymentFailure::fromAttributes($attributes['failure']);
48 | }
49 | if (isset($attributes['hosted_payment'])) {
50 | $this->hosted_payment = PaymentHostedPayment::fromAttributes($attributes['hosted_payment']);
51 | }
52 | if (isset($attributes['notification'])) {
53 | $this->notification = PaymentNotification::fromAttributes($attributes['notification']);
54 | }
55 | $schedules = array();
56 | if (isset($attributes['schedule'])) {
57 | foreach ($attributes['schedule'] as &$schedule) {
58 | $schedules[] = InstallmentPlanSchedule::fromAttributes($schedule);
59 | }
60 | }
61 | $this->schedule = $schedules;
62 | }
63 |
64 | /**
65 | * List the payments of this installment plan.
66 | *
67 | * @param Payplug\Payplug $payplug the client configuration
68 | *
69 | * @return null|Payment[] the array of payments of this installment plan
70 | *
71 | * @throws Payplug\Exception\UndefinedAttributeException
72 | * @throws Payplug\Exception\UnexpectedAPIResponseException
73 | */
74 | public function listPayments($payplug = null)
75 | {
76 | if ($payplug === null) {
77 | $payplug = Payplug\Payplug::getDefaultConfiguration();
78 | }
79 |
80 | if (!array_key_exists('id', $this->getAttributes())) {
81 | throw new Payplug\Exception\UndefinedAttributeException(
82 | "This installment plan object has no id. You can't list payments on it.");
83 | }
84 | $httpClient = new Payplug\Core\HttpClient($payplug);
85 | $response = $httpClient->get(
86 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::INSTALLMENT_PLAN_RESOURCE, $this->id)
87 | );
88 |
89 | $payments = array();
90 | foreach ($response['httpResponse']['schedule'] as $schedule) {
91 | foreach ($schedule['payment_ids'] as $payment_id) {
92 | $payments[$payment_id] = Payment::retrieve($payment_id, $payplug);
93 | }
94 | }
95 |
96 | return $payments;
97 |
98 | }
99 |
100 | /**
101 | * Aborts an InstallmentPlan.
102 | *
103 | * @param Payplug\Payplug $payplug the client configuration
104 | *
105 | * @return null|InstallmentPlan the aborted installment plan or null on error
106 | *
107 | * @throws Payplug\Exception\ConfigurationNotSetException
108 | */
109 | public function abort($payplug = null)
110 | {
111 | if ($payplug === null) {
112 | $payplug = Payplug\Payplug::getDefaultConfiguration();
113 | }
114 |
115 | $httpClient = new Payplug\Core\HttpClient($payplug);
116 | $response = $httpClient->patch(
117 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::INSTALLMENT_PLAN_RESOURCE, $this->id),
118 | array('aborted' => true)
119 | );
120 |
121 | return InstallmentPlan::fromAttributes($response['httpResponse']);
122 | }
123 |
124 | /**
125 | * Retrieves an InstallmentPlan.
126 | *
127 | * @param string $installmentPlanId the installment plan ID
128 | * @param Payplug\Payplug $payplug the client configuration
129 | *
130 | * @return null|InstallmentPlan the retrieved installment plan or null on error
131 | *
132 | * @throws Payplug\Exception\ConfigurationNotSetException
133 | */
134 | public static function retrieve($installmentPlanId, $payplug = null)
135 | {
136 | if ($payplug === null) {
137 | $payplug = Payplug\Payplug::getDefaultConfiguration();
138 | }
139 |
140 | $httpClient = new Payplug\Core\HttpClient($payplug);
141 | $response = $httpClient->get(
142 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::INSTALLMENT_PLAN_RESOURCE,
143 | $installmentPlanId)
144 | );
145 |
146 | return InstallmentPlan::fromAttributes($response['httpResponse']);
147 | }
148 |
149 | /**
150 | * Creates an InstallmentPlan.
151 | *
152 | * @param array $data API data for payment creation
153 | * @param Payplug\Payplug $payplug the client configuration
154 | *
155 | * @return null|InstallmentPlan the created installment plan instance
156 | *
157 | * @throws Payplug\Exception\ConfigurationNotSetException
158 | */
159 | public static function create(array $data, $payplug = null)
160 | {
161 | if ($payplug === null) {
162 | $payplug = Payplug\Payplug::getDefaultConfiguration();
163 | }
164 |
165 | $httpClient = new Payplug\Core\HttpClient($payplug);
166 | $response = $httpClient->post(
167 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::INSTALLMENT_PLAN_RESOURCE),
168 | $data
169 | );
170 |
171 | return InstallmentPlan::fromAttributes($response['httpResponse']);
172 | }
173 |
174 | /**
175 | * Returns an API resource that you can trust.
176 | *
177 | * @param Payplug\Payplug $payplug the client configuration.
178 | *
179 | * @return Payplug\Resource\APIResource The consistent API resource.
180 | *
181 | * @throws Payplug\Exception\UndefinedAttributeException when the local resource is invalid.
182 | */
183 | function getConsistentResource($payplug = null)
184 | {
185 | if (!array_key_exists('id', $this->_attributes)) {
186 | throw new Payplug\Exception\UndefinedAttributeException(
187 | 'The id of the installment plan is not set.');
188 | }
189 |
190 | return InstallmentPlan::retrieve($this->_attributes['id'], $payplug);
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # User-friendly check for sphinx-build
11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
13 | endif
14 |
15 | # Internal variables.
16 | PAPEROPT_a4 = -D latex_paper_size=a4
17 | PAPEROPT_letter = -D latex_paper_size=letter
18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
19 | # the i18n builder cannot share the environment and doctrees with the others
20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
21 |
22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
23 |
24 | help:
25 | @echo "Please use \`make ' where is one of"
26 | @echo " html to make standalone HTML files"
27 | @echo " dirhtml to make HTML files named index.html in directories"
28 | @echo " singlehtml to make a single large HTML file"
29 | @echo " pickle to make pickle files"
30 | @echo " json to make JSON files"
31 | @echo " htmlhelp to make HTML files and a HTML help project"
32 | @echo " qthelp to make HTML files and a qthelp project"
33 | @echo " devhelp to make HTML files and a Devhelp project"
34 | @echo " epub to make an epub"
35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
36 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
38 | @echo " text to make text files"
39 | @echo " man to make manual pages"
40 | @echo " texinfo to make Texinfo files"
41 | @echo " info to make Texinfo files and run them through makeinfo"
42 | @echo " gettext to make PO message catalogs"
43 | @echo " changes to make an overview of all changed/added/deprecated items"
44 | @echo " xml to make Docutils-native XML files"
45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
46 | @echo " linkcheck to check all external links for integrity"
47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
48 |
49 | clean:
50 | rm -rf $(BUILDDIR)/*
51 |
52 | html:
53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
54 | @echo
55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
56 |
57 | dirhtml:
58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
59 | @echo
60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
61 |
62 | singlehtml:
63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
64 | @echo
65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
66 |
67 | pickle:
68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
69 | @echo
70 | @echo "Build finished; now you can process the pickle files."
71 |
72 | json:
73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
74 | @echo
75 | @echo "Build finished; now you can process the JSON files."
76 |
77 | htmlhelp:
78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
79 | @echo
80 | @echo "Build finished; now you can run HTML Help Workshop with the" \
81 | ".hhp project file in $(BUILDDIR)/htmlhelp."
82 |
83 | qthelp:
84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
85 | @echo
86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PayPluge-commercelibrary.qhcp"
89 | @echo "To view the help file:"
90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PayPluge-commercelibrary.qhc"
91 |
92 | devhelp:
93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
94 | @echo
95 | @echo "Build finished."
96 | @echo "To view the help file:"
97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/PayPluge-commercelibrary"
98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PayPluge-commercelibrary"
99 | @echo "# devhelp"
100 |
101 | epub:
102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
103 | @echo
104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
105 |
106 | latex:
107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
108 | @echo
109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
111 | "(use \`make latexpdf' here to do that automatically)."
112 |
113 | latexpdf:
114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
115 | @echo "Running LaTeX files through pdflatex..."
116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
118 |
119 | latexpdfja:
120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
121 | @echo "Running LaTeX files through platex and dvipdfmx..."
122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
124 |
125 | text:
126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
127 | @echo
128 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
129 |
130 | man:
131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
132 | @echo
133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
134 |
135 | texinfo:
136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
137 | @echo
138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
139 | @echo "Run \`make' in that directory to run these through makeinfo" \
140 | "(use \`make info' here to do that automatically)."
141 |
142 | info:
143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
144 | @echo "Running Texinfo files through makeinfo..."
145 | make -C $(BUILDDIR)/texinfo info
146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
147 |
148 | gettext:
149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
150 | @echo
151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
152 |
153 | changes:
154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
155 | @echo
156 | @echo "The overview file is in $(BUILDDIR)/changes."
157 |
158 | linkcheck:
159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
160 | @echo
161 | @echo "Link check complete; look for any errors in the above output " \
162 | "or in $(BUILDDIR)/linkcheck/output.txt."
163 |
164 | doctest:
165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
166 | @echo "Testing of doctests in the sources finished, look at the " \
167 | "results in $(BUILDDIR)/doctest/output.txt."
168 |
169 | xml:
170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
171 | @echo
172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
173 |
174 | pseudoxml:
175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
176 | @echo
177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
178 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | PHP library for the Payplug API
2 | ===============================
3 |
4 | .. image:: https://travis-ci.org/payplug/payplug-php.svg?branch=master
5 | :target: https://travis-ci.org/payplug/payplug-php
6 | :alt: CI Status
7 |
8 | .. image:: https://img.shields.io/packagist/v/payplug/payplug-php.svg
9 | :target: https://packagist.org/packages/payplug/payplug-php
10 | :alt: Packagist
11 |
12 | This is the documentation of Payplug's PHP library. It is designed to
13 | help developers use Payplug as a payment solution in a simple, yet robust way.
14 |
15 | You can create a Payplug account at https://www.payplug.com/.
16 |
17 | Maintenance
18 | -----------
19 |
20 | CA certificate (cacert.pem) should be updated every year during the first week of December.
21 | Go to https://curl.se/docs/caextract.html to get a recent one.
22 |
23 | Prerequisites
24 | -------------
25 |
26 | Payplug's library relies on **cURL** to perform HTTP requests and requires **OpenSSL (1.0.1 or newer)** to secure transactions. You also need **PHP 5.3** or newer for the Payplug PHP V2.
27 |
28 | For version **PHP 5.2** or older, you must refer to Payplug PHP V1.
29 |
30 | Documentation
31 | -------------
32 |
33 | Please see https://docs.payplug.com/api for the latest documentation.
34 |
35 | Installation
36 | -------------
37 |
38 | **Option 1 - Strongly preferred)** via composer:
39 |
40 | - Get composer from the `composer website`_.
41 | - Make sure you have initialized your *composer.json*.
42 | - Run *composer require payplug/payplug-php* from your project directory.
43 |
44 | .. _composer website: https://getcomposer.org/download/
45 |
46 | **Option 2-** clone the repository:
47 |
48 | ::
49 |
50 | git clone https://github.com/payplug/payplug-php.git
51 |
52 | **Option 3)** download as a tarball:
53 |
54 | - Download the most recent tarball from the `download page V2`_ (V2 for **PHP 5.3** or newer)
55 | - Download the most recent tarball from the `download page V1`_ (V1 for **PHP 5.2** or older)
56 | - Unpack the tarball
57 | - Put the files somewhere in your project
58 |
59 | .. _download page V1: https://github.com/payplug/payplug-php/releases/tag/V1.1.2
60 | .. _download page V2: https://github.com/payplug/payplug-php/releases
61 |
62 | __ https://bitbucket.org/payplug/payplug_php/downloads#tag-downloads
63 |
64 | To get started, add the following to your PHP script (if you are not running a framework):
65 |
66 | .. code-block:: php
67 |
68 |
88 |
89 | For a bug fix, use:
90 |
91 | ::
92 |
93 | git checkout develop
94 | git pull origin develop
95 | git checkout -b
96 |
97 | 2. **Work on your feature or fix:**
98 |
99 | Make your code changes and commit them to your feature or fix branch.
100 |
101 | 3. **Create a merge request:**
102 |
103 | Once your feature or fix is ready, create a merge request from your branch to the `develop` branch. Get your changes reviewed by your peers.
104 |
105 | 4. **Release preparation:**
106 |
107 | When it's time for a release, create an intermediary branch called `release-` from the `develop` branch:
108 |
109 | ::
110 |
111 | git checkout develop
112 | git pull origin develop
113 | git checkout -b release-
114 |
115 | 5. **Finalize the release:**
116 |
117 | Test the code on the `release-` branch thoroughly. Fix any bugs or issues that arise.
118 |
119 | 6. **Merge to master:**
120 |
121 | Once the release is tested and stable, create a merge request from the `release-` branch to the `master` branch. This signifies a successful release.
122 |
123 | 7. **Tag the release:**
124 |
125 | After the merge to `master`, create a new tag to mark the release version:
126 |
127 | ::
128 |
129 | git checkout master
130 | git pull origin master
131 | git tag -a -m "Release "
132 | git push origin master --tags
133 |
134 | Usage
135 | -----
136 |
137 | Here's how simple it is to create a payment request:
138 |
139 | .. code-block:: php
140 |
141 | 'sk_live_YOUR_PRIVATE_KEY',
147 | 'apiVersion' => 'THE_API_VERSION_YOU_WANT',
148 | ));
149 |
150 | // Create a payment request of €9.99. The payment confirmation (IPN) will be sent to "'https://example.net/notifications?id='.$customer_id".
151 | // Note that all amounts must be expressed in centimes as positive whole numbers (€9.99 = 999 centimes).
152 | // Metadata allow you to include additional information when processing payments or refunds.
153 | $customer_id = '42710';
154 |
155 | $payment = Payplug\Payment::create(array(
156 | 'amount' => 999,
157 | 'currency' => 'EUR',
158 | 'billing' => array(
159 | 'title' => 'mr',
160 | 'first_name' => 'John',
161 | 'last_name' => 'Watson',
162 | 'email' => 'john.watson@example.net',
163 | 'address1' => '221B Baker Street',
164 | 'postcode' => 'NW16XE',
165 | 'city' => 'London',
166 | 'country' => 'GB',
167 | 'language' => 'en'
168 | ),
169 | 'shipping' => array(
170 | 'title' => 'mr',
171 | 'first_name' => 'John',
172 | 'last_name' => 'Watson',
173 | 'email' => 'john.watson@example.net',
174 | 'address1' => '221B Baker Street',
175 | 'postcode' => 'NW16XE',
176 | 'city' => 'London',
177 | 'country' => 'GB',
178 | 'language' => 'en',
179 | 'delivery_type' => 'BILLING'
180 | ),
181 | 'hosted_payment' => array(
182 | 'return_url' => 'https://example.net/return?id='.$customer_id,
183 | 'cancel_url' => 'https://example.net/cancel?id='.$customer_id
184 | ),
185 | 'notification_url' => 'https://example.net/notifications?id='.$customer_id,
186 | 'metadata' => array(
187 | 'customer_id' => $customer_id
188 | )
189 | ));
190 |
191 | // You will be able to find how the payment object is built in the documentation.
192 | // For instance, if you want to get a URL to the payment page, you can do:
193 | $paymentUrl = $payment->hosted_payment->payment_url;
194 |
195 | // Then, you can redirect the user to the payment page
196 | header("Location: $paymentUrl");
197 |
198 | Go further:
199 | -----------
200 | Tests:
201 | ++++++
202 | See tests/README.rst.
203 |
204 | Project Owners
205 | --------------
206 |
207 | This project is maintained by:
208 |
209 |
210 | - [Imène Lajili](https://github.com/ilajili)
211 | - [Manuel Mesquita](https://github.com/PPmmesquita)
212 |
213 | For any questions or concerns about the workflow, feel free to reach out to the project owners.
214 |
215 |
216 |
--------------------------------------------------------------------------------
/tests/unit_tests/Resource/OneySimulationResourceTest.php:
--------------------------------------------------------------------------------
1 | array(
16 | 'effective_annual_percentage_rate' => 19.25,
17 | 'nominal_annual_percentage_rate' => 17.74,
18 | 'total_cost' => 732,
19 | 'installments' => array(
20 | array (
21 | 'date' => '2020-03-06T01:00:00.000Z',
22 | 'amount' => 16834,
23 | ),
24 | array (
25 | 'date' => '2020-04-06T00:00:00.000Z',
26 | 'amount' => 16833,
27 | ),
28 | ),
29 | 'down_payment_amount' => 17565,
30 | )
31 | ));
32 |
33 | $this->assertObjectHasAttribute('x3_with_fees', $simulations);
34 | $this->assertEquals(true, is_array($simulations->x3_with_fees));
35 | $this->assertEquals(19.25, $simulations->x3_with_fees['effective_annual_percentage_rate']);
36 | $this->assertEquals(17.74, $simulations->x3_with_fees['nominal_annual_percentage_rate']);
37 | $this->assertEquals(732, $simulations->x3_with_fees['total_cost']);
38 |
39 | $installments = $simulations->x3_with_fees['installments'];
40 |
41 | $this->assertEquals(2, count($installments));
42 |
43 | $this->assertEquals('2020-03-06T01:00:00.000Z', $installments[0]['date']);
44 | $this->assertEquals(16834, $installments[0]['amount']);
45 | $this->assertEquals('2020-04-06T00:00:00.000Z', $installments[1]['date']);
46 | $this->assertEquals(16833, $installments[1]['amount']);
47 |
48 | $this->assertEquals(17565, $simulations->x3_with_fees['down_payment_amount']);
49 | }
50 |
51 | public function testGetOneySimulation4xWithFees()
52 | {
53 | $simulations = OneySimulationResource::fromAttributes(array(
54 | 'x4_with_fees' => array(
55 | 'effective_annual_percentage_rate' => 19.25,
56 | 'nominal_annual_percentage_rate' => 17.74,
57 | 'total_cost' => 732,
58 | 'installments' => array(
59 | array (
60 | 'date' => '2020-03-06T01:00:00.000Z',
61 | 'amount' => 16834,
62 | ),
63 | array (
64 | 'date' => '2020-04-06T00:00:00.000Z',
65 | 'amount' => 16833,
66 | ),
67 | array (
68 | 'date' => '2020-05-06T00:00:00.000Z',
69 | 'amount' => 16833,
70 | ),
71 | ),
72 | 'down_payment_amount' => 17565,
73 | )
74 | ));
75 |
76 | $this->assertObjectHasAttribute('x4_with_fees', $simulations);
77 | $this->assertEquals(true, is_array($simulations->x4_with_fees));
78 | $this->assertEquals(19.25, $simulations->x4_with_fees['effective_annual_percentage_rate']);
79 | $this->assertEquals(17.74, $simulations->x4_with_fees['nominal_annual_percentage_rate']);
80 | $this->assertEquals(732, $simulations->x4_with_fees['total_cost']);
81 |
82 | $installments = $simulations->x4_with_fees['installments'];
83 |
84 | $this->assertEquals(3, count($installments));
85 |
86 | $this->assertEquals('2020-03-06T01:00:00.000Z', $installments[0]['date']);
87 | $this->assertEquals(16834, $installments[0]['amount']);
88 | $this->assertEquals('2020-04-06T00:00:00.000Z', $installments[1]['date']);
89 | $this->assertEquals(16833, $installments[1]['amount']);
90 | $this->assertEquals('2020-05-06T00:00:00.000Z', $installments[2]['date']);
91 | $this->assertEquals(16833, $installments[2]['amount']);
92 |
93 | $this->assertEquals(17565, $simulations->x4_with_fees['down_payment_amount']);
94 | }
95 |
96 | public function testGetOneySimulationWithFees()
97 | {
98 | $simulations = OneySimulationResource::fromAttributes(array(
99 | 'x3_with_fees' => array(
100 | 'effective_annual_percentage_rate' => 19.25,
101 | 'nominal_annual_percentage_rate' => 17.74,
102 | 'total_cost' => 732,
103 | 'installments' => array(
104 | array (
105 | 'date' => '2020-03-06T01:00:00.000Z',
106 | 'amount' => 16834,
107 | ),
108 | array (
109 | 'date' => '2020-04-06T00:00:00.000Z',
110 | 'amount' => 16833,
111 | ),
112 | ),
113 | 'down_payment_amount' => 17565,
114 | ),
115 | 'x4_with_fees' => array(
116 | 'effective_annual_percentage_rate' => 19.25,
117 | 'nominal_annual_percentage_rate' => 17.74,
118 | 'total_cost' => 732,
119 | 'installments' => array(
120 | array (
121 | 'date' => '2020-03-06T01:00:00.000Z',
122 | 'amount' => 16834,
123 | ),
124 | array (
125 | 'date' => '2020-04-06T00:00:00.000Z',
126 | 'amount' => 16833,
127 | ),
128 | array (
129 | 'date' => '2020-05-06T00:00:00.000Z',
130 | 'amount' => 16833,
131 | ),
132 | ),
133 | 'down_payment_amount' => 17565,
134 | )
135 | ));
136 |
137 | // check 3x with fees
138 | $this->assertObjectHasAttribute('x3_with_fees', $simulations);
139 | $this->assertEquals(true, is_array($simulations->x3_with_fees));
140 | $this->assertEquals(19.25, $simulations->x3_with_fees['effective_annual_percentage_rate']);
141 | $this->assertEquals(17.74, $simulations->x3_with_fees['nominal_annual_percentage_rate']);
142 | $this->assertEquals(732, $simulations->x3_with_fees['total_cost']);
143 |
144 | $installments_x3 = $simulations->x3_with_fees['installments'];
145 |
146 | $this->assertEquals(2, count($installments_x3));
147 | $this->assertEquals('2020-03-06T01:00:00.000Z', $installments_x3[0]['date']);
148 | $this->assertEquals(16834, $installments_x3[0]['amount']);
149 | $this->assertEquals('2020-04-06T00:00:00.000Z', $installments_x3[1]['date']);
150 | $this->assertEquals(16833, $installments_x3[1]['amount']);
151 |
152 | $this->assertEquals(17565, $simulations->x3_with_fees['down_payment_amount']);
153 |
154 | // check 4x with fees
155 | $this->assertObjectHasAttribute('x4_with_fees', $simulations);
156 | $this->assertEquals(true, is_array($simulations->x4_with_fees));
157 | $this->assertEquals(19.25, $simulations->x4_with_fees['effective_annual_percentage_rate']);
158 | $this->assertEquals(17.74, $simulations->x4_with_fees['nominal_annual_percentage_rate']);
159 | $this->assertEquals(732, $simulations->x4_with_fees['total_cost']);
160 |
161 | $installments_x4 = $simulations->x4_with_fees['installments'];
162 |
163 | $this->assertEquals(3, count($installments_x4));
164 |
165 | $this->assertEquals('2020-03-06T01:00:00.000Z', $installments_x4[0]['date']);
166 | $this->assertEquals(16834, $installments_x4[0]['amount']);
167 | $this->assertEquals('2020-04-06T00:00:00.000Z', $installments_x4[1]['date']);
168 | $this->assertEquals(16833, $installments_x4[1]['amount']);
169 | $this->assertEquals('2020-05-06T00:00:00.000Z', $installments_x4[2]['date']);
170 | $this->assertEquals(16833, $installments_x4[2]['amount']);
171 |
172 | $this->assertEquals(17565, $simulations->x4_with_fees['down_payment_amount']);
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # PayPlug e-commerce library documentation build configuration file, created by
4 | # sphinx-quickstart on Thu Apr 10 10:05:26 2014.
5 | #
6 | # This file is execfile()d with the current directory set to its
7 | # containing dir.
8 | #
9 | # Note that not all possible configuration values are present in this
10 | # autogenerated file.
11 | #
12 | # All configuration values have a default; values that are commented out
13 | # serve to show the default.
14 |
15 | import sys
16 | import os
17 |
18 | # If extensions (or modules to document with autodoc) are in another directory,
19 | # add these directories to sys.path here. If the directory is relative to the
20 | # documentation root, use os.path.abspath to make it absolute, like shown here.
21 | #sys.path.insert(0, os.path.abspath('.'))
22 |
23 | # -- General configuration ------------------------------------------------
24 |
25 | # If your documentation needs a minimal Sphinx version, state it here.
26 | #needs_sphinx = '1.0'
27 |
28 | # Add any Sphinx extension module names here, as strings. They can be
29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
30 | # ones.
31 | extensions = []
32 |
33 | # Add any paths that contain templates here, relative to this directory.
34 | templates_path = ['_templates']
35 |
36 | # The suffix of source filenames.
37 | source_suffix = '.rst'
38 |
39 | # The encoding of source files.
40 | #source_encoding = 'utf-8-sig'
41 |
42 | # The master toctree document.
43 | master_doc = 'index'
44 |
45 | # General information about the project.
46 | project = u'PayPlug PHP library'
47 | copyright = u'2014, PayPlug'
48 |
49 | # The version info for the project you're documenting, acts as replacement for
50 | # |version| and |release|, also used in various other places throughout the
51 | # built documents.
52 | #
53 | # The short X.Y version.
54 | version = '1.0'
55 | # The full version, including alpha/beta/rc tags.
56 | release = '1.0'
57 |
58 | # The language for content autogenerated by Sphinx. Refer to documentation
59 | # for a list of supported languages.
60 | #language = None
61 |
62 | # There are two options for replacing |today|: either, you set today to some
63 | # non-false value, then it is used:
64 | #today = ''
65 | # Else, today_fmt is used as the format for a strftime call.
66 | #today_fmt = '%B %d, %Y'
67 |
68 | # List of patterns, relative to source directory, that match files and
69 | # directories to ignore when looking for source files.
70 | exclude_patterns = ['_build']
71 |
72 | # The reST default role (used for this markup: `text`) to use for all
73 | # documents.
74 | #default_role = None
75 |
76 | # If true, '()' will be appended to :func: etc. cross-reference text.
77 | #add_function_parentheses = True
78 |
79 | # If true, the current module name will be prepended to all description
80 | # unit titles (such as .. function::).
81 | #add_module_names = True
82 |
83 | # If true, sectionauthor and moduleauthor directives will be shown in the
84 | # output. They are ignored by default.
85 | #show_authors = False
86 |
87 | # The name of the Pygments (syntax highlighting) style to use.
88 | pygments_style = 'sphinx'
89 |
90 | # A list of ignored prefixes for module index sorting.
91 | #modindex_common_prefix = []
92 |
93 | # If true, keep warnings as "system message" paragraphs in the built documents.
94 | #keep_warnings = False
95 |
96 |
97 | # -- Options for HTML output ----------------------------------------------
98 |
99 | # The theme to use for HTML and HTML Help pages. See the documentation for
100 | # a list of builtin themes.
101 | html_theme = 'default'
102 |
103 | # Theme options are theme-specific and customize the look and feel of a theme
104 | # further. For a list of options available for each theme, see the
105 | # documentation.
106 | #html_theme_options = {}
107 |
108 | # Add any paths that contain custom themes here, relative to this directory.
109 | #html_theme_path = []
110 |
111 | # The name for this set of Sphinx documents. If None, it defaults to
112 | # " v documentation".
113 | #html_title = None
114 |
115 | # A shorter title for the navigation bar. Default is the same as html_title.
116 | #html_short_title = None
117 |
118 | # The name of an image file (relative to this directory) to place at the top
119 | # of the sidebar.
120 | #html_logo = None
121 |
122 | # The name of an image file (within the static path) to use as favicon of the
123 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
124 | # pixels large.
125 | #html_favicon = None
126 |
127 | # Add any paths that contain custom static files (such as style sheets) here,
128 | # relative to this directory. They are copied after the builtin static files,
129 | # so a file named "default.css" will overwrite the builtin "default.css".
130 | html_static_path = ['_static']
131 |
132 | # Add any extra paths that contain custom files (such as robots.txt or
133 | # .htaccess) here, relative to this directory. These files are copied
134 | # directly to the root of the documentation.
135 | #html_extra_path = []
136 |
137 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
138 | # using the given strftime format.
139 | #html_last_updated_fmt = '%b %d, %Y'
140 |
141 | # If true, SmartyPants will be used to convert quotes and dashes to
142 | # typographically correct entities.
143 | #html_use_smartypants = True
144 |
145 | # Custom sidebar templates, maps document names to template names.
146 | #html_sidebars = {}
147 |
148 | # Additional templates that should be rendered to pages, maps page names to
149 | # template names.
150 | #html_additional_pages = {}
151 |
152 | # If false, no module index is generated.
153 | #html_domain_indices = True
154 |
155 | # If false, no index is generated.
156 | #html_use_index = True
157 |
158 | # If true, the index is split into individual pages for each letter.
159 | #html_split_index = False
160 |
161 | # If true, links to the reST sources are added to the pages.
162 | #html_show_sourcelink = True
163 |
164 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
165 | #html_show_sphinx = True
166 |
167 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
168 | #html_show_copyright = True
169 |
170 | # If true, an OpenSearch description file will be output, and all pages will
171 | # contain a tag referring to it. The value of this option must be the
172 | # base URL from which the finished HTML is served.
173 | #html_use_opensearch = ''
174 |
175 | # This is the file name suffix for HTML files (e.g. ".xhtml").
176 | #html_file_suffix = None
177 |
178 | # Output file base name for HTML help builder.
179 | htmlhelp_basename = 'PayPluge-commercelibrarydoc'
180 |
181 |
182 | # -- Options for LaTeX output ---------------------------------------------
183 |
184 | latex_elements = {
185 | # The paper size ('letterpaper' or 'a4paper').
186 | #'papersize': 'letterpaper',
187 |
188 | # The font size ('10pt', '11pt' or '12pt').
189 | #'pointsize': '10pt',
190 |
191 | # Additional stuff for the LaTeX preamble.
192 | #'preamble': '',
193 | }
194 |
195 | # Grouping the document tree into LaTeX files. List of tuples
196 | # (source start file, target name, title,
197 | # author, documentclass [howto, manual, or own class]).
198 | latex_documents = [
199 | ('index', 'PayPluge-commercelibrary.tex', u'PayPlug e-commerce library Documentation',
200 | u'PayPlug', 'manual'),
201 | ]
202 |
203 | # The name of an image file (relative to this directory) to place at the top of
204 | # the title page.
205 | #latex_logo = None
206 |
207 | # For "manual" documents, if this is true, then toplevel headings are parts,
208 | # not chapters.
209 | #latex_use_parts = False
210 |
211 | # If true, show page references after internal links.
212 | #latex_show_pagerefs = False
213 |
214 | # If true, show URL addresses after external links.
215 | #latex_show_urls = False
216 |
217 | # Documents to append as an appendix to all manuals.
218 | #latex_appendices = []
219 |
220 | # If false, no module index is generated.
221 | #latex_domain_indices = True
222 |
223 |
224 | # -- Options for manual page output ---------------------------------------
225 |
226 | # One entry per manual page. List of tuples
227 | # (source start file, name, description, authors, manual section).
228 | man_pages = [
229 | ('index', 'paypluge-commercelibrary', u'PayPlug e-commerce library Documentation',
230 | [u'PayPlug'], 1)
231 | ]
232 |
233 | # If true, show URL addresses after external links.
234 | #man_show_urls = False
235 |
236 |
237 | # -- Options for Texinfo output -------------------------------------------
238 |
239 | # Grouping the document tree into Texinfo files. List of tuples
240 | # (source start file, target name, title, author,
241 | # dir menu entry, description, category)
242 | texinfo_documents = [
243 | ('index', 'PayPluge-commercelibrary', u'PayPlug e-commerce library Documentation',
244 | u'PayPlug', 'PayPluge-commercelibrary', 'One line description of project.',
245 | 'Miscellaneous'),
246 | ]
247 |
248 | # Documents to append as an appendix to all manuals.
249 | #texinfo_appendices = []
250 |
251 | # If false, no module index is generated.
252 | #texinfo_domain_indices = True
253 |
254 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
255 | #texinfo_show_urls = 'footnote'
256 |
257 | # If true, do not generate a @detailmenu in the "Top" node's menu.
258 | #texinfo_no_detailmenu = False
259 |
--------------------------------------------------------------------------------
/lib/Payplug/Resource/Payment.php:
--------------------------------------------------------------------------------
1 | initialize($attributes);
21 | return $object;
22 | }
23 |
24 | /**
25 | * Initializes the resource.
26 | * This method must be overridden when the resource has objects as attributes.
27 | *
28 | * @param array $attributes the attributes to initialize.
29 | */
30 | protected function initialize(array $attributes)
31 | {
32 | parent::initialize($attributes);
33 |
34 | if (isset($attributes['card'])) {
35 | $this->card = PaymentCard::fromAttributes($attributes['card']);
36 | }
37 |
38 | /*
39 | * @deprecated No longer used by API, use billing and shipping instead
40 | */
41 | if (isset($attributes['customer'])) {
42 | $this->customer = PaymentCustomer::fromAttributes($attributes['customer']);
43 | }
44 | if (isset($attributes['billing'])) {
45 | $this->billing = PaymentBilling::fromAttributes($attributes['billing']);
46 | }
47 | if (isset($attributes['shipping'])) {
48 | $this->shipping = PaymentShipping::fromAttributes($attributes['shipping']);
49 | }
50 | if (isset($attributes['hosted_payment'])) {
51 | $this->hosted_payment = PaymentHostedPayment::fromAttributes($attributes['hosted_payment']);
52 | }
53 | if (isset($attributes['failure'])) {
54 | $this->failure = PaymentPaymentFailure::fromAttributes($attributes['failure']);
55 | }
56 | if (isset($attributes['notification'])) {
57 | $this->notification = PaymentNotification::fromAttributes($attributes['notification']);
58 | }
59 | if (isset($attributes['authorization'])) {
60 | $this->authorization = PaymentAuthorization::fromAttributes($attributes['authorization']);
61 | }
62 | }
63 |
64 | /**
65 | * Open a refund on the payment.
66 | *
67 | * @param array $data the refund data
68 | * @param Payplug\Payplug $payplug the client configuration
69 | *
70 | * @return Refund|null the opened refund instance
71 | *
72 | * @throws Payplug\Exception\InvalidPaymentException when the id of the payment is invalid
73 | */
74 | public function refund($data = null, $payplug = null)
75 | {
76 | if (!array_key_exists('id', $this->getAttributes())) {
77 | throw new Payplug\Exception\InvalidPaymentException("This payment object has no id. It can't be refunded.");
78 | }
79 |
80 | return Refund::create($this->id, $data, $payplug);
81 | }
82 |
83 | /**
84 | * List the refunds of this payment.
85 | *
86 | * @param Payplug\Payplug $payplug the client configuration
87 | *
88 | * @return null|Refund[] the array of refunds of this payment
89 | *
90 | * @throws Payplug\Exception\InvalidPaymentException
91 | * @throws Payplug\Exception\UnexpectedAPIResponseException
92 | */
93 | public function listRefunds($payplug = null)
94 | {
95 | if (!array_key_exists('id', $this->getAttributes())) {
96 | throw new Payplug\Exception\InvalidPaymentException("This payment object has no id. You can't list refunds on it.");
97 | }
98 |
99 | return Refund::listRefunds($this->id, $payplug);
100 | }
101 |
102 | /**
103 | * Aborts a Payment.
104 | *
105 | * @param Payplug\Payplug $payplug the client configuration
106 | *
107 | * @return null|Payment the aborted payment or null on error
108 | *
109 | * @throws Payplug\Exception\ConfigurationNotSetException
110 | */
111 | public function abort($payplug = null)
112 | {
113 | if ($payplug === null) {
114 | $payplug = Payplug\Payplug::getDefaultConfiguration();
115 | }
116 |
117 | $httpClient = new Payplug\Core\HttpClient($payplug);
118 | $response = $httpClient->patch(
119 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::PAYMENT_RESOURCE, $this->id),
120 | array('aborted' => true)
121 | );
122 |
123 | return Payment::fromAttributes($response['httpResponse']);
124 | }
125 |
126 | /**
127 | * Captures a Payment.
128 | *
129 | * @param Payplug\Payplug $payplug the client configuration
130 | *
131 | * @return null|Payment the captured payment or null on error
132 | *
133 | * @throws Payplug\Exception\ConfigurationNotSetException
134 | */
135 | public function capture($payplug = null)
136 | {
137 | if ($payplug === null) {
138 | $payplug = Payplug\Payplug::getDefaultConfiguration();
139 | }
140 |
141 | $httpClient = new Payplug\Core\HttpClient($payplug);
142 | $response = $httpClient->patch(
143 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::PAYMENT_RESOURCE, $this->id),
144 | array('captured' => true)
145 | );
146 |
147 | return Payment::fromAttributes($response['httpResponse']);
148 | }
149 |
150 | /**
151 | * Retrieves a Payment.
152 | *
153 | * @param string $paymentId the payment ID
154 | * @param Payplug\Payplug $payplug the client configuration
155 | *
156 | * @return Payment the retrieved payment
157 | *
158 | * @throws Payplug\Exception\ConfigurationNotSetException
159 | * @throws Payplug\Exception\UndefinedAttributeException
160 | * @throws Payplug\Exception\NotFoundException
161 | */
162 | public static function retrieve($paymentId, $payplug = null)
163 | {
164 | if ($payplug === null) {
165 | $payplug = Payplug\Payplug::getDefaultConfiguration();
166 | }
167 |
168 | if (!$paymentId) {
169 | throw new Payplug\Exception\UndefinedAttributeException('The parameter paymentId is not set.');
170 | }
171 |
172 | $httpClient = new Payplug\Core\HttpClient($payplug);
173 | $response = $httpClient->get(
174 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::PAYMENT_RESOURCE, $paymentId)
175 | );
176 |
177 | return Payment::fromAttributes($response['httpResponse']);
178 | }
179 |
180 | /**
181 | * List payments.
182 | *
183 | * @param Payplug\Payplug $payplug the client configuration
184 | *
185 | * @param int $perPage the number of results per page
186 | * @param int $page the page number
187 | * @return null|Payment[] the array of payments
188 | *
189 | * @throws Payplug\Exception\InvalidPaymentException
190 | * @throws Payplug\Exception\UnexpectedAPIResponseException
191 | */
192 | public static function listPayments($perPage = null, $page = null, $payplug = null)
193 | {
194 | if ($payplug === null) {
195 | $payplug = Payplug\Payplug::getDefaultConfiguration();
196 | }
197 |
198 | $httpClient = new Payplug\Core\HttpClient($payplug);
199 | $pagination = array('per_page' => $perPage, 'page' => $page);
200 | $response = $httpClient->get(
201 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::PAYMENT_RESOURCE, null, array(), $pagination)
202 | );
203 |
204 | if (!array_key_exists('data', $response['httpResponse'])
205 | || !is_array($response['httpResponse']['data'])) {
206 | throw new Payplug\Exception\UnexpectedAPIResponseException(
207 | "Expected 'data' key in API response.",
208 | $response['httpResponse']
209 | );
210 | }
211 |
212 | $payments = array();
213 | foreach ($response['httpResponse']['data'] as &$payment) {
214 | $payments[] = Payment::fromAttributes($payment);
215 | }
216 |
217 | return $payments;
218 | }
219 |
220 | /**
221 | * Creates a Payment.
222 | *
223 | * @param array $data API data for payment creation
224 | * @param Payplug\Payplug $payplug the client configuration
225 | *
226 | * @return null|Payment the created payment instance
227 | *
228 | * @throws Payplug\Exception\ConfigurationNotSetException
229 | */
230 | public static function create(array $data, $payplug = null)
231 | {
232 | if ($payplug === null) {
233 | $payplug = Payplug\Payplug::getDefaultConfiguration();
234 | }
235 |
236 | $httpClient = new Payplug\Core\HttpClient($payplug);
237 | $response = $httpClient->post(
238 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::PAYMENT_RESOURCE),
239 | $data
240 | );
241 |
242 | return Payment::fromAttributes($response['httpResponse']);
243 | }
244 |
245 | /**
246 | * Update a Payment.
247 | *
248 | * @param array $data API data for payment creation
249 | * @param Payplug\Payplug $payplug the client configuration
250 | *
251 | * @return null|Payment the updated payment instance
252 | *
253 | * @throws Payplug\Exception\ConfigurationNotSetException
254 | */
255 | public function update(array $data, $payplug = null)
256 | {
257 | if ($payplug === null) {
258 | $payplug = Payplug\Payplug::getDefaultConfiguration();
259 | }
260 |
261 | $httpClient = new Payplug\Core\HttpClient($payplug);
262 | $response = $httpClient->patch(
263 | Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::PAYMENT_RESOURCE, $this->id),
264 | $data
265 | );
266 |
267 | return Payment::fromAttributes($response['httpResponse']);
268 | }
269 |
270 | /**
271 | * Returns an API resource that you can trust.
272 | *
273 | * @param Payplug\Payplug $payplug the client configuration.
274 | *
275 | * @return Payplug\Resource\APIResource The consistent API resource.
276 | *
277 | * @throws Payplug\Exception\UndefinedAttributeException when the local resource is invalid.
278 | */
279 | function getConsistentResource($payplug = null)
280 | {
281 | if (!array_key_exists('id', $this->_attributes)) {
282 | throw new Payplug\Exception\UndefinedAttributeException('The id of the payment is not set.');
283 | }
284 |
285 | return Payment::retrieve($this->_attributes['id'], $payplug);
286 | }
287 | }
288 |
--------------------------------------------------------------------------------
/lib/Payplug/Authentication.php:
--------------------------------------------------------------------------------
1 | post(
29 | Core\APIRoutes::getRoute(Core\APIRoutes::KEY_RESOURCE),
30 | array('email' => $email, 'password' => $password),
31 | false
32 | );
33 | return $response;
34 | }
35 |
36 | /**
37 | * Retrieve account info.
38 | *
39 | * @param Payplug $payplug the client configuration
40 | *
41 | * @return null|array the account settings
42 | *
43 | * @throws Exception\ConfigurationNotSetException
44 | * @throws ConfigurationException
45 | */
46 | public static function getAccount($payplug = null)
47 | {
48 | if ($payplug === null) {
49 | $payplug = Payplug::getDefaultConfiguration();
50 | }
51 | self::validateToken($payplug);
52 |
53 | $httpClient = new Core\HttpClient($payplug);
54 | $response = $httpClient->get(Core\APIRoutes::getRoute(Core\APIRoutes::ACCOUNT_RESOURCE));
55 |
56 | return $response;
57 | }
58 |
59 | /**
60 | * Retrieve the account permissions
61 | *
62 | * @param Payplug $payplug the client configuration
63 | *
64 | * @return null|array the account permissions
65 | *
66 | * @throws Exception\ConfigurationNotSetException
67 | * @throws ConfigurationException
68 | */
69 | public static function getPermissions($payplug = null)
70 | {
71 | if ($payplug === null) {
72 | $payplug = Payplug::getDefaultConfiguration();
73 | }
74 |
75 | self::validateToken($payplug);
76 |
77 | $httpClient = new Core\HttpClient($payplug);
78 | $response = $httpClient->get(Core\APIRoutes::getRoute(Core\APIRoutes::ACCOUNT_RESOURCE));
79 |
80 | return $response['httpResponse']['permissions'];
81 | }
82 |
83 | /**
84 | * Retrieve the account permissions, using email and password.
85 | * This function is for user-friendly interface purpose only.
86 | * You should probably not use this more than once, login/password MUST NOT be stored and API Keys are enough to interact with API.
87 | *
88 | * @param string $email the user email
89 | * @param string $password the user password
90 | *
91 | * @return null|array the account permissions
92 | *
93 | * @throws Exception\ConfigurationNotSetException
94 | * @throws ConfigurationException
95 | */
96 | public static function getPermissionsByLogin($email, $password)
97 | {
98 | $keys = self::getKeysByLogin($email, $password);
99 | $payplug = Payplug::init(array(
100 | 'secretKey' => $keys['httpResponse']['secret_keys']['live'],
101 | 'apiVersion' => null,
102 | ));
103 | self::validateToken($payplug);
104 |
105 | $httpClient = new Core\HttpClient($payplug);
106 | $response = $httpClient->get(Core\APIRoutes::getRoute(Core\APIRoutes::ACCOUNT_RESOURCE));
107 |
108 | return $response['httpResponse']['permissions'];
109 | }
110 |
111 | /**
112 | * Retrieve publisable keys
113 | *
114 | * @param Payplug $payplug the client configuration
115 | *
116 | * @return array|false
117 | *createClientIdAndSecret
118 | * @throws Exception
119 | */
120 | public static function getPublishableKeys($payplug = null)
121 | {
122 | if ($payplug === null) {
123 | $payplug = Payplug::getDefaultConfiguration();
124 | }
125 | $httpClient = new Core\HttpClient($payplug);
126 | try {
127 | $response = $httpClient->post(Core\APIRoutes::getRoute(Core\APIRoutes::PUBLISHABLE_KEYS));
128 | return $response;
129 | } catch (Exception $e) {
130 | return false;
131 | }
132 | }
133 |
134 | /**
135 | * Generate a token JWT from a given client id and secret
136 | *
137 | * @param string $client_id
138 | * @param string $client_secret
139 | *
140 | * @return array
141 | */
142 | public static function generateJWT($client_id = '', $client_secret = '')
143 | {
144 | if ($client_id == '') {
145 | return array();
146 | }
147 | if ($client_secret == '') {
148 | return array();
149 | }
150 |
151 | $httpClient = new Core\HttpClient(null);
152 | try {
153 | $route = Core\APIRoutes::getRoute(Core\APIRoutes::OAUTH2_TOKEN_RESOURCE, null, array(), array(), false);
154 | $response = $httpClient->post(
155 | $route,
156 | array(
157 | 'grant_type' => 'client_credentials',
158 | 'audience' => 'https://www.payplug.com',
159 | ), false, null, array(
160 | 'Content-Type: application/x-www-form-urlencoded',
161 | 'Authorization: Basic ' . base64_encode($client_id . ':' . $client_secret)
162 | ),
163 | 'x-www-form-urlencoded');
164 |
165 | if (!isset($response['httpResponse']) || empty($response['httpResponse'])) {
166 | return array();
167 | }
168 |
169 | $response['httpResponse']['expires_date'] = time() + $response['httpResponse']['expires_in'];
170 |
171 | return $response;
172 | } catch (Exception $e) {
173 | return array();
174 | }
175 | }
176 |
177 | /**
178 | * Generate a token JWT OneShot.
179 | *
180 | * @param string $authorization_code
181 | * @param string $callback_uri
182 | * @param string $client_id
183 | * @param string $code_verifier
184 | *
185 | * @return array the token JWT OneShot
186 | *
187 | * @throws Exception
188 | */
189 | public static function generateJWTOneShot($authorization_code='', $callback_uri='', $client_id = '', $code_verifier = '')
190 | {
191 | if ($authorization_code == '') {
192 | return array();
193 | }
194 |
195 | if ($callback_uri == '') {
196 | return array();
197 | }
198 |
199 | if ($client_id == '') {
200 | return array();
201 | }
202 |
203 | if ($code_verifier == '') {
204 | return array();
205 | }
206 |
207 | $httpClient = new Core\HttpClient(null);
208 | try {
209 | $route = Core\APIRoutes::getRoute(Core\APIRoutes::OAUTH2_TOKEN_RESOURCE, null, array(), array(), false);
210 | $response = $httpClient->post(
211 | $route,
212 | array(
213 | 'grant_type' => 'authorization_code',
214 | 'code' => $authorization_code,
215 | 'redirect_uri' => $callback_uri,
216 | 'client_id' => $client_id,
217 | 'code_verifier' => $code_verifier
218 | ),
219 | false,
220 | null,
221 | array(
222 | 'Accept: application/json',
223 | 'Content-Type: application/x-www-form-urlencoded'
224 | ),
225 | 'application/x-www-form-urlencoded'
226 | );
227 | } catch (Exception $e) {
228 | $response = array();
229 | }
230 |
231 | return $response;
232 | }
233 |
234 | /**
235 | * Validates the Payplug token
236 | *
237 | * @param Payplug $payplug
238 | * @return void
239 | * @throws ConfigurationException
240 | */
241 | private static function validateToken($payplug)
242 | {
243 | $token = $payplug->getToken();
244 | if (empty($token)) {
245 | throw new ConfigurationException('The Payplug configuration requires a valid token.');
246 | }
247 | }
248 |
249 | /**
250 | * Create a client ID and secret for a given mode
251 | *
252 | * @param $company_id
253 | * @param $client_name
254 | * @param $mode
255 | * @param $session
256 | * @param Payplug|null $payplug
257 | *
258 | * @return array
259 | * @throws ConfigurationException
260 | * @throws Exception\ConfigurationNotSetException
261 | * @throws Exception\ConnectionException
262 | * @throws Exception\HttpException
263 | * @throws Exception\UnexpectedAPIResponseException
264 | */
265 | public static function createClientIdAndSecret($company_id = '', $client_name = '', $mode = '', $session = null, $payplug = null)
266 | {
267 | if ($payplug === null) {
268 | $payplug = Payplug::getDefaultConfiguration();
269 | }
270 |
271 | $httpClient = new Core\HttpClient($payplug);
272 | $response = array();
273 | $route = Core\APIRoutes::getServiceRoute(Core\APIRoutes::CLIENT_RESOURCE);
274 | try {
275 | $response = $httpClient->post(
276 | $route,
277 | array(
278 | 'company_id' => $company_id,
279 | 'client_name' => $client_name,
280 | 'client_type' => 'client_credentials_flow',
281 | 'mode' => $mode,
282 | ));
283 | } catch (Exception $e) {
284 | return $response;
285 | }
286 |
287 | return $response;
288 | }
289 |
290 | /**
291 | * Get the return url to register user through the portal
292 | *
293 | * @param string $setup_redirection_uri
294 | * @param string $oauth_callback_uri
295 | *
296 | * @return array
297 | * @throws Exception\ConnectionException
298 | * @throws Exception\HttpException
299 | * @throws Exception\UnexpectedAPIResponseException
300 | */
301 | public static function getRegisterUrl($setup_redirection_uri = '', $oauth_callback_uri = '')
302 | {
303 | if (empty($setup_redirection_uri)) {
304 | throw new Exception\ConfigurationException('Expected string values for setup redirection uri.');
305 | }
306 | if (empty($oauth_callback_uri)) {
307 | throw new Exception\ConfigurationException('Expected string values for oauth callback uri.');
308 | }
309 |
310 | $url_datas = array(
311 | 'setup_redirection_uri' => $setup_redirection_uri,
312 | 'oauth_callback_uri' => $oauth_callback_uri,
313 | );
314 |
315 | $route = Core\APIRoutes::getServiceRoute(Core\APIRoutes::PLUGIN_SETUP_SERVICE, $url_datas);
316 |
317 | return $route;
318 | }
319 |
320 | /**
321 | * Redirect to callback page and provide an authorization_code
322 | *
323 | * @param $client_id
324 | * @param $redirect_uri
325 | * @param $code_verifier
326 | * @param Payplug|null $payplug
327 | * @throws ConfigurationException
328 | * @throws Exception\ConfigurationNotSetException
329 | * @throws Exception\ConnectionException
330 | * @throws Exception\HttpException
331 | * @throws Exception\UnexpectedAPIResponseException
332 | */
333 | public static function initiateOAuth($client_id='', $redirect_uri='', $code_verifier='')
334 | {
335 | $hash = hash("sha256", $code_verifier);
336 | $code_challenge = base64_encode(pack("H*", $hash));
337 | $code_challenge = strtr($code_challenge, "+/", "-_");
338 | $code_challenge = rtrim($code_challenge, "=");
339 |
340 | $portal_url_datas = array(
341 | 'client_id' => $client_id,
342 | 'redirect_uri' => $redirect_uri,
343 | 'response_type' => 'code',
344 | 'state' => bin2hex(openssl_random_pseudo_bytes(10)),
345 | 'scope' => 'openid offline profile email',
346 | 'audience' => 'https://www.payplug.com',
347 | 'code_challenge' => $code_challenge,
348 | 'code_challenge_method' => 'S256',
349 | );
350 |
351 | $portal_url = Core\APIRoutes::getRoute(Core\APIRoutes::OAUTH2_AUTH_RESOURCE, null, array(), $portal_url_datas, false);
352 |
353 | header("Location: $portal_url");
354 | }
355 |
356 | /**
357 | * Check if given token is expired and if so, regenerate a new one
358 | *
359 | * @param array $client_data
360 | * @param array $token
361 | *
362 | * @return array
363 | */
364 | public static function validateJWT($client_data = array(), $token = array())
365 | {
366 | if (!is_array($client_data) || empty($client_data)) {
367 | return array(
368 | 'result' => false,
369 | 'token' => null,
370 | 'need_update' => false,
371 | );
372 | }
373 | if (!is_array($token) || empty($token)) {
374 | return array(
375 | 'result' => false,
376 | 'token' => null,
377 | 'need_update' => false,
378 | );
379 | }
380 |
381 | $current_date = time();
382 | if ($token['expires_date'] > $current_date) {
383 | return array(
384 | 'result' => true,
385 | 'token' => $token,
386 | 'need_update' => false,
387 | );
388 | }
389 |
390 | $token = self::generateJWT($client_data['client_id'], $client_data['client_secret']);
391 | if (empty($token) || !isset($token['httpResponse'])) {
392 | return array(
393 | 'result' => false,
394 | 'token' => null,
395 | 'need_update' => false,
396 | );
397 | }
398 |
399 | return array(
400 | 'result' => true,
401 | 'token' => $token['httpResponse'],
402 | 'need_update' => true,
403 | );
404 | }
405 | }
406 |
--------------------------------------------------------------------------------
/tests/unit_tests/Resource/RefundTest.php:
--------------------------------------------------------------------------------
1 | _configuration = new Payplug\Payplug('abc');
22 | Payplug\Payplug::setDefaultConfiguration($this->_configuration);
23 |
24 | $this->_requestMock = $this->createMock('\Payplug\Core\IHttpRequest');
25 | Payplug\Core\HttpClient::$REQUEST_HANDLER = $this->_requestMock;
26 | }
27 |
28 | protected function setUpTwice()
29 | {
30 | $this->_configuration = new Payplug\Payplug('abc','1970-01-01');
31 | Payplug\Payplug::setDefaultConfiguration($this->_configuration);
32 |
33 | $this->_requestMock = $this->createMock('\Payplug\Core\IHttpRequest');
34 | Payplug\Core\HttpClient::$REQUEST_HANDLER = $this->_requestMock;
35 | }
36 |
37 | public function testCreateRefundFromAttributes()
38 | {
39 | $refund = Refund::fromAttributes(array(
40 | 'id' => 're_390312',
41 | 'payment_id' => 'pay_490329',
42 | 'object' => 'refund',
43 | 'amount' => 3300,
44 | 'currency' => 'EUR',
45 | 'created_at' => 1410437760
46 | ));
47 |
48 | $this->assertEquals('re_390312', $refund->id);
49 | $this->assertEquals('pay_490329', $refund->payment_id);
50 | $this->assertEquals('refund', $refund->object);
51 | $this->assertEquals(3300, $refund->amount);
52 | $this->assertEquals('EUR', $refund->currency);
53 | $this->assertEquals(1410437760, $refund->created_at);
54 | }
55 |
56 | public function testRefundCreateFromPaymentId()
57 | {
58 | $GLOBALS['CURLOPT_URL_DATA'] = null;
59 |
60 | $this->_requestMock
61 | ->expects($this->once())
62 | ->method('exec')
63 | ->will($this->returnValue('{"status":"ok"}'));
64 |
65 | $this->_requestMock
66 | ->expects($this->any())
67 | ->method('getinfo')
68 | ->will($this->returnCallback(function($option) {
69 | switch($option) {
70 | case CURLINFO_HTTP_CODE:
71 | return 200;
72 | }
73 | return null;
74 | }));
75 | $this->_requestMock
76 | ->expects($this->any())
77 | ->method('setopt')
78 | ->will($this->returnCallback(function($option, $value = null) {
79 | switch($option) {
80 | case CURLOPT_URL:
81 | $GLOBALS['CURLOPT_URL_DATA'] = $value;
82 | return true;
83 | }
84 | return true;
85 | }));
86 |
87 | $refund = Refund::create('a_payment_id', array('amount' => 3300));
88 |
89 | $this->assertEquals('ok', $refund->status);
90 | $this->assertContains('a_payment_id', $GLOBALS['CURLOPT_URL_DATA']);
91 |
92 | unset($GLOBALS['CURLOPT_URL_DATA']);
93 | }
94 |
95 | public function testRefundCreateFromPaymentObject()
96 | {
97 | $GLOBALS['CURLOPT_URL_DATA'] = null;
98 |
99 | $this->_requestMock
100 | ->expects($this->once())
101 | ->method('exec')
102 | ->will($this->returnValue('{"status":"ok"}'));
103 |
104 | $this->_requestMock
105 | ->expects($this->any())
106 | ->method('getinfo')
107 | ->will($this->returnCallback(function($option) {
108 | switch($option) {
109 | case CURLINFO_HTTP_CODE:
110 | return 200;
111 | }
112 | return null;
113 | }));
114 | $this->_requestMock
115 | ->expects($this->any())
116 | ->method('setopt')
117 | ->will($this->returnCallback(function($option, $value = null) {
118 | switch($option) {
119 | case CURLOPT_URL:
120 | $GLOBALS['CURLOPT_URL_DATA'] = $value;
121 | return true;
122 | }
123 | return true;
124 | }));
125 |
126 | $refund = Refund::create(
127 | Payment::fromAttributes(array('id' => 'a_payment_id')),
128 | array('amount' => 3300)
129 | );
130 |
131 | $this->assertEquals('ok', $refund->status);
132 | $this->assertContains('a_payment_id', $GLOBALS['CURLOPT_URL_DATA']);
133 |
134 | unset($GLOBALS['CURLOPT_URL_DATA']);
135 | }
136 |
137 | public function testRefundRetrieveFromPaymentId()
138 | {
139 | function testRefundRetrieveFromPaymentId_getinfo($option) {
140 | switch($option) {
141 | case CURLINFO_HTTP_CODE:
142 | return 200;
143 | }
144 | return null;
145 | }
146 | $GLOBALS['CURLOPT_URL_DATA'] = null;
147 | function testRefundRetrieveFromPaymentId_setopt($option, $value = null) {
148 | switch($option) {
149 | case CURLOPT_URL:
150 | $GLOBALS['CURLOPT_URL_DATA'] = $value;
151 | return true;
152 | }
153 | return true;
154 | }
155 |
156 | $this->_requestMock
157 | ->expects($this->once())
158 | ->method('exec')
159 | ->will($this->returnValue('{"status":"ok"}'));
160 |
161 | $this->_requestMock
162 | ->expects($this->any())
163 | ->method('getinfo')
164 | ->will($this->returnCallback(function($option) {
165 | switch($option) {
166 | case CURLINFO_HTTP_CODE:
167 | return 200;
168 | }
169 | return null;
170 | }));
171 | $this->_requestMock
172 | ->expects($this->any())
173 | ->method('setopt')
174 | ->will($this->returnCallback(function($option, $value = null) {
175 | switch($option) {
176 | case CURLOPT_URL:
177 | $GLOBALS['CURLOPT_URL_DATA'] = $value;
178 | return true;
179 | }
180 | return true;
181 | }));
182 |
183 | $refund = Refund::retrieve('a_payment_id', 'a_refund_id');
184 |
185 | $this->assertEquals('ok', $refund->status);
186 | $this->assertContains('a_payment_id', $GLOBALS['CURLOPT_URL_DATA']);
187 | $this->assertStringEndsWith('a_refund_id', $GLOBALS['CURLOPT_URL_DATA']);
188 |
189 | unset($GLOBALS['CURLOPT_URL_DATA']);
190 | }
191 |
192 | public function testRefundRetrieveFromPaymentObject()
193 | {
194 | $GLOBALS['CURLOPT_URL_DATA'] = null;
195 |
196 |
197 | $this->_requestMock
198 | ->expects($this->once())
199 | ->method('exec')
200 | ->will($this->returnValue('{"status":"ok"}'));
201 |
202 | $this->_requestMock
203 | ->expects($this->any())
204 | ->method('getinfo')
205 | ->will($this->returnCallback(function($option) {
206 | switch($option) {
207 | case CURLINFO_HTTP_CODE:
208 | return 200;
209 | }
210 | return null;
211 | }));
212 | $this->_requestMock
213 | ->expects($this->any())
214 | ->method('setopt')
215 | ->will($this->returnCallback(function($option, $value = null) {
216 | switch($option) {
217 | case CURLOPT_URL:
218 | $GLOBALS['CURLOPT_URL_DATA'] = $value;
219 | return true;
220 | }
221 | return true;
222 | }));
223 |
224 | $refund = Refund::retrieve(
225 | Payment::fromAttributes(array('id' => 'a_payment_id')),
226 | 'a_refund_id'
227 | );
228 |
229 | $this->assertEquals('ok', $refund->status);
230 | $this->assertContains('a_payment_id', $GLOBALS['CURLOPT_URL_DATA']);
231 | $this->assertStringEndsWith('a_refund_id', $GLOBALS['CURLOPT_URL_DATA']);
232 |
233 | unset($GLOBALS['CURLOPT_URL_DATA']);
234 | }
235 |
236 | public function testRefundsListThrowsExceptionOnWongAPIResponse()
237 | {
238 | $this->expectException('\PayPlug\Exception\UnexpectedAPIResponseException');
239 |
240 | $this->_requestMock
241 | ->expects($this->once())
242 | ->method('exec')
243 | ->will($this->returnValue('{"status":"this_is_an_invalid_response"}'));
244 |
245 | $this->_requestMock
246 | ->expects($this->any())
247 | ->method('getinfo')
248 | ->will($this->returnCallback(function($option) {
249 | switch($option) {
250 | case CURLINFO_HTTP_CODE:
251 | return 200;
252 | }
253 | return null;
254 | }));
255 |
256 | Refund::listRefunds('a_payment_id');
257 | }
258 |
259 | public function testRefundsListFromPaymentId()
260 | {
261 | $GLOBALS['CURLOPT_URL_DATA'] = null;
262 |
263 | $this->_requestMock
264 | ->expects($this->once())
265 | ->method('exec')
266 | ->will($this->returnValue('{"data":[{"id": "refund1"}, {"id": "refund2"}]}'));
267 |
268 | $this->_requestMock
269 | ->expects($this->any())
270 | ->method('getinfo')
271 | ->will($this->returnCallback(function($option) {
272 | switch($option) {
273 | case CURLINFO_HTTP_CODE:
274 | return 200;
275 | }
276 | return null;
277 | }));
278 | $this->_requestMock
279 | ->expects($this->any())
280 | ->method('setopt')
281 | ->will($this->returnCallback(function($option, $value = null) {
282 | switch($option) {
283 | case CURLOPT_URL:
284 | $GLOBALS['CURLOPT_URL_DATA'] = $value;
285 | return true;
286 | }
287 | return true;
288 | }));
289 |
290 | $refunds = Refund::listRefunds('a_payment_id');
291 |
292 | $this->assertContains('a_payment_id', $GLOBALS['CURLOPT_URL_DATA']);
293 | $this->assertEquals(2, count($refunds));
294 | $this->assertTrue('refund1' === $refunds[0]->id || 'refund2' === $refunds[1]->id);
295 | $this->assertTrue(
296 | (('refund1' === $refunds[1]->id) || ('refund2' === $refunds[1]->id))
297 | && ($refunds[0]->id !== $refunds[1]->id)
298 | );
299 |
300 | unset($GLOBALS['CURLOPT_URL_DATA']);
301 | }
302 |
303 | public function testRefundsListFromPaymentObject()
304 | {
305 | $GLOBALS['CURLOPT_URL_DATA'] = null;
306 |
307 | $this->_requestMock
308 | ->expects($this->once())
309 | ->method('exec')
310 | ->will($this->returnValue('{"data":[{"id": "refund1"}, {"id": "refund2"}]}'));
311 |
312 | $this->_requestMock
313 | ->expects($this->any())
314 | ->method('getinfo')
315 | ->will($this->returnCallback(function($option) {
316 | switch($option) {
317 | case CURLINFO_HTTP_CODE:
318 | return 200;
319 | }
320 | return null;
321 | }));
322 | $this->_requestMock
323 | ->expects($this->any())
324 | ->method('setopt')
325 | ->will($this->returnCallback(function($option, $value = null) {
326 | switch($option) {
327 | case CURLOPT_URL:
328 | $GLOBALS['CURLOPT_URL_DATA'] = $value;
329 | return true;
330 | }
331 | return true;
332 | }));
333 |
334 | $refunds = Refund::listRefunds(
335 | Payment::fromAttributes(array('id' => 'a_payment_id'))
336 | );
337 |
338 | $this->assertContains('a_payment_id', $GLOBALS['CURLOPT_URL_DATA']);
339 | $this->assertEquals(2, count($refunds));
340 | $this->assertTrue('refund1' === $refunds[0]->id || 'refund2' === $refunds[1]->id);
341 | $this->assertTrue(
342 | (('refund1' === $refunds[1]->id) || ('refund2' === $refunds[1]->id))
343 | && ($refunds[0]->id !== $refunds[1]->id)
344 | );
345 |
346 | unset($GLOBALS['CURLOPT_URL_DATA']);
347 | }
348 |
349 | public function testRetrieveConsistentRefundWhenIdIsUndefined()
350 | {
351 | $this->expectException('\PayPlug\Exception\UndefinedAttributeException');
352 |
353 | $payment = Refund::fromAttributes(array('this_refund' => 'has_no_id', 'payment_id' => 'pay_id'));
354 | $payment->getConsistentResource();
355 | }
356 |
357 | public function testRetrieveConsistentRefundWhenPaymentIdIsUndefined()
358 | {
359 | $this->expectException('\PayPlug\Exception\UndefinedAttributeException');
360 |
361 | $payment = Refund::fromAttributes(array('id' => 'an_id', 'no_payment_id' => ''));
362 | $payment->getConsistentResource();
363 | }
364 |
365 | public function testRetrieveConsistentRefund()
366 | {
367 | function testRetrieveConsistentRefund_getinfo($option) {
368 | switch($option) {
369 | case CURLINFO_HTTP_CODE:
370 | return 200;
371 | }
372 | return null;
373 | }
374 |
375 | $this->_requestMock
376 | ->expects($this->once())
377 | ->method('exec')
378 | ->will($this->returnValue('{"id": "re_345", "payment_id": "pay_789"}'));
379 |
380 | $this->_requestMock
381 | ->expects($this->any())
382 | ->method('setopt')
383 | ->will($this->returnValue(true));
384 | $this->_requestMock
385 | ->expects($this->any())
386 | ->method('getinfo')
387 | ->will($this->returnCallback(function($option) {
388 | switch($option) {
389 | case CURLINFO_HTTP_CODE:
390 | return 200;
391 | }
392 | return null;
393 | }));
394 |
395 | $refund1 = Refund::fromAttributes(array('id' => 're_123', 'payment_id' => 'pay_321'));
396 | $refund2 = $refund1->getConsistentResource($this->_configuration);
397 |
398 | $this->assertEquals('re_123', $refund1->id);
399 | $this->assertEquals('pay_321', $refund1->payment_id);
400 | $this->assertEquals('re_345', $refund2->id);
401 | $this->assertEquals('pay_789', $refund2->payment_id);
402 | }
403 | }
404 |
--------------------------------------------------------------------------------
/tests/unit_tests/Core/HttpClientTest.php:
--------------------------------------------------------------------------------
1 | _httpClient = new HttpClient(new Payplug('abc'));
21 |
22 | $this->_requestMock = $this->createMock('\Payplug\Core\IHttpRequest');
23 | HttpClient::$REQUEST_HANDLER = $this->_requestMock;
24 | }
25 |
26 | protected function setUpTwice()
27 | {
28 | $this->_httpClient = new HttpClient(new Payplug('abc','1970-01-01'));
29 |
30 | $this->_requestMock = $this->createMock('\Payplug\Core\IHttpRequest');
31 | HttpClient::$REQUEST_HANDLER = $this->_requestMock;
32 | }
33 |
34 | public function testPost()
35 | {
36 |
37 | $this->_requestMock
38 | ->expects($this->once())
39 | ->method('exec')
40 | ->will($this->returnValue('{"status":"ok"}'));
41 | $this->_requestMock
42 | ->expects($this->any())
43 | ->method('getinfo')
44 | ->will($this->returnCallback(function($option) {
45 | switch($option) {
46 | case CURLINFO_HTTP_CODE:
47 | return 200;
48 | }
49 | return null;
50 | }));
51 |
52 | $result = $this->_httpClient->post('somewhere');
53 |
54 | $this->assertEquals(array('status' => 'ok'), $result['httpResponse']);
55 | $this->assertEquals(200, $result['httpStatus']);
56 | }
57 |
58 | public function testPatch()
59 | {
60 | $this->_requestMock
61 | ->expects($this->once())
62 | ->method('exec')
63 | ->will($this->returnValue('{"status":"ok"}'));
64 | $this->_requestMock
65 | ->expects($this->any())
66 | ->method('getinfo')
67 | ->will($this->returnCallback(function($option) {
68 | switch($option) {
69 | case CURLINFO_HTTP_CODE:
70 | return 200;
71 | }
72 | return null;
73 | }));
74 |
75 | $result = $this->_httpClient->patch('somewhere');
76 |
77 | $this->assertEquals(array('status' => 'ok'), $result['httpResponse']);
78 | $this->assertEquals(200, $result['httpStatus']);
79 | }
80 |
81 | public function testDelete()
82 | {
83 | $this->_requestMock
84 | ->expects($this->once())
85 | ->method('exec')
86 | ->will($this->returnValue('{"status":"ok"}'));
87 | $this->_requestMock
88 | ->expects($this->any())
89 | ->method('getinfo')
90 | ->will($this->returnCallback(function($option) {
91 | switch($option) {
92 | case CURLINFO_HTTP_CODE:
93 | return 204;
94 | }
95 | return null;
96 | }));
97 |
98 | $result = $this->_httpClient->delete('somewhere');
99 |
100 | $this->assertEquals(array('status' => 'ok'), $result['httpResponse']);
101 | $this->assertEquals(204, $result['httpStatus']);
102 | }
103 |
104 | public function testGet()
105 | {
106 | function testGet_getinfo($option) {
107 | switch($option) {
108 | case CURLINFO_HTTP_CODE:
109 | return 200;
110 | }
111 | return null;
112 | }
113 |
114 | $this->_requestMock
115 | ->expects($this->once())
116 | ->method('exec')
117 | ->will($this->returnValue('{"status":"ok"}'));
118 | $this->_requestMock
119 | ->expects($this->any())
120 | ->method('getinfo')
121 | ->will($this->returnCallback(function($option) {
122 | switch($option) {
123 | case CURLINFO_HTTP_CODE:
124 | return 200;
125 | }
126 | return null;
127 | }));
128 |
129 | $result = $this->_httpClient->get('somewhere_else');
130 |
131 | $this->assertEquals(array('status' => 'ok'), $result['httpResponse']);
132 | $this->assertEquals(200, $result['httpStatus']);
133 | }
134 |
135 | public function testError500()
136 | {
137 |
138 | $this->expectException('\PayPlug\Exception\PayPlugServerException');
139 |
140 | $this->_requestMock
141 | ->expects($this->once())
142 | ->method('exec')
143 | ->will($this->returnValue('{"status":"not ok"}'));
144 | $this->_requestMock
145 | ->expects($this->any())
146 | ->method('getinfo')
147 | ->will($this->returnCallback(function($option) {
148 | switch($option) {
149 | case CURLINFO_HTTP_CODE:
150 | return 500;
151 | }
152 | return null;
153 | }));
154 |
155 | $this->_httpClient->get('somewhere');
156 | }
157 |
158 | public function testError400()
159 | {
160 |
161 | $this->expectException('\PayPlug\Exception\BadRequestException');
162 |
163 | $this->_requestMock
164 | ->expects($this->once())
165 | ->method('exec')
166 | ->will($this->returnValue('{"status":"not ok"}'));
167 | $this->_requestMock
168 | ->expects($this->any())
169 | ->method('getinfo')
170 | ->will($this->returnCallback(function($option) {
171 | switch($option) {
172 | case CURLINFO_HTTP_CODE:
173 | return 400;
174 | }
175 | return null;
176 | }));
177 |
178 | $this->_httpClient->get('somewhere');
179 | }
180 |
181 | public function testError401()
182 | {
183 |
184 | $this->expectException('\PayPlug\Exception\UnauthorizedException');
185 |
186 | $this->_requestMock
187 | ->expects($this->once())
188 | ->method('exec')
189 | ->will($this->returnValue('{"status":"not ok"}'));
190 | $this->_requestMock
191 | ->expects($this->any())
192 | ->method('getinfo')
193 | ->will($this->returnCallback(function($option) {
194 | switch($option) {
195 | case CURLINFO_HTTP_CODE:
196 | return 401;
197 | }
198 | return null;
199 | }));
200 |
201 | $this->_httpClient->get('somewhere');
202 | }
203 |
204 | public function testError403()
205 | {
206 |
207 | $this->expectException('\PayPlug\Exception\ForbiddenException');
208 |
209 | $this->_requestMock
210 | ->expects($this->once())
211 | ->method('exec')
212 | ->will($this->returnValue('{"status":"not ok"}'));
213 | $this->_requestMock
214 | ->expects($this->any())
215 | ->method('getinfo')
216 | ->will($this->returnCallback(function($option) {
217 | switch($option) {
218 | case CURLINFO_HTTP_CODE:
219 | return 403;
220 | }
221 | return null;
222 | }));
223 |
224 | $this->_httpClient->get('somewhere');
225 | }
226 |
227 | public function testError404()
228 | {
229 |
230 | $this->expectException('\PayPlug\Exception\NotFoundException');
231 |
232 | $this->_requestMock
233 | ->expects($this->once())
234 | ->method('exec')
235 | ->will($this->returnValue('{"status":"not ok"}'));
236 | $this->_requestMock
237 | ->expects($this->any())
238 | ->method('getinfo')
239 | ->will($this->returnCallback(function($option) {
240 | switch($option) {
241 | case CURLINFO_HTTP_CODE:
242 | return 404;
243 | }
244 | return null;
245 | }));
246 |
247 | $this->_httpClient->get('somewhere');
248 | }
249 |
250 | public function testError405()
251 | {
252 |
253 | $this->expectException('\PayPlug\Exception\NotAllowedException');
254 |
255 | $this->_requestMock
256 | ->expects($this->once())
257 | ->method('exec')
258 | ->will($this->returnValue('{"status":"not ok"}'));
259 | $this->_requestMock
260 | ->expects($this->any())
261 | ->method('getinfo')
262 | ->will($this->returnCallback(function($option) {
263 | switch($option) {
264 | case CURLINFO_HTTP_CODE:
265 | return 405;
266 | }
267 | return null;
268 | }));
269 |
270 | $this->_httpClient->get('somewhere');
271 | }
272 |
273 | public function testErrorUnknown()
274 | {
275 |
276 | $this->expectException('\PayPlug\Exception\HttpException');
277 |
278 | $this->_requestMock
279 | ->expects($this->once())
280 | ->method('exec')
281 | ->will($this->returnValue('{"status":"not ok"}'));
282 | $this->_requestMock
283 | ->expects($this->any())
284 | ->method('getinfo')
285 | ->will($this->returnCallback(function($option) {
286 | switch($option) {
287 | case CURLINFO_HTTP_CODE:
288 | return 418;
289 | }
290 | return null;
291 | }));
292 |
293 | $this->_httpClient->get('somewhere');
294 | }
295 |
296 | public function testNotEmptyData()
297 | {
298 | $GLOBALS['CURLOPT_POSTFIELDS_DATA'] = null;
299 |
300 | $this->_requestMock
301 | ->expects($this->once())
302 | ->method('exec')
303 | ->will($this->returnValue('{"status":"ok"}'));
304 | $this->_requestMock
305 | ->expects($this->any())
306 | ->method('getinfo')
307 | ->will($this->returnCallback(function($option) {
308 | switch($option) {
309 | case CURLINFO_HTTP_CODE:
310 | return 200;
311 | }
312 | return null;
313 | }));
314 | $this->_requestMock
315 | ->expects($this->any())
316 | ->method('setopt')
317 | ->will($this->returnCallback(function($option, $value = null) {
318 | switch($option) {
319 | case CURLOPT_POSTFIELDS:
320 | $GLOBALS['CURLOPT_POSTFIELDS_DATA'] = json_decode($value, true);
321 | return true;
322 | }
323 | return true;
324 | }));
325 |
326 | $result = $this->_httpClient->get('somewhere_else', array('foo' => 'bar'));
327 |
328 | $this->assertEquals(array('foo' => 'bar'), $GLOBALS['CURLOPT_POSTFIELDS_DATA']);
329 | $this->assertEquals(array('status' => 'ok'), $result['httpResponse']);
330 | $this->assertEquals(200, $result['httpStatus']);
331 |
332 | unset($GLOBALS['CURLOPT_POSTFIELDS_DATA']);
333 | }
334 |
335 | function testInvalidAPIResponse()
336 | {
337 | $this->expectException('\PayPlug\Exception\UnexpectedAPIResponseException');
338 |
339 |
340 |
341 | $this->_requestMock
342 | ->expects($this->once())
343 | ->method('exec')
344 | ->will($this->returnValue('This is not JSON'));
345 | $this->_requestMock
346 | ->expects($this->any())
347 | ->method('getinfo')
348 | ->will($this->returnCallback(function($option) {
349 | switch($option) {
350 | case CURLINFO_HTTP_CODE:
351 | return 200;
352 | }
353 | return null;
354 | }));
355 |
356 | $this->_httpClient->get('somewhere_else');
357 | }
358 |
359 | function testConnectionError()
360 | {
361 | $this->expectException('\PayPlug\Exception\ConnectionException');
362 |
363 |
364 |
365 | function testConnectionError_errno($option) {
366 | switch($option) {
367 | case CURLINFO_HTTP_CODE:
368 | return 0;
369 | }
370 | return null;
371 | }
372 |
373 | $this->_requestMock
374 | ->expects($this->once())
375 | ->method('exec')
376 | ->will($this->returnValue(false));
377 | $this->_requestMock
378 | ->expects($this->any())
379 | ->method('errno')
380 | ->will($this->returnValue(7)); // CURLE_COULDNT_CONNECT
381 | $this->_requestMock
382 | ->expects($this->any())
383 | ->method('error')
384 | ->will($this->returnValue('Failed to connect() to host or proxy.'));
385 | $this->_requestMock
386 | ->expects($this->any())
387 | ->method('getinfo')
388 | ->will($this->returnCallback(function($option) {
389 | switch($option) {
390 | case CURLINFO_HTTP_CODE:
391 | return 0;
392 | }
393 | return null;
394 | }));
395 |
396 | $this->_httpClient->get('somewhere');
397 | }
398 |
399 | function testFormatUserAgentProduct()
400 | {
401 | $result = \Payplug\Test\TestUtils::invokePrivateMethod(
402 | $this->_httpClient, 'formatUserAgentProduct',
403 | array('PayPlug-PHP', '2.2.1' , 'PHP/5.5.34; curl/7.43.0')
404 | );
405 |
406 | $this->assertEquals($result, 'PayPlug-PHP/2.2.1 (PHP/5.5.34; curl/7.43.0)');
407 | }
408 |
409 | function testGetUserAgent()
410 | {
411 | $userAgent = $this->_httpClient->getUserAgent();
412 | // Expected result is something like 'PayPlug-PHP/1.0.0 (PHP/5.5.35; curl/7.44.0)'
413 | $this->assertRegExp(
414 | '/^PayPlug-PHP\/(\d+\.?){1,3} \(PHP\/(\d+\.?){1,3}(\w|\+|\~|\.|\-)*; curl\/(\d+\.?){1,3}\)$/',
415 | $userAgent
416 | );
417 | }
418 |
419 | function testGetUserAgentWithAdditionalProduct()
420 | {
421 | \Payplug\Core\HttpClient::addDefaultUserAgentProduct('PayPlug-Test', '1.0.0', 'Comment/1.6.3');
422 | \Payplug\Core\HttpClient::addDefaultUserAgentProduct('Another-Test', '5.8.13');
423 |
424 | $userAgent = $this->_httpClient->getUserAgent();
425 |
426 | $this->assertNotContains('PayPlug-Test', $userAgent);
427 | $this->assertStringEndsWith('Another-Test/5.8.13', $userAgent);
428 | }
429 |
430 | function testGetDefaultUserAgentWithAdditionalProduct()
431 | {
432 | \Payplug\Core\HttpClient::setDefaultUserAgentProduct('PayPlug-Test', '1.0.0', 'Comment/1.6.3');
433 | \Payplug\Core\HttpClient::setDefaultUserAgentProduct('Another-Test', '5.8.13');
434 |
435 | $userAgent = $this->_httpClient->getUserAgent();
436 |
437 | $this->assertNotContains('PayPlug-Test', $userAgent);
438 | $this->assertStringEndsWith('Another-Test/5.8.13', $userAgent);
439 | }
440 | }
441 |
--------------------------------------------------------------------------------