2namespace Bitrix\Sale\Cashbox;
7use Bitrix\Main\Localization\Loc;
8use Bitrix\Main\PhoneNumber;
9use Bitrix\Main\Web\Uri;
13Loc::loadMessages(__FILE__);
21 private const MAX_NAME_LENGTH = 128;
23 private const URL =
'https://api.yookassa.ru/v3/receipts/';
25 private const CODE_NO_VAT = 1;
26 private const CODE_VAT_0 = 2;
27 private const CODE_VAT_10 = 3;
28 private const CODE_VAT_20 = 4;
29 private const CODE_VAT_5 = 7;
30 private const CODE_VAT_7 = 8;
32 private const MARK_CODE_BASE64 = 1;
33 private const MARK_CODE_NOT_ENCODING = 2;
35 private const SETTLEMENT_TYPE_PREPAYMENT =
'prepayment';
36 private const CHECK_TYPE_PAYMENT =
'payment';
38 private const MARK_CODE_TYPE_GS1M =
'gs_1m';
41 private const MARK_MODE =
'0';
45 return Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_TITLE');
51 if (!$checkParamsResult->isSuccess())
69 if (isset($checkData[
'client_email']))
71 $fields[
'customer'][
'email'] = $checkData[
'client_email'];
74 if (isset($checkData[
'client_phone']))
79 $phoneNumber = $phoneParser->parse($checkData[
'client_phone']);
80 if ($phoneNumber->isValid())
88 $paymentMode = $paymentModeMap[$check::getType()];
89 $paymentObjectMap = $this->getPaymentObjectMap();
91 foreach ($checkData[
'items'] as $item)
100 'description' => mb_substr($item[
'name'], 0, self::MAX_NAME_LENGTH),
103 'currency' => (string)$item[
'currency'],
105 'vat_code' => (
int)
$vat,
106 'quantity' => (string)$item[
'quantity'],
107 'measure' => (
string)$measure,
108 'payment_subject' => $paymentObjectMap[$item[
'payment_object']],
109 'payment_mode' => $paymentMode,
114 if (!empty($item[
'marking_code']))
116 $receiptItem[
'mark_mode'] = self::MARK_MODE;
118 if ((
int)$markCodeEncoding === self::MARK_CODE_BASE64)
120 $receiptItem[
'mark_code_info'] = $this->buildPositionMarkCodeInBase64($item);
124 $receiptItem[
'mark_code_info'] = $this->buildPositionMarkCode($item);
128 $fields[
'items'][] = $receiptItem;
131 if ($this->needDataForSecondCheck(
$payment))
133 $fields[
'send'] =
true;
134 $fields[
'type'] = self::CHECK_TYPE_PAYMENT;
135 $fields[
'payment_id'] =
$payment->getField(
'PS_INVOICE_ID');
136 $fields[
'settlements'] = [];
138 foreach ($checkData[
'payments'] as $paymentItem)
140 $fields[
'settlements'][] = [
141 'type' => self::SETTLEMENT_TYPE_PREPAYMENT,
144 'currency' => (string)$paymentItem[
'currency'],
153 private function buildPositionMarkCode(
array $item):
array
156 self::MARK_CODE_TYPE_GS1M => $item[
'marking_code'],
160 private function buildPositionMarkCodeInBase64(
array $item):
array
163 self::MARK_CODE_TYPE_GS1M => base64_encode($item[
'marking_code']),
180 'payment_id' =>
$payment->getField(
'PS_INVOICE_ID'),
189 $headers = $this->getHeaders(
$payment);
190 foreach ($headers as
$name => $value)
192 $httpClient->setHeader(
$name, $value);
195 if (
$method === self::SEND_METHOD_HTTP_POST)
197 $data = self::encode($fields);
204 $uri->addParams($fields);
210 $result->addError(
new Error(Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_ERROR_EMPTY_RESPONSE')));
212 $errors = $httpClient->getError();
226 $result->addError(
new Error(Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_ERROR_DECODE_RESPONSE')));
248 if (isset(
$data[
'type']) &&
$data[
'type'] ===
'error')
250 $errorCode =
$data[
'code'] ??
'';
253 case 'internal_server_error':
254 case 'too_many_requests':
255 $processCheckResult->addError(
new Error(Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_ERROR_CHECK_WAIT')));
259 $processCheckResult->addError(
new Error(Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_ERROR_CHECK_PROCESSING')));
263 return $processCheckResult;
266 $processCheckResult->setData(
$data);
268 return $processCheckResult;
277 'ORDER_ID' =>
$payment->getOrderId(),
279 'order' => [
'ID' =>
'DESC'],
284 if (isset(
$data[
'type']) &&
$data[
'type'] ===
'list')
286 $checkData =
$data[
'items'] ?? [];
294 'checkId' => $checkList[0][
'ID'],
296 'MESSAGE' => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_ERROR_CHECK_NOT_FOUND'),
300 $applyCheckResult = static::applyCheckResult($externalCheck);
301 $onAfterProcessCheckResult->addErrors($applyCheckResult->getErrors());
304 foreach ($checkData as
$key => $externalCheck)
306 $checkStatus = $externalCheck[
'status'] ??
'';
307 switch ($checkStatus)
310 $externalCheck[
'error'] = [
311 'MESSAGE' => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_STATUS_CHECK_PENDING'),
317 $externalCheck[
'error'] = [
318 'MESSAGE' => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_STATUS_CHECK_CANCELLED'),
324 $externalCheck[
'checkId'] = $checkList[
$key][
'ID'];
325 $applyCheckResult = static::applyCheckResult($externalCheck);
326 if (!$applyCheckResult->isSuccess())
328 $onAfterProcessCheckResult->addErrors($applyCheckResult->getErrors());
334 $onAfterProcessCheckResult->addError(
new Error(Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_ERROR_CHECK_NOT_FOUND')));
337 return $onAfterProcessCheckResult;
344 $id =
$data[
'checkId'] ??
null;
373 if (!empty(
$data[
'registered_at']))
391 return 'YANDEX_CHECKOUT_SHOP_ID';
398 return [md5(static::getYandexToken())];
401 return parent::getKkmValue(
$service);
409 'LABEL' => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SETTINGS_SNO'),
414 'LABEL' => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SETTINGS_SNO_LABEL'),
417 1 => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SNO_OSN'),
418 2 => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SNO_UI'),
419 3 => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SNO_UIO'),
420 4 => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SNO_ENVD'),
421 5 => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SNO_ESN'),
422 6 => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SNO_PATENT'),
429 'LABEL' => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SETTINGS_MARKING'),
434 'LABEL' => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SETTINGS_MARKING_LABEL'),
435 'VALUE' => self::MARK_CODE_BASE64,
437 self::MARK_CODE_BASE64 =>
'base64',
438 self::MARK_CODE_NOT_ENCODING => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SETTINGS_MARKING_NOT_ENCODE'),
445 'LABEL' => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SETTINGS_VAT'),
451 'LABEL' => Loc::getMessage(
'SALE_CASHBOX_YOOKASSA_SETTINGS_VAT_LABEL_NOT_VAT'),
452 'VALUE' => self::CODE_NO_VAT,
457 if (Loader::includeModule(
'catalog'))
461 'EXCLUDE_VAT' =>
'N',
467 0 => self::CODE_VAT_0,
468 5 => self::CODE_VAT_5,
469 7 => self::CODE_VAT_7,
470 10 => self::CODE_VAT_10,
471 20 => self::CODE_VAT_20,
476 $value = $defaultVatList[(int)
$vat[
'RATE']] ??
'';
480 'LABEL' =>
$vat[
'NAME'] .
' (' . (
int)
$vat[
'RATE'] .
'%)',
490 'LABEL' => Loc::getMessage(
'SALE_CASHBOX_MEASURE_SUPPORT_SETTINGS_DEFAULT_VALUE'),
494 if (Loader::includeModule(
'catalog'))
496 $measuresList = \CCatalogMeasure::getList();
497 while ($measure = $measuresList->fetch())
499 $measureItems[$measure[
'CODE']] = [
501 'LABEL' => $measure[
'MEASURE_TITLE'],
508 'LABEL' => Loc::getMessage(
'SALE_CASHBOX_MEASURE_SUPPORT_SETTINGS'),
511 'ITEMS' => $measureItems,
543 private function getPaymentObjectMap():
array
587 return self::SEND_METHOD_HTTP_GET;
592 return (
bool)
$payment->getField(
'PS_INVOICE_ID');
598 'Content-Type' =>
'application/json',
599 'Idempotence-Key' => $this->getIdempotenceKey(),
604 $headers[
'Authorization'] = $this->getAuthorizationHeader(
$payment);
606 catch (\Exception $ex)
608 $headers[
'Authorization'] =
'Basic '.$this->getBasicAuthString(
$payment);
614 private function getIdempotenceKey(): string
616 return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
617 mt_rand(0, 0xffff), mt_rand(0, 0xffff),
619 mt_rand(0, 0x0fff) | 0x4000,
620 mt_rand(0, 0x3fff) | 0x8000,
621 mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
625 private function getAuthorizationHeader(Sale\Payment
$payment)
629 $token = static::getYandexToken();
630 return 'Bearer '.$token;
633 return 'Basic '.$this->getBasicAuthString(
$payment);
636 private static function isOAuth(): bool
638 return Main\Config\Option::get(
'sale',
'YANDEX_CHECKOUT_OAUTH',
false) ==
true;
641 private static function getYandexToken()
643 if (!Main\Loader::includeModule(
'seo'))
648 $authAdapter = Seo\Checkout\Service::getAuthAdapter(Seo\Checkout\Service::TYPE_YOOKASSA);
649 $token = $authAdapter->getToken();
652 $authAdapter = Seo\Checkout\Service::getAuthAdapter(Seo\Checkout\Service::TYPE_YANDEX);
653 $token = $authAdapter->getToken();
659 private function getBasicAuthString(Sale\Payment
$payment)
661 return base64_encode(
672 private static function decode(
string $data)
676 return Main\Web\Json::decode(
$data);
678 catch (Main\ArgumentException $exception)
686 return Main\Web\Json::encode(
$data, JSON_UNESCAPED_UNICODE);
static getList(array $parameters=array())
const PARAM_CALCULATION_ATTR
const PARAM_FISCAL_DOC_ATTR
const PARAM_FISCAL_DOC_NUMBER
const PARAM_FISCAL_RECEIPT_NUMBER
getValueFromSettings($name, $code)
getPaySystemSetting(Sale\Payment $payment, string $code)
checkParams(Check $check)
send(string $url, Sale\Payment $payment, array $fields, string $method=self::SEND_METHOD_HTTP_POST)
static getPaySystemCodeForKkm()
onAfterProcessCheck(Sale\Result $result, Sale\Payment $payment)
static getSettings($modelId=0)
static isOfdSettingsNeeded()
getDataForCheck(Sale\Payment $payment)
buildCheckQuery(Check $check)
static getKkmValue(Sale\PaySystem\Service $service)
static extractCheckData(array $data)
processCheckResult(Sale\Result $result)
processPrintResult(Sale\Result $result)
const PAYMENT_OBJECT_EXCISE
const PAYMENT_OBJECT_LOTTERY
const PAYMENT_OBJECT_SOCIAL_INSURANCE
const PAYMENT_OBJECT_COMPOSITE
const PAYMENT_OBJECT_LOTTERY_PRIZE
const PAYMENT_OBJECT_MEDICAL_INSURANCE_IP
const PAYMENT_OBJECT_COMMODITY_MARKING_EXCISE
const PAYMENT_OBJECT_NON_OPERATING_GAIN
const PAYMENT_OBJECT_COMMODITY_MARKING_NO_MARKING_EXCISE
const PAYMENT_OBJECT_RESORT_FEE
const PAYMENT_OBJECT_PENSION_INSURANCE_IP
const PAYMENT_OBJECT_FINE
const PAYMENT_OBJECT_PROPERTY_RIGHT
const PAYMENT_OBJECT_GAMBLING_PRIZE
const PAYMENT_OBJECT_COMMODITY_MARKING
const PAYMENT_OBJECT_COMMODITY
const PAYMENT_OBJECT_AGENT_COMMISSION
const PAYMENT_OBJECT_ANOTHER
const PAYMENT_OBJECT_DEPOSIT
const PAYMENT_OBJECT_MEDICAL_INSURANCE
const PAYMENT_OBJECT_SERVICE
const PAYMENT_OBJECT_INTELLECTUAL_ACTIVITY
const PAYMENT_OBJECT_INSURANCE_PREMIUM
const PAYMENT_OBJECT_AGENT_WITHDRAWALS
const PAYMENT_OBJECT_PAYMENT
const PAYMENT_OBJECT_EXPENSE
const PAYMENT_OBJECT_GAMBLING_BET
const PAYMENT_OBJECT_CASINO_PAYMENT
const PAYMENT_OBJECT_PENSION_INSURANCE
const PAYMENT_OBJECT_COMMODITY_MARKING_NO_MARKING
const PAYMENT_OBJECT_SALES_TAX
static getPaymentByCheck(Check $check)
static getList(array $parameters=array())
static getObjectById($id)
static addDebugInfo(?string $message, $cashboxId=null)
static getTag2108Value(?string $measureCode)
static roundPrecision($value)
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
if(file_exists($_SERVER['DOCUMENT_ROOT'] . "/urlrewrite.php")) $uri
if(empty($signedUserToken)) $key