yf_payment_api class| File | plugins/payment/classes/yf_payment_api.class.php |
|---|---|
| Use | _class('payment_api') |
A site sets them in its config with $PROJECT_CONF['payment_api']['NAME'] = ...;
| Property | Default | Description |
|---|---|---|
$BALANCE_LIMIT_LOWER |
0 |
|
$CONFIG |
null |
|
$CONFIRMATION_TIME |
'-6 hour' |
|
$DECIMALS |
2 |
|
$DECIMAL_POINT |
',' |
|
$DEPOSITION_TIME |
'-6 hour' |
|
$DUMP_PATH |
'/tmp' |
|
$IS_BALANCE_LIMIT_LOWER |
true |
|
$IS_PAYOUT_CONFIRMATION |
false |
|
$MAIL_COPY_TO |
null |
|
$OPERATION_LIMIT |
10 |
|
$PAYOUT_LIMIT_MIN |
0 |
|
$REMOVED_PROVIDERSarray |
|
Deleted payment providers: name => reason; a payment_provider row or call naming one warns once per request and gets no class |
$ROUND_MODE |
3 |
|
$SECURITY_CODE |
'' |
|
$THOUSANDS_SEPARATOR |
' ' |
|
$TX_VAR |
'tx_isolation' |
|
$TZ |
'' |
|
$_class_path |
'' |
|
$_removed_warnedarray |
[] |
@conf_skip removed providers already reported in this request |
$account |
null |
|
$account_id |
null |
|
$account_type |
null |
|
$account_type_id |
null |
|
$account_type_id_default |
'main' |
|
$currencies |
null |
|
$currencies_default |
['UNT' => ['currency_id' => 'UNT', 'name' => 'point', 'short' => 'pt.', 'sign' => 'p.', 'number' => 0, 'minor_units' => 0], 'USD' => ['currency_id' => 'USD', 'name' => 'US dollar', 'short' => 'dollar', 'sign' => '$', 'number' => 840, 'minor_units' => 2], 'EUR' => ['currency_id' => 'EUR', 'name' => 'Euro', 'short' => 'euro', 'sign' => '€', 'number' => 978, 'minor_units' => 2], 'UAH' => ['currency_id' => 'UAH', 'name' => 'Ukrainian hryvnia', 'short' => 'UAH', 'sign' => '₴', 'number' => 980, 'minor_units' => 2]] |
|
$currency |
null |
|
$currency_id |
null |
|
$currency_id_default |
'UNT' |
|
$dump |
null |
|
$payout_currency_allow |
['USD', 'UAH'] |
|
$provider |
null |
|
$provider_id |
null |
|
$provider_index |
null |
|
$status |
null |
|
$status_index |
null |
|
$transaction |
null |
|
$type |
null |
|
$type_id |
null |
|
$type_index |
null |
|
$type_name |
null |
|
$user_id |
null |
|
$user_id_default |
null |
public function account($options = null)
public function account($options = null)
{
// by account_id
$result = $this->get_account__by_id($options);
if ( ! empty($result)) {
return $result;
}
// options
$_ = &$options;
$db = db()->table('payment_account')->order_by('account_id');
// user_id
$value = $this->_default([
($_['user_id'] ?? null),
$this->user_id,
]);
$value = $this->check_user_id($value);
if (empty($value)) {
return null;
}
$this->user_id($value);
$db->where('user_id', '=', db()->es_for_builder($value));
$options['user_id'] = $value;
// by account_type_id
$value = (int) ($_['account_type_id'] ?? null) ?: $this->account_type_id;
empty($value) && (list($value) = $this->get_account_type__by_name($options));
if (empty($value)) {
return null;
}
$db->where('account_type_id', '=', db()->es_for_builder($value));
$options['account_type_id'] = $value;
// by currency_id
$value = ($_['currency_id'] ?? null) ?: $this->currency_id;
empty($value) && list($value) = $this->get_currency__by_id();
if (empty($value)) {
return null;
}
$db->where('currency_id', '=', db()->es_for_builder($value));
$options['currency_id'] = $value;
// get from db
$result = $db->get_deep_array(1);
if (empty($result)) {
// account not exists
list($account_id, $account) = $this->account_create($options);
} else {
$account = reset($result);
$account_id = $account['account_id'];
}
$this->account = $account;
$this->account_id = (int) $account_id;
// get currency
$account_id && $result = $this->get_currency__by_id([
'currency_id' => $account['currency_id'],
]);
return [$account_id, $account];
}
public function account_create($options = null)
public function account_create($options = null)
{
// options
$_ = &$options;
$data = [];
// user_id
$value = $this->_default([
($_['user_id'] ?? null),
$this->user_id,
]);
$value = $this->check_user_id($value);
if (empty($value)) {
return null;
}
$data['user_id'] = $value;
// account_type_id
$value = (int) ($_['account_type_id'] ?? null) ?: $this->account_type_id;
empty($value) && (list($value) = $this->get_account_type__by_name($options));
if (empty($value)) {
return null;
}
$data['account_type_id'] = $value;
// currency_id
$value = (int) ($_['currency_id'] ?? null) ?: $this->currency_id;
empty($value) && list($value) = $this->get_currency__by_id();
if (empty($value)) {
return null;
}
$data['currency_id'] = $value;
// balance
$data['balance'] = 0;
// date
$value = $this->sql_datetime();
$data['datetime_create'] = $value;
$data['datetime_update'] = $value;
// create
$id = db()->table('payment_account')->insert(db()->es_for_builder($data));
if ($id < 1) {
return null;
}
$result = $this->get_account__by_id(['account_id' => $id]);
return $result;
}
public function account_type($value = -1)
public function account_type($value = -1)
{
$id = &$this->account_type_id;
$object = &$this->account_type;
if ($value !== -1) {
list($id, $object) = $value;
}
return [$id, $object];
}
public function balance_update($data, $options = null)
public function balance_update($data, $options = null)
{
$result = $this->_object_update('account', $data, $options);
return $result;
}
public function cancel($options = null)
public function cancel($options = null)
{
$_operation_id = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// var
if (($_operation_id ?? null) < 1) {
$result = [
'status' => false,
'status_message' => 'Invalid operation ID',
];
return $result;
}
$this->transaction_start($options);
// operation
$operation = $this->operation([
'operation_id' => $_operation_id,
]);
if (empty($operation)) {
$result = [
'status' => false,
'status_message' => 'Operation not found: ' . $_operation_id,
];
$this->transaction_rollback();
return $result;
}
if ($_user_mode ?? null) {
$account_result = $this->get_account($options);
if (empty($account_result)) {
return $account_result;
}
list($account_id, $account) = $account_result;
// check user account
if ($operation['account_id'] != $account_id) {
$result = [
'status' => false,
'status_message' => 'User operation not found: ' . $_operation_id,
];
$this->transaction_rollback();
return $result;
}
}
// status
$object = $this->get_status(['status_id' => $operation['status_id']]);
list($status_id, $status) = $object;
if (empty($status_id)) {
return $object;
}
// check in_progress
if ($status['name'] == 'cancelled') {
$result = [
'status' => true,
'status_message' => 'The operation has already been cancelled',
];
$this->transaction_rollback();
return $result;
}
if ($status['name'] != 'in_progress' && $status['name'] != 'confirmation') {
$result = [
'status' => false,
'status_message' => 'The operation cannot be cancelled',
];
$this->transaction_rollback();
return $result;
}
// check provider
$provider_options = [];
if ( ! ($_user_mode ?? null)) {
$provider_options['all'] = true;
}
$providers = $this->provider($provider_options);
if ( ! ($providers[$operation['provider_id']] ?? null)) {
$result = [
'status' => false,
'status_message' => 'Operations of this provider cannot be cancelled',
];
$this->transaction_rollback();
return $result;
}
// revert
if ($operation['direction'] == 'out') {
$options['is_revert'] = true;
}
$result = $this->_cancel($options);
if (@$result['status']) {
$result['status_message'] = 'Operation cancelled';
$this->transaction_commit();
} else {
$this->transaction_rollback();
}
return $result;
}
public function cancel_user($options = null)
public function cancel_user($options = null)
{
$options['user_mode'] = true;
$result = $this->cancel($options);
return $result;
}
public function check_user_id($value = null)
public function check_user_id($value = null)
{
if (empty($value) || $value != (int) $value || $value < 0) {
return null;
}
return $value;
}
public function config($options = null)
public function config($options = null)
{
! empty($options) && $this->CONFIG = $options;
$config = &$this->CONFIG;
if (is_array($config)) {
foreach ($config as $key => $item) {
if (is_array($this->$key)) {
$this->$key = $this->_replace($this->$key, $item);
} else {
$this->$key = $item;
}
}
}
// setup currencies
$this->currencies = $this->_replace($this->currencies_default, $this->currencies);
}
public function confirmation(&$options = null, &$data = null, &$operation_data = null)
public function confirmation(&$options = null, &$data = null, &$operation_data = null)
{
$_user_mode = $_type_name = null;
$status = true;
$result = [
'status' => &$status,
'status_message' => &$status_message,
];
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// user mode
if ( ! $_user_mode
|| ! ($_type_name == 'payment' && @$this->IS_PAYOUT_CONFIRMATION)
) {
return $result;
}
// check user mail
$user_mail = $this->is_user_mail($operation_data);
if ( ! ($user_mail['status'] ?? null)) {
return $user_mail;
}
// code by operation_id, amount
$operation_id = &$data['operation_id'];
$amount = &$data['amount'];
list($code, $salt, $raw) = $this->confirmation_code(['data' => [
'operation_id' => $operation_id,
'amount' => $amount,
]]);
// status: confirmation
$object = $this->get_status(['name' => 'confirmation']);
list($status_id, $status) = $object;
if (empty($status_id)) {
return $object;
}
$data['status_id'] = $status_id;
$operation_data['status'] = $status;
// store confirmation data
$operation_data['options'] = ['confirmation' => [
'code' => $code,
'salt' => $salt,
]];
// message
$status_message = t('The operation requires confirmation. We have sent you an email with instructions to confirm the payout.');
$operation_data['status_message'] = &$status_message;
return $result;
}
public function confirmation_code($options = null)
public function confirmation_code($options = null)
{
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// processing
$salt = ($_salt ?? null) ?: $this->_hash(time() . @$this->SECURITY_CODE);
$raw = implode('-', (array) ($_data ?? null));
$code = $this->_hash($raw . $salt);
$result = [$code, $salt, $raw];
return $result;
}
public function confirmation_code_check($options = null)
public function confirmation_code_check($options = null)
{
$_operation_id = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// operation
$operation = $this->operation([
'operation_id' => ($_operation_id ?? null),
]);
if ( ! $operation) {
$result = [
'status' => false,
'status_message' => 'Operation not found',
];
return $result;
}
// check status
$object = $this->get_status(['status_id' => $operation['status_id']]);
list($status_id, $status) = $object;
if (empty($status_id)) {
return $object;
}
if ($status['name'] != 'confirmation') {
$result = [
'status' => true,
'status_message' => 'The operation does not need confirmation',
];
return $result;
}
// check
$confirmation = &$operation['options']['confirmation'];
// check already confirmed
$is_confirmed = ($confirmation['status'][0] ?? null) ?: ($confirmation['status'] ?? null);
if ($is_confirmed) {
$result = [
'status' => true,
'status_message' => 'The operation has already been confirmed',
];
return $result;
}
// check datetime
$time_code = strtotime($operation['datetime_update']);
$time = time();
$is_expired = ($time - $time_code) > strtotime($this->CONFIRMATION_TIME);
if ($is_expired) {
$result = [
'status' => false,
'status_message' => 'Confirmation code has expired',
];
return $result;
}
// check code
$code = &$confirmation['code'];
$salt = &$confirmation['salt'];
if ($code !== ($_code ?? null)) {
$result = [
'status' => false,
'status_message' => 'Invalid code',
];
return $result;
}
// confirmation is ok
$confirmation_ok_options = [
'operation_id' => $_operation_id,
];
$result = $this->confirmation_ok($confirmation_ok_options);
return $result;
}
public function confirmation_ok($options = null)
public function confirmation_ok($options = null)
{
$_operation_id = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// operation
$operation = $this->operation([
'operation_id' => $_operation_id,
]);
if ( ! $operation) {
$result = [
'status' => false,
'status_message' => 'Operation not found',
];
return $result;
}
// status to in_progress
$object = $this->get_status(['name' => 'in_progress']);
list($status_id, $status) = $object;
if (empty($status_id)) {
return $object;
}
// update operation
$sql_datetime = $this->sql_datetime();
$update_options = [
'confirmation' => [
'status' => true,
],
];
$update_data = [
'operation_id' => $_operation_id,
'status_id' => $status_id,
'datetime_update' => $sql_datetime,
'options' => $update_options,
];
$result = $this->operation_update($update_data);
if (@$result['status']) {
$result['status_message'] = 'Operation confirmed';
// mail
$this->mail([
'tpl' => 'payout_request',
'user_id' => $this->user_id(),
'admin' => true,
'data' => [
'operation_id' => $_operation_id,
'amount' => $operation['amount'],
],
]);
}
return $result;
}
public function currency($value = -1)
public function currency($value = -1)
{
$id = &$this->currency_id;
$object = &$this->currency;
if ($value !== -1) {
list($id, $object) = $value;
}
return [$id, $object];
}
public function currency_conversion($options = null)
public function currency_conversion($options = null)
{
$_ = &$options;
$conversion_type = ($_['type'] ?? null) == 'sell' ? 'sell' : 'buy';
$currency_id = ($_['currency_id'] ?? null);
$amount = ($_['amount'] ?? null);
if (empty($currency_id) || empty($amount)) {
return null;
}
// rate
$currency_rate = $this->currency_rates([
'type' => $conversion_type,
]);
if (empty($currency_rate[$currency_id])) {
return null;
}
// calc
$rate = $currency_rate[$currency_id]['rate'];
$value = $currency_rate[$currency_id]['value'];
$result = $amount * $rate / $value;
// round
list($currency_id, $currency) = $this->get_currency__by_id([
'currency_id' => $currency_id,
]);
if (empty($currency_id)) {
return null;
}
$result = $this->_number_float($result, $currency['minor_units']);
return $result;
}
public function currency_load_rate($options = null)
public function currency_load_rate($options = null)
{
$currency__api = _class('payment_api__currency');
$result = $currency__api->load_rate($options);
return $result;
}
public function currency_rate($options = null)
public function currency_rate($options = null)
{
$currency__api = _class('payment_api__currency');
$result = $currency__api->rate($options);
return $result;
}
public function currency_rates($options = null)
public function currency_rates($options = null)
{
$_ = &$options;
// default
$currency_id = ($_['currency_id'] ?? null)
?: $this->currency_id
?: $this->currency_id_default;
$_['currency_id'] = &$currency_id;
$type = ($_['type'] ?? null) == 'sell' ? 'sell' : 'buy';
$_['type'] = &$type;
// start
$currency__api = _class('payment_api__currency');
$result = $currency__api->rates($options);
return $result;
}
public function currency_rates__buy($options = null)
public function currency_rates__buy($options = null)
{
$_ = (array) $options;
$_['type'] = 'buy';
$result = $this->currency_rates($_);
return $result;
}
public function currency_rates__provider($options = null)
public function currency_rates__provider($options = null)
{
$currency__api = _class('payment_api__currency');
$result = $currency__api->provider($options);
return $result;
}
public function currency_rates__sell($options = null)
public function currency_rates__sell($options = null)
{
$_ = (array) $options;
$_['type'] = 'sell';
$result = $this->currency_rates($_);
return $result;
}
public function deposition($options = null)
public function deposition($options = null)
{
$_ = &$options;
$is_transaction = isset($_['is_transaction']) ? $_['is_transaction'] : true;
$_['type_name'] = __FUNCTION__;
$_['operation_title'] = ($_['operation_title'] ?? null) ?: 'Account top-up';
$result = $this->_operation_check($options);
list($status, $data, $operation_data) = $result;
if (empty($status)) {
return $result;
}
// update payment operation
$title = $_['operation_title'];
$data += [
'direction' => 'in',
'title' => $title,
];
// create operation
$status = db()->table('payment_operation')->insert($data);
if (empty($status)) {
$result = [
'status' => false,
'status_message' => 'Error creating the operation',
];
return $result;
}
$operation_id = (int) $status;
$data['operation_id'] = $operation_id;
// try provider operation
$provider_class = 'provider_' . $operation_data['provider']['name'];
$result = $this->_class($provider_class, __FUNCTION__, [
'is_transaction' => $is_transaction,
'options' => $options,
'provider' => $operation_data['provider'],
'data' => $data,
'operation_data' => $operation_data,
]);
$result['operation_id'] = $operation_id;
return $result;
}
public function deposition_system($options = null)
public function deposition_system($options = null)
{
$options['provider_name'] = 'system';
$result = $this->deposition($options);
return $result;
}
public function deposition_user($options = null)
public function deposition_user($options = null)
{
$options['user_mode'] = true;
$result = $this->deposition($options);
return $result;
}
public function dump($options = null)
public function dump($options = null)
{
$_name = $_operation_id = $_var = null;
static $is_first = true;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// time
$ts = &$this->dump['ts'];
! $ts && $ts = microtime(true);
$time = &$this->dump['time'];
! $time && $time = date('Y-m-d_H-i-s', $ts);
// name
! ($_is_transaction ?? null) && $name = &$this->dump['name'];
($_name ?? null) && $name = $_name;
$name = ($name ?? null) ?: 'payment_api_dump';
// number
! ($_is_transaction ?? null) && $number = &$this->dump['number'];
if ( ! ($number ?? null) || ($_operation_id ?? null)) {
$number = [];
($_operation_id ?? null) && $number[] = $_operation_id;
$number[] = $time;
$number = implode('_', $number);
}
// path
$path = ($_path ?? null) ?: @$this->DUMP_PATH ?: '/tmp';
// file path
$file = ($_file_path ?? null) ?: sprintf('%s/%s__%s.txt', $path, $name, $number);
$html_errors = ini_get('html_errors');
ini_set('html_errors', 0);
$result = '';
if ($is_first) {
$result .= 'SERVER:' . PHP_EOL . var_export($_SERVER, true) . PHP_EOL . PHP_EOL;
$result .= 'GET:' . PHP_EOL . var_export($_GET, true) . PHP_EOL . PHP_EOL;
$result .= 'POST:' . PHP_EOL . var_export($_POST, true) . PHP_EOL . PHP_EOL;
}
isset($_var) && $result .= 'VAR:' . PHP_EOL . var_export($_var, true) . PHP_EOL . PHP_EOL;
! empty($result) && file_put_contents($file, $result, FILE_APPEND);
$is_first = false;
($_is_new ?? null) && $is_first = true;
ini_set('html_errors', $html_errors);
return $result;
}
public function expired($options = null)
public function expired($options = null)
{
$options['status_name'] = 'expired';
$result = $this->cancel($options);
return $result;
}
public function fee($amount, $fee)
public function fee($amount, $fee)
{
$rt = 0;
$fix = 0;
$min = 0;
if (is_array($fee)) {
$rt = ($fee['rt'] ?? null) ?: $rt;
$fix = ($fee['fix'] ?? null) ?: $fix;
$min = ($fee['min'] ?? null) ?: $min;
} else {
$rt = ($fee ?? null) ?: $rt;
}
$result = $amount + $amount * ($rt / 100) + $fix;
($min > 0 && $min > $result) && $result = $min;
return $result;
}
public function get_account($options = null)
public function get_account($options = null)
{
$result = $this->account($options);
list($account_id, $account) = $result;
if ($account_id <= 0) {
$result = [
'status' => false,
'status_message' => 'Account does not exist',
];
}
return $result;
}
public function get_account__by_id($options = null)
public function get_account__by_id($options = null)
{
$account = &$this->account;
$account_id = $this->account_id;
// cache
if (empty($options['force']) && ! empty($account[$account_id])) {
return [$account_id, $account];
}
// get from db
$_ = &$options;
$account_id = (int) ($_['account_id'] ?? 0);
if (empty($account_id)) {
return null;
}
$result = db()->table('payment_account')
->where('account_id', '=', $account_id)
->order_by('account_id')
->get_deep_array(1);
if (empty($result)) {
$account = null;
$account_id = null;
} else {
$account = &$result[$account_id];
}
$this->account = $account;
$this->account_id = $account_id;
// get currency
$account_id && $this->get_currency__by_id([
'to_set' => true,
'currency_id' => $account['currency_id'],
]);
return [$account_id, $account];
}
public function get_account_type__by_name($options = null)
public function get_account_type__by_name($options = null)
{
$_ = &$options;
$name = ($_['name'] ?? null) ?: $this->account_type_id_default;
$result = db()->table('payment_account_type')
->where('name', '=', db()->es_for_builder($name))
->get_deep_array(1);
if (empty($result)) {
$account_type = null;
$account_type_id = null;
} else {
$account_type = reset($result);
$account_type_id = $account_type['account_type_id'];
}
return [$account_type_id, $account_type];
}
public function get_balance($options = null)
public function get_balance($options = null)
{
$result = $this->get_account($options);
list($account_id, $account) = $result;
// need user authentication
if ($account_id <= 0) {
return $result;
}
$value = $account['balance'];
// prepare balance
$decimals = $this->currency['minor_units'] ?? null;
$balance = $this->_number_float($value, $decimals);
return [$balance, $result];
}
public function get_currency__by_id($options = null)
public function get_currency__by_id($options = null)
{
$_ = &$options;
$to_set = $_['to_set'] ?? null;
$currency_id = ($_['currency_id'] ?? null)
?: $this->currency_id
?: $this->currency_id_default;
$result = $this->currencies[$currency_id] ?? null;
if (empty($result)) {
$currency = null;
$currency_id = null;
} else {
$currency = $result;
}
if ($to_set) {
$this->currency = $currency;
$this->currency_id = $currency_id;
}
return [$currency_id, $currency];
}
public function get_provider($options = null)
public function get_provider($options = null)
{
$_ = &$options;
$object = $this->provider($options);
$object_id_name = 'provider_id';
if (empty($object)) {
$name = ($_['exists'] ?? null) ?: ($_[$object_id_name] ?? null) ?: $_['name'];
$result = [
'status' => false,
'status_message' => 'Provider does not exist: "' . $name . '"',
];
return $result;
}
// 'exists' returns a bool
if (is_bool($object)) {
return $object;
}
if (count($object) == 1) {
$object = reset($object);
$object_id = (int) $object[$object_id_name];
$result = [$object_id, $object];
} else {
$result = $object;
}
return $result;
}
public function get_status($options = null)
public function get_status($options = null)
{
$_ = &$options;
$payment_status = $this->status($options);
if (empty($payment_status)) {
$name = ($_['exists'] ?? null) ?: ($_['status_id'] ?? null) ?: $_['name'];
$result = [
'status' => false,
'status_message' => 'Status does not exist: "' . $name . '"',
];
return $result;
}
// 'exists' returns a bool
if (is_bool($payment_status)) {
return $payment_status;
}
if (count($payment_status) == 1) {
$payment_status = reset($payment_status);
$payment_status_id = (int) $payment_status['status_id'];
$result = [$payment_status_id, $payment_status];
} else {
$result = $payment_status;
}
return $result;
}
public function get_type($options = null)
public function get_type($options = null)
{
$_ = &$options;
$object = $this->type($options);
if (empty($object)) {
$name = ($_['exists'] ?? null) ?: ($_['type_id'] ?? null) ?: $_['name'];
$result = [
'status' => false,
'status_message' => 'Payment type does not exist: "' . $name . '"',
];
return $result;
}
// 'exists' returns a bool
if (is_bool($object)) {
return $object;
}
if (count($object) == 1) {
$object = reset($object);
$object_id = (int) $object['type_id'];
$result = [$object_id, $object];
} else {
$result = $object;
}
return $result;
}
public function is_provider($options = null)
public function is_provider($options = null)
{
$result = null;
$object = $this->provider($options);
if (is_array($object) && count($object) == 1) {
return $object;
}
return $result;
}
public function is_user_mail($options = null)
public function is_user_mail($options = null)
{
$_user_id = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// var
$status = false;
$result = [
'status' => &$status,
'status_message' => &$status_message,
];
// check user
if (@$_user_id < 1) {
$status_message = 'User not found for sending email';
return $result;
}
$user = user($_user_id);
if (empty($user)) {
$status_message = 'User not found for sending email';
return $result;
}
// check mail
if ( ! @$user['email']) {
$status_message = 'Enter and confirm your email address in your account';
return $result;
}
// check mail verification
if (@$user['email'] != @$user['email_validated']) {
$status_message = 'Confirm your email address in your account';
return $result;
}
$status = true;
$result['user'] = $user;
$result['mail'] = $user['email'];
$result['name'] = ($user['name'] ?? null) ?: $user['login'];
return $result;
}
public function mail($options = null)
public function mail($options = null)
{
$result = true;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// tpl by type, status
if (empty($_tpl) && ! (empty($_type) && empty($_status))) {
$_tpl = $_type . '_' . $_status;
}
if (empty($_tpl)) {
return null;
}
if (empty($_type) || empty($_status)) {
list($type, $status) = @explode('_', $_tpl);
if ( ! ($_type ?? null) && ($type ?? null)) {
$_type = $type;
$options['type'] = $_type;
}
if ( ! ($_status ?? null) && ($status ?? null)) {
$_status = $status;
$options['status'] = $_status;
}
}
// var
$payment_api = $this;
$mail_class = _class('email');
// check user
if (($_user_id ?? null) > 0) {
$user_mail = $this->is_user_mail($options);
// check email, validate email
if ( ! ($_force ?? null) && ! ($user_mail['status'] ?? null)) {
return $user_mail;
}
$user = $user_mail['user'];
$mail_to = $user['email'];
$mail_name = $user['name'];
}
// check data
$data = [];
if ( ! empty($_data)) {
// import data
is_array($_data) && extract($_data, EXTR_PREFIX_ALL | EXTR_REFS, '_');
// amount
if ( ! empty($__amount)) {
$__amount = $payment_api->money_text($__amount);
}
$data = $_data;
}
// url
$url = [
'user_payments' => url_user('/payments'),
];
switch ($status) {
case 'confirmation':
$url['user_confirmation'] = url_user([
'object' => 'payment',
'operation_id' => ($__operation_id ?? null),
'code' => ($__code ?? null),
'is_confirmation' => 1,
]);
$url['user_confirmation_cancel'] = url_user([
'object' => 'payment',
'operation_id' => ($__operation_id ?? null),
'is_cancel' => 1,
]);
break;
}
// mail
$mail_admin_to = $mail_class->ADMIN_EMAIL;
$mail_admin_name = $mail_class->ADMIN_NAME;
$mail = [
'support_mail' => $mail_admin_to,
'support_name' => $mail_admin_name,
];
// compile
$data = array_replace_recursive($data, [
'url' => $url,
'mail' => $mail,
]);
$is_admin = ! empty($_is_admin);
$admin = ! empty($_admin);
// user
if ( ! $is_admin) {
$r = @$mail_class->_send_email_safe($mail_to, $mail_name, $_tpl, $data);
// mail fail
! $r && $this->mail_log([
'name' => 'mail_user',
'data' => [
'status' => 'fail',
'operation_id' => $__operation_id,
'user_id' => $_user_id,
'mail' => $mail_to,
'name' => $mail_name,
'tpl' => $_tpl,
],
]);
$result &= $r;
// mail copy
! $admin && $this->mail_copy(['tpl' => $_tpl, 'type' => $_type, 'status' => $_status, 'subject' => ($_subject ?? null), 'data' => $data]);
}
// admin
if ($admin || $is_admin) {
$url = [
'user_manage' => $this->url_admin([
'object' => 'members',
'action' => 'edit',
'id' => $_user_id,
]),
'user_balance' => $this->url_admin([
'object' => 'manage_payment',
'action' => 'balance',
'user_id' => $_user_id,
]),
'manage_payin' => $this->url_admin([
'object' => 'manage_deposit',
'action' => 'view',
'operation_id' => $__operation_id,
]),
'manage_payout' => $this->url_admin([
'object' => 'manage_payout',
'action' => 'view',
'operation_id' => $__operation_id,
]),
];
// compile
$data = array_replace_recursive($data, [
'url' => $url,
'user_title' => $user['name'] . ' (id: ' . $_user_id . ')',
]);
$tpl = $_tpl . '_admin';
$r = @$mail_class->_send_email_safe($mail_admin_to, $mail_admin_name, $tpl, $data);
// mail fail
! $r && $this->mail_log([
'name' => 'mail_admin',
'data' => [
'status' => 'fail',
'operation_id' => $__operation_id,
'user_id' => $_user_id,
'mail' => $mail_admin_to,
'name' => $mail_admin_name,
'tpl' => $tpl,
],
]);
// mail copy
$result_copy = $this->mail_copy(['tpl' => $tpl, 'type' => $_type, 'status' => $_status, 'subject' => ($_subject ?? null), 'data' => $data]);
! $result_copy && $this->mail_copy(['tpl' => $_tpl, 'type' => $_type, 'status' => $_status, 'subject' => ($_subject ?? null), 'data' => $data]);
}
return $result;
}
public function mail_copy($options = null)
public function mail_copy($options = null)
{
$_subject = $_tpl = $_data = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
if (empty($_tpl) || empty($_data)) {
return null;
}
// prepare admin mail
$mails = [];
$this->mail_copy_find($mails, $options);
// processing
$result = true;
if (is_array($mails)) {
$mail_class = _class('email');
$override = [];
($_subject ?? null) && $override['subject'] = $_subject;
$name = 'Payment admin';
$instant_send = true;
foreach ($mails as $mail) {
$r = @$mail_class->_send_email_safe($mail, $name, $_tpl, $_data, $instant_send, $override);
! $r && $this->mail_log([
'name' => 'mail_copy',
'data' => [
'status' => 'fail',
'operation_id' => $_data['operation_id'],
'mail' => $mail,
'name' => $name,
'tpl' => $_tpl,
],
]);
$result &= $r;
}
}
return $result;
}
public function mail_copy_find(&$mails, $options = null)
public function mail_copy_find(&$mails, $options = null)
{
$_type = $_status = null;
if (empty($this->MAIL_COPY_TO)) {
return;
}
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// var
$mail_copy = &$this->MAIL_COPY_TO;
// add: all, type, status
if ($mail_copy['all'] ?? null) {
$mail_ref = &$mail_copy['all'];
foreach (@['all', $_type, $_status] as $key) {
foreach ((array) ($mail_ref[$key] ?? null) as $value) {
$mails[$value] = $value;
}
}
}
// add by type: all, status
if ($mail_copy[$_type] ?? null) {
$mail_ref = &$mail_copy[$_type];
foreach (@['all', $_status] as $key) {
foreach ((array) ($mail_ref[$key] ?? null) as $value) {
$mails[$value] = $value;
}
}
}
}
public function mail_log($options = null)
public function mail_log($options = null)
{
$_name = $_data = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
if ( ! ($_name ?? null) || ! ($_data ?? null)) {
return null;
}
// log
$this->dump([
'is_transaction' => true,
'name' => $_name,
'var' => $_data,
]);
return true;
}
public function money_format($options = null)
public function money_format($options = null)
{
$_decimals = $_decimal_point = $_thousands_separator = $_value = $_nbsp = $_sign = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// currency
list($currency_id, $currency) = $this->get_currency__by_id(['currency_id' => ($_currency_id ?? null)]);
$decimals = $currency['minor_units'];
$sign = $currency['sign'];
switch ($_format ?? null) {
case 'html':
$thousands_separator = ' ';
// no break
default:
$thousands_separator = ' ';
break;
}
// format number
isset($_decimals) && $decimals = $_decimals;
is_string($_decimal_point) && $decimal_point = $_decimal_point;
is_string($_thousands_separator) && $thousands_separator = $_thousands_separator;
$value = $this->_number_round($_value, $decimals);
$value = $this->_number_format($value, $decimals, $decimal_point, $thousands_separator);
// decoration
$nbsp = '';
switch ($_format ?? null) {
case 'html':
$sign = '<span class="currency">' . $sign . '</span>';
$value = '<span class="money">' . $value . '</span>';
$nbsp = ' ';
$thousands_separator = ' ';
// no break
default:
$nbsp = ' ';
$thousands_separator = ' ';
break;
}
// render
$result = $value;
$_nbsp && $result .= $nbsp;
$_sign && $result .= $sign;
return $result;
}
public function money_html($options = null)
public function money_html($options = null)
{
! is_array($options) && $options = [
'value' => $options,
];
$options += [
'format' => 'html',
'sign' => true,
];
$result = $this->money_format($options);
return $result;
}
public function money_text($options = null)
public function money_text($options = null)
{
! is_array($options) && $options = [
'value' => $options,
];
$options += [
'sign' => true,
];
$result = $this->money_format($options);
return $result;
}
public function operation($options = null)
public function operation($options = null)
{
$_ = &$options;
$is_no_count = &$_['no_count'];
$is_sql = &$_['sql'];
$is_where = &$_['where'];
$is_no_limit = &$_['no_limit'];
$is_no_order_by = &$_['no_order_by'];
// by operation_id
$result = null;
$operation_id = &$_['operation_id'];
$is_array_operation_id = false;
if (isset($operation_id)) {
if ((is_int($operation_id) || ctype_digit($operation_id)) && $operation_id > 0) {
$operation_id = (int) $operation_id;
} elseif (is_array($operation_id) && count($operation_id) > 0) {
$is_array_operation_id = true;
} else {
return null;
}
}
$db = db()->table('payment_operation');
if ($operation_id > 0) {
if ($is_array_operation_id) {
$db->where('operation_id', 'in', db()->es_for_builder($operation_id));
} else {
$db->where('operation_id', $operation_id);
}
// sql only or fetch
if ($is_sql) {
$result = $db->sql();
} else {
$result = $db->all();
if ( ! $result) {
return $result;
}
$this->_operation_fetch(['data' => &$result]);
if ( ! $is_array_operation_id) {
$result = reset($result);
}
}
return $result;
}
// by account
$account_result = $this->get_account($options);
if (empty($account_result)) {
return $account_result;
}
list($account_id, $account) = $account_result;
$db->where('account_id', $account_id);
if ($is_where) {
$db->where_raw($is_where);
}
if ( ! $is_no_order_by) {
$db->order_by('datetime_update', 'DESC');
}
// limit
if ( ! $is_no_limit) {
$limit = (int) ($_['limit'] ?? null) ?: $this->OPERATION_LIMIT;
$limit_from = $_['limit_from'];
if (empty($limit_from)) {
$page = (int) $_['page'];
$page = $page < 1 ? 1 : $page;
$limit_from = ($page - 1) * $limit;
}
$db->limit($limit, $limit_from);
}
// sql only or fetch
if ($is_sql) {
$result = $db->sql();
} else {
$result = $db->all();
}
$count = null;
if ( ! $is_no_count) {
$count = $db->order_by()->limit(null)->count('*', $is_sql);
}
$this->_operation_fetch(['data' => &$result]);
return [$result, $count];
}
public function operation_update($data, $options = null)
public function operation_update($data, $options = null)
{
$result = $this->_object_update('operation', $data, $options);
return $result;
}
public function payment($options = null)
public function payment($options = null)
{
$_ = &$options;
$is_transaction = isset($_['is_transaction']) ? $_['is_transaction'] : true;
$_['type_name'] = __FUNCTION__;
$_['operation_title'] = ($_['operation_title'] ?? null) ?: 'Payment';
$result = $this->_operation_check($options);
list($status, $data, $operation_data) = $result;
if (empty($status)) {
return $result;
}
// update payment operation
$title = $_['operation_title'];
$data += [
'direction' => 'out',
'title' => $title,
];
// create operation
$is_transaction && db()->begin();
$status = db()->table('payment_operation')->insert($data);
if (empty($status)) {
$result = [
'status' => false,
'status_message' => 'Error creating the operation',
];
return $result;
}
$operation_id = (int) $status;
$data['operation_id'] = $operation_id;
// user confirmation
$result = $this->confirmation($options, $data, $operation_data);
if ( ! ($result['status'] ?? null)) {
$is_transaction && db()->rollback();
return $result;
}
$is_transaction && db()->commit();
// try provider operation
$provider_class = 'provider_' . $operation_data['provider']['name'];
$result = $this->_class($provider_class, __FUNCTION__, [
'is_transaction' => $is_transaction,
'options' => $options,
'provider' => $operation_data['provider'],
'data' => $data,
'operation_data' => $operation_data,
]);
$result['operation_id'] = $operation_id;
return $result;
}
public function payment_system($options = null)
public function payment_system($options = null)
{
$options['provider_name'] = 'system';
$result = $this->payment($options);
return $result;
}
public function payment_user($options = null)
public function payment_user($options = null)
{
$options['user_mode'] = true;
$result = $this->payment($options);
return $result;
}
public function provider($options = null)
public function provider($options = null)
{
$_provider_id = $_name = $_system = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// get providers
$provider = $this->provider;
$provider_index = $this->provider_index;
if (empty($provider)) {
$is_admin = main()->ADMIN_ID > 0;
$active = $is_admin || ($_is_service ?? null) ? 1 : 2;
$provider = db()->table('payment_provider')
->where('active', '>=', $active)
->order_by('order')
->get_deep_array(1);
if (empty($provider)) {
$provider = null;
$provider_index = null;
return $provider;
}
foreach ((array) $provider as $index => $item) {
$name = $item['name'];
$id = (int) $item['provider_id'];
$provider_index['all'][$id] = &$provider[$id];
$class = 'provider_' . $name;
$provider_class = $this->_class($class);
if ( ! ($provider_class && $provider_class->ENABLE)) {
unset($provider[$index]);
continue;
}
$system = (int) $item['system'];
$provider_index['system'][$system][$id] = &$provider[$id];
$provider_index['name'][$name][$id] = &$provider[$id];
}
}
/**
* options
* $_all
* $_exists
* $_provider_id
* $_name
* $_system.
*/
// test: exists by provider_id
if ( ! empty($_exists)) {
$result = ! empty($provider[$_exists]);
}
// all
elseif ( ! empty($_all)) {
$result = $provider_index['all'];
}
// by provider_id
elseif (isset($_provider_id)) {
$provider[$_provider_id] && $result = [$_provider_id => $provider[$_provider_id]];
}
// by name
elseif ( ! empty($_name)) {
$result = $provider_index['name'][$_name];
}
// by system
elseif (isset($_system)) {
$result = $provider_index['system'][(int) $_system];
}
// by default: all, not system
else {
$result = $provider_index['system'][0];
}
if (is_array($result)) {
foreach ($result as $index => $item) {
$_options = &$result[$index]['options'];
$_options && $_options = (array) json_decode($_options, true);
}
}
return $result;
}
public function provider_class($options = null)
public function provider_class($options = null)
{
$_provider_name = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
$result = null;
if ( ! empty($_provider_name)) {
$class = 'provider_' . $_provider_name;
$provider_class = $this->_class($class);
if ($provider_class && $provider_class->ENABLE) {
$result = $provider_class;
}
}
return $result;
}
public function provider_options(&$provider, $options = null)
public function provider_options(&$provider, $options = null)
{
if ( ! isset($options) || ! is_array($provider)) {
return false;
}
foreach ($provider as $id => $item) {
$name = $item['name'];
$class = 'provider_' . $name;
$provider_class = $this->_class($class);
if (empty($provider_class)) {
continue;
}
foreach ($options as $item) {
$provider[$id]['_' . $item] = &$provider_class->{$item};
}
}
return true;
}
public function provider_update($data, $options = null)
public function provider_update($data, $options = null)
{
$result = $this->_object_update('provider', $data, $options);
return $result;
}
public function sql_datetime($timestamp = null)
public function sql_datetime($timestamp = null)
{
$tz = '';
if ($this->TZ) {
$tz = date_default_timezone_get();
date_default_timezone_set($this->TZ);
}
$tpl = 'Y-m-d H:i:s';
if (is_int($timestamp)) {
$result = date($tpl, $timestamp);
} else {
$result = date($tpl);
}
if ($tz) {
date_default_timezone_set($tz);
}
return $result;
}
public function status($options = null)
public function status($options = null)
{
// get status
$status = $this->status;
$status_index = $this->status_index;
if (empty($status)) {
$status = db()->table('payment_status')->get_deep_array(1);
if (empty($status)) {
$status = null;
$status_index = null;
return $status;
}
foreach ((array) $status as $index => $item) {
$id = (int) $item['status_id'];
$name = $item['name'];
$status_index['name'][$name][$id] = &$status[$id];
}
}
// options
$_ = &$options;
$exists = ($_['exists'] ?? null);
$status_id = ($_['status_id'] ?? null);
$name = ($_['name'] ?? null);
// test: exists by status_id
if ( ! empty($exists)) {
$result = ! empty($status[$exists]);
}
// by status_id
elseif ( ! empty($status_id)) {
$result = [$status_id => $status[$status_id]];
}
// by name
elseif ( ! empty($name)) {
$result = $status_index['name'][$name];
}
// by default: all
else {
$result = $status;
}
return $result;
}
public function transaction($options = null)
public function transaction($options = null)
{
$_ = &$options;
$operation = ($_['operation'] ?? null);
if ( ! in_array($operation, ['payment', 'deposition'])) {
$result = [
'status' => false,
'status_message' => 'Unknown operation: ' . $operation,
];
return $result;
}
$result = $this->$operation($options);
return $result;
}
public function transaction_commit($options = null)
public function transaction_commit($options = null)
{
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
$result = $this->transaction_finish(['state' => 'COMMIT']);
return $result;
}
public function transaction_finish($options = null)
public function transaction_finish($options = null)
{
$_state = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
$tx = &$this->transaction;
$result = null;
$state = null;
if ($_state ?? null) {
$state = strtoupper($_state);
switch ($state) {
case 'COMMIT':
case 'ROLLBACK':
break;
default:
$state = null;
break;
}
}
if ($state && ($tx['level'] ?? null)) {
$result = db()->query($state);
$result &= $this->transaction_isolation(['level' => $tx['level']]);
$result && $tx['level'] = null;
}
return $result;
}
public function transaction_isolation($options = null)
public function transaction_isolation($options = null)
{
$_level = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// var
$result = null;
$new_level = null;
if ($_level ?? null) {
$level = strtoupper($_level);
switch ($_level) {
case 'READ UNCOMMITTED':
case 'READ COMMITTED':
case 'REPEATABLE READ':
case 'SERIALIZABLE':
$new_level = $level;
break;
}
if ($new_level) {
$result = db()->query('SET SESSION TRANSACTION ISOLATION LEVEL ' . $new_level);
return $result;
}
}
// get currency level
$r = db()->get_2d('SHOW VARIABLES LIKE "%' . $this->TX_VAR . '%"');
($r[$this->TX_VAR] ?? null) && $result = str_replace('-', ' ', $r[$this->TX_VAR]);
return $result;
}
public function transaction_rollback($options = null)
public function transaction_rollback($options = null)
{
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
$result = $this->transaction_finish(['state' => 'ROLLBACK']);
return $result;
}
public function transaction_start($options = null)
public function transaction_start($options = null)
{
$_operation_id = $_status_id = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
$tx = &$this->transaction;
$result = null;
// save last transaction isolation level
if ( ! ($tx['level'] ?? false)) {
$tx['level'] = $this->transaction_isolation();
// set highest level of isolation
$result = $this->transaction_isolation(['level' => 'SERIALIZABLE']);
$result &= db()->query('START TRANSACTION');
// lock operation id
if ($result) {
$items = (array) $_operation_id;
foreach ($items as $item) {
if (@(int) $item > 0) {
$sql_datetime = $this->sql_datetime();
$operation_id = (int) $item;
$data = [
'operation_id' => $operation_id,
'datetime_update' => $sql_datetime,
];
$_status_id && $data['status_id'] = $_status_id;
$r = $this->operation_update($data);
$result &= @(bool) $r['status'];
}
}
}
}
return $result;
}
public function transaction_system($options = null)
public function transaction_system($options = null)
{
$_ = &$options;
$operation = ($_['operation'] ?? '') . '_system';
if ( ! in_array($operation, ['payment_system', 'deposition_system'])) {
$result = [
'status' => false,
'status_message' => 'Unknown operation: ' . $operation,
];
return $result;
}
$result = $this->$operation($options);
return $result;
}
public function transfer($options = null)
public function transfer($options = null)
{
$_ = &$options;
$is_transaction = isset($_['is_transaction']) ? $_['is_transaction'] : true;
// check: from, to
if ( ! ($_['from'] ?? null) || ! ($_['to'] ?? null)) {
$result = [
'status' => false,
'status_message' => 'Not enough data',
];
return $result;
}
// prepare
$_['type_name'] = __FUNCTION__;
$_['operation_title'] = ($_['operation_title'] ?? null) ?: 'Transfer';
$data = $_;
unset($data['from'], $data['to']);
// prepare operation
$options_from = $data;
$options_from['user_id'] = (int) $_['from']['user_id'];
$options_from['direction'] = 'out';
$options_to = $data;
$options_to['user_id'] = (int) $_['to']['user_id'];
$options_to['direction'] = 'in';
// prepare to operation
$result = $this->_operation_check($options_from);
list($status, $data_from, $operation_data_from) = $result;
if (empty($status)) {
return $result;
}
$result = $this->_operation_check($options_to);
list($status, $data_to, $operation_data_to) = $result;
if (empty($status)) {
return $result;
}
// check currency
$currency_id_from = &$operation_data_from['account']['currency_id'];
$currency_id_to = &$operation_data_to['account']['currency_id'];
if ($currency_id_from != $currency_id_to) {
$result = [
'status' => false,
'status_message' => 'Error: currencies must be the same',
];
return $result;
}
// update payment operation
$title = $_['operation_title'];
$data_from += [
'direction' => 'out',
'title' => $title,
];
$data_to += [
'direction' => 'in',
'title' => $title,
];
// create operation
$is_transaction && db()->begin();
$status_from = db()->table('payment_operation')->insert($data_from);
$status_to = db()->table('payment_operation')->insert($data_to);
if ( ! ($status_from ?? null) || ! ($status_to ?? null)) {
$is_transaction && db()->rollback();
$result = [
'status' => false,
'status_message' => 'Error creating the operation',
];
return $result;
}
// get operation_id
$operation_id_from = (int) $status_from;
$data_from['operation_id'] = $operation_id_from;
$operation_id_to = (int) $status_to;
$data_to['operation_id'] = $operation_id_to;
$is_transaction && db()->commit();
// try provider operation
$object = $this->provider_class($options);
if (empty($object)) {
$result = [
'status' => false,
'status_message' => 'Unknown provider class',
];
return $result;
}
$result = $object->{ __FUNCTION__ }([
'is_transaction' => $is_transaction,
'options' => $options,
'provider' => $operation_data_from['provider'],
'data' => [
'from' => $data_from,
'to' => $data_to,
],
'operation_data' => [
'from' => $operation_data_from,
'to' => $operation_data_to,
],
]);
$result['operation_id_from'] = $operation_id_from;
$result['operation_id_to'] = $operation_id_to;
return $result;
}
public function transfer_system($options = null)
public function transfer_system($options = null)
{
$options['provider_name'] = 'system';
$result = $this->transfer($options);
return $result;
}
public function transfer_user($options = null)
public function transfer_user($options = null)
{
$options['user_mode'] = true;
$result = $this->transfer($options);
return $result;
}
public function type($options = null)
public function type($options = null)
{
// get type
$type = $this->type;
$type_index = $this->type_index;
if (empty($type)) {
$type = db()->table('payment_type')->where('active', 1)->get_deep_array(1);
foreach ((array) $type as $index => $item) {
$id = (int) $item['type_id'];
$name = $item['name'];
$type_index['name'][$name][$id] = &$type[$id];
}
}
// options
$_ = &$options;
$exists = ($_['exists'] ?? null);
$type_id = ($_['type_id'] ?? null);
$name = ($_['name'] ?? null);
// test: exists by type_id
if ( ! empty($exists)) {
$result = ! empty($type[$exists]);
}
// by type_id
elseif ( ! empty($type_id)) {
$result = [$type_id => $type[$type_id]];
}
// by name
elseif ( ! empty($name)) {
$result = $type_index['name'][$name];
}
// by default: all
else {
$result = $type;
}
return $result;
}
public function url_admin($options = null)
public function url_admin($options = null)
{
if (empty($options)) {
return null;
}
$result = url_admin($options);
if (substr($result, 0, 2) == '//') {
$result = str_replace('//', 'http://', $result);
}
return $result;
}
public function user_id($value = -1)
public function user_id($value = -1)
{
$object = &$this->user_id;
if ($value !== -1 && $this->check_user_id($value)) {
$object = $value;
}
return $object;
}
_ hooks and helpers; not routable as page actionspublic function _amount_revert($options = null)
public function _amount_revert($options = null)
{
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// operation
$operation = $this->operation(['operation_id' => ($_operation_id ?? null)]);
if (empty($operation)) {
$result = [
'status' => false,
'status_message' => 'Operation not found: ' . @$_operation_id,
];
return $result;
}
// amount revert
$account_id = $operation['account_id'];
$amount = $operation['amount'];
// update account
$sql_amount = $this->_number_mysql($amount);
$sql_datetime = $this->sql_datetime();
$data = [
'account_id' => $account_id,
'datetime_update' => db()->escape_val($sql_datetime),
'balance' => '( balance + ' . $sql_amount . ' )',
];
$result = $this->balance_update($data, ['is_escape' => false]);
return $result;
}
public function _cancel($options = null)
public function _cancel($options = null)
{
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// var
if (($_operation_id ?? null) < 1) {
$result = [
'status' => false,
'status_message' => 'Invalid operation ID',
];
return $result;
}
// start transaction
$this->transaction_start($options);
// revert funds
if ($_is_revert ?? null) {
$result = $this->_amount_revert($options);
if (empty($result['status'])) {
$this->transaction_rollback();
return $result;
}
}
// update operation balance
$options = [
'status_name' => ($_status_name ?? null) ?: 'cancelled',
'is_finish' => true,
] + $options;
$result = $this->_operation_balance_update($options);
if (empty($result['status'])) {
$this->transaction_rollback();
return $result;
}
// finish transaction
$this->transaction_commit();
return $result;
}
public function _class($class, $method = null, $options = null)
public function _class($class, $method = null, $options = null)
{
$removed = strncmp((string) $class, 'provider_', 9) === 0 ? substr((string) $class, 9) : '';
if ($removed !== '' && isset($this->REMOVED_PROVIDERS[$removed])) {
if ( ! isset($this->_removed_warned[$removed])) {
$this->_removed_warned[$removed] = true;
trigger_error('PAYMENT: provider "' . $removed . '" ' . $this->REMOVED_PROVIDERS[$removed], E_USER_WARNING);
}
return null;
}
$_path = $this->_class_path;
// plugins
$_class_name = 'payment_api' . '__' . $class;
$_class = _class_safe($_class_name, $_path);
$status = $_class instanceof $_class_name;
if ( ! $status) {
// yf
$_class_name = 'yf_payment_api' . '__' . $class;
$_class = _class_safe($_class_name, $_path);
$status = $_class instanceof $_class_name;
if ( ! $status) {
return $_class;
}
}
if (empty($method)) {
return $_class;
}
$status = method_exists($_class, $method);
if ( ! $status) {
return $_class;
}
$result = $_class->{ $method }($options);
return $result;
}
public function _default($list = null)
public function _default($list = null)
{
$result = null;
foreach ($list as $index => $value) {
if ($value !== null) {
$result = &$list[$index];
break;
}
}
return $result;
}
public function _hash($str)
public function _hash($str)
{
$hash = hash('sha256', ($str ?? null), true);
$base64 = base64_encode($hash);
$clean = str_replace(['+', '=', '/'], '', $base64);
$result = substr($clean, 0, 16);
return $result;
}
public function _init()
public function _init()
{
$this->config();
$this->user_id_default = (int) main()->USER_ID;
$this->user_id($this->user_id_default);
$mysql_version = db()->get_one('SELECT VERSION()');
if ($mysql_version >= 8) {
$this->TX_VAR = 'transaction_isolation';
}
}
public function _merge()
public function _merge()
{
$options = func_get_args();
$data = [];
foreach ($options as $option) {
if (is_array($option) && ! empty($option)) {
$data[] = $option;
}
}
$result = call_user_func_array('array_merge_recursive', $data);
return $result;
}
public function _number_api($float = 0, $decimals = null, $decimal_point = null, $thousands_separator = '', $decimal_point_force = null)
public function _number_api($float = 0, $decimals = null, $decimal_point = null, $thousands_separator = '', $decimal_point_force = null)
{
return $this->_number_format($float, $decimals, $decimal_point ?: '.', $thousands_separator, $decimal_point_force);
}
public function _number_float($float = 1, $decimals = null, $decimal_point = null, $thousands_separator = '', $decimal_point_force = null)
public function _number_float($float = 1, $decimals = null, $decimal_point = null, $thousands_separator = '', $decimal_point_force = null)
{
return (float) $this->_number_format($float, $decimals, $decimal_point ?: '.', $thousands_separator, $decimal_point_force);
}
public function _number_format($float = 0, $decimals = null, $decimal_point = null, $thousands_separator = null, $decimal_point_force = null)
public function _number_format($float = 0, $decimals = null, $decimal_point = null, $thousands_separator = null, $decimal_point_force = null)
{
if (isset($decimal_point_force)) {
$_decimal_point = $decimal_point_force;
} else {
$locale_info = localeconv();
$_decimal_point = $locale_info['decimal_point'];
}
$float = (float) str_replace($_decimal_point, '.', $float);
! isset($decimals) && $decimals = @$this->DECIMALS ?: 2;
! isset($decimal_point) && $decimal_point = @$this->DECIMAL_POINT ?: ',';
! isset($thousands_separator) && $thousands_separator = @$this->THOUSANDS_SEPARATOR ?: ' ';
if (empty($this->FORCE_DECIMALS) && (int) $float == $float) {
$decimals = 0;
}
$float = number_format($float, $decimals, $decimal_point, '`');
$float = str_replace('`', $thousands_separator, $float);
return $float;
}
public function _number_from_mysql($float = 0)
public function _number_from_mysql($float = 0)
{
return $this->_number_format($float);
}
public function _number_mysql($float = 0, $decimals = null, $decimal_point = null, $thousands_separator = '', $decimal_point_force = null)
public function _number_mysql($float = 0, $decimals = null, $decimal_point = null, $thousands_separator = '', $decimal_point_force = null)
{
return $this->_number_format($float, $decimals, $decimal_point ?: '.', $thousands_separator, $decimal_point_force);
}
public function _number_round($float = 0, $precision = null, $mode = null)
public function _number_round($float = 0, $precision = null, $mode = null)
{
$precision = $precision ?: $this->DECIMALS;
$mode = $mode ?: $this->ROUND_MODE;
$result = round($float, $precision, $mode);
$result = $result == 0 ? 0.0 : $result; // fix php round bug: -0.00
return $result;
}
public function _operation_balance_update($options = null)
public function _operation_balance_update($options = null)
{
$_status_name = $_operation_id = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// operation
$operation = $this->operation($options);
if (empty($operation)) {
$result = [
'status' => false,
'status_message' => 'Operation not found: ' . @$_operation_id,
];
return $result;
}
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// update balance
$account_id = $operation['account_id'];
$object = $this->get_account__by_id(['account_id' => $account_id, 'force' => true]);
if (empty($object)) {
$result = [
'status' => false,
'status_message' => 'Error updating the account',
];
return $result;
}
list($account_id, $account) = $object;
$balance = $account['balance'];
// status
$status_id = &$_status_id;
if ($_status_name ?? null) {
$object = $this->get_status(['name' => $_status_name]);
list($status_id, $status) = $object;
if (empty($status_id)) {
return $object;
}
}
// prepare
$sql_datetime = $this->sql_datetime();
$data = [
'operation_id' => $_operation_id,
'balance' => $balance,
'datetime_update' => $sql_datetime,
];
($status_id ?? null) && $data['status_id'] = $status_id;
($_is_finish ?? null) && $data['datetime_finish'] = $sql_datetime;
$result = $this->operation_update($data);
return $result;
}
public function _operation_check($options = null)
public function _operation_check($options = null)
{
$result = [];
$data = [];
// options
$_ = &$options;
// check user_id
$value = $this->_default([
($_['user_id'] ?? null),
$this->user_id,
]);
$value = $this->check_user_id($value);
if (empty($value)) {
$result = [
'status' => -1,
'status_message' => 'Authorization required',
];
return $result;
}
$data['user_id'] = $value;
// check direction
$direction = null;
switch ($_['direction'] ?? null) {
case 'in':
$direction = 'in';
break;
case 'out':
$direction = 'out';
break;
}
$direction && $data['direction'] = $direction;
// check type
$object = [];
if ( ! empty($_['type_name'])) {
$object['name'] = $_['type_name'];
} elseif ( ! empty($_['type_id'])) {
$object['type_id'] = (int) $_['type_id'];
}
$object = $this->type($object);
if (empty($object)) {
$result = [
'status' => false,
'status_message' => 'This operation is not available',
];
return $result;
}
$type = reset($object);
$type_id = (int) $type['type_id'];
$data['type'] = $type;
// check status
$status = $this->status(['name' => 'in_progress']);
if (empty($status)) {
$result = [
'status' => false,
'status_message' => 'Status does not exist: "in_progress"',
];
return $result;
}
$status = reset($status);
$status_id = (int) $status['status_id'];
$data['status'] = $status;
// check account
$_options = $options;
unset($_options['currency_id']);
list($balance, $account_result) = $this->get_balance($_options);
if (empty($account_result)) {
return $account_result;
}
list($account_id, $account) = $account_result;
list($currency_id, $currency) = $this->get_currency__by_id([
'currency_id' => $account['currency_id'],
]);
$data['account'] = $account;
// check amount
$decimals = $currency['minor_units'];
$amount = $this->_number_float($_['amount'], $decimals);
if ($amount <= 0) {
$result = [
'status' => false,
'status_message' => 'Amount must be greater than zero',
];
return $result;
}
$sql_amount = $this->_number_mysql($amount, $decimals);
$data['sql_amount'] = $sql_amount;
// check balance limit lower
! isset($_['is_balance_limit_lower']) && $_['is_balance_limit_lower'] = $this->IS_BALANCE_LIMIT_LOWER;
$balance_limit_lower = $this->_default([
$_['balance_limit_lower'],
$account['options']['balance_limit_lower'] ?? null,
$this->BALANCE_LIMIT_LOWER,
0,
]);
$balance_limit_lower = $this->_number_float($balance_limit_lower);
if (($_['user_mode'] ?? null) && (
($type['name'] == 'payment' && $_['is_balance_limit_lower'] && ($balance - $amount < $balance_limit_lower))
|| ($type['name'] == 'transfer' && $direction === 'out' && $_['is_balance_limit_lower'] && ($balance - $amount < $balance_limit_lower))
)) {
$result = [
'status' => false,
'status_code' => 'BALANCE_LIMIT_LOW',
'status_message' => 'Insufficient funds in the account',
];
return $result;
}
// check payout limit min
$payout_limit_min = @$this->PAYOUT_LIMIT_MIN;
$payout_limit_min = $this->_number_float($payout_limit_min);
if (($_['user_mode'] ?? null) &&
($type['name'] == 'payment' && ($balance < $payout_limit_min))
) {
$result = [
'status' => false,
'status_message' => 'Minimum payout amount: ' . $payout_limit_min . $currency['sign'],
];
return $result;
}
// prepare provider
$object = [];
if ( ! empty($_['provider_name'])) {
$object['name'] = $_['provider_name'];
} elseif ( ! empty($_['provider_id'])) {
$object['provider_id'] = (int) $_['provider_id'];
}
$object = $this->provider($object);
if (empty($object)) {
$result = [
'status' => false,
'status_message' => 'Unknown provider',
];
return $result;
}
$provider = reset($object);
if (($_['user_mode'] ?? null) && (bool) $provider['system']) {
$result = [
'status' => false,
'status_message' => 'Unknown provider',
];
return $result;
}
$provider_id = (int) $provider['provider_id'];
$data['provider'] = $provider;
// provider class
$object = $this->provider_class([
'provider_name' => $provider['name'],
]);
if (empty($object)) {
$result = [
'status' => false,
'status_message' => 'Unknown provider class',
];
return $result;
}
// provider validate
$result = $object->validate($options);
if (empty($result['status'])) {
return $result;
}
// prepare result
$sql_datetime = $this->sql_datetime();
$data['sql_datetime'] = $sql_datetime;
$result = [
'account_id' => $account_id,
'provider_id' => $provider_id,
'status_id' => $status_id, // in_progress
'type_id' => $type_id, // deposition, payment, etc
'amount' => $sql_amount,
'datetime_start' => $sql_datetime,
'datetime_update' => $sql_datetime,
];
return [true, $result, $data];
}
public function _operation_fetch($options = null)
public function _operation_fetch($options = null)
{
$_data = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
if (is_array($_data)) {
$datetime_key = ['start', 'finish', 'update'];
foreach ($_data as $index => &$item) {
$_item_options = &$item['options'];
$_item_options && $_item_options = (array) json_decode($_item_options, true);
foreach ($datetime_key as $key) {
$item['_ts_' . $key] = strtotime($item['datetime_' . $key] ?? 'now');
}
}
}
}
public function _replace()
public function _replace()
{
$options = func_get_args();
$data = [];
foreach ($options as $option) {
if (is_array($option) && ! empty($option)) {
$data[] = $option;
}
}
$result = call_user_func_array('array_replace_recursive', $data);
return $result;
}