Deploy Ladill Woo Manager / deploy (push) Failing after 2s
Standalone app with SSO shell, WordPress plugin connect flow, webhook ingest, fulfillment inbox, and plugin activation API — no payment processing. Co-authored-by: Cursor <cursoragent@cursor.com>
178 lines
5.3 KiB
PHP
178 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Notifications;
|
|
|
|
use App\Models\MiniPayment;
|
|
use App\Models\User;
|
|
use App\Notifications\Mini\MiniAlertNotification;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class MiniNotificationService
|
|
{
|
|
public function __construct(private FcmService $fcm) {}
|
|
|
|
public function paymentReceived(MiniPayment $payment): void
|
|
{
|
|
$payment->loadMissing(['qrCode', 'merchant']);
|
|
$merchant = $payment->merchant;
|
|
|
|
if (! $merchant) {
|
|
return;
|
|
}
|
|
|
|
$businessName = $payment->qrCode?->content()['business_name'] ?? $payment->qrCode?->label ?? 'Payment QR';
|
|
$amount = $this->formatMoney($payment->currency, $payment->merchant_amount_minor ?: $payment->amount_minor);
|
|
$title = 'Payment received';
|
|
$message = sprintf('%s paid %s to %s.', $this->payerLabel($payment), $amount, $businessName);
|
|
|
|
$this->alert(
|
|
$merchant,
|
|
$title,
|
|
$message,
|
|
'payment',
|
|
route('mini.payments.index'),
|
|
'payment_received',
|
|
[
|
|
'payment_id' => $payment->id,
|
|
'reference' => $payment->reference,
|
|
'amount_minor' => $payment->amount_minor,
|
|
'currency' => $payment->currency,
|
|
],
|
|
respectPayoutPref: false,
|
|
);
|
|
}
|
|
|
|
public function withdrawalSubmitted(User $user, float $amountMajor, string $currency = 'GHS'): void
|
|
{
|
|
if (! $this->payoutAlertsEnabled($user)) {
|
|
return;
|
|
}
|
|
|
|
$amount = sprintf('%s %s', $currency, number_format($amountMajor, 2));
|
|
$this->alert(
|
|
$user,
|
|
'Withdrawal requested',
|
|
sprintf('Your withdrawal of %s has been submitted and is being processed.', $amount),
|
|
'payout',
|
|
route('mini.payouts'),
|
|
'withdrawal_submitted',
|
|
['amount_major' => $amountMajor, 'currency' => $currency],
|
|
respectPayoutPref: false,
|
|
);
|
|
}
|
|
|
|
public function walletCredited(User $user, int $amountMinor, string $currency, string $description): void
|
|
{
|
|
if (! $this->payoutAlertsEnabled($user)) {
|
|
return;
|
|
}
|
|
|
|
$amount = $this->formatMoney($currency, $amountMinor);
|
|
$this->alert(
|
|
$user,
|
|
'Wallet credited',
|
|
sprintf('%s added to your wallet. %s', $amount, $description),
|
|
'wallet',
|
|
route('mini.payouts'),
|
|
'wallet_credited',
|
|
['amount_minor' => $amountMinor, 'currency' => $currency],
|
|
respectPayoutPref: false,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $extra
|
|
*/
|
|
private function alert(
|
|
User $user,
|
|
string $title,
|
|
string $message,
|
|
string $icon,
|
|
?string $url,
|
|
string $milestone,
|
|
array $extra = [],
|
|
bool $respectPayoutPref = true,
|
|
): void {
|
|
if ($respectPayoutPref && ! $this->payoutAlertsEnabled($user)) {
|
|
return;
|
|
}
|
|
|
|
$user->notify(new MiniAlertNotification($title, $message, $icon, $url, $milestone, $extra));
|
|
$this->pushToDevices($user, $title, $message, array_merge($extra, [
|
|
'milestone' => $milestone,
|
|
'url' => $url,
|
|
]));
|
|
}
|
|
|
|
/** @param array<string, mixed> $data */
|
|
private function pushToDevices(User $user, string $title, string $body, array $data = []): void
|
|
{
|
|
if (! $this->fcm->isConfigured() || ! $this->shouldAttemptFcm($user)) {
|
|
return;
|
|
}
|
|
|
|
$windowDays = (int) config('notifications.fcm_active_user_within_days', 60);
|
|
$cutoff = now()->subDays($windowDays);
|
|
|
|
$tokens = $user->pushTokens()
|
|
->where(function ($query) use ($cutoff) {
|
|
$query->where('last_seen_at', '>=', $cutoff)
|
|
->orWhere('updated_at', '>=', $cutoff);
|
|
})
|
|
->get();
|
|
|
|
foreach ($tokens as $device) {
|
|
try {
|
|
$outcome = $this->fcm->send($device->token, $title, $body, $data);
|
|
|
|
if ($outcome === FcmService::INVALID_TOKEN) {
|
|
$device->delete();
|
|
}
|
|
} catch (\Throwable $e) {
|
|
Log::warning('Mini push failed', [
|
|
'user_id' => $user->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function shouldAttemptFcm(User $user): bool
|
|
{
|
|
if ($user->pushTokens()->doesntExist()) {
|
|
return false;
|
|
}
|
|
|
|
$windowDays = (int) config('notifications.fcm_active_user_within_days', 60);
|
|
|
|
if ($user->last_app_active_at === null) {
|
|
return false;
|
|
}
|
|
|
|
return $user->last_app_active_at->gte(now()->subDays($windowDays));
|
|
}
|
|
|
|
private function payoutAlertsEnabled(User $user): bool
|
|
{
|
|
return (bool) ($user->getOrCreateQrSetting()->notify_payouts ?? true);
|
|
}
|
|
|
|
private function formatMoney(string $currency, int $amountMinor): string
|
|
{
|
|
return sprintf('%s %s', $currency, number_format($amountMinor / 100, 2));
|
|
}
|
|
|
|
private function payerLabel(MiniPayment $payment): string
|
|
{
|
|
if ($payment->payer_name) {
|
|
return $payment->payer_name;
|
|
}
|
|
|
|
if ($payment->payer_phone) {
|
|
return $payment->payer_phone;
|
|
}
|
|
|
|
return 'A customer';
|
|
}
|
|
}
|