Files
Moodle/classes/privacy/provider.php
T

177 lines
6.6 KiB
PHP

<?php
declare(strict_types=1);
namespace paygw_moneta\privacy;
use context;
use context_system;
use core_payment\privacy\paygw_provider;
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\core_userlist_provider;
use core_privacy\local\request\transform;
use core_privacy\local\request\userlist;
use core_privacy\local\request\writer;
use paygw_moneta\local\order\Order;
use paygw_moneta\local\order\TransactionRepository;
/**
* Privacy API: заказы `paygw_moneta_transactions` содержат id покупателя,
* сумму, статус и снимок чека с его e-mail/телефоном.
*
* Оплаченные заказы ядро выгружает и удаляет вместе с платежом
* ({@see paygw_provider}); неоплаченные не привязаны к платежу, поэтому
* плагин обслуживает их сам в системном контексте.
*
* @package paygw_moneta
* @copyright 2026 Moneta Labs {@link https://moneta.ru/}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\plugin\provider,
core_userlist_provider,
paygw_provider
{
public static function get_metadata(collection $collection): collection
{
$collection->add_database_table(TransactionRepository::TABLE, [
'userid' => 'privacy:metadata:transactions:userid',
'amount' => 'privacy:metadata:transactions:amount',
'status' => 'privacy:metadata:transactions:status',
'merchantorderid' => 'privacy:metadata:transactions:merchantorderid',
'providertransactionid' => 'privacy:metadata:transactions:providertransactionid',
'snapshot' => 'privacy:metadata:transactions:snapshot',
'timecreated' => 'privacy:metadata:transactions:timecreated',
], 'privacy:metadata:transactions');
$collection->add_external_location_link('moneta', [
'userid' => 'privacy:metadata:moneta:userid',
'fullname' => 'privacy:metadata:moneta:fullname',
'email' => 'privacy:metadata:moneta:email',
'phone' => 'privacy:metadata:moneta:phone',
'amount' => 'privacy:metadata:moneta:amount',
], 'privacy:metadata:moneta');
return $collection;
}
public static function get_contexts_for_userid(int $userid): contextlist
{
global $DB;
$contextlist = new contextlist();
if ($DB->record_exists(TransactionRepository::TABLE, ['userid' => $userid])) {
$contextlist->add_system_context();
}
return $contextlist;
}
public static function get_users_in_context(userlist $userlist): void
{
if (!$userlist->get_context() instanceof context_system) {
return;
}
$userlist->add_from_sql('userid', 'SELECT userid FROM {' . TransactionRepository::TABLE . '}', []);
}
public static function export_user_data(approved_contextlist $contextlist): void
{
global $DB;
foreach ($contextlist->get_contexts() as $context) {
if (!$context instanceof context_system) {
continue;
}
$records = $DB->get_records(
TransactionRepository::TABLE,
['userid' => $contextlist->get_user()->id],
'timecreated',
);
$orders = [];
foreach ($records as $record) {
$orders[] = self::describe(Order::fromRecord($record));
}
if ($orders !== []) {
writer::with_context($context)->export_data(
[get_string('gatewayname', 'paygw_moneta')],
(object) ['orders' => $orders],
);
}
}
}
public static function delete_data_for_all_users_in_context(context $context): void
{
global $DB;
if ($context instanceof context_system) {
$DB->delete_records(TransactionRepository::TABLE);
}
}
public static function delete_data_for_user(approved_contextlist $contextlist): void
{
global $DB;
foreach ($contextlist->get_contexts() as $context) {
if ($context instanceof context_system) {
$DB->delete_records(TransactionRepository::TABLE, ['userid' => $contextlist->get_user()->id]);
}
}
}
public static function delete_data_for_users(approved_userlist $userlist): void
{
global $DB;
if (!$userlist->get_context() instanceof context_system || $userlist->get_userids() === []) {
return;
}
[$sql, $params] = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
$DB->delete_records_select(TransactionRepository::TABLE, "userid {$sql}", $params);
}
public static function export_payment_data(context $context, array $subcontext, \stdClass $payment): void
{
global $DB;
$record = $DB->get_record(TransactionRepository::TABLE, ['paymentid' => $payment->id]);
if ($record === false) {
return;
}
$subcontext[] = get_string('gatewayname', 'paygw_moneta');
writer::with_context($context)->export_data($subcontext, self::describe(Order::fromRecord($record)));
}
public static function delete_data_for_payment_sql(string $paymentsql, array $paymentparams): void
{
global $DB;
$DB->delete_records_select(TransactionRepository::TABLE, "paymentid IN ({$paymentsql})", $paymentparams);
}
/**
* Заказ в выгрузке: без внутренних идентификаторов и без данных кассы сверх контактов.
*/
private static function describe(Order $order): \stdClass
{
return (object) [
'merchantorderid' => $order->merchantOrderId,
'providertransactionid' => $order->providerTransactionId,
'amount' => $order->amount->toDecimal() . ' ' . $order->currency,
'status' => $order->status->value,
'description' => $order->snapshot->description,
'client' => $order->snapshot->receipt->client->toArray(),
'timecreated' => transform::datetime($order->timeCreated),
'timecompleted' => $order->timeCompleted === null ? null : transform::datetime($order->timeCompleted),
];
}
}