first commit

This commit is contained in:
2025-08-02 16:30:27 +02:00
commit 23646bfcee
14851 changed files with 1750626 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
<?php
namespace KupShop\AgeVerifyBundle\Admin\Tabs;
use KupShop\AdminBundle\Admin\WindowTab;
class AgeVerifySettingsTab extends WindowTab
{
protected $title = 'flapAgeVerify';
protected $template = 'AgeVerifySettingsTab.tpl';
public static function getTypes()
{
return [
'settings' => 1,
];
}
/**
* @return string
*/
public function getLabel()
{
return translate($this->title, 'ageVerifyUsersTab');
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace KupShop\AgeVerifyBundle\Admin\Tabs;
use KupShop\AdminBundle\Admin\WindowTab;
use KupShop\AgeVerifyBundle\Utils\AgeVerifyUtil;
use Query\Operator;
class AgeVerifyUsersTab extends WindowTab
{
protected $title = 'flapAgeVerify';
protected $template = 'AgeVerifyUsersTab.tpl';
/** @required */
public AgeVerifyUtil $ageVerifyUtil;
public static function getTypes()
{
return [
'users' => 1,
];
}
public function handleUpdate()
{
parent::handleUpdate();
$data = getVal('age_verification');
$this->ageVerifyUtil->setVerificationData($data['legal_age'],
$data['verification_type'],
null,
$this->getID());
// Smazat OAUTH vazbu, když ruším ověření
if ($data['verification_type'] === '' && findModule(\Modules::USER_OAUTH)) {
sqlQueryBuilder()->delete('users_provider_ids')
->where(Operator::equals(['id_user' => $this->getID(), 'provider' => 'bankid']))
->execute();
}
}
public function handleRemoveVerificationOrder()
{
sqlQueryBuilder()->update('users_age_verification')
->directValues(['id_order' => null])
->where(Operator::equals(['id_user' => $this->getID()]))
->execute();
$this->returnOK('Objednávka odpárována.');
}
public function getVars($smarty_tpl_vars)
{
$vars = parent::getVars($smarty_tpl_vars);
$vars['verification_types'] = array_merge([null => '----'], AgeVerifyUtil::$VERIFICATION_TYPES);
$vars['age_verification'] = $this->selectSQL('users_age_verification', ['id_user' => $this->getID()])->fetchAssociative();
if (findModule(\Modules::USER_OAUTH)) {
$vars['oauth'] = $this->selectSQL('users_provider_ids', ['id_user' => $this->getID(), 'provider' => 'bankid'])->fetchAssociative();
}
return $vars;
}
/**
* @return string
*/
public function getLabel()
{
return translate($this->title, 'ageVerifyUsersTab');
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\Admin\Tabs;
class PosAgeVerifyUsersTab extends AgeVerifyUsersTab
{
protected $title = 'flapAgeVerify';
public static function getTypes(): array
{
return [
'PosUsers' => 1];
}
/**
* @return string
*/
public function getLabel()
{
return translate($this->title, 'ageVerifyUsersTab');
}
}

View File

@@ -0,0 +1,22 @@
<?php
$txt_str['ageVerifySettingsTab'] = [
'flapAgeVerify' => 'Ověření plnoletosti',
'enabled' => 'Aktivovat omezení objednávek',
'clientId' => 'Client ID',
'clientSecret' => 'Client Secret',
'ageVerifySettings' => 'Nastavení ověření věku',
'bankIdSettings' => 'Nastavení Bankovní identity',
'bank_id_enabled' => 'Aktivovat bankovní identitu',
'bank_id_sandbox' => 'Použít dev server',
'packageSettings' => 'Nastavení ověření zásilky',
'product' => 'Produkt',
'package_enabled' => 'Aktivovat ověření zásilkou',
'verifaceSettings' => 'Nastavení VeriFace',
'veriface_enabled' => 'Aktivovat VeriFace',
'adultoSettings' => 'Nastavení Adulto',
'adulto_enabled' => 'Aktivovat Adulto',
'api_key' => 'API klíč',
'private_key' => 'Privátní klíč',
'public_key' => 'Veřejný klíč',
];

View File

@@ -0,0 +1,9 @@
<?php
$txt_str['ageVerifyUsersTab'] = [
'flapAgeVerify' => 'Ověření plnoletosti',
'birthdate' => 'Datum narození',
'legalAge' => 'Plnoletý',
'legalAgeUpdated' => 'Aktualizace',
'verificationType' => 'Zdroj ověření',
];

View File

@@ -0,0 +1,108 @@
<div id="flapAgeVerify" class="tab-pane fade in boxFlex">
<div class="row bottom-space">
<div class="col-md-12">
<h1 class="h4 main-panel-title">{'ageVerifySettings'|translate:"ageVerifySettingsTab"}</h1>
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'enabled'|translate:"ageVerifySettingsTab"}</label>
</div>
<div class="col-md-4">
{print_toggle name="age_verify][enabled" value={$body.data.age_verify.enabled}}
</div>
</div>
{* Zásilka *}
<div class="row bottom-space">
<div class="col-md-12">
<h1 class="h4 main-panel-title">{'packageSettings'|translate:"ageVerifySettingsTab"}</h1>
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'package_enabled'|translate:"ageVerifySettingsTab"}</label>
</div>
<div class="col-md-4">
{print_toggle name="age_verify][package][enabled" value={$body.data.age_verify.package.enabled} numeric = 1}
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'product'|translate:"ageVerifySettingsTab"}</label>
</div>
<div class="col-md-8">
<select class="selecter" name="data[age_verify][package][product]"
data-type="products" data-preload="products" data-autocomplete="Products">
{if $body.data.age_verify.package.product}
<option selected value="{$body.data.age_verify.package.product}">{$body.data.age_verify.package.product}</option>
{/if}
</select>
</div>
</div>
{* VeriFace *}
<div class="row bottom-space">
<div class="col-md-12">
<h1 class="h4 main-panel-title">{'verifaceSettings'|translate:"ageVerifySettingsTab"}</h1>
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'veriface_enabled'|translate:"ageVerifySettingsTab"}</label>
</div>
<div class="col-md-4">
{print_toggle name="age_verify][veriface][enabled" value={$body.data.age_verify.veriface.enabled} numeric = 1}
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'api_key'|translate:"ageVerifySettingsTab"}</label>
</div>
<div class="col-md-8">
<input type="text" class="form-control input-sm" name="data[age_verify][veriface][api_key]"
value="{$body.data.age_verify.veriface.api_key}">
</div>
</div>
{* Adulto *}
{block "adulto"}
<div class="row bottom-space">
<div class="col-md-12">
<h1 class="h4 main-panel-title">{'adultoSettings'|translate:"ageVerifySettingsTab"}</h1>
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'adulto_enabled'|translate:"ageVerifySettingsTab"}</label>
</div>
<div class="col-md-4">
{print_toggle name="age_verify][adulto][enabled" value={$body.data.age_verify.adulto.enabled} numeric = 1}
</div>
</div>
{/block}
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'private_key'|translate:"ageVerifySettingsTab"}</label>
</div>
<div class="col-md-8">
<input type="text" class="form-control input-sm" name="data[age_verify][adulto][private_key]"
value="{$body.data.age_verify.adulto.private_key}">
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'public_key'|translate:"ageVerifySettingsTab"}</label>
</div>
<div class="col-md-8">
<input type="text" class="form-control input-sm" name="data[age_verify][adulto][public_key]"
value="{$body.data.age_verify.adulto.public_key}">
</div>
</div>
</div>

View File

@@ -0,0 +1,46 @@
<div id="flapAgeVerify" class="tab-pane fade in boxFlex">
<legend>{'flapAgeVerify'|translate: "ageVerifyUsersTab"}</legend>
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'legalAge'|translate: "ageVerifyUsersTab"}</label>
</div>
<div class="col-md-4">
{print_select name="age_verification[legal_age]" var=[0 => '','N' => 'Ne','Y' => 'Ano'] selected=$tab.data.age_verification.legal_age}
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'legalAgeUpdated'|translate: "ageVerifyUsersTab"}</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control input-sm" value="{$tab.data.age_verification.date_updated|format_datetime:"admin"}" readonly>
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'verificationType'|translate: "ageVerifyUsersTab"}</label>
</div>
<div class="col-md-4">
{print_select name="age_verification[verification_type]" var=$tab.data.verification_types selected=$tab.data.age_verification.verification_type}
</div>
</div>
{if $tab.data.age_verification.id_order}
<div class="form-group">
<div class="col-md-4 control-label">
<label>Uživatel již vytvořil ověřovací <a href="javascript:nw('orders', {$tab.data.age_verification.id_order})">objednávku.</a></label>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-danger" name="acn" value="removeVerificationOrder">Odpárovat objednávku</button>
</div>
</div>
{/if}
{if $tab.data.oauth}
<div class="form-group">
<div class="col-md-5 control-label">
<label>Uživatel má propojený účet na Bankovní identitu.</label>
</div>
</div>
{/if}
</div>

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle;
class AgeVerifyBundle extends \Symfony\Component\HttpKernel\Bundle\Bundle
{
}

View File

@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\Controller;
use KupShop\AgeVerifyBundle\Utils\AgeVerifyUtil;
use KupShop\AgeVerifyBundle\Utils\BankIdUtil;
use KupShop\AgeVerifyBundle\Utils\VerifaceUtil;
use KupShop\AgeVerifyBundle\View\AdultoView;
use KupShop\AgeVerifyBundle\View\AgeVerifyView;
use KupShop\AgeVerifyBundle\View\PackageOrderView;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\KupShopBundle\Exception\RedirectException;
use KupShop\KupShopBundle\Routing\TranslatedRoute;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Service\Attribute\Required;
class AgeVerifyController extends \Symfony\Bundle\FrameworkBundle\Controller\AbstractController
{
#[Required]
public BankIdUtil $bankIdUtil;
#[Required]
public UserContext $userContext;
#[Required]
public AgeVerifyUtil $ageVerifyUtil;
#[TranslatedRoute(path: '/#account#/#age_verify_url:AgeVerify#', name: 'ageVerify')]
public function ageVerifyAction(AgeVerifyView $view)
{
return $view->getResponse();
}
#[TranslatedRoute(path: '/#account#/#age_verify_url:AgeVerify#/#bank_id_url:AgeVerify#', name: 'ageVerifyBankId')]
public function bankIdAction(Request $request)
{
$url = $this->bankIdUtil->getRedirectUrl();
return new RedirectResponse($url);
}
#[TranslatedRoute(path: '/#account#/#age_verify_url:AgeVerify#/#package_url:AgeVerify#', name: 'ageVerifyPackage')]
public function packageOrderAction(Request $request, PackageOrderView $view)
{
$userId = $this->userContext->getActiveId();
if ($this->ageVerifyUtil->isLegalAged($userId)) {
throw new RedirectException(path('ageVerify'));
}
if ($request->isMethod('POST')) {
if ($order = $view->submitForm()) {
return new RedirectResponse(path('kupshop_content_orders_order',
['id' => $order->id, 'cf' => $order->getSecurityCode(), 'status' => 1]));
}
}
return $view->getResponse();
}
#[TranslatedRoute(path: '/#account#/#age_verify_url:AgeVerify#/#adulto_url:AgeVerify#', name: 'ageVerifyAdulto')]
public function adultoOrderAction(Request $request, AdultoView $view): Response
{
$userId = $this->userContext->getActiveId();
if ($this->ageVerifyUtil->isLegalAged($userId)) {
throw new RedirectException(path('ageVerify'));
}
if ($request->isMethod('POST')) {
if ($errorMsg = $view->submitForm()['error'] ?? null) {
addUserMessage($errorMsg);
}
return new RedirectResponse(path('ageVerify'));
}
return $view->getResponse();
}
#[Route('/_bankid')]
public function webhookBankIdAction(Request $request)
{
$code = $request->get('code');
$this->bankIdUtil->fetchData($code);
return new RedirectResponse(path('ageVerify'));
}
#[TranslatedRoute(path: '/#age_verify_url:AgeVerify#/#veriface_url:AgeVerify#', name: 'ageVerifyVeriface')]
public function verifaceAction(VerifaceUtil $verifaceUtil, SessionInterface $session, Request $request)
{
if ($request->get('source') == 'cart') {
$session->set('redirectToCart', $request->headers->get('referer'));
}
return new RedirectResponse($verifaceUtil->getRedirectUrl());
}
#[Route('/_veriface')]
public function webhookVerifaceAction(VerifaceUtil $verifaceUtil, Request $request, SessionInterface $session)
{
$verifaceUtil->saveData($request->get('sessionId'));
if ($redirect = $session->remove('redirectToCart')) {
return new RedirectResponse($redirect);
}
return new RedirectResponse(path('ageVerify'));
}
#[Route('/_veriface_notification', methods: ['POST'])]
public function verifaceNotificationAction(VerifaceUtil $verifaceUtil, Request $request)
{
$data = json_decode($request->getContent() ?? '', true);
$sessionId = $data['sessionId'];
$email = $data['referenceId'];
// Řeším jen v případě, kdy je status konečný - https://docs.veriface.eu/sk/docs/verification-statuses
if (in_array($data['status'], ['VERIFIED', 'REFUSED', 'VERIFIED_WARNING', 'CANCELLED', 'VERIFIED_MANUAL', 'REFUSED_MANUAL', 'EXPIRED', 'ERROR'])) {
$verifaceUtil->saveData($sessionId, \User::createFromLogin($email));
}
return new Response('OK');
}
}

View File

@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\EventSubscribers;
use KupShop\AgeVerifyBundle\Utils\AdultoUtil;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\OrderingBundle\Event\CartEvent;
use KupShop\OrderingBundle\Exception\CartValidationException;
use Query\Operator;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\Service\Attribute\Required;
class CartSubscriber implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
{
#[Required]
public UserContext $userContext;
#[Required]
public AdultoUtil $adultoUtil;
#[Required]
public RequestStack $requestStack;
protected int $lastStepIndex = 2;
public static function getSubscribedEvents()
{
return [
CartEvent::CHECK_CART_ITEMS => [
['checkLegalAge', 200],
],
CartEvent::CHECK_CART => [
['checkCartLegalAge', 200],
],
];
}
public function checkCartLegalAge(CartEvent $event)
{
$cfg = \Settings::getDefault();
if (($cfg['age_verify']['enabled'] ?? 'N') == 'N') {
return;
}
$cart = $event->getCart();
if ($cart->getActualStepIndex() != $this->lastStepIndex) {
return;
}
if ($cart->getData('age_verification')) {
return;
}
$mainRequest = $this->requestStack->getMainRequest();
if (!empty($cfg['age_verify']['adulto']['enabled'])) {
$result = $this->adultoUtil->checkAdultoUid($mainRequest->get('adultocz-uid') ?? '');
if (!$result['success']) {
throw new CartValidationException(translate('registered_with_legal_age', 'AgeVerify'));
} else {
$cart->setData('age_verification', $result['data']);
return;
}
}
$this->checkAgeRegisteredUser($cart);
}
public function checkLegalAge(CartEvent $event)
{
$cfg = \Settings::getDefault();
if (($cfg['age_verify']['enabled'] ?? 'N') == 'N') {
return;
}
$cart = $event->getCart();
if ($cart->getActualStepIndex() != $this->lastStepIndex) {
return;
}
if ($cart->getData('age_verification')) {
return;
}
if (!empty($cfg['age_verify']['adulto']['enabled'])) {
return;
}
$this->checkAgeRegisteredUser($cart);
}
protected function checkAgeRegisteredUser(\Cart $cart): void
{
$user = $this->userContext->getActive();
if (!$user) {
throw new CartValidationException(translate('registered_with_legal_age', 'AgeVerify'));
}
$legalAge = sqlQueryBuilder()->select('legal_age')->from('users_age_verification')->where(Operator::equals(['id_user' => $user->id]))->execute()->fetchOne();
if (empty($legalAge)) {
throw new CartValidationException(translate('dont_verify', 'AgeVerify'));
} elseif ($legalAge == 'N') {
throw new CartValidationException(translate('not_legal_age', 'AgeVerify'));
}
if ($deliveryBirthdate = $cart->getData('delivery_birthdate')) {
$date = \DateTime::createFromFormat('Y-m-d', $deliveryBirthdate)->add(\DateInterval::createFromDateString('+18YEARS'));
if ($date > (new \DateTime())) {
throw new CartValidationException(translate('delivery_legal_age', 'AgeVerify'));
}
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\EventSubscribers;
use KupShop\OrderingBundle\Event\OrderDeliveryEvent;
use Query\Operator;
class OrderEventSubscriber implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
OrderDeliveryEvent::class => [
['updateLegalAge', 200],
],
];
}
public function updateLegalAge(OrderDeliveryEvent $event)
{
$orderId = $event->getOrder()->id;
sqlQueryBuilder()->update('users_age_verification')
->directValues(['legal_age' => 'Y'])
->where(Operator::equals(['id_order' => $orderId]))
->execute();
}
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\Exception;
class AgeVerificationException extends \Exception
{
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\Exception;
class BankIdException extends \Exception
{
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\Exception;
class VeriFaceException extends \Exception
{
}

View File

@@ -0,0 +1,3 @@
bankid:
resource: "@AgeVerifyBundle/Controller"
type: annotation

View File

@@ -0,0 +1,7 @@
services:
_defaults:
autowire: true
autoconfigure: true
KupShop\AgeVerifyBundle\:
resource: ../../{Utils,Controller,View,Admin/Tabs,EventSubscribers}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\Resources\script;
use KupShop\AdminBundle\Util\Script\Script;
use KupShop\AgeVerifyBundle\Utils\VerifaceUtil;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
class VerifaceDebugScript extends Script
{
protected static $defaultParameters = [
'sessionId' => null,
'userId' => null,
];
protected function run(array $arguments)
{
$verifaceUtil = ServiceContainer::getService(VerifaceUtil::class);
$verifaceUtil->saveData($arguments['sessionId'], \User::createFromId($arguments['userId']));
}
}
return VerifaceDebugScript::class;

View File

@@ -0,0 +1,22 @@
<?php
use KupShop\AgeVerifyBundle\Utils\AgeVerifyUtil;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\Contexts;
function smarty_function_get_age_verification($params, &$smarty)
{
$result = [];
$userId = Contexts::get(UserContext::class)->getActiveId();
if ($userId) {
$result = ServiceContainer::getService(AgeVerifyUtil::class)->getVerificationData($userId);
}
if (!empty($params['assign'])) {
$smarty->assign($params['assign'], $result);
} else {
return $result;
}
}

View File

@@ -0,0 +1,21 @@
{extends "index.tpl"}
{block content}
<div class="page-package-verification">
{block "page-content"}
<div class="form-verification">
<form method="post" action="{path('ageVerifyAdulto')}" id="cart">
{block "form"}
<div class="adulto-cz" data-sitekey="{$body.adulto_public_key}"></div>
<input type="hidden" name="customer_age" value="18" data-adultocz="customer_age">
<button type="submit" name="Odeslat">Potvrdit ověření</button>
{/block}
</form>
</div>
{/block}
</div>
{/block}
{block "body" append}
<script async src="https://api.js.m2a.cz/api.js"></script>
{/block}

View File

@@ -0,0 +1,113 @@
{extends "account/account-wrapper.tpl"}
{block "account-content"}
<div class="container">
{get_age_verification assign="age_verification"}
<div class="age-verification-wrapper">
<h3 class="text-center">Možnosti ověření</h3>
<div class="banners-verification">
<div class="verify-wrapper">
{$verified = $age_verification.legal_age == 'Y' or $age_verification.id_order != null}
{if $ctrl.active_language == "cs"}
{if $dbcfg.age_verify.adulto.enabled}
<div
class="verify-item verify-adulto {if $age_verification.verification_type|in_array:['adulto'] and $age_verification.legal_age == 'Y'}disabled{/if} {if $verified}account_verified{/if}">
{if $age_verification.verification_type == 'adulto' and $age_verification.legal_age == 'Y'}
<div class="verified">
<span class="fc icons_check-outline">{t}Ověřeno{/t}</span>
</div>
{/if}
<div class="image-wrapper">
<div class="image">
<img src="{static_url url="/templates/images/veriface-bg.png"}" width="436" height="188" loading="lazy"
alt="Adulto"
class="img-responsive">
</div>
</div>
<div class="text">
<div class="descr">
<h5>{t}Dokladem totožnosti{/t}</h5>
<p>{t}Využijte možnosti ověření službou Adulto.{/t}</p>
</div>
<a href="{path("ageVerifyAdulto")}" class="btn btn-primary" {if $verified}disabled{/if}>Službou Adulto</a>
</div>
</div>
{/if}
{if $dbcfg.oauth.bank_id.enabled}
<div
class="verify-item verify-bankid {if $age_verification.verification_type|in_array:['mojeid', 'store', 'package', 'veriface'] and $age_verification.legal_age == 'Y'}disabled{/if} {if $verified}account_verified{/if}">
{if $age_verification.verification_type == 'bankId' and $age_verification.legal_age == 'Y'}
<div class="verified">
<span class="fc icons_check-outline">{t}Ověřeno{/t}</span>
</div>
{/if}
<div class="image-wrapper">
<div class="image">
<img src="{static_url url="/templates/images/bankid-bg.png"}" width="171" height="35" loading="lazy"
alt="BankID"
class="bankid img-responsive">
</div>
</div>
<div class="text">
<div class="descr">
<h5>{t}Bankovní identitou{/t}</h5>
<p>{t}Využijte možnosti ověření bankovní identitou.{/t}</p>
</div>
<a href="/login-bind/bankid" class="btn btn-primary" {if $verified}disabled{/if}>BankID</a>
</div>
</div>
{/if}
{if $dbcfg.age_verify.veriface.enabled}
<div
class="verify-item verify-veriface {if $age_verification.verification_type|in_array:['mojeid', 'store', 'package', 'bankId'] and $age_verification.legal_age == 'Y'}disabled{/if} {if $verified}account_verified{/if}">
{if $age_verification.verification_type == 'veriface' and $age_verification.legal_age == 'Y'}
<div class="verified">
<span class="fc icons_check-outline">{t}Ověřeno{/t}</span>
</div>
{/if}
<div class="image-wrapper">
<div class="image">
<img src="{static_url url="/templates/images/veriface-bg.png"}" width="436" height="188" loading="lazy"
alt="Veriface"
class="img-responsive">
</div>
</div>
<div class="text">
<div class="descr">
<h5>{t}Dokladem totožnosti{/t}</h5>
<p>{t}Využijte možnosti ověření dokladem totožnosti.{/t}</p>
</div>
<a href="{path("ageVerifyVeriface")}" class="btn btn-primary" {if $verified}disabled{/if}>Doklad totožnosti</a>
</div>
</div>
{/if}
{if $dbcfg.age_verify.package.enabled}
<div
class="verify-item verify-package {if $age_verification.verification_type|in_array:['mojeid', 'bankId', 'store', 'veriface'] and $age_verification.legal_age == 'Y'}disabled{/if} {if $verified}account_verified{/if}">
{if $age_verification.verification_type == 'package' and $age_verification.legal_age == 'Y'}
<div class="verified">
<span class="fc icons_check-outline">{t}Ověřeno{/t}</span>
</div>
{/if}
<img src="{static_url url="/templates/images/package.png"}" width="436" height="188" loading="lazy"
alt="Ověření zásilkou"
class="package img-responsive">
<div class="text">
<div class="descr">
<h5>{t}Ověření zásilkou{/t}</h5>
<p>Vaši identitu umíme ověřit také zasláním dárkového poukazu na adresu.</p>
</div>
<a href="{path('ageVerifyPackage')}" class="btn btn-primary" {if $verified}disabled{/if}>Balíkem</a>
</div>
</div>
{/if}
{/if}
</div>
</div>
</div>
<div class="age-verify-text">
{insert_page code="verify"}
</div>
</div>
{/block}

View File

@@ -0,0 +1,100 @@
{extends "index.tpl"}
{block content}
<div class="page-package-verification">
{block "page-content"}{/block}
{get_smarty assign='smarty_object'}
{foreach $body.deliveries as $id => $delivery}
{$delivery->getInitTemplate($smarty_object)}
{/foreach}
<div class="form-verification">
<form method="post" action="{path('ageVerifyPackage')}" id="cart">
{block "form"}
<h5>Dorucovaci adresa</h5>
Ulice a čp:
<input type="text" name="data[street]" value="{$body.user.street}">
<br>
Mesto:
<input type="text" name="data[city]" value="{$body.user.city}">
<br>
PSC:
<input type="text" name="data[zip]" value="{$body.user.zip}">
<br>
Tel:
<input type="text" name="data[phone]" value="{$body.user.phone}">
<br>
Email:
<input type="text" name="data[email]" value="{$body.user.email}">
<br>
Datum narození:
<input type="date" name="data[birthdate]" value="{$body.user.birthdate}">
<div data-box="deliveries" class="deliveries-box">
{include "ordering/ordering.delivery.deliveries.tpl" deliveries=$view->getDeliveries() availability=1}
</div>
<div data-box="payments" class="payments-box" data-reload="payments">
{if $body.delivery_id}
{include "ordering/ordering.delivery.payments.tpl" payments=$view->getPayments()}
{/if}
</div>
<input type="submit" value="Odeslat" name="submitOrder">
{/block}
</form>
</div>
</div>
{/block}
{block "body" append}
<script>
{literal}
function reloadAgeVerifyParts(id_delivery) {
let form = $("#cart");
const data = form.serializeArray();
const parts = $('[data-reload]');
$.post(window.location.href, data, (data) => {
const newHtml = wpj.domUtils.parseHtml(data);
wpj.domUtils.crossFadeParts(parts, newHtml);
}, 'html');
$('.delivery_description').hide();
const id = '#delivery_description_' + id_delivery
$(id).show();
}
wpj.onReady.push(() => {
let form = $("#cart");
form.find('input:radio').on('change', (e) => {
reloadAgeVerifyParts(e.target.value);
}
);
form.find('input[type=hidden]').on('change', (e) => {
const match = e.target.name.match(/delivery_data\[(\d+)\]\[.*\]/);
const id = match ? match[1] : null;
reloadAgeVerifyParts(id);
});
form.find('input[name=delivery_id]').on('click', (e) => {
const input = $(e.currentTarget)
form.trigger("cartdeliverychange", {delivery_id: input.val()})
})
form.on('submit', (e) => {
var $checked_delivery_wrapper = form.find('[name="delivery_id"]:checked').closest('[data-cart="item"]');
if ($checked_delivery_wrapper.find('[data-delivery-validity_check]').length) {
var $invalid = $checked_delivery_wrapper.find('[data-delivery-invalid]');
if ($invalid.length) {
window.alert($invalid.data('delivery-invalid'));
return false;
}
}
})
});
{/literal}
</script>
{/block}

View File

@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\Resources\upgrade;
use KupShop\AgeVerifyBundle\Utils\AgeVerifyUtil;
class AgeVerifyUpgrade extends \UpgradeNew
{
public function check_userAgeVerification()
{
return $this->checkTableExists('users_age_verification');
}
/** Create age verification table */
public function upgrade_userAgeVerification()
{
sqlQuery("create table users_age_verification(
id_user int(11) unsigned primary key references users(id) on UPDATE cascade on delete CASCADE ,
legal_age ENUM('Y', 'N') NOT NULL DEFAULT 'N',
date_updated DATETIME ON UPDATE NOW() default NOW(),
verification_type ENUM('bankId'));
");
$this->upgradeOK();
}
public function check_verificationSources()
{
return $this->checkEnumOptions('users_age_verification', 'verification_type', array_keys(AgeVerifyUtil::$VERIFICATION_TYPES));
}
/** Update verification sources ENUM */
public function upgrade_verificationSources()
{
$this->updateEnumOptions('users_age_verification', 'verification_type', array_keys(AgeVerifyUtil::$VERIFICATION_TYPES));
$this->upgradeOK();
}
public function check_orderIdField()
{
return $this->checkColumnExists('users_age_verification', 'id_order');
}
/** Create id_order field on age verification */
public function upgrade_orderIdField()
{
sqlQuery('ALTER TABLE users_age_verification ADD COLUMN id_order INT UNSIGNED NULL
REFERENCES orders(id) ON UPDATE CASCADE ON DELETE CASCADE;');
$this->upgradeOK();
}
public function check_ageVerificationData()
{
return $this->checkColumnExists('users_age_verification', 'data');
}
/** Add data field to users_age_verification */
public function upgrade_ageVerificationData()
{
sqlQuery('ALTER TABLE users_age_verification ADD COLUMN data LONGTEXT NULL');
$this->upgradeOK();
}
}

View File

@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\Utils;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Symfony\Contracts\Service\Attribute\Required;
class AdultoUtil
{
private const API_URL = 'https://api.result.adulto.cz';
private const REQUIRED_AGE = 18;
#[Required]
public AgeVerifyUtil $ageVerifyUtil;
#[Required]
public LoggerInterface $logger;
public function checkAdultoUid(string $adultoUid = ''): array
{
// na lokálu nechci ověřovat přes Adulto protože na to nemaj žádnej sandbox
if (isLocalDevelopment()) {
return ['success' => true, 'data' => 'Verification skipped in local development.'];
}
if (empty($adultoUid)) {
return ['success' => false, 'message' => 'Váš věk nebyl ověřen.'];
}
$klic = \Settings::getDefault()['age_verify']['adulto']['private_key'] ?? '';
$api_url = self::API_URL.'/?secret='.$klic.'&response='.$adultoUid;
// Volání API
$response = file_get_contents($api_url);
// $response = '{"adultocz-verify-id":"2024378270","adultocz-verify-uid":"454686ff2e5b6df020ccbc9242497aa587201319674d","adultocz-visit-cookie":"1548674d-79a7-4edf-9c48-3b25893c348c-1733484546544","adultocz-visit-time":"2024-12-06 12:29:06","adultocz-verify-time":"2024-12-06 12:32:40","adultocz-verify-method":"bankid","adultocz-verify-status":"true","adultocz-verify-age":"18","adultocz-verify-adult":"true"}';
$this->logger->log(LogLevel::INFO, $response);
// Ověření API komunikace
if ($response === false) {
return ['success' => false, 'message' => 'Chyba při komunikaci s Adulto.'];
}
// JSON odpověď API
$adultocz = json_decode($response);
// Podmínka pro úspěšné ověření
if (
$adultocz // Existuje JSON odpověď
&& isset($adultocz->{'adultocz-verify-adult'})
&& $adultocz->{'adultocz-verify-adult'} === 'true'
&& isset($adultocz->{'adultocz-verify-age'})
&& $adultocz->{'adultocz-verify-age'} == self::REQUIRED_AGE
) {
$this->ageVerifyUtil->setVerificationData('Y', 'adulto', data: $response);
// Smazání cookie pro zapomenutí ověření
setcookie('adultocz_local', '', time() - 3600, '/', '', true, true);
return ['success' => true, 'data' => $adultocz];
} elseif ($adultocz
&& isset($adultocz->{'adultocz-verify-age'})
&& $adultocz->{'adultocz-verify-age'} != self::REQUIRED_AGE
) {
// Ověřený věk není dostatečný
return ['success' => false, 'message' => 'Nemůžeme prodávat osobám mladším '.self::REQUIRED_AGE.' let.'];
} else {
// Ověřený věk není dostatečný
return ['success' => false, 'message' => 'Neplatná odpověď při komunikaci s API.'];
}
}
}

View File

@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\Utils;
use Doctrine\DBAL\Exception;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\KupShopBundle\Views\Traits\MessagesTrait;
use Query\Operator;
class AgeVerifyUtil
{
use MessagesTrait;
/** @required */
public UserContext $userContext;
public static array $VERIFICATION_TYPES = [
'bankId' => 'Bankovní identita',
'store' => 'Prodejna',
'package' => 'Balík',
'veriface' => 'Veriface',
'mojeid' => 'MojeID',
'adulto' => 'Adulto',
];
/**
* @param $legalAge string Y/N
* @param string $type bankId/store/package/veriface/adulto
* @param int|null $userId \User
*
* @return void
*
* @throws Exception
*/
public function setVerificationData(string $legalAge, string $type, ?string $birthdate = null, ?int $userId = null, ?string $data = null): bool
{
if (empty($userId) && empty($userId = $this->userContext->getActiveId())) {
return false;
}
if (!empty($birthdate)) {
sqlQueryBuilder()->update('users')
->directValues([
'birthdate' => $birthdate,
'figure' => 'Y',
])
->where(Operator::equals(['id' => $userId]))
->execute();
}
sqlQueryBuilder()->insert('users_age_verification')
->directValues([
'id_user' => $userId,
'legal_age' => $legalAge,
'verification_type' => $type,
'data' => $data,
])
->onDuplicateKeyUpdate([
'id_user',
'legal_age',
'verification_type',
'data',
])
->execute();
if (!isAdministration()) {
if ($legalAge == 'Y') {
$this->addSuccessMessage('Ověření plnoletosti proběhlo úspěšně.');
} else {
$this->addErrorMessage('Ověření plnoletosti se nezdařilo.'); // TODO: uživatel není plnoletý??
}
}
return true;
}
public function isLegalAged($userId): bool
{
if (empty($userId)) {
return false;
}
$data = sqlQueryBuilder()->select('legal_age')->from('users_age_verification')
->where(Operator::equals(['id_user' => $userId]))->execute()->fetchOne();
return $data == 'Y';
}
public function getVerificationData($userId)
{
return sqlQueryBuilder()->select('legal_age, verification_type, id_order')
->from('users_age_verification')
->where(Operator::equals(['id_user' => $userId]))
->execute()->fetchAllAssociative()[0] ?? [];
}
}

View File

@@ -0,0 +1,123 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\Utils;
use KupShop\AgeVerifyBundle\Exception\BankIdException;
use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Util\System\CurlUtil;
use Symfony\Component\HttpClient\Exception\ClientException;
class BankIdUtil
{
/** @required */
public AgeVerifyUtil $ageVerifyUtil;
/** @required */
public CurlUtil $curlUtil;
public const SCOPES = [
'openid',
'profile.birthdate',
];
public const BANKID = 'bankId';
public const SANDBOX_URL = 'https://oidc.sandbox.bankid.cz/';
public const PROD_URL = '/'; // TODO
public function fetchData($code)
{
$query = [
'grant_type' => 'authorization_code',
'client_id' => $this->getClientId(),
'client_secret' => $this->getClientSecret(),
'redirect_uri' => $this->getRedirectUri(),
'code' => $code,
];
$client = $this->curlUtil->getClient(headers: ['Content-Type' => 'application/x-www-form-urlencoded'])
->request('POST', $this->getUrl('token'), ['body' => $query]);
try {
$response = $client->getContent();
$response = json_decode($response, true);
} catch (ClientException $e) {
throw new BankIdException('Unable to fetch access token');
}
if (!isset($response['access_token'])) {
throw new BankIdException('Unable to fetch access token');
}
$client = $this->curlUtil->getClient(['Authorization' => "Bearer {$response['access_token']}"])
->request('POST', $this->getUrl('userinfo'));
try {
$data = $client->getContent();
} catch (ClientException $e) {
throw new BankIdException('Unable to fetch verified birthdate');
}
$data = json_decode($data, true);
if (!isset($data['verified_claims']['claims']['birthdate'])) {
throw new BankIdException('Unable to fetch verified birthdate');
}
$birthdate = $data['verified_claims']['claims']['birthdate'];
$date = \DateTime::createFromFormat('Y-m-d', $birthdate)->add(\DateInterval::createFromDateString('+18YEARS'));
$this->ageVerifyUtil->setVerificationData($date <= (new \DateTime()) ? 'Y' : 'N', self::BANKID, $birthdate);
}
public function getRedirectUrl()
{
$query = http_build_query([
'client_id' => $this->getClientId(),
'redirect_uri' => $this->getRedirectUri(),
'scope' => implode(' ', self::SCOPES),
'response_type' => 'code',
'state' => 'BankID',
'prompt' => 'login',
'display' => 'page',
'acr_values' => 'loa2',
]);
return "{$this->getUrl('auth')}?{$query}";
}
protected function getUrl($path)
{
if (isDevelopment() || \Settings::getDefault()['oauth']['bank_id']['sandbox']) {
return self::SANDBOX_URL.$path;
}
return self::PROD_URL.$path;
}
protected function getClientId()
{
$settings = \Settings::getDefault();
return $settings['oauth']['bank_id']['client_id'] ?? '';
}
protected function getClientSecret()
{
$settings = \Settings::getDefault();
return $settings['oauth']['bank_id']['client_secret'] ?? '';
}
protected function getRedirectUri()
{
if (isDevelopment()) {
return Config::get()['Addr']['full_original'].'_bankid';
}
return Config::get()['Addr']['full'].'_bankid';
}
}

View File

@@ -0,0 +1,213 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\Utils;
use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\AgeVerifyBundle\Exception\VeriFaceException;
use KupShop\GraphQLBundle\EventListener\JsShopRefreshListener;
use KupShop\KupShopBundle\Context\DomainContext;
use KupShop\KupShopBundle\Context\UserContext;
use Query\Operator;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Contracts\Service\Attribute\Required;
use veriface\Dto\IndicatorDto;
use veriface\VeriFace;
class VerifaceUtil
{
protected ?VeriFace $veriFace = null;
#[Required]
public AgeVerifyUtil $ageVerifyUtil;
#[Required]
public DomainContext $domainContext;
#[Required]
public UserContext $userContext;
#[Required]
public SessionInterface $session;
public const MESSAGE = 'Veriface - Ověření plnoletosti se nezdařilo.';
public function __construct()
{
$dbcfg = \Settings::getDefault();
$apiKey = $dbcfg['age_verify']['veriface']['api_key'] ?? false;
if ($apiKey) {
$this->veriFace = VeriFace::byApiKey($apiKey);
}
}
protected function getMessage()
{
if ($user = $this->userContext->getActive()) {
return self::MESSAGE.': '.$user->email;
}
return self::MESSAGE;
}
public function getRedirectUrl(): string
{
$this->checkVeriface();
if (!($email = $this->userContext->getActive()?->email)) {
$data = $this->session->get('verifaceData');
$email = $data['email'] ?? '';
}
$verification = $this->veriFace->createVerification(type: 'LINK_LONG', referenceId: $email);
return 'https://app.veriface.eu/?oc='.$verification->openCode.'&redirectUri='.$this->getRedirectUri();
}
public function saveData($sessionId, $user = null): bool
{
$this->checkVeriface();
$verification = $this->veriFace->getVerification($sessionId);
if ($verification === null) {
$this->ageVerifyUtil->addErrorMessage('Ověření plnoletosti se nezdařilo.');
addActivityLog(ActivityLog::SEVERITY_WARNING,
ActivityLog::TYPE_COMMUNICATION,
$this->getMessage(),
['message' => 'Nepodařilo se získat data z Veriface.', 'sessionId' => $sessionId]);
return false;
}
// Pro zalogování
$status = [
'verifaceStatus' => $verification->status,
'endUserStatus' => $verification->verificationEndUserStatus ?? '',
'sessionId' => $sessionId,
'documentStatus' => $verification->documentStatus,
'selfieStatus' => $verification->selfieStatus,
'livenessCheckStatus' => $verification->livenessCheckStatus,
'extractedData' => json_encode($verification->extractedData),
];
// Uložení průběhu v čitelnější formě
foreach ($verification->indicators as $key => $indicator) {
$status["indicator_{$key}"] = json_encode($indicator);
}
// Ověření chcíplo na straně uživatele
if ($verification->verificationEndUserStatus != 'SUCCESS') {
$this->ageVerifyUtil->addErrorMessage('Ověření plnoletosti se nezdařilo.');
$status['message'] = 'Chyba v procesu ověření.';
addActivityLog(ActivityLog::SEVERITY_WARNING, ActivityLog::TYPE_COMMUNICATION, $this->getMessage(), $status);
return false;
}
$verifaceName = $verification->name;
if ($verifaceName) {
$nameParts = explode(' ', $verifaceName);
$firstname = reset($nameParts);
$surname = end($nameParts);
if (str_contains($firstname, '/')) {
$firstname = explode('/', $firstname)[1];
}
if (str_contains($surname, '/')) {
$surname = explode('/', $surname)[1];
}
$verifaceName = "{$firstname} {$surname}";
}
$age = array_filter($verification->indicators, function ($dto) {
return $dto->code == 'document_age.age' && $dto->status == 'OK';
});
if (empty($age)) {
$this->ageVerifyUtil->addErrorMessage('Ověření plnoletosti se nezdařilo.');
$status['message'] = 'Nepodařilo se získat věk.';
addActivityLog(ActivityLog::SEVERITY_WARNING, ActivityLog::TYPE_COMMUNICATION, $this->getMessage(), $status);
return false;
}
/** @var IndicatorDto $age */
$age = reset($age);
// Registrace z košíku
if ($data = $this->session->get('verifaceData')) {
/** @var \User $user */
$user = sqlGetConnection()->transactional(function () use ($data) {
$idUser = addUserEmail(email: $data['email'], fields: ['figure' => 'Y', 'phone' => $data['phone']]);
return \User::createFromId($idUser);
});
$user->login(getUserKey());
$user->updatePassword($data['password']);
$this->session->remove('verifaceData');
if (findModule(\Modules::JS_SHOP)) {
$this->session->set(JsShopRefreshListener::SESSION_NAME, true);
}
if ($firstname && $surname) {
sqlQueryBuilder()->update('users')
->directValues([
'name' => ucfirst(mb_strtolower($firstname)),
'surname' => ucfirst(mb_strtolower($surname)),
])
->where(Operator::equals(['id' => $user->id]))
->execute();
}
} else {
// Když si nepošlu usera ze shora (webhook) tak zkusím přihlášeného
if (!$user) {
$user = $this->userContext->getActive();
}
if ($user) {
$transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: Lower(); :: NFC;',
\Transliterator::FORWARD);
$eshopName = "{$user->name} {$user->surname}";
similar_text($transliterator->transliterate($eshopName), $transliterator->transliterate($verifaceName), $percent);
if ($percent < 80) {
$this->ageVerifyUtil->addErrorMessage('Ověření plnoletosti se nezdařilo: jméno a přijmení se neshoduje s údaji v e-shopu.');
$status['message'] = 'Jméno a příjmení v eshopu se neshoduje s údaji z dokladu.';
addActivityLog(ActivityLog::SEVERITY_WARNING, ActivityLog::TYPE_COMMUNICATION, $this->getMessage(), $status);
return false;
}
} else {
$this->ageVerifyUtil->addErrorMessage('Ověření plnoletosti se nezdařilo.');
$status['message'] = 'Není přihlašený uživatel.';
addActivityLog(ActivityLog::SEVERITY_WARNING, ActivityLog::TYPE_COMMUNICATION, $this->getMessage(), $status);
return false;
}
}
// Plnoletý = 18+ a validní status
$this->ageVerifyUtil->setVerificationData($age->params >= 18 && in_array($verification->status,
['VERIFIED', 'VERIFIED_WARNING', 'VERIFIED_MANUAL']) ? 'Y' : 'N',
'veriface',
\DateTime::createFromFormat('d.m.Y', $verification->birthDate)->format('Y-m-d'),
$user->id);
return true;
}
protected function checkVeriface(): void
{
if ($this->veriFace === null) {
throw new VeriFaceException('Veriface není inicializován. Je vyplněný API klíč?');
}
}
protected function getRedirectUri()
{
return "{$this->domainContext->getActiveWithScheme()}/_veriface";
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\View;
use KupShop\AgeVerifyBundle\Utils\AdultoUtil;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\KupShopBundle\Util\Contexts;
use KupShop\KupShopBundle\Views\View;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\Service\Attribute\Required;
class AdultoView extends View
{
protected $template = 'ageverify/adulto.tpl';
protected $title = 'Ověření pomocí Adulto';
#[Required]
public RequestStack $requestStack;
#[Required]
public AdultoUtil $adultoUtil;
public function getBreadcrumbs(): ?array
{
return getReturnNavigation(-1, 'USER', [$this->getTitle()]);
}
public function getBodyVariables(): array
{
$vars = parent::getBodyVariables();
$vars['adulto_public_key'] = \Settings::getDefault()['age_verify']['adulto']['public_key'] ?? '';
$vars['user'] = Contexts::get(UserContext::class)->getActive();
return $vars;
}
public function submitForm(): array
{
$mainRequest = $this->requestStack->getMainRequest();
return $this->adultoUtil->checkAdultoUid($mainRequest->get('adultocz-uid') ?? '');
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\View;
use KupShop\AgeVerifyBundle\Utils\AgeVerifyUtil;
use KupShop\KupShopBundle\Context\UserContext;
class AgeVerifyView extends \KupShop\KupShopBundle\Views\View
{
/** @required */
public UserContext $userContext;
/** @required */
public AgeVerifyUtil $ageVerifyUtil;
protected $template = 'ageverify/ageverify.tpl';
public function getBodyVariables()
{
$vars = parent::getBodyVariables();
$vars['user'] = $this->userContext->getActive();
return $vars;
}
public function getBreadcrumbs()
{
return getReturnNavigation(-1, 'USER', [$this->getTitle()]);
}
public function getTitle()
{
return translate('age_verify', 'AgeVerify');
}
}

View File

@@ -0,0 +1,290 @@
<?php
declare(strict_types=1);
namespace KupShop\AgeVerifyBundle\View;
use KupShop\AgeVerifyBundle\Exception\AgeVerificationException;
use KupShop\AgeVerifyBundle\Utils\AgeVerifyUtil;
use KupShop\KupShopBundle\Context\ContextManager;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\KupShopBundle\Exception\RedirectException;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\Price\Price;
use KupShop\KupShopBundle\Util\Price\PriceCalculator;
use KupShop\KupShopBundle\Views\View;
use KupShop\KupShopBundle\Wrapper\PriceWrapper;
use KupShop\OrderingBundle\Entity\Purchase\ProductPurchaseItem;
use KupShop\OrderingBundle\Entity\Purchase\PurchaseState;
use KupShop\OrderingBundle\Event\OrderEvent;
use KupShop\OrderingBundle\Util\Purchase\PurchaseUtil;
use Query\Operator;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\Service\Attribute\Required;
class PackageOrderView extends View
{
protected $template = 'ageverify/packageOrder.tpl';
protected $title = 'Ověření přes zásilkovnu';
#[Required]
public ContextManager $contextManager;
#[Required]
public UserContext $userContext;
#[Required]
public RequestStack $requestStack;
#[Required]
public AgeVerifyUtil $ageVerifyUtil;
#[Required]
public PurchaseUtil $purchaseUtil;
#[Required]
public EventDispatcherInterface $eventDispatcher;
protected array $deliveries = [];
protected array $payments = [];
protected $id_delivery;
protected $id_payment;
protected ?Price $product_price = null;
public function getBreadcrumbs(): ?array
{
return getReturnNavigation(-1, 'USER', [$this->getTitle()]);
}
public function getBodyVariables(): array
{
$vars = parent::getBodyVariables();
$vars['user'] = $this->userContext->getActive();
$mainRequest = $this->requestStack->getMainRequest();
$this->id_delivery = $mainRequest->get('delivery_id') ?? false;
$this->id_payment = $mainRequest->get('payment_id') ?? false;
$vars['deliveries'] = $this->getDeliveries();
$vars['payments'] = $this->getPayments();
if ($this->id_delivery) {
$vars['deliveries'][$this->id_delivery]->storeDeliveryInfo(getVal($this->id_delivery, $mainRequest->get('delivery_data')));
}
$vars['payment_id'] = $this->id_payment;
$vars['delivery_id'] = $this->id_delivery;
return $vars;
}
public function submitForm()
{
$user = $this->userContext->getActive();
$userId = $user->id;
$userVerification = sqlQueryBuilder()->select('*')->from('users_age_verification')->where(Operator::equals(['id_user' => $userId]))
->execute()->fetchAssociative();
$mainRequest = $this->requestStack->getMainRequest();
if ($mainRequest->get('submitOrder') === null) {
return false;
}
$data = $mainRequest->get('data');
$id_delivery = $mainRequest->get('delivery_id');
$id_payment = explode('-', $mainRequest->get('payment_id', ''))[0] ?? false;
if ($data['email'] ?? false) {
$data['name'] ??= $user->invoice['name'] ?? '';
$data['surname'] ??= $user->invoice['surname'] ?? '';
}
$order = null;
$id_product = \Settings::getDefault()['age_verify']['package']['product'];
if ($userVerification === false || (empty($userVerification['id_order']) && $userVerification['legal_age'] != 'Y')) {
$deliveryType = $this->getDeliveryType($id_delivery, $id_payment);
if (!$deliveryType) {
return;
}
$purchaseState = $this->createPurchaseState();
$product = new \Product($id_product);
$product->createFromDB();
$purchaseItem = new ProductPurchaseItem(
$id_product,
null,
1,
PriceCalculator::toPrice($product->getProductPrice())
);
$purchaseItem->setProduct($product);
$purchaseState->addProduct($purchaseItem)
->setDeliveryTypeId($deliveryType->id);
$delivery_data = $mainRequest->get('delivery_data');
$deliveryInfo = [];
if ($delivery_data[$id_delivery] ?? false) {
$deliveryInfo = $purchaseState->getDeliveryType()->getDelivery()->storeDeliveryInfo($delivery_data[$id_delivery]);
}
$purchaseState->setCustomData([
'order' => [
'id_user' => $user->id,
'invoice_name' => $user->invoice['name'] ?? '',
'invoice_surname' => $user->invoice['surname'] ?? '',
'invoice_firm' => $user->invoice['firm'] ?? '',
'invoice_ico' => $user->invoice['ico'] ?? '',
'invoice_dic' => $user->invoice['dic'] ?? '',
'invoice_street' => $user->invoice['street'] ?? '',
'invoice_city' => $user->invoice['city'] ?? '',
'invoice_zip' => $user->invoice['zip'] ?? '',
'invoice_country' => $user->invoice['country'] ?? '',
'invoice_phone' => $user->invoice['phone'] ?? '',
'invoice_email' => $user->invoice['email'] ?? '',
'invoice_state' => $user->invoice['state'] ?? '',
'delivery_name' => $data['name'] ?? '',
'delivery_surname' => $data['surname'] ?? '',
'delivery_street' => $data['street'] ?? '',
'delivery_city' => $data['city'] ?? '',
'delivery_zip' => $data['zip'] ?? '',
'delivery_phone' => $data['phone'] ?? '',
'date_created' => date('Y-m-d H:i:s'),
'date_updated' => date('Y-m-d H:i:s'),
'note_admin' => json_encode(['delivery_data' => $deliveryInfo]),
],
'age_verification' => true,
]);
$order = $this->purchaseUtil->createOrderFromPurchaseState($purchaseState);
$orderEvent = new OrderEvent($order);
$this->eventDispatcher->dispatch($orderEvent, OrderEvent::ORDER_FINISHED);
if (!empty($order)) {
sqlQueryBuilder()->insert('users_age_verification')
->directValues([
'id_user' => $userId,
'verification_type' => 'package',
'id_order' => $order->id,
])
->onDuplicateKeyUpdate(['id_user' => 'id_user', 'id_order'])
->execute();
if ($birthdate = $mainRequest->get('cart_data')['birthdate'] ?? false) {
sqlQueryBuilder()->update('users')->directValues([
'birthdate' => $birthdate,
])->where(Operator::equals(['id' => $userId]))->execute();
}
} else {
throw new AgeVerificationException('Failed to create order.');
}
} elseif (!empty($userVerification['id_order'])) {
addUserMessage(translate('orderAlreadyCreated', 'AgeVerify'));
throw new RedirectException(path('ageVerify'));
}
return $order;
}
public function getDeliveries(): array
{
if (empty($this->deliveries)) {
$deliveryTypes = array_filter(\DeliveryType::getAll(), function ($deliveryType) {
return $deliveryType->getDelivery()->getCustomData()['balikobot']['require_full_age'] ?? false;
});
foreach ($deliveryTypes as $deliveryType) {
$delivery = $deliveryType->getDelivery();
if ($delivery->accept($this->getProductPrice(), false)) {
$this->deliveries[$deliveryType->id_delivery] = $delivery;
}
}
}
return $this->deliveries;
}
public function getPayments(): array
{
if (empty($this->payments)) {
$deliveryTypes = array_filter(\DeliveryType::getAll(true), function ($deliveryType) {
return ($deliveryType->getDelivery()->getCustomData()['balikobot']['require_full_age'] ?? false) && $deliveryType->id_delivery == $this->id_delivery;
});
foreach ($deliveryTypes as $deliveryType) {
$payment = $deliveryType->getPayment();
if ($payment->accept($this->getProductPrice(), false)) {
$this->payments[$deliveryType->id_payment] = [
'class' => $payment,
'name' => $payment->getName(),
'exception' => $payment->exception,
'photo' => $payment->getPhoto(null),
'enabled' => \Payment::isEnabled($payment->class),
];
}
}
}
return $this->payments;
}
public function getDeliveryPrice($id)
{
/** @var Price $price */
$price = $this->getDeliveries()[$id]->getPrice();
return ServiceContainer::getService(PriceWrapper::class)->setObject($price);
}
public function getPaymentPrice($id)
{
foreach (\DeliveryType::getAll() as $deliveryType) {
if ($deliveryType->id_payment == $id) {
$price = $deliveryType->price_payment;
}
}
return ServiceContainer::getService(PriceWrapper::class)->setObject($price);
}
protected function getProductPrice()
{
if (!$this->product_price) {
$product = new \Product();
$product->createFromDB(\Settings::getDefault()['age_verify']['package']['product']);
$this->product_price = $product->getProductPrice();
}
return $this->product_price;
}
protected function getDeliveryType($id_delivery, $id_payment): ?\DeliveryType
{
foreach (\DeliveryType::getAll(false) as $deliveryType) {
if ($deliveryType->id_delivery == $id_delivery && $deliveryType->id_payment == $id_payment) {
$delivery = $deliveryType->getDelivery();
$delivery->accept($this->getProductPrice(), false);
return $deliveryType;
}
}
return null;
}
protected function createPurchaseState(): PurchaseState
{
return new PurchaseState([]);
}
}