| Current Path : /var/www/homesaver/www/bitrix/modules/fermaofd.ferma/classes/general/ferma-api/ |
| Current File : /var/www/homesaver/www/bitrix/modules/fermaofd.ferma/classes/general/ferma-api/CFermaApiService.php |
<?php
require_once __DIR__ . '/../../../const.php';
use Bitrix\Main\Web\Json;
use Bitrix\Sale\BasketBase;
use Bitrix\Sale\Order;
use Bitrix\Sale\OrderBase;
IncludeModuleLangFile(__DIR__ . '/ferma-api.php');
class CFermaApiService
{
/**
* @var string
*/
private $apiHost;
/**
* @var string
*/
private $apiLogin;
/**
* @var string
*/
private $apiPassword;
/**
* @var string
*/
private $agentInn;
/**
* @var string
*/
private $agentTaxation;
/**
* @var string
*/
private $accessToken;
/**
* @var \CFermaApiClient
*/
private $client;
/**
* @var bool
*/
private $logJson = false;
private static $instances = [];
public function __construct(
$apiHost,
$apiLogin,
$apiPassword,
$agentInn,
$agentTaxation,
$errorsEmail,
$timeout = 15
)
{
$this->apiHost = $apiHost;
$this->apiLogin = $apiLogin;
$this->apiPassword = $apiPassword;
$this->agentInn = $agentInn;
$this->agentTaxation = $agentTaxation;
$this->client = CFermaApiClient::instance($apiHost, $errorsEmail, $timeout);
$this->logJson = COption::GetOptionString(FERMAOFD_FERMA_MODULE_ID, 'receipt_json_log', 'N') === 'Y';
}
/**
* @param string $apiHost
* @param string $apiLogin
* @param string $apiPassword
* @param string $agentInn
* @param string $agentTaxation
* @param string $errorsEmail
* @return CFermaApiClient
*/
public static function instance($apiHost, $apiLogin, $apiPassword, $agentInn, $agentTaxation, $errorsEmail)
{
$hash = sha1($apiHost . '/' . $apiLogin . '/' . $apiPassword);
if (!isset(static::$instances[$hash])) {
static::$instances[$hash] = new static($apiHost, $apiLogin, $apiPassword, $agentInn, $agentTaxation, $errorsEmail);
}
return static::$instances[$hash];
}
public function getEnvId()
{
switch ($this->apiHost) {
case 'https://ferma.ofd.ru':
return 'prod';
case 'https://ferma-test.ofd.ru':
return 'demo';
default:
return null;
}
}
public function getAccessToken()
{
if (null === $this->accessToken) {
$this->renewAccessToken();
}
return $this->accessToken;
}
/**
* @param string $receiptId
* @return array
* @throws \CFermaApiException
*/
public function getElectronReceiptStatus($receiptId)
{
return $this->client->post('/api/kkt/cloud/status', ['Request' => ['ReceiptId' => $receiptId]], $this->getAccessToken());
}
/**
* @param array $electronReceiptData
* @return array
* @throws \CFermaApiException
*/
public function sendElectronReceipt($orderId, $receiptData)
{
global $DB;
if ($this->logJson) {
CMainOfdFerma::log(
GetMessage('FERMAOFD_SUBMIT_RECEIPT_TRY'), null,
Json::encode($receiptData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), 'DEBUG'
);
}
try {
$response = $this->client->post('/api/kkt/cloud/receipt', ['Request' => $receiptData], $this->getAccessToken());
} catch (CFermaApiException $e) {
throw new \RuntimeException(sprintf(GetMessage('FERMAOFD_FAILED_SENDING') . ': %s.', $e->getMessage()));
}
if (empty($response)) {
throw new \RuntimeException(GetMessage('FERMAOFD_FAILED_SENDING_FERMA_EMPTY_RESPONSE'));
}
$total = array_reduce(
$receiptData['CustomerReceipt']['Items'],
static function ($result, $item) {
return $result + $item['Amount'];
},
0
);
if ($response['Status'] === 'Failed') {
$description = null;
if (isset($response['Data']['ValidationErrors'])) {
$description = implode('<br>', array_map(static function ($error) {
return $error['Description'];
}, $response['Data']['ValidationErrors']));
}
COfdFermaReceipt::Add([
'SITE_ID' => SITE_ID,
'RECEIPT_ID' => null,
'ORDER_ID' => $orderId,
'PAYMENT_METHOD_ID' => $receiptData['CustomerReceipt']['Items'][0]['PaymentMethod'],
'CONTENT' => Json::encode($receiptData),
'TYPE' => $receiptData['Type'], // Income, IncomeReturn, IncomePrepayment, IncomeReturnPrepayment
'TOTAL_PRICE' => $total,
'ENV' => $this->getEnvId(),
'=DATE_CREATE' => $DB->GetNowFunction(),
'=DATE_UPDATE' => $DB->GetNowFunction(),
'STATUS_ID' => -1,
'STATUS_NAME' => 'ERROR',
'STATUS_MESSAGE' => $response['Error']['Message'] ?? '������ �������� ����',
'DESCRIPTION' => $description,
]);
} else {
$id = COfdFermaReceipt::Add([
'SITE_ID' => SITE_ID,
'RECEIPT_ID' => $response['Data']['ReceiptId'],
'ORDER_ID' => $orderId,
'PAYMENT_METHOD_ID' => $receiptData['CustomerReceipt']['Items'][0]['PaymentMethod'],
'CONTENT' => Json::encode($receiptData),
'TYPE' => $receiptData['Type'], // Income, IncomeReturn, IncomePrepayment, IncomeReturnPrepayment
'TOTAL_PRICE' => $total,
'ENV' => $this->getEnvId(),
'=DATE_CREATE' => $DB->GetNowFunction(),
'=DATE_UPDATE' => $DB->GetNowFunction(),
]);
CAgent::AddAgent(
'COfdFermaAgent::checkReceiptStatusAgent(' . Json::encode($id) . ');',
FERMAOFD_FERMA_MODULE_ID,
'Y', 10
);
}
return $response;
}
public function prepareElectronReceipt($type, Order $order)
{
list($email, $phone) = Ferma_getCustomerContacts($order->getId());
$deliveryInfo = Ferma_getDeliveryInfo($order);
switch ($type) {
case 'IncomeReturn':
$paymentMethod = COption::GetOptionString(FERMAOFD_FERMA_MODULE_ID, 'receipt_refund_default_method', '4');
break;
case 'IncomePrepayment':
$paymentMethod = COption::GetOptionString(FERMAOFD_FERMA_MODULE_ID, 'receipt_prepayment_default_method', '1');
break;
default:
case 'Income':
$paymentMethod = COption::GetOptionString(FERMAOFD_FERMA_MODULE_ID, 'receipt_payment_default_method', '4');
break;
}
$siteRes = CSite::GetByID($order->getSiteId());
$site = $siteRes->Fetch();
return $this->createElectronReceipt(
$type, $email, $phone,
1, //
$order->getId(),
$order->getField('DATE_PAYED'),
$order->getPaymentCollection(),
$this->formatOrderItems($paymentMethod, $order->getBasket(), $deliveryInfo),
$site ? $site['SERVER_NAME'] : null
);
}
public static function createInvoiceId($orderId, $type)
{
// unique invoice id
/** @noinspection NonSecureUniqidUsageInspection */
return $orderId . '/' . $type . '/' . uniqid();
}
public function createElectronReceipt(
$type,
$email,
$phone,
$paymentType,
$orderId,
$orderDate,
$payments,
$items,
$billAddress
)
{
$date = new \DateTime;
if ($type === 'Income') {
$date = new \DateTime(empty($orderDate) ? 'now' : $orderDate);
}
if ($payments === null) {
$PaymentItems = array_map(function ($item) use ($type) {
return [
'PaymentType' => $type === 'IncomePrepayment' ? '2' : '1',
'Sum' => $item['Amount'],
];
}, $items);
} elseif ($payments instanceof \Bitrix\Sale\PaymentCollection) {
/*$PaymentItems = array_map(function ($payment) use ($type) {
return [
'PaymentType' => $type === 'IncomePrepayment' ? '2' : $this->getPaymentMethod($payment['PAY_SYSTEM_ID']),
'Sum' => $payment['SUM'],
];
}, array_filter($payments->toArray(), static function ($payment) {
return $payment['PAID'] === 'Y' && $payment['IS_RETURN'] === 'N';
}));*/
} else {
$PaymentItems = [];
}
return [
'Inn' => $this->agentInn,
'Type' => $type,
'InvoiceId' => static::createInvoiceId($orderId, $type),
'LocalDate' => $date->format('Y-m-d\TH:i:s'),
'CustomerReceipt' => [
'BillAddress' => $billAddress,
'Email' => $email,
'Phone' => $phone,
'TaxationSystem' => $this->agentTaxation,
'PaymentType' => $paymentType ? $paymentType : null,
'Items' => array_values($items),
'PaymentItems' => $PaymentItems,
],
];
}
private $paymentMethods = [];
private function getPaymentMethod($paymentSystemId)
{
if (isset($this->paymentMethods[$paymentSystemId])) {
return $this->paymentMethods[$paymentSystemId];
}
return $this->paymentMethods[$paymentSystemId] = COption::GetOptionString(FERMAOFD_FERMA_MODULE_ID, 'payment_method_' . $paymentSystemId, '1');
}
private function formatOrderItems(string $paymentMethod, BasketBase $basket, $deliveryInfo = null)
{
$result = [];
$productsId = [];
/** @var Order $order */
$order = $basket->getOrder();
/** @var \Bitrix\Sale\BasketItem $item */
foreach ($basket as $item) {
$productsId[$item->getProductId()] = true;
}
if (empty($productsId)) {
return $result;
}
$products = Ferma_getProducts(array_keys($productsId));
$vats = Ferma_getVats();
$defaultPaymentType = COption::GetOptionString(FERMAOFD_FERMA_MODULE_ID, 'payment_default_type', '1');
$defaultVat = COption::GetOptionString(FERMAOFD_FERMA_MODULE_ID, 'payment_default_vat', 'VatNo');
/** @var \Bitrix\Sale\BasketItem $item */
foreach ($basket as $item) {
$price = $item->getPrice();
$quantity = $item->getQuantity();
$vatId = $products[$item->getProductId()]['VAT_ID'];
$paymentType = $products[$item->getProductId()]['PROPERTIES']['OFD_FERMA_PAYMENT_TYPE']['VALUE_XML_ID'];
$markingCode = $order
? $this->findMarkingCodeInOrder($order, $item->getProductId())
: false;
if ($markingCode) {
for ($i = 0; $i < $quantity; $i++) {
$result[] = [
'Label' => $item->getField('NAME'),
'Price' => $price,
'Quantity' => 1,
'Amount' => $price * $quantity,
'Vat' => empty($vatId) ? $defaultVat : CVatCollection::determinateVat($vats[$vatId]),
'PaymentMethod' => $paymentMethod,
'PaymentType' => $paymentType ?: $defaultPaymentType,
'MarkingCodeData' => $markingCode ? [
'Type' => 'UNKNOWN_PRODUCT_CODE',
'Code' => $markingCode,
] : null,
];
$markingCode = $this->findMarkingCodeInOrder($order, $item->getProductId());
}
} else {
$result[] = [
'Label' => $item->getField('NAME'),
'Price' => $price,
'Quantity' => $quantity,
'Amount' => $price * $quantity,
'Vat' => empty($vatId) ? $defaultVat : CVatCollection::determinateVat($vats[$vatId]),
'PaymentMethod' => $paymentMethod,
'PaymentType' => $paymentType ?: $defaultPaymentType,
];
}
}
$deliveryReceipt = COption::GetOptionString(FERMAOFD_FERMA_MODULE_ID, 'payment_delivery', 'Include');
if ($deliveryReceipt === 'Include' && !empty($deliveryInfo) && !empty($deliveryInfo['PRICE'])) {
$result[] = [
'Label' => $deliveryInfo['NAME'],
'Price' => $deliveryInfo['PRICE'],
'Quantity' => 1,
'Amount' => $deliveryInfo['PRICE'],
'Vat' => $defaultVat,
'PaymentMethod' => $paymentMethod,
'PaymentType' => '4',
];
}
return $result;
}
private $usedCodes = [];
private function findMarkingCodeInOrder(Order $order, $productId)
{
$shipmentCollection = $order->getShipmentCollection();
/** @var \Bitrix\Sale\Shipment $shipment */
foreach ($shipmentCollection as $shipment) {
$itemCollection = $shipment->getShipmentItemCollection();
/** @var \Bitrix\Sale\ShipmentItem $item */
foreach ($itemCollection as $item) {
$itemStoreCollection = $item->getShipmentItemStoreCollection();
$basketItem = $item->getBasketItem();
if (!$basketItem || $productId !== $basketItem->getProductId()) {
continue;
}
/** @var \Bitrix\Sale\ShipmentItemStore $itemStore */
foreach ($itemStoreCollection as $itemStore) {
$result = $itemStore->getMarkingCode();
if (!$result || isset($this->usedCodes[$result])) {
continue;
}
$this->usedCodes[$result] = true;
return $result;
}
}
}
return null;
}
private function renewAccessToken()
{
$response = $this->client->post('/api/Authorization/CreateAuthToken', [
'Login' => $this->apiLogin,
'Password' => $this->apiPassword,
]);
if ($response['Status'] === 'Success') {
$this->accessToken = $response['Data']['AuthToken'];
} elseif ($response['Status'] === 'Failed' && isset($response['Error']['Message'])) {
throw new CFermaApiException(sprintf('Api return failed response: %s.', $response['Error']['Message']));
} else {
throw new CFermaApiException('Api return invalid response.');
}
}
}
function Ferma_getProducts($productsId)
{
CModule::IncludeModule('catalog');
$result = [];
$products = \Bitrix\Catalog\ProductTable::GetList([
'select' => ['*'],
'filter' => [
'=ID' => $productsId,
],
]);
$itemsId = [];
$flatItemsId = [];
$props = [];
while ($product = $products->fetch()) {
$result[$product['ID']] = $product;
$data = CCatalogSku::GetProductInfo($product['ID']);
if ($data) {
if (!isset($itemsId[$data['IBLOCK_ID']])) {
$itemsId[$data['IBLOCK_ID']] = [];
}
$itemsId[$data['IBLOCK_ID']][$product['ID']] = $data['ID'];
$flatItemsId[$data['ID']] = $product['ID'];
$props[$data['ID']] = [];
}
}
foreach ($itemsId as $iBlockId => $map) {
CIBlockElement::GetPropertyValuesArray($props, $iBlockId, [
'ID' => array_values($map),
]);
}
foreach ($props as $itemId => $propsCollection) {
$productId = $flatItemsId[$itemId];
if (!isset($result[$productId])) {
continue;
}
$result[$productId]['PROPERTIES'] = $propsCollection;
}
return $result;
}
function Ferma_getVats()
{
$result = [];
$vats = CCatalogVat::GetList();
while ($product = $vats->NavNext()) {
$result[$product['ID']] = $product;
}
return $result;
}
function Ferma_getCustomerContacts($orderId)
{
$email = null;
$phone = null;
$props = (new CSaleOrderPropsValue())->GetList([], [
'ORDER_ID' => $orderId,
'CODE' => ['EMAIL', 'PHONE'],
]);
while ($item = $props->Fetch()) {
if ($item['CODE'] === 'EMAIL') {
$email = $item['VALUE'];
}
if ($item['CODE'] === 'PHONE') {
$phone = $item['VALUE'];
}
}
return [$email, $phone ? Ferma_clearPhone($phone) : null];
}
function Ferma_getDeliveryInfo(OrderBase $order)
{
$price = $order->getDeliveryPrice();
if (empty($price)) {
return null;
}
return [
'NAME' => GetMessage('FERMAOFD_DELIVERY'),
'PRICE' => $price,
];
}
function Ferma_clearPhone($phone)
{
return preg_replace('/[^0-9+]+/', '', $phone);
}