diff --git a/README.md b/README.md index 6300d6b..5523438 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ PHP SDK for payment system [WayForPay](https://wayforpay.com). - [Complete 3DS](#complete-3ds) - [Check](#check) - [Refund](#refund) + - [Service URL](#service-url) + - [Return URL](#return-url) - [TODO](#todo) - [Contributing](#contributing) @@ -63,34 +65,26 @@ All examples require `composer install` before using after cloning from GitHub. See [purchase.php](examples/purchase.php). -```bash -$ php examples/purchase.php > pay.html -``` - -After you can see at `pay.html` form with pay button. Open file in your browser and press `Pay`. - -You can open file via default browser in Linux-based OS like: +Run PHP built-in server ```bash -$ x-www-browser pay.html +$ php -S localhost:8000 ``` +Then open `http://localhost:8000/examples/purchase.php` in browser. + #### Purchase Widget See [purchase-widget.php](examples/purchase-widget.php). -```bash -$ php examples/purchase-widget.php > widget.html -``` - -After you can see at `widget.html` widget with pay button. Open file in your browser and press `Pay`. - -You can open file via default browser in Linux-based OS like: +Run PHP built-in server ```bash -$ x-www-browser widget.html +$ php -S localhost:8000 ``` +Then open `http://localhost:8000/examples/purchase-widget.php` in browser. + #### Transactions List See [transaction-list.php](examples/transaction-list.php). @@ -170,7 +164,7 @@ Order status: Refunded Response will be instance of `CheckResponse`. Order can be retrieved via `getOrder` method. -### Refund +#### Refund ```bash $ php examples/refund.php @@ -180,6 +174,34 @@ Order status: Refunded Response will be instance of `RufundResponse`. +### Service URL + +You can set service URL in wizard via + +```php +$wizard->setServiceUrl('http://localhost:8000/examples/serviceUrl.php') +``` + +After payment processing WayForPay send payment data to specified URL. You can parse and check data like in example. + +See [serviceUrl.php](examples/serviceUrl.php). + +#### ⚠️⚠️⚠️ WARNING ⚠️⚠️⚠️ + +*Service URL must be accessible via Internet. WayForPay can't send data to local machine!* + +### Return URL + +You can set service URL in wizard via + +```php +$wizard->setReturnUrl('http://localhost:8000/examples/returnUrl.php') +``` + +After payment processing WayForPay send payment data to specified URL. You can parse and check data like in example. + +See [returnUrl.php](examples/returnUrl.php). + ## TODO * Methods diff --git a/examples/purchase-widget.php b/examples/purchase-widget.php index 42956c3..a9a9806 100644 --- a/examples/purchase-widget.php +++ b/examples/purchase-widget.php @@ -40,6 +40,8 @@ ->setProducts(new ProductCollection(array( new Product('test', 0.01, 1) ))) + ->setReturnUrl('http://localhost:8000/examples/returnUrl.php') + ->setServiceUrl('http://localhost:8000/examples/serviceUrl.php') ->getForm() ->getWidget(); diff --git a/examples/purchase.php b/examples/purchase.php index 245427c..430e4eb 100644 --- a/examples/purchase.php +++ b/examples/purchase.php @@ -40,6 +40,8 @@ ->setProducts(new ProductCollection(array( new Product('test', 0.01, 1) ))) + ->setReturnUrl('http://localhost:8000/examples/returnUrl.php') + ->setServiceUrl('http://localhost:8000/examples/serviceUrl.php') ->getForm() ->getAsString(); diff --git a/examples/returnUrl.php b/examples/returnUrl.php new file mode 100644 index 0000000..df1f654 --- /dev/null +++ b/examples/returnUrl.php @@ -0,0 +1,36 @@ + + * @copyright Copyright 2019 WayForPay + * @license https://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require_once __DIR__ . '/../vendor/autoload.php'; + +use WayForPay\SDK\Credential\AccountSecretTestCredential; +use WayForPay\SDK\Exception\WayForPaySDKException; +use WayForPay\SDK\Handler\ServiceUrlHandler; + +// Use test credential or yours +$credential = new AccountSecretTestCredential(); +//$credential = new AccountSecretCredential('account', 'secret'); + +try { + $handler = new ServiceUrlHandler($credential); + $response = $handler->parseRequestFromGlobals(); + + if ($response->getReason()->isOK()) { + echo "Success"; + } else { + echo "Error: " . $response->getReason()->getMessage(); + } +} catch (WayForPaySDKException $e) { + echo "WayForPay SDK exception: " . $e->getMessage(); +} \ No newline at end of file diff --git a/examples/serviceUrl.php b/examples/serviceUrl.php new file mode 100644 index 0000000..1bca205 --- /dev/null +++ b/examples/serviceUrl.php @@ -0,0 +1,32 @@ + + * @copyright Copyright 2019 WayForPay + * @license https://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require_once __DIR__ . '/../vendor/autoload.php'; + +use WayForPay\SDK\Credential\AccountSecretTestCredential; +use WayForPay\SDK\Exception\WayForPaySDKException; +use WayForPay\SDK\Handler\ServiceUrlHandler; + +// Use test credential or yours +$credential = new AccountSecretTestCredential(); +//$credential = new AccountSecretCredential('account', 'secret'); + +try { + $handler = new ServiceUrlHandler($credential); + $response = $handler->parseRequestFromPostRaw(); + + echo $handler->getSuccessResponse($response->getTransaction()); +} catch (WayForPaySDKException $e) { + echo "WayForPay SDK exception: " . $e->getMessage(); +} \ No newline at end of file diff --git a/src/Domain/PaymentSystems.php b/src/Domain/PaymentSystems.php index 8dc3ae6..f108726 100644 --- a/src/Domain/PaymentSystems.php +++ b/src/Domain/PaymentSystems.php @@ -31,6 +31,7 @@ class PaymentSystems const VISA_CHECKOUT = 'visaCheckout'; const GOOGLE_PAY = 'googlePay'; const APPLE_PAY = 'applePay'; + const PAY_PARTS_MONO = 'payPartsMono'; private $default; diff --git a/src/Domain/TransactionBase.php b/src/Domain/TransactionBase.php index 505b6f7..1f4cc2b 100644 --- a/src/Domain/TransactionBase.php +++ b/src/Domain/TransactionBase.php @@ -15,6 +15,7 @@ namespace WayForPay\SDK\Domain; use DateTime; +use WayForPay\SDK\Exception\InvalidFieldException; class TransactionBase { @@ -177,7 +178,7 @@ public function __construct( $baseCurrency = null ) { if (!in_array($status, $this->statusAllowed)) { - throw new \InvalidArgumentException( + throw new InvalidFieldException( 'Unexpected transaction type `' . $status . '`, expect one of ' . implode(', ', $this->statusAllowed) ); diff --git a/src/Domain/TransactionService.php b/src/Domain/TransactionService.php new file mode 100644 index 0000000..086a30a --- /dev/null +++ b/src/Domain/TransactionService.php @@ -0,0 +1,167 @@ + + * @copyright Copyright 2019 WayForPay + * @license https://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace WayForPay\SDK\Domain; + +use DateTime; + +class TransactionService extends TransactionBase +{ + /** + * @var CardToken + */ + private $recToken; + + /** + * @var string + */ + private $authCode; + + /** + * @var string + */ + private $repayUrl; + + /** + * @param array $data + * @return TransactionService + * @throws \Exception + */ + public static function fromArray(array $data) + { + $default = array( + 'merchantAccount' => '', + 'orderReference' => '', + 'merchantSignature' => '', + 'amount' => 0, + 'currency' => '', + 'authCode' => 0, + 'email' => '', + 'phone' => '', + 'createdDate' => 0, + 'processingDate' => 0, + 'cardPan' => '', + 'cardType' => '', + 'issuerBankCountry' => '', + 'issuerBankName' => '', + 'recToken' => '', + 'transactionStatus' => '', + 'reason' => '', + 'reasonCode' => 0, + 'fee' => 0, + 'paymentSystem' => '', + 'repayUrl' => '', + ); + + $data = array_merge($default, $data); + + return new self( + $data['merchantTransactionType'], + $data['orderReference'], + new DateTime('@' . $data['createdDate']), + $data['amount'], + $data['currency'], + $data['transactionStatus'], + new DateTime('@' . $data['processingDate']), + $data['reasonCode'], + $data['reason'], + isset($data['email']) ? $data['email'] : null, + isset($data['phone']) ? $data['phone'] : null, + isset($data['paymentSystem']) ? $data['paymentSystem'] : null, + isset($data['cardPan']) ? $data['cardPan'] : null, + isset($data['cardType']) ? $data['cardType'] : null, + isset($data['issuerBankCountry']) ? $data['issuerBankCountry'] : null, + isset($data['issuerBankName']) ? $data['issuerBankName'] : null, + isset($data['fee']) ? $data['fee'] : null, + isset($data['baseAmount']) ? $data['baseAmount'] : null, + isset($data['baseCurrency']) ? $data['baseCurrency'] : null, + isset($data['authCode']) ? $data['authCode'] : null, + isset($data['recToken']) ? $data['recToken'] : null, + isset($data['repayUrl']) ? $data['repayUrl'] : null + ); + } + + public function __construct( + $merchantTransactionType, + $orderReference, + DateTime $createdDate, + $amount, $currency, + $status, DateTime + $processingDate, + $reasonCode, + $reason, + $email = null, + $phone = null, + $paymentSystem = null, + $cardPan = null, + $cardType = null, + $issuerBankCountry = null, + $issuerBankName = null, + $fee = null, + $baseAmount = null, + $baseCurrency = null, + $authCode = null, + $recToken = null, + $repayUrl = null + ) { + parent::__construct( + $orderReference, + $createdDate, + $amount, + $currency, + $status, + $processingDate, + $reasonCode, + $reason, + $email, + $phone, + $paymentSystem, + $cardPan, + $cardType, + $issuerBankCountry, + $issuerBankName, + $fee, + $baseAmount, + $baseCurrency + ); + + $this->repayUrl = strval($repayUrl); + $this->authCode = strval($authCode); + $this->recToken = $recToken ? new CardToken($recToken) : null; + } + + /** + * @return CardToken + */ + public function getRecToken() + { + return $this->recToken; + } + + /** + * @return string + */ + public function getAuthCode() + { + return $this->authCode; + } + + /** + * @return string + */ + public function getRepayUrl() + { + return $this->repayUrl; + } +} \ No newline at end of file diff --git a/src/Exception/ApiException.php b/src/Exception/ApiException.php index 1425f1a..85dc8d6 100644 --- a/src/Exception/ApiException.php +++ b/src/Exception/ApiException.php @@ -16,7 +16,7 @@ use WayForPay\SDK\Domain\Reason; -class ApiException extends \RuntimeException +class ApiException extends WayForPaySDKException { public function __construct(Reason $reason) { diff --git a/src/Exception/InvalidFieldException.php b/src/Exception/InvalidFieldException.php new file mode 100644 index 0000000..6cb4c8d --- /dev/null +++ b/src/Exception/InvalidFieldException.php @@ -0,0 +1,19 @@ + + * @copyright Copyright 2019 WayForPay + * @license https://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace WayForPay\SDK\Exception; + +class InvalidFieldException extends WayForPaySDKException +{ +} \ No newline at end of file diff --git a/src/Exception/JsonParseException.php b/src/Exception/JsonParseException.php new file mode 100644 index 0000000..ea224c1 --- /dev/null +++ b/src/Exception/JsonParseException.php @@ -0,0 +1,19 @@ + + * @copyright Copyright 2019 WayForPay + * @license https://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace WayForPay\SDK\Exception; + +class JsonParseException extends WayForPaySDKException +{ +} \ No newline at end of file diff --git a/src/Exception/SignatureException.php b/src/Exception/SignatureException.php index 18f67ec..b83d24a 100644 --- a/src/Exception/SignatureException.php +++ b/src/Exception/SignatureException.php @@ -14,6 +14,6 @@ namespace WayForPay\SDK\Exception; -class SignatureException extends \RuntimeException +class SignatureException extends WayForPaySDKException { } \ No newline at end of file diff --git a/src/Exception/WayForPaySDKException.php b/src/Exception/WayForPaySDKException.php new file mode 100644 index 0000000..075eb48 --- /dev/null +++ b/src/Exception/WayForPaySDKException.php @@ -0,0 +1,19 @@ + + * @copyright Copyright 2019 WayForPay + * @license https://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace WayForPay\SDK\Exception; + +class WayForPaySDKException extends \RuntimeException +{ +} \ No newline at end of file diff --git a/src/Handler/ServiceUrlHandler.php b/src/Handler/ServiceUrlHandler.php new file mode 100644 index 0000000..fd09589 --- /dev/null +++ b/src/Handler/ServiceUrlHandler.php @@ -0,0 +1,120 @@ + + * @copyright Copyright 2019 WayForPay + * @license https://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace WayForPay\SDK\Handler; + + +use WayForPay\SDK\Credential\AccountSecretCredential; +use WayForPay\SDK\Domain\TransactionService; +use WayForPay\SDK\Exception\JsonParseException; +use WayForPay\SDK\Exception\SignatureException; +use WayForPay\SDK\Helper\SignatureHelper; +use WayForPay\SDK\Response\ServiceResponse; + +class ServiceUrlHandler +{ + const STATUS_ACCEPT = 'accept'; + + /** + * @var AccountSecretCredential + */ + private $credential; + + public function __construct(AccountSecretCredential $credential) + { + $this->credential = $credential; + } + + /** + * @return ServiceResponse + * @throws \Exception + */ + public function parseRequestFromGlobals() + { + return $this->parseRequestFromArray($_REQUEST); + } + + /** + * @return ServiceResponse + * @throws \Exception + */ + public function parseRequestFromPostRaw() + { + $data = \json_decode(file_get_contents('php://input'), TRUE); + + if ($data === null) { + throw new JsonParseException(\json_last_error_msg(), \json_last_error()); + } + + return $this->parseRequestFromArray($data); + } + + /** + * @param array $data + * @return ServiceResponse + * @throws \Exception + */ + public function parseRequestFromArray(array $data) + { + $response = new ServiceResponse($data); + + $transaction = $response->getTransaction(); + $expectedSignature = SignatureHelper::calculateSignature( + array( + $this->credential->getAccount(), + $transaction->getOrderReference(), + $transaction->getAmount(), + $transaction->getCurrency(), + $transaction->getAuthCode(), + $transaction->getCardPan(), + $transaction->getStatus(), + $response->getReason()->getCode() + ), + $this->credential->getSecret() + ); + + if (!isset($data['merchantSignature']) + || $expectedSignature !== $data['merchantSignature'] + ) { + throw new SignatureException( + 'Response signature mismatch: expected ' . $expectedSignature . + ', got ' . (isset($data['merchantSignature']) ? $data['merchantSignature'] : '') + ); + } + + return $response; + } + + /** + * @param TransactionService $transaction + * @return string + */ + public function getSuccessResponse(TransactionService $transaction) + { + $time = time(); + return \json_encode(array( + 'orderReference' => $transaction->getOrderReference(), + 'status' => self::STATUS_ACCEPT, + 'time' => $time, + 'signature' => SignatureHelper::calculateSignature( + array( + $transaction->getOrderReference(), + self::STATUS_ACCEPT, + $time + ), + $this->credential->getSecret() + ) + )); + } +} \ No newline at end of file diff --git a/src/Response/Response.php b/src/Response/Response.php index ec85486..7e01828 100644 --- a/src/Response/Response.php +++ b/src/Response/Response.php @@ -17,6 +17,7 @@ use WayForPay\SDK\Contract\ResponseInterface; use WayForPay\SDK\Domain\Reason; use WayForPay\SDK\Exception\ApiException; +use WayForPay\SDK\Exception\InvalidFieldException; class Response implements ResponseInterface { @@ -28,16 +29,17 @@ class Response implements ResponseInterface /** * Response constructor. * @param array $data - * @throws \Exception + * @throws InvalidFieldException + * @throws ApiException */ public function __construct(array $data) { if (!isset($data['reason'])) { - throw new \InvalidArgumentException('Field `reason` required'); + throw new InvalidFieldException('Field `reason` required'); } if (!isset($data['reasonCode'])) { - throw new \InvalidArgumentException('Field `reason` required'); + throw new InvalidFieldException('Field `reason` required'); } $this->reason = new Reason($data['reasonCode'], $data['reason']); diff --git a/src/Response/ServiceResponse.php b/src/Response/ServiceResponse.php new file mode 100644 index 0000000..5d2595e --- /dev/null +++ b/src/Response/ServiceResponse.php @@ -0,0 +1,45 @@ + + * @copyright Copyright 2019 WayForPay + * @license https://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace WayForPay\SDK\Response; + +use WayForPay\SDK\Domain\TransactionService; + +class ServiceResponse extends Response +{ + /** + * @var TransactionService + */ + private $transaction; + + /** + * ServiceResponse constructor. + * @param array $data + * @throws \Exception + */ + public function __construct(array $data) + { + parent::__construct($data); + + $this->transaction = TransactionService::fromArray($data); + } + + /** + * @return TransactionService + */ + public function getTransaction() + { + return $this->transaction; + } +} \ No newline at end of file