first commit

This commit is contained in:
2024-11-11 18:46:54 +01:00
commit a630d17338
25634 changed files with 4923715 additions and 0 deletions

View File

@@ -0,0 +1,679 @@
<?php
namespace Imoje\Payment;
use Exception;
/**
* Class Api
*
* @package Imoje\Payment
*/
class Api
{
const TRANSACTION = 'transaction';
const PROFILE = 'profile';
const MERCHANT = 'merchant';
const SERVICE = 'service';
const TRANSACTION_TYPE_SALE = 'sale';
const TRANSACTION_TYPE_REFUND = 'refund';
/**
* @var array
*/
private static $serviceUrls = [
Util::ENVIRONMENT_PRODUCTION => 'https://api.imoje.pl/v1',
Util::ENVIRONMENT_SANDBOX => 'https://sandbox.api.imoje.pl/v1',
];
/**
* @var string
*/
private $authorizationToken;
/**
* @var string
*/
private $merchantId;
/**
* @var string
*/
private $serviceId;
/**
* @var string
*/
private $environment;
/**
* Api constructor.
*
* @param string $authorizationToken
* @param string $merchantId
* @param string $serviceId
* @param string $environment
*/
public function __construct($authorizationToken, $merchantId, $serviceId, $environment = '')
{
$this->authorizationToken = $authorizationToken;
$this->merchantId = $merchantId;
$this->serviceId = $serviceId;
if (!$environment) {
$environment = Util::ENVIRONMENT_PRODUCTION;
}
$this->environment = $environment;
}
/**
* @param string $firstName
* @param string $lastName
* @param string $street
* @param string $city
* @param string $region
* @param string $postalCode
* @param string $countryCodeAlpha2
*
* @return array
*/
public static function prepareAddressData(
$firstName,
$lastName,
$street,
$city,
$region,
$postalCode,
$countryCodeAlpha2
) {
$array = [
'firstName' => $firstName,
'lastName' => $lastName,
'street' => $street,
'city' => $city,
'postalCode' => $postalCode,
'countryCodeAlpha2' => $countryCodeAlpha2,
];
if ($region) {
$array['region'] = $region;
}
return $array;
}
/**
* @param array $transaction
*
* @return array
*/
public static function parseStringToArray($transaction)
{
$array = [];
if ($transaction['action']['method'] === Util::METHOD_REQUEST_POST) {
parse_str($transaction['action']['contentBodyRaw'], $array);
}
if ($transaction['action']['method'] === Util::METHOD_REQUEST_GET) {
$urlParsed = parse_url($transaction['action']['url']);
if (isset($urlParsed['query']) && $urlParsed['query']) {
parse_str($urlParsed['query'], $array);
}
}
return $array;
}
/**
* @param string $blikProfileId
* @param int $amount
* @param string $currency
* @param string $orderId
* @param string $title
* @param string $clientIp
* @param string $blikKey
* @param string $blikCode
*
* @return string
*/
public function prepareBlikOneclickData(
$blikProfileId,
$amount,
$currency,
$orderId,
$title,
$clientIp,
$blikKey = '',
$blikCode = ''
) {
$array = [
'serviceId' => $this->serviceId,
'blikProfileId' => $blikProfileId,
'amount' => Util::convertAmountToFractional($amount),
'currency' => $currency,
'orderId' => $orderId,
'title' => $title,
'clientIp' => $clientIp,
];
if ($blikKey) {
$array['blikKey'] = $blikKey;
}
if ($blikCode) {
$array['blikCode'] = $blikCode;
}
return json_encode($array);
}
/**
* @param string $id
*
* @return array
*/
public function getTransaction($id)
{
return $this->call(
$this->getTransactionUrl($id),
Util::METHOD_REQUEST_GET
);
}
/**
* @param int $amount
* @param string $currency
* @param string $orderId
* @param string $paymentMethod
* @param string $paymentMethodCode
* @param string $successReturnUrl
* @param string $failureReturnUrl
* @param string $customerFirstName
* @param string $customerLastName
* @param string $customerEmail
* @param string $type
* @param string $clientIp
* @param string $blikCode
* @param array $address
* @param string $cid
*
* @return string
*/
public function prepareData(
$amount,
$currency,
$orderId,
$paymentMethod,
$paymentMethodCode,
$successReturnUrl,
$failureReturnUrl,
$customerFirstName,
$customerLastName,
$customerEmail,
$type = 'sale',
$clientIp = '',
$blikCode = '',
$address = [],
$cid = ''
) {
if (!$clientIp) {
$clientIp = $_SERVER['REMOTE_ADDR'];
}
$array = [
'type' => $type,
'serviceId' => $this->serviceId,
'amount' => Util::convertAmountToFractional($amount),
'currency' => $currency,
'orderId' => (string) $orderId,
'title' => (string) $orderId,
'paymentMethod' => $paymentMethod,
'paymentMethodCode' => $paymentMethodCode,
'successReturnUrl' => $successReturnUrl,
'failureReturnUrl' => $failureReturnUrl,
'clientIp' => $clientIp,
'customer' => [
'firstName' => $customerFirstName,
'lastName' => $customerLastName,
'email' => $customerEmail,
],
];
if ($blikCode) {
$array['blikCode'] = $blikCode;
}
if (isset($address['billing']) && $address['billing']) {
$array['billing'] = $address['billing'];
}
if (isset($address['shipping']) && $address['shipping']) {
$array['shipping'] = $address['shipping'];
}
if ($cid) {
$array['customer']['cid'] = (string) $cid;
}
return json_encode($array);
}
/**
* @param int $amount
*
* @return string
*/
public function prepareRefundData(
$amount
) {
return json_encode([
'amount' => $amount,
'serviceId' => $this->serviceId,
'type' => self::TRANSACTION_TYPE_REFUND,
]);
}
// endregion
/**
* @param string $body
*
* @return array
*/
public function createTransaction($body, $url = '', $method = '')
{
if (!$url) {
$url = $this->getTransactionCreateUrl();
}
if (!$method) {
$method = Util::METHOD_REQUEST_POST;
}
return $this->call(
$url,
$method,
$body
);
}
/**
* @return array
*/
public function getServiceInfo()
{
return $this->call(
$this->getServiceInfoUrl(),
Util::METHOD_REQUEST_GET
);
}
/**
* @param string $cid
*
* @return array
*/
public function getBlikProfileList($cid)
{
return $this->call(
$this->getProfileBlikUrl($cid),
Util::METHOD_REQUEST_GET
);
}
/**
* @param string $body
*
* @return array
*/
public function createRefund($body, $transactionUuid)
{
return $this->call(
$this->getRefundCreateUrl($transactionUuid),
Util::METHOD_REQUEST_POST,
$body
);
}
/**
* Creates full form with order data.
*
* @param array $transaction
* @param string $submitValue
* @param string $submitClass
* @param string $submitStyle
*
* @return string
* @throws Exception
*/
public function buildOrderForm($transaction, $submitValue = '', $submitClass = '', $submitStyle = '')
{
if (!isset($transaction['body']['action'])) {
throw new Exception(json_encode([
'action' => 'apiBuildOrderForm',
'error' => 'Doesnt action exist',
]
));
}
return Util::createOrderForm(
self::parseStringToArray($transaction['body']),
$transaction['body']['action']['url'],
$transaction['body']['action']['method'],
$submitValue,
$submitClass,
$submitStyle
);
}
/**
* @return string
*/
public function getDebitBlikProfileUrl()
{
$baseUrl = self::getServiceUrl();
if ($baseUrl) {
return $baseUrl
. '/'
. self::MERCHANT
. '/'
. $this->merchantId
. '/'
. self::TRANSACTION
. '/'
. self::PROFILE
. '/blik';
}
return '';
}
/**
* @return string
*/
public function getDeactivateBlikProfileUrl()
{
$baseUrl = self::getServiceUrl();
if ($baseUrl) {
return $baseUrl
. '/'
. self::MERCHANT
. '/'
. $this->merchantId
. '/'
. self::PROFILE
. '/deactivate/blik';
}
return '';
}
/**
* @param string $url
* @param string $methodRequest
* @param string $body
*
* @return array
*/
private function call($url, $methodRequest, $body = '')
{
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $methodRequest);
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
curl_setopt($curl, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->authorizationToken,
]);
$resultCurl = json_decode(curl_exec($curl), true);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if (($httpCode !== 200) || !$resultCurl) {
$array = [
'success' => false,
'data' => [
'httpCode' => $httpCode,
'error' => curl_error($curl),
'body' => '',
],
];
if (isset($resultCurl['apiErrorResponse']['message']) && $resultCurl['apiErrorResponse']['message']) {
$array['data']['body'] = $resultCurl['apiErrorResponse']['message'];
}
return $array;
}
if (isset($resultCurl['transaction']['statusCode']) && $resultCurl['transaction']['statusCode']) {
if ($this->check_retype_code($resultCurl['transaction']['statusCode'])) {
$resultCurl['code'] = 1;
}
if ($this->check_new_param_alias($resultCurl['transaction']['statusCode'])) {
$resultCurl['newParamAlias'] = 1;
}
}
return [
'success' => true,
'body' => $resultCurl,
];
}
/**
* Verify that error code needs to retype BLIK code in frontend
*
* @param string $code
*
* @return bool
*/
private function check_retype_code($code)
{
$array = [
'BLK-ERROR-210002',
'BLK-ERROR-210003',
'BLK-ERROR-210004',
'BLK-ERROR-210005',
'BLK-ERROR-210006',
'BLK-ERROR-210007',
'BLK-ERROR-210008',
'BLK-ERROR-210009',
'BLK-ERROR-210010',
'BLK-ERROR-210011',
'BLK-ERROR-210012',
'BLK-ERROR-210013',
'BLK-ERROR-210014',
'BLK-ERROR-210015',
'BLK-ERROR-210016',
'BLK-ERROR-210017',
'BLK-ERROR-210018',
'BLK-ERROR-210019',
'BLK-ERROR-210020',
'BLK-ERROR-210021',
'BLK-ERROR-210022',
];
return in_array($code, $array);
}
/**
* Verify that error code needs to create new paramAlias
*
* @param string $code
*
* @return bool
*/
private function check_new_param_alias($code)
{
$array = [
'BLK-ERROR-210002',
'BLK-ERROR-210003',
'BLK-ERROR-210004',
];
return in_array($code, $array);
}
/**
* @return string
*/
private function getTransactionUrl($id)
{
$baseUrl = self::getServiceUrl();
if ($baseUrl) {
return $baseUrl
. '/'
. self::MERCHANT
. '/'
. $this->merchantId
. '/'
. self::TRANSACTION
. '/'
. $id;
}
return '';
}
/**
* @return string
*/
private function getServiceUrl()
{
if (isset(self::$serviceUrls[$this->environment])) {
return self::$serviceUrls[$this->environment];
}
return '';
}
/**
* @return string
*/
private function getTransactionCreateUrl()
{
$baseUrl = self::getServiceUrl();
if ($baseUrl) {
return $baseUrl
. '/'
. self::MERCHANT
. '/'
. $this->merchantId
. '/'
. self::TRANSACTION;
}
return '';
}
/**
* @return string
*/
private function getServiceInfoUrl()
{
$baseUrl = self::getServiceUrl();
if ($baseUrl) {
return $baseUrl
. '/'
. self::MERCHANT
. '/'
. $this->merchantId
. '/'
. self::SERVICE
. '/'
. $this->serviceId;
}
return '';
}
/**
* @param string $cid
*
* @return string
*/
private function getProfileBlikUrl($cid)
{
$baseUrl = $this->getServiceUrl();
if ($baseUrl) {
return $baseUrl
. '/'
. self::MERCHANT
. '/'
. $this->merchantId
. '/'
. self::PROFILE
. '/'
. self::SERVICE
. '/'
. $this->serviceId
. '/cid/'
. $cid
. '/blik';
}
return '';
}
/**
* @param string $transactionUuid
*
* @return string
*/
private function getRefundCreateUrl($transactionUuid)
{
$baseUrl = $this->getServiceUrl();
if ($baseUrl) {
return $baseUrl
. '/'
. self::MERCHANT
. '/'
. $this->merchantId
. '/'
. self::TRANSACTION
. '/'
. $transactionUuid
. '/refund';
}
return '';
}
}

View File

@@ -0,0 +1,215 @@
<?php
namespace Imoje\Payment;
/**
* Class CartData
*
* @package Imoje\Payment
*/
class CartData
{
const TYPE_SCHEMA_ITEMS = 'cartDataItems';
const TYPE_SCHEMA_ADDRESS = 'cartDataAddress';
const TYPE_SCHEMA_DISCOUNT = 'cartDataDiscount';
const TYPE_SCHEMA_SHIPPING = 'cartDataShipping';
/**
* @var array
*/
private $items;
/**
* @var array
*/
private $addressBilling;
/**
* @var int
*/
private $createdAt;
/**
* @var int
*/
private $amount;
/**
* @var array
*/
private $addressDelivery;
/**
* @var array
*/
private $shipping = [];
/**
* @var array
*/
private $discount = [];
/**
* @param $id
* @param $vat
* @param $name
* @param $amount
* @param $quantity
*
* @return void
*/
public function addItem($id, $vat, $name, $amount, $quantity)
{
$this->items[] = [
'id' => $id,
'vat' => $vat,
'name' => $name,
'amount' => $amount,
'quantity' => $quantity,
];
}
/**
* @param int $vat
* @param string $name
* @param int $amount
*
* @return void
*/
public function setDiscount($vat, $name, $amount)
{
$this->discount = [
'vat' => $vat,
'name' => $name,
'amount' => $amount,
];
}
/**
* @param int $vat
* @param string $name
* @param int $amount
*
* @return void
*/
public function setShipping($vat, $name, $amount)
{
$this->shipping = [
'vat' => $vat,
'name' => $name,
'amount' => $amount,
];
}
/**
* @param int $amount
*
* @return void
*/
public function setAmount($amount)
{
$this->amount = $amount;
}
/**
* @param int $createdAt
*
* @return void
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
}
/**
* @param string $city
* @param string $name
* @param string $phone
* @param string $street
* @param string $country
* @param string $postalCode
*
* @return void
*/
public function setAddressBilling($city, $name, $phone, $street, $country, $postalCode)
{
$this->addressBilling = [
'city' => $city,
'name' => $name,
'phone' => (string) $phone,
'street' => $street,
'country' => $country,
'postalCode' => (string) $postalCode,
];
}
/**
* @param string $city
* @param string $name
* @param string $phone
* @param string $street
* @param string $country
* @param string $postalCode
*
* @return void
*/
public function setAddressDelivery($city, $name, $phone, $street, $country, $postalCode)
{
$this->addressDelivery = [
'city' => $city,
'name' => $name,
'phone' => (string) $phone,
'street' => $street,
'country' => $country,
'postalCode' => (string) $postalCode,
];
}
/**
* @return string
*/
public function prepareCartData()
{
return base64_encode(gzencode(json_encode($this->prepareCartDataArray()), 5));
}
/**
* @return array
*/
public function prepareCartDataArray()
{
$data = [
'address' => [
'billing' => $this->addressBilling,
'delivery' => $this->addressDelivery,
],
'items' => $this->items,
];
if (!empty($this->discount)) {
$data['discount'] = $this->discount;
}
if (!empty($this->shipping)) {
$data['shipping'] = $this->shipping;
}
if (!empty($this->createdAt)) {
$data['createdAt'] = $this->createdAt;
}
if (!empty($this->amount)) {
$data['amount'] = $this->amount;
}
return $data;
}
}

View File

@@ -0,0 +1,218 @@
<?php
namespace Imoje\Payment;
/**
* Class Configuration
*
* @package Imoje\Payment
*/
class Configuration
{
const ROUTE_PAY = '/payment';
const ROUTE_CHECK_PAYMENT_METHODS = '/check/methods';
const METHOD_REQUEST_POST = 'POST';
/**
* @var array
*/
private static $serviceUrls = [
Util::ENVIRONMENT_PRODUCTION => 'https://paywall.imoje.pl',
Util::ENVIRONMENT_SANDBOX => 'https://sandbox.paywall.imoje.pl',
];
/**
* @var string
*/
private static $headerSignatureName = 'HTTP_X_IMOJE_SIGNATURE';
/**
* @var string
*/
private static $serviceKey;
/**
* @var string
*/
private static $serviceId;
/**
* @var string
*/
private static $merchantId;
/**
* @var bool
*/
private static $apiMode = false;
/**
* @var string
*/
private static $environment;
/**
* @var string
*/
private static $authorizationToken;
/**
* @var array
*/
private static $serviceApiUrls = [
Util::ENVIRONMENT_PRODUCTION => 'https://api.imoje.pl/v1',
//Util::ENVIRONMENT_SANDBOX => 'https://sandbox.api.imoje.pl', sandbox doesnt exist at this moment
];
/**
* @return string
*/
public static function getServiceKey()
{
return self::$serviceKey;
}
/**
* @param string $serviceKey
*/
public static function setServiceKey($serviceKey)
{
self::$serviceKey = trim($serviceKey);
}
/**
* @return string
*/
public static function getServiceId()
{
return self::$serviceId;
}
/**
* @param string $serviceId
*/
public static function setServiceId($serviceId)
{
self::$serviceId = trim($serviceId);
}
/**
* @return string
*/
public static function getMerchantId()
{
return self::$merchantId;
}
/**
* @param string $merchantId
*/
public static function setMerchantId($merchantId)
{
self::$merchantId = trim($merchantId);
}
/**
* @return string
*/
public static function getEnvironment()
{
return self::$environment;
}
/**
* @param string $environment
*/
public static function setEnvironment($environment)
{
self::$environment = $environment;
}
/**
* @return string
*/
public static function getApiMode()
{
return self::$apiMode;
}
/**
* @param bool $apiMode
*/
public static function setApiMode($apiMode)
{
self::$apiMode = $apiMode;
}
/**
* @return string
*/
public static function getAuthorizationToken()
{
return self::$authorizationToken;
}
/**
* @param string $authorizationToken
*/
public static function setAuthorizationToken($authorizationToken)
{
self::$authorizationToken = $authorizationToken;
}
/**
* @param string $environment
*
* @return string|bool
*/
public static function getServiceUrl($environment)
{
if(isset(self::$serviceUrls[$environment])) {
return self::$serviceUrls[$environment];
}
return false;
}
/**
* @return string
*/
public static function getHeaderSignatureName()
{
return self::$headerSignatureName;
}
/**
* @param string $environment
*
* @return string|bool
*/
public static function getServiceApiUrl($environment)
{
if(isset(self::$serviceApiUrls[$environment])) {
return self::$serviceApiUrls[$environment];
}
return false;
}
/**
* @param string $environment
* @param string $mid
*
* @return string|bool
*/
public static function getTransactionCreateUrlApi($environment, $mid)
{
if(!($baseApiUrl = self::getServiceApiUrl($environment))) {
return false;
};
return $baseApiUrl . '/merchant/' . $mid . '/transaction';
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Imoje\Payment;
/**
* Class Payment
*
* @package Imoje\Payment
*/
class Payment
{
/**
* Payment constructor.
*
* @param string $environment
* @param string $serviceKey
* @param string $serviceId
* @param string $merchantId
* @param bool $apiMode
* @param string $authorizationToken
*/
public function __construct($environment, $serviceKey, $serviceId, $merchantId, $apiMode = false, $authorizationToken = '')
{
Configuration::setEnvironment($environment);
Configuration::setServiceKey($serviceKey);
Configuration::setServiceId($serviceId);
Configuration::setMerchantId($merchantId);
if($apiMode) {
Configuration::setApiMode($apiMode);
Configuration::setAuthorizationToken($authorizationToken);
}
}
/**
* Creates full form with order data.
*
* @param array $orderData
* @param string $submitValue
* @param string $url
*
* @return string
*/
public function buildOrderForm($orderData, $submitValue = '', $url = '')
{
return Util::createOrderForm(Util::prepareOrderData($orderData), $submitValue, $url);
}
}

View File

@@ -0,0 +1,169 @@
<?php
namespace Imoje\Payment;
use DateTime;
use Twisto\Address;
use Twisto\Customer;
use Twisto\Item;
use Twisto\Order;
/**
* Class Twisto
*
* @package Imoje\Payment
*/
class Twisto
{
/**
* @var string[]
*/
private static $urlsScript = [
Util::ENVIRONMENT_PRODUCTION => 'https://api.twisto.pl/v2/lib/twisto.js',
Util::ENVIRONMENT_SANDBOX => 'https://static.test.twisto.pl/api/v2/twisto.js',
];
/**
* @param string $environment
*
* @return string
*/
public static function getUrlScript($environment)
{
return self::$urlsScript[$environment];
}
/**
* @return string
*/
public static function getDataSandbox()
{
return json_encode([
'status' => 'accepted-verification-required',
'transaction_id' => 'sandbox',
]);
}
/**
* @param string $itemName
*
* @return bool|string
*/
public static function getItemName($itemName
) {
return substr($itemName, 0, 255);
}
/**
* @param array $paymentMethods
* @param bool $twistoEnabled
*
* @return bool
*/
public static function isActive($paymentMethods, $twistoEnabled)
{
return isset(
$paymentMethods['twisto'],
$paymentMethods['twisto']['pk'],
$paymentMethods['twisto']['sk']
)
&& $paymentMethods['twisto']
&& $paymentMethods['twisto']['pk']
&& $paymentMethods['twisto']['sk']
&& $twistoEnabled;
}
/**
* @param string $secretKey
* @param string $total
* @param array $cartData
* @param string $email
* @param DateTime $dateCreated
* @param array $previousOrders
*
* @return string
*/
public static function getPayload($secretKey, $total, $cartData, $email, DateTime $dateCreated, $previousOrders)
{
if(empty($cartData)) {
return '';
}
require __DIR__ . '/../vendor/twistopayments/twisto.php/src/Twisto.php';
$twistoItems = [];
$twistoItems[] = new Item(
Item::TYPE_SHIPMENT,
self::getItemName($cartData['shipping']['name']),
'shipment',
1,
round($cartData['shipping']['amount'], 2),
$cartData['shipping']['vatRate']);
if(isset($cartData['discount'])) {
$twistoItems[] = new Item(
Item::TYPE_DISCOUNT,
self::getItemName($cartData['discount']['name']),
'discount',
1,
-$cartData['discount']['amount'],
$cartData['discount']['vatRate']);
}
foreach($cartData['items'] as $item) {
$twistoItems[] = new Item(
Item::TYPE_DEFAULT,
self::getItemName($item['name']),
$item['productId'],
$item['quantity'],
Util::multiplyValues($item['amount'], $item['quantity'], 2),
$item['vatRate']
);
}
$cartTotal = 0;
foreach($twistoItems as $k => $v) {
if(isset($v->price_vat)) {
$cartTotal += $v->price_vat;
}
}
if($cartTotal !== $total) {
$twistoItems[] = new Item(
Item::TYPE_ROUND,
'round',
'round',
1,
round($total - $cartTotal, 2),
0
);
}
$addressBilling = $cartData['addressBilling'];
$addressShipping = $cartData['addressDelivery'];
$data = new Order($dateCreated,
new Address($addressBilling['name'],
$addressBilling['street'],
$addressBilling['city'],
str_replace('-', '', $addressBilling['postalCode']),
$addressBilling['countryCode'],
$addressBilling['phone']),
new Address($addressShipping['name'],
$addressShipping['street'],
$addressShipping['city'],
str_replace('-', '', $addressShipping['postalCode']),
$addressShipping['countryCode'],
$addressShipping['phone']),
$total, $twistoItems);
$twisto = new \Twisto\Twisto();
$twisto->setSecretKey($secretKey);
return $twisto->getCheckPayload(new Customer($email), $data, $previousOrders);
}
}

View File

@@ -0,0 +1,795 @@
<?php
namespace Imoje\Payment;
use JsonSchema\Validator;
/**
* Class Util
*
* @package Imoje\Payment
*/
class Util
{
// region notification codes
const NC_OK = 0;
const NC_INVALID_SIGNATURE = 1;
const NC_SERVICE_ID_NOT_MATCH = 2;
const NC_ORDER_NOT_FOUND = 3;
const NC_INVALID_SIGNATURE_HEADERS = 4;
const NC_EMPTY_NOTIFICATION = 5;
const NC_NOTIFICATION_IS_NOT_JSON = 6;
const NC_INVALID_JSON_STRUCTURE = 7;
const NC_INVALID_ORDER_STATUS = 8;
const NC_AMOUNT_NOT_MATCH = 9;
const NC_UNHANDLED_STATUS = 10;
const NC_ORDER_STATUS_NOT_CHANGED = 11;
const NC_CART_NOT_FOUND = 12;
const NC_ORDER_STATUS_IS_NOT_SETTLED_ORDER_ARRANGEMENT_AFTER_IPN = 13;
const NC_ORDER_EXISTS_ORDER_ARRANGEMENT_AFTER_IPN = 14;
const NC_REQUEST_IS_NOT_POST = 15;
const NC_MISSING_TWISTO_RESPONSE_IN_POST = 16;
const NC_MISSING_ORDER_ID_IN_POST = 17;
const NC_CURL_IS_NOT_INSTALLED = 18;
const NC_MISSING_SIGNATURE_IN_POST = 19;
const NC_COULD_NOT_INSERT_TRANSACTION_ID_TO_DB = 20;
const NC_UNKNOWN = 100;
// endregion
// region notification status
const NS_OK = 'ok';
const NS_ERROR = 'error';
// endregion
const METHOD_REQUEST_POST = 'POST';
const METHOD_REQUEST_GET = 'GET';
const ENVIRONMENT_PRODUCTION = 'production';
const ENVIRONMENT_SANDBOX = 'sandbox';
private static $cdnUrl = 'https://data.imoje.pl';
/**
* @var array
*/
private static $supportedCurrencies = [
'pln' => 'PLN',
'eur' => 'EUR',
'czk' => 'CZK',
'gbp' => 'GBP',
'usd' => 'USD',
'uah' => 'UAH',
'hrk' => 'HRK',
'huf' => 'HUF',
'sek' => 'SEK',
'ron' => 'RON',
'chf' => 'CHF',
'bgn' => 'BGN',
];
/**
* @var array
*/
private static $paymentMethods = [
'blik' => 'blik',
'paylater' => 'imoje_paylater',
'pbl' => 'pbl',
'card' => 'card',
'ing' => 'ing',
];
/**
* @var array
*/
private static $paymentMethodCodeList = [
'blik' => 'blik',
'alior' => 'alior',
'bnpparibas' => 'bnpparibas',
'bos' => 'bos',
'bs' => 'bs',
'bspb' => 'bspb',
'bzwbk' => 'bzwbk',
'citi' => 'citi',
'creditagricole' => 'creditagricole',
'envelo' => 'envelo',
'getin' => 'getin',
'ideabank' => 'ideabank',
'ing' => 'ing',
'inteligo' => 'inteligo',
'ipko' => 'ipko',
'millennium' => 'millennium',
'mtransfer' => 'mtransfer',
'nest' => 'nest',
'noble' => 'noble',
'pbs' => 'pbs',
'pekao24' => 'pekao24',
'plusbank' => 'plusbank',
'pocztowy' => 'pocztowy',
'tmobile' => 'tmobile',
'paylater' => 'imoje_twisto',
'blik_oneclick' => 'blik_oneclick',
];
/**
* @var array
*/
private static $paymentMethodCodeLogoExt = [
'alior' => 'alior.svg',
'bnpparibas' => 'bnpparibas.png',
'bos' => 'bos.png',
'bs' => 'bs.png',
'bspb' => 'bspb.png',
'bzwbk' => 'bzwbk.png',
'citi' => 'citi.png',
'creditagricole' => 'creditagricole.svg',
'envelo' => 'envelo.png',
'getin' => 'getin.svg',
'ideabank' => 'ideabank.png',
'ing' => 'ing.png',
'inteligo' => 'inteligo.png',
'ipko' => 'ipko.png',
'millennium' => 'millennium.svg',
'mtransfer' => 'mtransfer.png',
'nest' => 'nest.svg',
'noble' => 'noble.png',
'pbs' => 'pbs.png',
'pekao24' => 'pekao24.svg',
'plusbank' => 'plusbank.png',
'pocztowy' => 'pocztowy.svg',
'tmobile' => 'tmobile.svg',
];
/**
* @var array
*/
private static $hashMethods = [
'sha224' => 'sha224',
'sha256' => 'sha256',
'sha384' => 'sha384',
'sha512' => 'sha512',
];
/**
* @var array
*/
private static $transactionStatuses = [
'new' => 'new',
'authorized' => 'authorized',
'pending' => 'pending',
'submitted_for_settlement'
=> 'submitted_for_settlement',
'rejected' => 'rejected',
'settled' => 'settled',
'error' => 'error',
'cancelled' => 'cancelled',
];
/**
* Functions that return true when passed currency is on supported currencies list.
*
* @param string $currencyCode ISO4217
*
* @return bool
*/
public static function canUseForCurrency($currencyCode)
{
return isset(self::$supportedCurrencies[strtolower($currencyCode)]);
}
/**
* @param array $order
* @param string $url
* @param string $method
* @param string $submitValue
*
* @return string
*/
public static function createOrderForm($order, $url = '', $method = '', $submitValue = '')
{
if (!$submitValue) {
$submitValue = 'Continue';
}
if (!$url) {
$url = self::getServiceUrl();
}
if (!$method) {
$method = 'POST';
}
$form = '<form method="' . $method . '" action="' . $url . '">';
if (is_array($order)) {
foreach ($order as $key => $value) {
$form .= '<input type="hidden" value="' . htmlentities($value) . '" name="' . $key . '" id="imoje_' . $key . '">';
}
}
$form .= '<button type="submit" id="submit-payment-form">' . $submitValue . '</button>';
$form .= '</form>';
return $form;
}
/**
* @return string
*/
public static function getServiceUrl()
{
$url = Configuration::getServiceUrl(Configuration::getEnvironment());
if ($url === false) {
return '';
}
return $url . Configuration::ROUTE_PAY;
}
/**
* @param int $amount
* @param string $currency
* @param string $orderId
* @param string $orderDescription
* @param string $customerFirstName
* @param string $customerLastName
* @param null|string $customerEmail
* @param null|string $customerPhone
* @param null|string $urlSuccess
* @param null|string $urlFailure
* @param null|string $urlReturn
* @param null|string $version
* @param null|string $cartData
* @param null|string $visibleMethod
*
* @return array
*/
public static function prepareData(
$amount, $currency, $orderId, $orderDescription = null, $customerFirstName, $customerLastName,
$customerEmail = null, $customerPhone = null, $urlSuccess = null,
$urlFailure = null, $urlReturn = null, $version = null, $cartData = null, $visibleMethod = null
) {
$data = [];
$data['amount'] = Util::convertAmountToFractional($amount);
$data['currency'] = $currency;
$data['orderId'] = $orderId;
if ($orderDescription) {
$data['orderDescription'] = $orderDescription;
}
$data['customerFirstName'] = $customerFirstName;
$data['customerLastName'] = $customerLastName;
if ($customerPhone) {
$data['customerPhone'] = $customerPhone;
}
if ($customerEmail) {
$data['customerEmail'] = $customerEmail;
}
if ($urlSuccess) {
$data['urlSuccess'] = $urlSuccess;
}
if ($urlFailure) {
$data['urlFailure'] = $urlFailure;
}
if ($urlReturn) {
$data['urlReturn'] = $urlReturn;
}
if ($version) {
$data['version'] = $version;
}
if ($cartData) {
$data['cartData'] = $cartData;
}
if ($visibleMethod) {
$data['visibleMethod'] = $visibleMethod;
}
return $data;
}
/**
* @param float $amount
*
* @return int
*/
public static function convertAmountToFractional($amount)
{
return (int) round( (float)$amount * 100, 2 );// self::multiplyValues(round($amount, 2), 100, 0);
}
/**
* @param number $firstValue
* @param number $secondValue
* @param number $precision
*
* @return float
*/
public static function multiplyValues($firstValue, $secondValue, $precision)
{
return round($firstValue * $secondValue, $precision);
}
/**
* @param string $serviceId
* @param int $amount
* @param string $currency
* @param string $orderId
* @param string $paymentMethod
* @param string $paymentMethodCode
* @param string $successReturnUrl
* @param string $failureReturnUrl
* @param string $customerFirstName
* @param string $customerLastName
* @param string $customerEmail
* @param string $clientIp
* @param string $type
*
* @return string
*/
public static function prepareDataApi(
$serviceId,
$amount,
$currency,
$orderId,
$paymentMethod,
$paymentMethodCode,
$successReturnUrl,
$failureReturnUrl,
$customerFirstName,
$customerLastName,
$customerEmail,
$type = 'sale',
$clientIp = ''
) {
if (!$clientIp) {
$clientIp = $_SERVER['REMOTE_ADDR'];
}
return json_encode([
'type' => $type,
'serviceId' => $serviceId,
'amount' => Util::convertAmountToFractional($amount),
'currency' => $currency,
'orderId' => (string) $orderId,
'paymentMethod' => $paymentMethod,
'paymentMethodCode' => $paymentMethodCode,
'successReturnUrl' => $successReturnUrl,
'failureReturnUrl' => $failureReturnUrl,
'clientIp' => $clientIp,
'customer' => [
'firstName' => $customerFirstName,
'lastName' => $customerLastName,
'email' => $customerEmail,
],
]);
}
/**
* @param string $string
*
* @return array
*/
public static function parseStringToArray($string)
{
$array = [];
parse_str($string, $array);
return $array;
}
/**
* Simple functions that adds signature and service_id to order array depends on Configuration::$apiMode.
*
* @param array $order
* @param string $hashMethod
*
* @return array
*/
public static function prepareOrderData($order, $hashMethod = 'sha256')
{
if (Configuration::getApiMode()) {
return $order;
}
$order['serviceId'] = Configuration::getServiceId();
$order['merchantId'] = Configuration::getMerchantId();
$order['signature'] = self::createSignature($order, Configuration::getServiceKey(), $hashMethod);
return $order;
}
/**
* @param array $orderData
* @param string $serviceKey
* @param string $hashMethod
*
* @return string|bool
*/
private static function createSignature($orderData, $serviceKey, $hashMethod = 'sha256')
{
if (!isset(self::$hashMethods[$hashMethod])
|| !is_array($orderData)) {
return false;
}
ksort($orderData);
$data = [];
foreach ($orderData as $key => $value) {
$data[] = $key . '=' . $value;
}
return self::hashSignature($hashMethod, implode('&', $data), $serviceKey) . ';' . $hashMethod;
}
/**
* @param string $hashMethod
* @param string $data
* @param string $serviceKey
*
* @return string
*/
public static function hashSignature($hashMethod, $data, $serviceKey)
{
return hash($hashMethod, $data . $serviceKey);
}
/**
* @return array
*/
public static function getSupportedCurrencies()
{
return self::$supportedCurrencies;
}
/**
* @param string $paymentMethod
*
* @return string
*/
public static function getPaymentMethod($paymentMethod)
{
if (isset(self::$paymentMethods[$paymentMethod])) {
return self::$paymentMethods[$paymentMethod];
}
return '';
}
/**
* @param string $paymentMethodCode
*
* @return string
*/
public static function getPaymentMethodCodeLogo($paymentMethodCode)
{
if (isset(self::$paymentMethodCodeLogoExt[$paymentMethodCode])) {
return self::$cdnUrl . '/img/pay/' . self::$paymentMethodCodeLogoExt[$paymentMethodCode];
}
return '';
}
/**
* @param string $paymentMethodCode
*
* @return string
*/
public static function getPaymentMethodCode($paymentMethodCode)
{
if (isset(self::$paymentMethodCodeList[$paymentMethodCode])) {
return self::$paymentMethodCodeList[$paymentMethodCode];
}
return '';
}
/**
* @param string $status
* @param string $code
* @param bool|string $debugMode
* @param string|int|null $statusBefore
* @param string|int|null $statusAfter
* @param bool|string $arrangementCreateOrder
**
*
* @return string
*/
public static function notificationResponse($status, $code = '', $debugMode = false, $statusBefore = null, $statusAfter = null, $arrangementCreateOrder = false)
{
$response = [
'status' => $status,
];
if ($debugMode && $code) {
$response['data'] = [
'code' => $code,
];
}
if ($arrangementCreateOrder) {
$response['data']['creatingOrderMode'] = $arrangementCreateOrder;
}
if ($statusBefore) {
$response['data']['statusBefore'] = $statusBefore;
}
if ($statusAfter) {
$response['data']['statusAfter'] = $statusAfter;
}
// region add additional data for some cases and set proper header
switch ($status) {
case self::NS_OK:
header('HTTP/1.1 200 OK');
break;
case self::NS_ERROR:
header('HTTP/1.1 404 Not Found');
break;
default:
break;
}
// endregion
header('Content-Type: application/json');
return json_encode($response);
}
/**
* @param $transaction
*
* @return string|void
*/
public static function calculateAmountToRefund($transaction)
{
$refundAmount = 0;
if (isset($transaction['body']['transaction']['refunds']) && $transaction['body']['transaction']['refunds']) {
foreach ($transaction['body']['transaction']['refunds'] as $refund) {
if ($refund['transaction']['status'] === 'settled') {
$refundAmount += $refund['transaction']['amount'];
}
}
}
return $transaction['body']['transaction']['amount'] - $refundAmount;
}
/**
* Verify body and signature of notification
*
* @param string $serviceKey
* @param string $serviceId
*
* @return bool|array
*/
public static function checkRequestNotification($serviceKey, $serviceId)
{
if (!isset($_SERVER['CONTENT_TYPE'], $_SERVER[Configuration::getHeaderSignatureName()]) || strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== 0) {
return self::NC_INVALID_SIGNATURE_HEADERS;
}
$payload = file_get_contents('php://input', true);
if (!$payload) {
return self::NC_EMPTY_NOTIFICATION;
}
if (!self::isJson($payload)) {
return self::NC_NOTIFICATION_IS_NOT_JSON;
}
$header = $_SERVER[Configuration::getHeaderSignatureName()];
$header = (explode(';', $header));
$algoFromNotification = explode('=', $header[3]);
$algoFromNotification = $algoFromNotification[1];
$headerSignature = explode('=', $header[2]);
if ($headerSignature[1] !== self::hashSignature($algoFromNotification, $payload, $serviceKey)) {
return self::NC_INVALID_SIGNATURE;
}
if (!self::validateNotificationJson($payload)['success']) {
return self::NC_INVALID_JSON_STRUCTURE;
}
$payloadDecoded = json_decode($payload, true);
if ($payloadDecoded['transaction']['serviceId'] !== $serviceId) {
return self::NC_SERVICE_ID_NOT_MATCH;
}
return $payloadDecoded;
}
/**
* @param string $variable
*
* @return bool
*/
public static function isJson($variable)
{
json_decode($variable);
return (json_last_error() === JSON_ERROR_NONE);
}
/**
* @param string $notification
*
* @return array
*/
private static function validateNotificationJson($notification)
{
$notification = json_decode($notification);
$schema = [
'title' => 'order',
'type' => 'object',
'properties' => [
'transaction' => [
'type' => 'object',
'properties' => [
'amount' => [
'type' => 'integer',
'minimum' => 0,
'exclusiveMinimum' => true,
],
'currency' => [
'type' => 'string',
'enum' => array_values(self::$supportedCurrencies),
],
'status' => [
'type' => 'string',
'enum' => array_values(self::getTransactionStatuses()),
],
'orderId' => [
'type' => 'string',
],
'serviceId' => [
'type' => 'string',
],
'type' => [
'type' => 'string',
'enum' => [
'sale',
'refund',
],
],
],
'required' => [
'amount',
'currency',
'status',
'orderId',
'serviceId',
'type',
],
],
],
'required' => [
'transaction',
],
];
$validator = new Validator();
$validator->validate($notification, json_decode(json_encode($schema)));
if ($validator->isValid()) {
return [
'success' => true,
'errors' => [],
];
}
$errors = [];
foreach ($validator->getErrors() as $error) {
$errors[$error['property']] = $error['message'];
}
return [
'success' => false,
'errors' => $errors,
];
}
/**
* @return array
*/
public static function getTransactionStatuses()
{
return self::$transactionStatuses;
}
/**
* @param string|array $json
*/
public static function doResponseJson($json)
{
header('Content-Type: application/json');
echo $json;
die();
}
/**
* @param string|int| $data
*
* @return false|string
*/
public static function getCid($data)
{
return hash('md5', $data);
}
/**
* @param array $payloadDecoded
* @param int $amount
* @param string $currency
*
* @return bool
*/
public static function checkRequestAmount(array $payloadDecoded, $amount, $currency)
{
return $payloadDecoded['transaction']['amount'] === $amount && $payloadDecoded['transaction']['currency'] === $currency;
}
/**
* @param string $name
* @param string $street
* @param string $city
* @param string $postalCode
* @param string $countryCode
* @param string $phone
* @param string $region
*
* @return array
*/
public static function formatAddress($name, $street, $city, $postalCode, $countryCode, $phone, $region = '')
{
return [
'name' => $name,
'street' => $street,
'city' => $city,
'postalCode' => $postalCode,
'countryCode' => $countryCode,
'phone' => $phone,
'region' => $region,
];
}
/**
* @param int $amount
*
* @return float
*/
public static function convertAmountToMain($amount)
{
return (float) round($amount / 100, 2);
}
}

View File

@@ -0,0 +1,11 @@
<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;