diff --git a/README.md b/README.md index d446667..c287175 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ | /api/v1/transaction/refund | refund | | /api/v1/transaction/by/sessionId | purchaseInfo | | /api/v1/card/info | cardInfo | +| /api/v1/card/pay | cardPay | +| /api/v1/card/charge | cardCharge | ## Install diff --git a/src/Gateway.php b/src/Gateway.php index 6d7b103..3d8c5f8 100644 --- a/src/Gateway.php +++ b/src/Gateway.php @@ -6,7 +6,9 @@ use Omnipay\Common\AbstractGateway; use Omnipay\Common\Message\AbstractRequest; +use Omnipay\Przelewy24\Message\CardChargeRequest; use Omnipay\Przelewy24\Message\CardInfoRequest; +use Omnipay\Przelewy24\Message\CardPayRequest; use Omnipay\Przelewy24\Message\CompletePurchaseRequest; use Omnipay\Przelewy24\Message\MethodsRequest; use Omnipay\Przelewy24\Message\PurchaseInfoRequest; @@ -172,4 +174,22 @@ public function refund(array $options = []): RefundsRequest { return $this->createRequest(RefundsRequest::class, $options); } + + /** + * @param string[] $options + * @return AbstractRequest|CardPayRequest + */ + public function cardPay(array $options): CardPayRequest + { + return $this->createRequest(CardPayRequest::class, $options); + } + + /** + * @param string[] $options + * @return AbstractRequest|CardChargeRequest + */ + public function cardCharge(array $options): CardChargeRequest + { + return $this->createRequest(CardChargeRequest::class, $options); + } } diff --git a/src/Message/CardChargeRequest.php b/src/Message/CardChargeRequest.php new file mode 100644 index 0000000..3d422a9 --- /dev/null +++ b/src/Message/CardChargeRequest.php @@ -0,0 +1,26 @@ +validate('token'); + + return [ + 'token' => $this->getToken(), + ]; + } + + public function sendData($data): CardChargeResponse + { + $httpResponse = $this->sendRequest('POST', 'card/charge', $data); + + $responseData = json_decode($httpResponse->getBody()->getContents(), true); + + return $this->response = new CardChargeResponse($this, $responseData); + } +} diff --git a/src/Message/CardChargeResponse.php b/src/Message/CardChargeResponse.php new file mode 100644 index 0000000..8b49eb2 --- /dev/null +++ b/src/Message/CardChargeResponse.php @@ -0,0 +1,30 @@ +transactionId = (string) $data['data']['orderId']; + } + } + } + + public function getTransactionId(): ?string + { + return $this->transactionId; + } +} diff --git a/src/Message/CardPayRequest.php b/src/Message/CardPayRequest.php new file mode 100644 index 0000000..63288d3 --- /dev/null +++ b/src/Message/CardPayRequest.php @@ -0,0 +1,70 @@ +getParameter('number'); + } + + public function setNumber(string $value): self + { + return $this->setParameter('number', $value); + } + + public function getExpiry(): string + { + return $this->getParameter('expiry'); + } + + public function setExpiry(string $value): self + { + return $this->setParameter('expiry', $value); + } + + public function getCvv(): string + { + return $this->getParameter('cvv'); + } + + public function setCvv(string $value): self + { + return $this->setParameter('cvv', $value); + } + + public function getName(): string + { + return $this->getParameter('name'); + } + + public function setName(string $value): self + { + return $this->setParameter('name', $value); + } + + public function getData(): array + { + $this->validate('transactionId', 'number', 'expiry', 'cvv', 'name'); + + return [ + 'transactionToken' => $this->getTransactionId(), + 'cardNumber' => $this->getNumber(), + 'cardDate' => $this->getExpiry(), + 'cvv' => $this->getCvv(), + 'clientName' => $this->getName(), + ]; + } + + public function sendData($data): CardPayResponse + { + $httpResponse = $this->sendRequest('POST', 'card/pay', $data); + + $responseData = json_decode($httpResponse->getBody()->getContents(), true); + + return $this->response = new CardPayResponse($this, $responseData); + } +} diff --git a/src/Message/CardPayResponse.php b/src/Message/CardPayResponse.php new file mode 100644 index 0000000..98d8f42 --- /dev/null +++ b/src/Message/CardPayResponse.php @@ -0,0 +1,67 @@ +transactionId = (string) $data['data']['orderId']; + } + if (isset($data['data']['redirectUrl'])) { + $this->redirectUrl = $data['data']['redirectUrl']; + } + } + } + + public function getCode(): int + { + if ( + isset($this->data['code']) + && 0 === $this->data['code'] + && isset($this->data['error']) + && strlen($this->data['error']) > 0 + ) { + return Response::HTTP_CONFLICT; + } + + if (isset($this->data['code'])) { + return $this->data['code']; + } + + return Response::HTTP_OK; + } + + public function getTransactionId(): ?string + { + return $this->transactionId; + } + + public function getRedirectUrl(): ?string + { + return $this->redirectUrl; + } + + public function isRedirect(): bool + { + return $this->isSuccessful() && null !== $this->getRedirectUrl(); + } +} diff --git a/tests/GatewayTest.php b/tests/GatewayTest.php index 3efe35e..5d2ee59 100644 --- a/tests/GatewayTest.php +++ b/tests/GatewayTest.php @@ -3,7 +3,9 @@ declare(strict_types=1); use Omnipay\Przelewy24\Gateway; +use Omnipay\Przelewy24\Message\CardChargeRequest; use Omnipay\Przelewy24\Message\CardInfoRequest; +use Omnipay\Przelewy24\Message\CardPayRequest; use Omnipay\Przelewy24\Message\CompletePurchaseRequest; use Omnipay\Przelewy24\Message\MethodsRequest; use Omnipay\Przelewy24\Message\PurchaseInfoRequest; @@ -225,6 +227,24 @@ public function it_should_create_card_info() $this->assertInstanceOf(CardInfoRequest::class, $request); } + /** + * @test + */ + public function it_should_create_card_pay() + { + $request = $this->gateway->cardPay([]); + $this->assertInstanceOf(CardPayRequest::class, $request); + } + + /** + * @test + */ + public function it_should_create_card_charge() + { + $request = $this->gateway->cardCharge([]); + $this->assertInstanceOf(CardChargeRequest::class, $request); + } + public function refund_data_provider(): array { return [ diff --git a/tests/Message/CardChargeRequestTest.php b/tests/Message/CardChargeRequestTest.php new file mode 100644 index 0000000..5e2c791 --- /dev/null +++ b/tests/Message/CardChargeRequestTest.php @@ -0,0 +1,66 @@ +request = new CardChargeRequest($this->getHttpClient(), $this->getHttpRequest()); + $this->request->initialize([ + "token" => "29fa01f8-6bb8-4187-9fb0-ec6e1a62a731", + ]); + } + + public function testGetData(): void + { + $data = $this->request->getData(); + + $this->assertSame('29fa01f8-6bb8-4187-9fb0-ec6e1a62a731', $data['token']); + } + + public function testSendSuccess() + { + $this->setMockHttpResponse('CardChargeSuccess.txt'); + /** @var CardChargeResponse $response */ + $response = $this->request->send(); + + $this->assertInstanceOf(CardChargeResponse::class, $response); + $this->assertTrue($response->isSuccessful()); + $this->assertSame('1234567890', $response->getTransactionId()); + } + + public function testSendAuthFailure() + { + $this->setMockHttpResponse('CardChargeAuthFailure.txt'); + $response = $this->request->send(); + + $this->assertInstanceOf(CardChargeResponse::class, $response); + $this->assertFalse($response->isSuccessful()); + $this->assertSame(Response::HTTP_UNAUTHORIZED, $response->getCode()); + $this->assertSame('Incorrect authentication', $response->getMessage()); + } + + public function testSendInvalidDataFailure() + { + $this->setMockHttpResponse('CardChargeInvalidDataFailure.txt'); + $response = $this->request->send(); + + $this->assertInstanceOf(CardChargeResponse::class, $response); + $this->assertFalse($response->isSuccessful()); + $this->assertSame(Response::HTTP_BAD_REQUEST, $response->getCode()); + $this->assertSame('Invalid input data', $response->getMessage()); + } +} diff --git a/tests/Message/CardPayRequestTest.php b/tests/Message/CardPayRequestTest.php new file mode 100644 index 0000000..c6f5a16 --- /dev/null +++ b/tests/Message/CardPayRequestTest.php @@ -0,0 +1,87 @@ +request = new CardPayRequest($this->getHttpClient(), $this->getHttpRequest()); + $this->request->initialize([ + "transactionId" => "29fa01f8-6bb8-4187-9fb0-ec6e1a62a731", + "number" => "9010100052000004", + "expiry" => "0535", + "cvv" => "1234", + "name" => "Franek Dolas", + ]); + } + + public function testGetData(): void + { + $data = $this->request->getData(); + + $this->assertSame('29fa01f8-6bb8-4187-9fb0-ec6e1a62a731', $data['transactionToken']); + $this->assertSame('9010100052000004', $data['cardNumber']); + $this->assertSame('0535', $data['cardDate']); + $this->assertSame('1234', $data['cvv']); + $this->assertSame('Franek Dolas', $data['clientName']); + } + + public function testSendSuccess() + { + $this->setMockHttpResponse('CardPaySuccess.txt'); + /** @var CardPayResponse $response */ + $response = $this->request->send(); + + $this->assertInstanceOf(CardPayResponse::class, $response); + $this->assertTrue($response->isSuccessful()); + $this->assertTrue($response->isRedirect()); + $this->assertSame('1234567890', $response->getTransactionId()); + $this->assertSame('https://this-is-redirect-url.com', $response->getRedirectUrl()); + } + + public function testSendAuthFailure() + { + $this->setMockHttpResponse('CardPayAuthFailure.txt'); + $response = $this->request->send(); + + $this->assertInstanceOf(CardPayResponse::class, $response); + $this->assertFalse($response->isSuccessful()); + $this->assertSame(Response::HTTP_UNAUTHORIZED, $response->getCode()); + $this->assertSame('Incorrect authentication', $response->getMessage()); + } + + public function testSendInvalidDataFailure() + { + $this->setMockHttpResponse('CardPayInvalidDataFailure.txt'); + $response = $this->request->send(); + + $this->assertInstanceOf(CardPayResponse::class, $response); + $this->assertFalse($response->isSuccessful()); + $this->assertSame(Response::HTTP_BAD_REQUEST, $response->getCode()); + $this->assertSame('Invalid input data', $response->getMessage()); + } + + public function testSendUnableToPayFailure() + { + $this->setMockHttpResponse('CardPayUnableToPayFailure.txt'); + $response = $this->request->send(); + + $this->assertInstanceOf(CardPayResponse::class, $response); + $this->assertFalse($response->isSuccessful()); + $this->assertSame(Response::HTTP_CONFLICT, $response->getCode()); + $this->assertSame('Unable to make payment.', $response->getMessage()); + } +} diff --git a/tests/Mock/CardChargeAuthFailure.txt b/tests/Mock/CardChargeAuthFailure.txt new file mode 100644 index 0000000..188b2a2 --- /dev/null +++ b/tests/Mock/CardChargeAuthFailure.txt @@ -0,0 +1,16 @@ +HTTP/1.1 422 Unprocessable Entity +Server: nginx/1.4.4 +Date: Sat, 20 May 2022 07:00:00 GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 101 +Connection: keep-alive +Access-Control-Allow-Credentials: true +Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, DELETE +Access-Control-Max-Age: 300 +Cache-Control: no-cache, no-store +Strict-Transport-Security: max-age=31556926; includeSubDomains + +{ + "error": "Incorrect authentication", + "code": 401 +} diff --git a/tests/Mock/CardChargeInvalidDataFailure.txt b/tests/Mock/CardChargeInvalidDataFailure.txt new file mode 100644 index 0000000..51f71e3 --- /dev/null +++ b/tests/Mock/CardChargeInvalidDataFailure.txt @@ -0,0 +1,16 @@ +HTTP/1.1 422 Unprocessable Entity +Server: nginx/1.4.4 +Date: Sat, 20 May 2022 07:00:00 GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 101 +Connection: keep-alive +Access-Control-Allow-Credentials: true +Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, DELETE +Access-Control-Max-Age: 300 +Cache-Control: no-cache, no-store +Strict-Transport-Security: max-age=31556926; includeSubDomains + +{ + "error": "Invalid input data", + "code": 400 +} diff --git a/tests/Mock/CardChargeSuccess.txt b/tests/Mock/CardChargeSuccess.txt new file mode 100644 index 0000000..bc2d170 --- /dev/null +++ b/tests/Mock/CardChargeSuccess.txt @@ -0,0 +1,18 @@ +HTTP/1.1 422 Unprocessable Entity +Server: nginx/1.4.4 +Date: Sat, 20 May 2022 07:00:00 GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 101 +Connection: keep-alive +Access-Control-Allow-Credentials: true +Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, DELETE +Access-Control-Max-Age: 300 +Cache-Control: no-cache, no-store +Strict-Transport-Security: max-age=31556926; includeSubDomains + +{ + "data": { + "orderId": 1234567890 + }, + "responseCode": 0 +} diff --git a/tests/Mock/CardPayAuthFailure.txt b/tests/Mock/CardPayAuthFailure.txt new file mode 100644 index 0000000..188b2a2 --- /dev/null +++ b/tests/Mock/CardPayAuthFailure.txt @@ -0,0 +1,16 @@ +HTTP/1.1 422 Unprocessable Entity +Server: nginx/1.4.4 +Date: Sat, 20 May 2022 07:00:00 GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 101 +Connection: keep-alive +Access-Control-Allow-Credentials: true +Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, DELETE +Access-Control-Max-Age: 300 +Cache-Control: no-cache, no-store +Strict-Transport-Security: max-age=31556926; includeSubDomains + +{ + "error": "Incorrect authentication", + "code": 401 +} diff --git a/tests/Mock/CardPayInvalidDataFailure.txt b/tests/Mock/CardPayInvalidDataFailure.txt new file mode 100644 index 0000000..51f71e3 --- /dev/null +++ b/tests/Mock/CardPayInvalidDataFailure.txt @@ -0,0 +1,16 @@ +HTTP/1.1 422 Unprocessable Entity +Server: nginx/1.4.4 +Date: Sat, 20 May 2022 07:00:00 GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 101 +Connection: keep-alive +Access-Control-Allow-Credentials: true +Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, DELETE +Access-Control-Max-Age: 300 +Cache-Control: no-cache, no-store +Strict-Transport-Security: max-age=31556926; includeSubDomains + +{ + "error": "Invalid input data", + "code": 400 +} diff --git a/tests/Mock/CardPaySuccess.txt b/tests/Mock/CardPaySuccess.txt new file mode 100644 index 0000000..a2d196f --- /dev/null +++ b/tests/Mock/CardPaySuccess.txt @@ -0,0 +1,19 @@ +HTTP/1.1 422 Unprocessable Entity +Server: nginx/1.4.4 +Date: Sat, 20 May 2022 07:00:00 GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 101 +Connection: keep-alive +Access-Control-Allow-Credentials: true +Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, DELETE +Access-Control-Max-Age: 300 +Cache-Control: no-cache, no-store +Strict-Transport-Security: max-age=31556926; includeSubDomains + +{ + "data": { + "orderId": 1234567890, + "redirectUrl": "https://this-is-redirect-url.com" + }, + "responseCode": 0 +} diff --git a/tests/Mock/CardPayUnableToPayFailure.txt b/tests/Mock/CardPayUnableToPayFailure.txt new file mode 100644 index 0000000..b2c2852 --- /dev/null +++ b/tests/Mock/CardPayUnableToPayFailure.txt @@ -0,0 +1,16 @@ +HTTP/1.1 422 Unprocessable Entity +Server: nginx/1.4.4 +Date: Sat, 20 May 2022 07:00:00 GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 101 +Connection: keep-alive +Access-Control-Allow-Credentials: true +Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, DELETE +Access-Control-Max-Age: 300 +Cache-Control: no-cache, no-store +Strict-Transport-Security: max-age=31556926; includeSubDomains + +{ +"error": "Unable to make payment.", +"code": 0 +}