123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- -module(kvs_account).
- -include_lib("kvs/include/accounts.hrl").
- -include_lib("kvs/include/membership.hrl").
- -include_lib("kvs/include/payments.hrl").
- -include_lib("kvs/include/log.hrl").
- -include_lib("kvs/include/feed_state.hrl").
- -compile(export_all).
- transaction(Account, Currency, 0, TransactionInfo) -> ok;
- transaction(Account, Currency, Amount, TransactionInfo) when Amount /= 0->
- {Remitter, Acceptor} = if Amount > 0 -> {system, Account}; true -> {Account, system} end,
- transaction(Remitter, Acceptor, Currency, abs(Amount), TransactionInfo).
- transaction(Remitter, Acceptor, Currency, Amount, TransactionInfo) ->
- TX = #transaction{id = generate_id(), acceptor = Acceptor, remitter = Remitter,
- amount = Amount, commit_time = now(), currency = Currency, info = TransactionInfo},
- commit_transaction(TX).
- debet(Account, Currency) ->
- case kvs:get(account,{Account, Currency}) of
- {ok,#account{debet = Debet}} -> {ok, Debet};
- Error -> Error end.
- credit(AccountId, Currency) ->
- case kvs:get(account,{AccountId, Currency}) of
- {ok,#account{credit = Credit}} -> {ok, Credit};
- Error -> Error end.
- balance(AccountId, Currency) ->
- case kvs:get(account,{AccountId, Currency}) of
- {ok, #account{debet = Debet, credit = Credit}} -> {ok, Debet - Credit};
- Error -> Error end.
- create_account(AccountId) ->
- Currencies = get_currencies(),
- try [{ok, Currency} = {create_account(AccountId, Currency), Currency} || Currency <- Currencies]
- catch _:_ -> {error, unable_create_account} end.
- create_account(AccountId, Currency) ->
- Account = #account{id = {AccountId, Currency},
- credit = 0, debet = 0, last_change = 0},
- case kvs:put(Account) of
- ok -> ok;
- Error -> ?ERROR("create_account: put to db error: ~p", [Error]),
- {error, unable_to_store_account} end.
- check_quota(User) -> check_quota(User, 0).
- check_quota(User, Amount) ->
- SoftLimit = kvs:get_config("accounts/quota_limit/soft", -20),
- {ok, Balance} = balance(User, quota),
- BalanceAfterChange = Balance - Amount,
- if
- BalanceAfterChange > SoftLimit ->
- ok;
- true ->
- HardLimit = kvs:get(config, "accounts/quota_limit/hard", -100),
- if
- BalanceAfterChange =< HardLimit ->
- {error, hard_limit};
- true ->
- {error, soft_limit}
- end
- end.
- commit_transaction(#transaction{remitter = R, acceptor = A, currency = Currency, amount = Amount} = TX) ->
- case change_accounts(R, A, Currency, Amount) of
- ok -> skip;
- %mqs:notify([transaction, user, R, add_transaction], TX),% notify_transaction(R,TX),
- %mqs:notify([transaction, user, A, add_transaction], TX);%notify_transaction(A,TX);
- Error -> skip
- % case TX#transaction.info of
- % #tx_game_event{} ->
- % mqs:notify_transaction(R,TX),
- % mqs:notify_transaction(A,TX);
- % _ ->
- % ?ERROR("commit transaction error: change accounts ~p", [Error]),
- % Error
- % end
- end.
- change_accounts(Remitter, Acceptor, Currency, Amount) ->
- case {kvs:get(account,{Remitter, Currency}), kvs:get(account,{Acceptor, Currency})} of
- {{ok, RA = #account{}}, {ok, AA = #account{}}} ->
- ?INFO("transacrion: RemitterAccount ~p, AcceptorAccount: ~p", [RA, AA]),
- %% check balance for remitter according to currency and amount
- case check_remitter_balance(RA, Amount) of
- ok -> RA1 = RA#account{credit = RA#account.credit + Amount, last_change = -Amount },
- AA1 = AA#account{debet = AA#account.debet + Amount, last_change = Amount},
- kvs:put([AA1, RA1]);
- {error, Reason} -> {error, {remitter_balance, Reason}} end;
- {{error, Reason}, #account{}} -> {error, {remitter_account_unavailable, Reason}};
- {#account{}, {error, Reason}} -> {error, {acceptor_account_unavailable, Reason}};
- {RE, AE} -> {error, both_accounts_unavailable} end.
- check_remitter_balance(#account{id = {system, _}}, _) -> ok;
- check_remitter_balance(_Account, _Amount) -> ok.
- get_currencies() -> [internal, currency, money, quota, points].
- generate_id() ->
- {MegSec, Sec, MicroSec} = now(),
- H = erlang:phash2(make_ref()),
- lists:concat([MegSec*1000000000000, Sec*1000000, MicroSec, "-", H]).
- transactions(UserId) -> tx_list(UserId, undefined, 10000).
- tx_list(UserId, undefined, PageAmount) ->
- case kvs:get(user_transaction, UserId) of
- {ok, O} when O#user_transaction.top =/= undefined -> tx_list(UserId, O#user_transaction.top, PageAmount);
- {error, notfound} -> [] end;
- tx_list(UserId, StartFrom, Limit) ->
- case kvs:get(transaction,StartFrom) of
- {ok, #transaction{next = N}=P} -> [ P | kvs:traversal(transaction, #transaction.next, N, Limit)];
- X -> [] end.
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- handle_notice(["kvs_account", "user", User, "transaction"] = Route,
- Message, #state{owner = Owner, type =Type} = State) ->
- ?INFO("queue_action(~p): add_transaction: Owner=~p, Route=~p, Message=~p", [self(), {Type, Owner}, Route, Message]),
- MP = Message,
- add_transaction_to_user(User,MP),
- {noreply, State};
- handle_notice(Route, Message, State) -> error_logger:info_msg("Unknown ACCOUNTS notice").
- %%%%%%%%%%%%%%%%%%%%%%%%%
- add_transaction_to_user(UserId,Purchase) ->
- {ok,Team} = case kvs:get(user_transaction, UserId) of
- {ok,T} -> {ok,T};
- _ -> ?INFO("user_transaction not found"),
- Head = #user_transaction{ user = UserId, top = undefined},
- {kvs:put(Head),Head}
- end,
- EntryId = Purchase#transaction.id, %kvs:next_id("membership_purchase",1),
- Prev = undefined,
- case Team#user_transaction.top of
- undefined -> Next = undefined;
- X -> case kvs:get(transaction, X) of
- {ok, TopEntry} ->
- Next = TopEntry#transaction.id,
- EditedEntry = #transaction {
- commit_time = TopEntry#transaction.commit_time,
- amount = TopEntry#transaction.amount,
- remitter = TopEntry#transaction.remitter,
- acceptor = TopEntry#transaction.acceptor,
- currency = TopEntry#transaction.currency,
- info = TopEntry#transaction.info,
- id = TopEntry#transaction.id,
- next = TopEntry#transaction.next,
- prev = EntryId },
- kvs:put(EditedEntry); % update prev entry
- {error,notfound} -> Next = undefined
- end
- end,
- Entry = #transaction{id = EntryId,
- commit_time = Purchase#transaction.commit_time,
- amount = Purchase#transaction.amount,
- remitter = Purchase#transaction.remitter,
- acceptor = Purchase#transaction.acceptor,
- currency = Purchase#transaction.currency,
- info = Purchase#transaction.info,
- next = Next,
- prev = Prev},
- case kvs:put(Entry) of ok -> kvs:put(#user_transaction{ user = UserId, top = EntryId}), {ok, EntryId};
- Error -> ?INFO("Cant write transaction"), {failure,Error} end.
|