Files
memefast/app/Helpers/FirstParty/Credits/CreditsHelper.php
2025-07-03 00:48:06 +08:00

213 lines
6.9 KiB
PHP

<?php
namespace App\Helpers\FirstParty\Credits;
use App\Helpers\FirstParty\Purchase\PurchaseHelper;
use App\Models\UserCredit;
use App\Models\UserCreditTransaction;
use Illuminate\Support\Facades\DB;
class CreditsHelper
{
public static function getCreditPackByStripePriceId($stripe_price_id)
{
$credit_packs = self::getCreditPacks(null, true);
foreach ($credit_packs as $credit_pack) {
$stripe_price_ids = PurchaseHelper::getPlanSystemProperty($credit_pack, 'stripe.stripe_price_ids');
if (in_array($stripe_price_id, $stripe_price_ids)) {
return $credit_pack;
}
}
return null;
}
public static function getCreditsPackById($id, $enabled = true, $system = false)
{
$credits_packs = self::getCreditPacks($enabled, $system);
foreach ($credits_packs as $credits_pack) {
if ($credits_pack['id'] == $id) {
return $credits_pack;
}
}
return null;
}
public static function getCreditPacks($enabled = null, $system = true)
{
$credit_packs = [];
$one_times = PurchaseHelper::getOneTimePurchases($enabled, $system);
foreach ($one_times as $one_time) {
if ($one_time['type'] == 'alacarte_credits') {
$credit_packs[] = $one_time;
}
}
return $credit_packs;
}
// ////
public static function getUserCredits(int $user_id): UserCredit
{
$user_credit = UserCredit::where('user_id', $user_id)->first();
if (! $user_credit) {
$user_credit = UserCredit::create([
'user_id' => $user_id,
'subscription_credits' => 0,
'alacarte_credits' => 0,
'spend_subscription_first' => true,
]);
}
return $user_credit;
}
// when user purchase annual subscription, need to 12x the deposit amount
public static function deposit(int $user_id, int $amount, string $type, ?string $description = null): bool
{
if ($amount <= 0) {
throw new \InvalidArgumentException('Amount must be positive');
}
if (! in_array($type, [UserCredit::TYPE_SUBSCRIPTION, UserCredit::TYPE_ALACARTE])) {
throw new \InvalidArgumentException('Type must be subscription or alacarte');
}
return DB::transaction(function () use ($user_id, $amount, $type, $description) {
$user_credit = self::getUserCredits($user_id);
if ($type === UserCredit::TYPE_SUBSCRIPTION) {
$user_credit->subscription_credits += $amount;
} else {
$user_credit->alacarte_credits += $amount;
}
$user_credit->save();
self::logTransaction($user_id, $type, 'deposit', $amount, $user_credit, $description);
return true;
});
}
public static function spend(int $user_id, int $amount, ?string $description = null, ?bool $use_subscription_first = null): bool
{
if ($amount <= 0) {
throw new \InvalidArgumentException('Amount must be positive');
}
if (! self::canSpend($user_id, $amount)) {
return false;
}
return DB::transaction(function () use ($user_id, $amount, $description, $use_subscription_first) {
$user_credit = self::getUserCredits($user_id);
$remaining = $amount;
$subscription_first = $use_subscription_first ?? $user_credit->spend_subscription_first;
$spend_order = $subscription_first
? [UserCredit::TYPE_SUBSCRIPTION, UserCredit::TYPE_ALACARTE]
: [UserCredit::TYPE_ALACARTE, UserCredit::TYPE_SUBSCRIPTION];
foreach ($spend_order as $type) {
if ($remaining <= 0) {
break;
}
$available = $type === UserCredit::TYPE_SUBSCRIPTION
? $user_credit->subscription_credits
: $user_credit->alacarte_credits;
if ($available > 0) {
$to_spend = min($remaining, $available);
if ($type === UserCredit::TYPE_SUBSCRIPTION) {
$user_credit->subscription_credits -= $to_spend;
} else {
$user_credit->alacarte_credits -= $to_spend;
}
self::logTransaction($user_id, $type, 'spend', -$to_spend, $user_credit, $description);
$remaining -= $to_spend;
}
}
$user_credit->save();
return true;
});
}
public static function canSpend(int $user_id, int $amount): bool
{
$user_credit = self::getUserCredits($user_id);
return self::_getTotalBalance($user_credit) >= $amount;
}
public static function summary(int $user_id): array
{
$user_credit = self::getUserCredits($user_id);
return [
'subscription' => $user_credit->subscription_credits,
'alacarte' => $user_credit->alacarte_credits,
'total' => self::_getTotalBalance($user_credit),
'preference' => $user_credit->spend_subscription_first ? 'subscription_first' : 'alacarte_first',
];
}
public static function balance(int $user_id, ?string $type = null): int
{
$user_credit = self::getUserCredits($user_id);
return match ($type) {
UserCredit::TYPE_SUBSCRIPTION => $user_credit->subscription_credits,
UserCredit::TYPE_ALACARTE => $user_credit->alacarte_credits,
default => self::_getTotalBalance($user_credit),
};
}
private static function _getTotalBalance(UserCredit $user_credit): int
{
return $user_credit->subscription_credits + $user_credit->alacarte_credits;
}
public static function setSpendSubscriptionFirst(int $user_id, bool $spendSubscriptionFirst): bool
{
$user_credit = self::getUserCredits($user_id);
$user_credit->spend_subscription_first = $spendSubscriptionFirst;
return $user_credit->save();
}
public static function transactionHistory(int $user_id, int $limit = 20): \Illuminate\Database\Eloquent\Collection
{
$user_credit = self::getUserCredits($user_id);
return $user_credit->transactions()->limit($limit)->get();
}
private static function logTransaction(int $user_id, string $type, string $operation, int $amount, UserCredit $user_credit, ?string $description = null): void
{
UserCreditTransaction::create([
'user_id' => $user_id,
'credit_type' => $type,
'operation' => $operation,
'amount' => $amount,
'subscription_balance_after' => $user_credit->subscription_credits,
'alacarte_balance_after' => $user_credit->alacarte_credits,
'description' => $description,
]);
}
}