feat: ui polish, docs, api hardening, and common pages
This commit is contained in:
@@ -398,6 +398,23 @@ class EmojiApiController extends Controller
|
||||
private function trackDailyUsage(Request $request, string $q, string $category, string $subcategory): array
|
||||
{
|
||||
$dailyLimit = max((int) config('dewemoji.free_daily_limit', 30), 1);
|
||||
$rateLimitEnabled = (bool) config('dewemoji.rate_limit_enabled', true);
|
||||
|
||||
// Local development should not silently look broken because of daily metering.
|
||||
if (!$rateLimitEnabled || app()->environment('local')) {
|
||||
return [
|
||||
'blocked' => false,
|
||||
'meta' => [
|
||||
'used' => 0,
|
||||
'limit' => $dailyLimit,
|
||||
'remaining' => $dailyLimit,
|
||||
'window' => 'daily',
|
||||
'window_ends_at' => Carbon::tomorrow('UTC')->toIso8601String(),
|
||||
'count_basis' => 'distinct_query',
|
||||
'metering' => 'disabled',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$key = trim((string) $request->query('key', ''));
|
||||
if ($key === '') {
|
||||
|
||||
@@ -131,10 +131,14 @@ class SiteController extends Controller
|
||||
|
||||
$items = $decoded['emojis'] ?? [];
|
||||
$match = null;
|
||||
$byEmoji = [];
|
||||
foreach ($items as $item) {
|
||||
$char = (string) ($item['emoji'] ?? '');
|
||||
if ($char !== '' && !isset($byEmoji[$char])) {
|
||||
$byEmoji[$char] = $item;
|
||||
}
|
||||
if (($item['slug'] ?? '') === $slug) {
|
||||
$match = $item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,8 +146,20 @@ class SiteController extends Controller
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$relatedDetails = [];
|
||||
foreach (array_slice($match['related'] ?? [], 0, 8) as $relatedEmoji) {
|
||||
$relatedEmoji = (string) $relatedEmoji;
|
||||
$ref = $byEmoji[$relatedEmoji] ?? null;
|
||||
$relatedDetails[] = [
|
||||
'emoji' => $relatedEmoji,
|
||||
'slug' => (string) ($ref['slug'] ?? ''),
|
||||
'name' => (string) ($ref['name'] ?? $relatedEmoji),
|
||||
];
|
||||
}
|
||||
|
||||
return view('site.emoji-detail', [
|
||||
'emoji' => $match,
|
||||
'relatedDetails' => $relatedDetails,
|
||||
'canonicalPath' => '/emoji/'.$slug,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -175,6 +175,15 @@ class LicenseVerificationService
|
||||
}
|
||||
|
||||
$purchase = is_array($json['purchase'] ?? null) ? $json['purchase'] : [];
|
||||
if (!$this->isTruthy($purchase['is_valid'] ?? true)) {
|
||||
continue;
|
||||
}
|
||||
if ($this->isTruthy($purchase['refunded'] ?? false)) {
|
||||
continue;
|
||||
}
|
||||
if ($this->isTruthy($purchase['chargebacked'] ?? false)) {
|
||||
continue;
|
||||
}
|
||||
$isRecurring = !empty($purchase['recurrence']);
|
||||
|
||||
return [
|
||||
@@ -231,6 +240,10 @@ class LicenseVerificationService
|
||||
if ($apiKey === '') {
|
||||
$apiKey = trim((string) config('dewemoji.billing.providers.mayar.secret_key', ''));
|
||||
}
|
||||
$productIds = config('dewemoji.billing.providers.mayar.product_ids', []);
|
||||
if (!is_array($productIds)) {
|
||||
$productIds = [];
|
||||
}
|
||||
if ($url === '') {
|
||||
return ['ok' => false, 'err' => 'mayar_missing_url'];
|
||||
}
|
||||
@@ -240,10 +253,15 @@ class LicenseVerificationService
|
||||
$request = Http::timeout($timeout)
|
||||
->withHeaders(['Accept' => 'application/json']);
|
||||
if ($apiKey !== '') {
|
||||
$request = $request->withToken($apiKey);
|
||||
$request = $request->withToken($apiKey)
|
||||
->withHeaders(['X-API-Key' => $apiKey]);
|
||||
}
|
||||
|
||||
$response = $request->post($url, ['license_key' => $key]);
|
||||
$response = $request->post($url, [
|
||||
'license_key' => $key,
|
||||
'license' => $key,
|
||||
'key' => $key,
|
||||
]);
|
||||
|
||||
if (!$response->successful()) {
|
||||
return ['ok' => false, 'err' => 'mayar_http_'.$response->status()];
|
||||
@@ -255,17 +273,38 @@ class LicenseVerificationService
|
||||
}
|
||||
|
||||
$data = is_array($json['data'] ?? null) ? $json['data'] : [];
|
||||
$valid = (($json['success'] ?? false) === true) || (($json['valid'] ?? false) === true) || (($data['valid'] ?? false) === true);
|
||||
$status = strtolower((string) ($data['status'] ?? $json['status'] ?? ''));
|
||||
$valid = (($json['success'] ?? false) === true)
|
||||
|| (($json['valid'] ?? false) === true)
|
||||
|| (($data['valid'] ?? false) === true)
|
||||
|| in_array($status, ['active', 'paid', 'completed', 'valid'], true);
|
||||
if (!$valid) {
|
||||
return ['ok' => false, 'err' => 'mayar_invalid'];
|
||||
}
|
||||
|
||||
$productId = $this->firstString([
|
||||
$data['product_id'] ?? null,
|
||||
$data['productId'] ?? null,
|
||||
$data['product_code'] ?? null,
|
||||
$json['product_id'] ?? null,
|
||||
]);
|
||||
if (!empty($productIds) && ($productId === null || !in_array($productId, $productIds, true))) {
|
||||
return ['ok' => false, 'err' => 'mayar_no_match'];
|
||||
}
|
||||
|
||||
$planType = strtolower((string) ($data['type'] ?? 'lifetime'));
|
||||
$expiresAt = $this->firstString([
|
||||
$data['expires_at'] ?? null,
|
||||
$data['expired_at'] ?? null,
|
||||
$data['expiry_date'] ?? null,
|
||||
$data['valid_until'] ?? null,
|
||||
$json['expires_at'] ?? null,
|
||||
]);
|
||||
return [
|
||||
'ok' => true,
|
||||
'plan' => 'pro',
|
||||
'product_id' => (string) ($data['product_id'] ?? '') ?: null,
|
||||
'expires_at' => isset($data['expires_at']) ? (string) $data['expires_at'] : null,
|
||||
'product_id' => $productId,
|
||||
'expires_at' => $expiresAt,
|
||||
'meta' => [
|
||||
'plan_type' => $planType,
|
||||
],
|
||||
@@ -332,4 +371,38 @@ class LicenseVerificationService
|
||||
'details' => $details,
|
||||
];
|
||||
}
|
||||
|
||||
private function isTruthy(mixed $value): bool
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value;
|
||||
}
|
||||
if (is_numeric($value)) {
|
||||
return (int) $value === 1;
|
||||
}
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'true', 'yes', 'y', 'on'], true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int,mixed> $values
|
||||
*/
|
||||
private function firstString(array $values): ?string
|
||||
{
|
||||
foreach ($values as $value) {
|
||||
if (!is_string($value)) {
|
||||
continue;
|
||||
}
|
||||
$value = trim($value);
|
||||
if ($value !== '') {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user