Files
dewemoji/app/resources/views/site/pricing.blade.php
2026-02-14 18:08:19 +07:00

681 lines
37 KiB
PHP

@extends('site.layout')
@section('title', 'Pricing - Dewemoji')
@section('meta_description', 'Choose Dewemoji pricing for Free, Personal subscription, and Lifetime access for website, extension, and API usage.')
@push('jsonld')
<script type="application/ld+json">
{
"@@context": "https://schema.org",
"@@graph": [
{
"@@type": "Product",
"name": "Dewemoji Personal",
"description": "Personal plan unlocks Dewemoji extension and API access.",
"brand": {"@@type": "Brand", "name": "Dewemoji"},
"offers": [
{"@@type":"Offer","price":"30000","priceCurrency":"IDR","availability":"https://schema.org/InStock","url":"{{ route('pricing') }}"},
{"@@type":"Offer","price":"300000","priceCurrency":"IDR","availability":"https://schema.org/InStock","url":"{{ route('pricing') }}"}
]
},
{
"@@type": "Product",
"name": "Dewemoji Lifetime",
"description": "Lifetime access to Dewemoji extension and API.",
"brand": {"@@type": "Brand", "name": "Dewemoji"},
"offers": {"@@type":"Offer","price":"900000","priceCurrency":"IDR","availability":"https://schema.org/InStock","url":"{{ route('pricing') }}"}
}
]
}
</script>
@endpush
@section('content')
<div class="flex h-screen w-full">
<style>
#qris-code canvas,
#qris-code img {
background: #ffffff;
}
.qris-modal-card.glass-card:hover {
background: #ffffff !important;
border-color: rgba(15, 23, 42, 0.12) !important;
transform: none !important;
box-shadow: none !important;
}
html.dark .qris-modal-card.glass-card:hover {
background: rgba(2, 6, 23, 0.9) !important;
border-color: rgba(255, 255, 255, 0.08) !important;
}
</style>
<aside class="hidden lg:flex w-20 lg:w-64 h-full glass-panel flex-col justify-between p-4 z-50 shrink-0">
<div>
<div class="flex items-center gap-3 px-2 mb-8 mt-2">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-white to-gray-300 flex items-center justify-center shadow-lg shadow-white/20 shrink-0">
<img src="/assets/logo/logo-mark.svg" alt="Dewemoji logo" class="w-7 h-7 object-contain" />
</div>
<h1 class="font-display font-bold text-lg tracking-tight hidden lg:block">Dewemoji</h1>
</div>
<nav class="space-y-1">
<a href="{{ route('home') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
<i data-lucide="layout-grid" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Discover</span>
</a>
<a href="{{ route('api-docs') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
<i data-lucide="book-open" class="w-5 h-5"></i><span class="text-sm hidden lg:block">API Docs</span>
</a>
<a href="{{ route('pricing') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl bg-white/10 text-brand-sun border border-white/5 transition-all">
<i data-lucide="badge-dollar-sign" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Pricing</span>
</a>
<a href="{{ route('support') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
<i data-lucide="life-buoy" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Support</span>
</a>
</nav>
</div>
<div class="space-y-1">
@auth
<a href="{{ route('dashboard.overview') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
<i data-lucide="layout-dashboard" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Dashboard</span>
</a>
@endauth
@guest
<a href="{{ route('login') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
<i data-lucide="log-in" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Login</span>
</a>
@endguest
<a href="{{ route('privacy') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
<i data-lucide="shield-check" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Privacy</span>
</a>
<a href="{{ route('terms') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
<i data-lucide="file-text" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Terms</span>
</a>
</div>
</aside>
<main class="flex-1 flex flex-col h-full min-w-0 relative z-10">
<header class="glass-header px-6 py-5 shrink-0 flex justify-between items-center gap-4">
<div class="flex items-center gap-3 text-xs text-gray-500">
<span>Prices shown in <strong class="text-gray-300">{{ $currencyPref ?? 'USD' }}</strong></span>
<form method="POST" action="{{ route('pricing.currency') }}" class="flex items-center gap-2">
@csrf
<button type="submit" name="currency" value="IDR"
class="px-3 py-1.5 rounded-full text-[11px] font-semibold border {{ ($currencyPref ?? 'USD') === 'IDR' ? 'bg-white/10 text-white border-white/10' : 'text-gray-400 border-white/5 hover:text-white hover:border-white/10' }}">
IDR
</button>
<button type="submit" name="currency" value="USD"
class="px-3 py-1.5 rounded-full text-[11px] font-semibold border {{ ($currencyPref ?? 'USD') === 'USD' ? 'bg-white/10 text-white border-white/10' : 'text-gray-400 border-white/5 hover:text-white hover:border-white/10' }}">
USD
</button>
</form>
<span class="hidden md:inline">PayPal fixed rate Rp {{ number_format($usdRate ?? 15000) }}/USD</span>
</div>
<button class="text-sm font-semibold text-brand-oceanSoft hover:text-white transition-colors">Restore Purchases</button>
<button id="theme-toggle" class="w-9 h-9 rounded-full theme-surface border border-white/10 shadow-lg flex items-center justify-center text-gray-300 hover:text-white transition-colors">
<span class="sr-only">Toggle theme</span>
<i data-lucide="moon" class="w-4 h-4" data-theme-icon="dark"></i>
<i data-lucide="sun" class="w-4 h-4 hidden" data-theme-icon="light"></i>
</button>
</header>
<div class="flex-1 overflow-y-auto p-6 md:p-10">
<div class="max-w-5xl mx-auto">
<div class="text-center mb-12">
<h1 class="font-display text-4xl md:text-5xl font-bold mb-3">Supercharge your <span class="text-gradient">emoji workflow</span></h1>
<p class="text-gray-400">Use Dewemoji for search, extension, and API in one account system.</p>
</div>
@php
$pref = $currencyPref ?? 'USD';
$monthlyIdr = $pricing['personal_monthly']['idr'] ?? 30000;
$annualIdr = $pricing['personal_annual']['idr'] ?? 300000;
$lifetimeIdr = $pricing['personal_lifetime']['idr'] ?? 900000;
$monthlyUsd = $pricing['personal_monthly']['usd'] ?? 2;
$annualUsd = $pricing['personal_annual']['usd'] ?? 20;
$lifetimeUsd = $pricing['personal_lifetime']['usd'] ?? 60;
$qrisUrl = $payments['qris_url'] ?? '';
$paypalUrl = $payments['paypal_url'] ?? '';
$paypalJoiner = $paypalUrl && str_contains($paypalUrl, '?') ? '&' : '?';
$paypalLifetimeUrl = $paypalUrl ? $paypalUrl.$paypalJoiner.'plan=personal_lifetime' : '';
$canQris = $pakasirEnabled ?? false;
$paypalEnabled = $paypalEnabled ?? false;
$paypalPlans = $paypalPlans ?? ['personal_monthly' => false, 'personal_annual' => false];
@endphp
<div class="mb-6 flex flex-wrap items-center justify-center gap-3 text-sm text-gray-400">
<div class="rounded-full border border-white/10 bg-white/5 p-1 flex items-center">
<button type="button" class="period-toggle rounded-full px-4 py-2 font-semibold text-gray-200" data-period="monthly">Monthly</button>
<button type="button" class="period-toggle rounded-full px-4 py-2 font-semibold text-gray-400 hover:text-gray-200" data-period="annual">Annual</button>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-5 mb-8" data-default-currency="{{ $pref === 'IDR' ? 'IDR' : 'USD' }}">
<section class="glass-card rounded-3xl p-6 flex flex-col">
<h2 class="font-display text-xl font-bold">Starter</h2>
<p class="text-sm text-gray-500 mt-1">For casual usage</p>
<div class="mt-5 mb-6"><span class="text-4xl font-bold">$0</span><span class="text-gray-500">/mo</span></div>
<div class="flex-1"></div>
<button class="mt-6 w-full py-2.5 rounded-xl bg-white/5 border border-white/10 text-gray-300">Current Plan</button>
</section>
<section class="relative rounded-3xl p-6 flex flex-col border border-brand-ocean/40 bg-gradient-to-b from-brand-ocean/15 to-transparent shadow-[0_0_28px_rgba(32,83,255,0.18)] md:-translate-y-2">
<div class="absolute -top-3 right-4 rounded-full bg-brand-sun text-black text-[10px] font-bold px-3 py-1 shadow-lg">Most Popular</div>
<div class="flex items-center justify-between gap-3">
<h2 class="font-display text-xl font-bold text-brand-oceanSoft">Personal</h2>
<div class="rounded-full border border-white/10 bg-white/5 p-1 flex items-center text-xs">
<button type="button" class="currency-toggle rounded-full px-3 py-1.5 font-semibold text-gray-200" data-currency="USD">USD</button>
<button type="button" class="currency-toggle rounded-full px-3 py-1.5 font-semibold text-gray-400 hover:text-gray-200" data-currency="IDR">IDR</button>
</div>
</div>
<p class="text-sm text-gray-400 mt-1">For private keywords + sync</p>
<div class="mt-4 mb-2" id="personal-price"
data-monthly-usd="{{ rtrim(rtrim(number_format($monthlyUsd, 2), '0'), '.') }}"
data-annual-usd="{{ rtrim(rtrim(number_format($annualUsd, 2), '0'), '.') }}"
data-monthly-idr="{{ number_format($monthlyIdr) }}"
data-annual-idr="{{ number_format($annualIdr) }}">
<span class="text-4xl font-bold">$0</span><span class="text-gray-400">/mo</span>
</div>
<div class="mb-6 text-xs text-gray-500" id="personal-secondary"
data-monthly-usd-note="≈ Rp {{ number_format($monthlyIdr) }} (QRIS)"
data-annual-usd-note="≈ Rp {{ number_format($annualIdr) }} (QRIS)"
data-monthly-idr-note="${{ rtrim(rtrim(number_format($monthlyUsd, 2), '0'), '.') }} (PayPal fixed rate)"
data-annual-idr-note="${{ rtrim(rtrim(number_format($annualUsd, 2), '0'), '.') }} (PayPal fixed rate)">
</div>
<div class="flex-1"></div>
<div class="mt-6 space-y-2">
<div class="hidden text-xs text-gray-500" id="personal-pay-note"></div>
<button type="button"
id="personal-pay-btn"
data-paypal-enabled="{{ $paypalEnabled && $paypalPlans['personal_monthly'] ? 'true' : 'false' }}"
data-paypal-annual-enabled="{{ $paypalEnabled && $paypalPlans['personal_annual'] ? 'true' : 'false' }}"
data-qris-enabled="{{ $canQris ? 'true' : 'false' }}"
class="!text-white w-full py-2.5 rounded-xl bg-brand-ocean hover:bg-brand-oceanSoft text-white font-semibold text-center block">
Pay now
</button>
</div>
<div class="hidden">
<button type="button" data-paypal-plan="personal_monthly" data-original="Start Personal"></button>
<button type="button" data-paypal-plan="personal_annual" data-original="Start Personal"></button>
<button type="button" data-qris-plan="personal_monthly" data-original="Start Personal"></button>
<button type="button" data-qris-plan="personal_annual" data-original="Start Personal"></button>
</div>
</section>
<section class="glass-card rounded-3xl p-6 flex flex-col">
<div class="flex items-center justify-between gap-3">
<h2 class="font-display text-xl font-bold">Lifetime</h2>
<div class="rounded-full border border-white/10 bg-white/5 p-1 flex items-center text-xs">
<button type="button" class="currency-toggle rounded-full px-3 py-1.5 font-semibold text-gray-200" data-currency="USD">USD</button>
<button type="button" class="currency-toggle rounded-full px-3 py-1.5 font-semibold text-gray-400 hover:text-gray-200" data-currency="IDR">IDR</button>
</div>
</div>
<p class="text-sm text-gray-500 mt-1">Pay once, use forever</p>
<div class="mt-5 mb-2" id="lifetime-price"
data-lifetime-usd="{{ rtrim(rtrim(number_format($lifetimeUsd, 2), '0'), '.') }}"
data-lifetime-idr="{{ number_format($lifetimeIdr) }}">
<span class="text-4xl font-bold">$0</span>
</div>
<div class="mb-4 text-xs text-gray-500" id="lifetime-secondary"
data-lifetime-usd-note="≈ Rp {{ number_format($lifetimeIdr) }} (QRIS)"
data-lifetime-idr-note="${{ rtrim(rtrim(number_format($lifetimeUsd, 2), '0'), '.') }} (PayPal fixed rate)">
</div>
<div class="flex-1"></div>
<div class="mt-6 space-y-2">
<div class="hidden text-xs text-gray-500" id="lifetime-pay-note"></div>
<button type="button"
id="lifetime-pay-btn"
data-paypal-enabled="{{ $paypalLifetimeUrl ? 'true' : 'false' }}"
data-qris-enabled="{{ $canQris ? 'true' : 'false' }}"
class="force-white w-full py-2.5 rounded-xl border border-brand-ocean/60 text-brand-ocean font-semibold text-center block hover:bg-brand-ocean/10">
Pay now
</button>
</div>
<div class="hidden">
<a href="{{ $paypalLifetimeUrl ?: '#' }}"
target="_blank" rel="noopener noreferrer"
data-paypal-lifetime="true"
class="{{ $paypalLifetimeUrl ? '' : 'pointer-events-none' }}">
PayPal Lifetime
</a>
<button type="button"
data-qris-plan="personal_lifetime" data-original="Get Lifetime Access">
QRIS Lifetime
</button>
</div>
</section>
</div>
<section class="glass-card rounded-2xl p-6 text-sm text-gray-300">
<h3 class="font-semibold text-white mb-4">Plan comparison</h3>
<div class="overflow-x-auto">
<table class="min-w-full text-left text-sm">
<thead class="text-xs uppercase tracking-[0.2em] text-gray-400">
<tr>
<th class="py-3 pr-4">Feature</th>
<th class="py-3 pr-4">Starter</th>
<th class="py-3 pr-4">Personal</th>
<th class="py-3 pr-4">Lifetime</th>
</tr>
</thead>
<tbody class="divide-y divide-white/10 text-gray-300">
<tr>
<td class="py-3 pr-4 border-t border-white/10">Emoji search + discovery</td>
<td class="py-3 pr-4 border-t border-white/10">Included</td>
<td class="py-3 pr-4 border-t border-white/10">Included</td>
<td class="py-3 pr-4 border-t border-white/10">Included</td>
</tr>
<tr>
<td class="py-3 pr-4 border-t border-white/10">Private keywords</td>
<td class="py-3 pr-4 border-t border-white/10">Up to 20</td>
<td class="py-3 pr-4 border-t border-white/10">Unlimited</td>
<td class="py-3 pr-4 border-t border-white/10">Unlimited</td>
</tr>
<tr>
<td class="py-3 pr-4 border-t border-white/10">Keyword sync</td>
<td class="py-3 pr-4 border-t border-white/10">Account-only</td>
<td class="py-3 pr-4 border-t border-white/10">All channels</td>
<td class="py-3 pr-4 border-t border-white/10">All channels</td>
</tr>
<tr>
<td class="py-3 pr-4 border-t border-white/10">API access</td>
<td class="py-3 pr-4 border-t border-white/10">Not included</td>
<td class="py-3 pr-4 border-t border-white/10">Included</td>
<td class="py-3 pr-4 border-t border-white/10">Included</td>
</tr>
<tr>
<td class="py-3 pr-4 border-t border-white/10">Public search usage</td>
<td class="py-3 pr-4 border-t border-white/10">Hourly limits apply</td>
<td class="py-3 pr-4 border-t border-white/10">Unlimited</td>
<td class="py-3 pr-4 border-t border-white/10">Unlimited</td>
</tr>
<tr>
<td class="py-3 pr-4 border-t border-white/10">Support</td>
<td class="py-3 pr-4 border-t border-white/10">Standard</td>
<td class="py-3 pr-4 border-t border-white/10">Priority</td>
<td class="py-3 pr-4 border-t border-white/10">Priority</td>
</tr>
<tr>
<td class="py-3 pr-4 border-t border-white/10">Updates</td>
<td class="py-3 pr-4 border-t border-white/10">Regular</td>
<td class="py-3 pr-4 border-t border-white/10">All updates</td>
<td class="py-3 pr-4 border-t border-white/10">All updates</td>
</tr>
</tbody>
</table>
</div>
</section>
</div>
</div>
</main>
</div>
<div id="qris-modal" class="hidden fixed inset-0 z-50 items-center justify-center">
<div class="absolute inset-0 bg-black/70 backdrop-blur-sm"></div>
<div class="relative z-10 w-full max-w-lg rounded-3xl glass-card qris-modal-card p-6 bg-white/95 text-slate-900 dark:bg-slate-950/90 dark:text-white">
<div class="flex items-center justify-between">
<div>
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">QRIS Payment</div>
<div class="mt-2 text-xl font-semibold text-white">Scan to pay</div>
</div>
</div>
<div class="mt-6 grid gap-4 md:grid-cols-2 items-center">
<div class="rounded-2xl bg-white/10 border border-white/10 p-4 flex items-center justify-center">
<div id="qris-code" class="rounded-xl bg-white p-3 shadow-lg"></div>
</div>
<div class="space-y-3 text-sm text-gray-300">
<div class="rounded-xl bg-white/5 border border-white/10 p-3">
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">Amount</div>
<div id="qris-amount" class="mt-1 text-lg font-semibold text-white">Rp 0</div>
</div>
<div class="rounded-xl bg-white/5 border border-white/10 p-3">
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">Expires</div>
<div id="qris-expiry" class="mt-1 text-sm text-gray-300">Complete within 30 minutes</div>
</div>
<div id="qris-text" class="hidden"></div>
</div>
</div>
<div class="mt-6 flex items-center justify-end gap-2">
<button id="qris-cancel" class="rounded-full bg-rose-500 text-white font-semibold px-4 py-2 text-sm hover:bg-rose-600">Cancel payment</button>
</div>
</div>
</div>
@endsection
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
<script>
(() => {
const buttons = document.querySelectorAll('[data-paypal-plan]');
if (!buttons.length) return;
const csrf = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
const isAuthed = @json(auth()->check());
buttons.forEach((btn) => {
btn.addEventListener('click', async () => {
const plan = btn.dataset.paypalPlan;
if (!plan) return;
if (!isAuthed) {
window.location.href = "{{ route('login') }}";
return;
}
const original = btn.dataset.original || btn.textContent;
btn.disabled = true;
btn.textContent = 'Redirecting...';
try {
const res = await fetch("{{ route('billing.paypal.create') }}", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
...(csrf ? { 'X-CSRF-TOKEN': csrf } : {}),
},
body: JSON.stringify({ plan_code: plan }),
});
const data = await res.json().catch(() => null);
if (!res.ok || !data?.approve_url) {
const reason = data?.error ? ` (${data.error})` : '';
alert('Could not start PayPal checkout. Please try again.' + reason);
btn.disabled = false;
btn.textContent = original;
return;
}
window.location.href = data.approve_url;
} catch (e) {
alert('Checkout failed. Please try again.');
btn.disabled = false;
btn.textContent = original;
}
});
});
})();
</script>
<script>
(() => {
const periodButtons = document.querySelectorAll('.period-toggle');
const currencyButtons = document.querySelectorAll('.currency-toggle');
const priceWrap = document.getElementById('personal-price');
const secondary = document.getElementById('personal-secondary');
const payBtn = document.getElementById('personal-pay-btn');
const payNote = document.getElementById('personal-pay-note');
const lifetimePrice = document.getElementById('lifetime-price');
const lifetimeSecondary = document.getElementById('lifetime-secondary');
const lifetimePay = document.getElementById('lifetime-pay-btn');
const lifetimeNote = document.getElementById('lifetime-pay-note');
if (!priceWrap || !secondary || !payBtn || !lifetimePrice || !lifetimeSecondary || !lifetimePay) return;
let period = 'monthly';
let currency = document.querySelector('[data-default-currency]')?.dataset.defaultCurrency || 'USD';
const setActive = (nodes, value) => {
nodes.forEach((btn) => {
const active = btn.dataset[btn.classList.contains('period-toggle') ? 'period' : 'currency'] === value;
btn.classList.toggle('text-gray-200', active);
btn.classList.toggle('text-gray-400', !active);
});
};
const paypalIcon = `<svg role="img" viewBox="0 0 24 24" aria-hidden="true" class="w-4 h-4"><path fill="currentColor" d="M15.607 4.653H8.941L6.645 19.251H1.82L4.862 0h7.995c3.754 0 6.375 2.294 6.473 5.513-.648-.478-2.105-.86-3.722-.86m6.57 5.546c0 3.41-3.01 6.853-6.958 6.853h-2.493L11.595 24H6.74l1.845-11.538h3.592c4.208 0 7.346-3.634 7.153-6.949a5.24 5.24 0 0 1 2.848 4.686M9.653 5.546h6.408c.907 0 1.942.222 2.363.541-.195 2.741-2.655 5.483-6.441 5.483H8.714Z"/></svg>`;
const qrisIcon = `<svg viewBox="0 0 24 24" aria-hidden="true" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3h6v6H3z"/><path d="M15 3h6v6h-6z"/><path d="M3 15h6v6H3z"/><path d="M15 15h2v2h-2z"/><path d="M17 19h2v2h-2z"/><path d="M19 15h2v2h-2z"/><path d="M15 19h2v2h-2z"/></svg>`;
const setButtonLabel = (btn, label, icon) => {
if (!btn) return;
btn.classList.add('inline-flex', 'items-center', 'justify-center', 'gap-2');
btn.innerHTML = `${icon}<span>${label}</span>`;
};
const updatePrice = () => {
const amount = currency === 'USD'
? priceWrap.dataset[period === 'monthly' ? 'monthlyUsd' : 'annualUsd']
: priceWrap.dataset[period === 'monthly' ? 'monthlyIdr' : 'annualIdr'];
const suffix = period === 'monthly' ? '/mo' : '/yr';
if (currency === 'USD') {
priceWrap.innerHTML = `<span class="text-4xl font-bold">$${amount}</span><span class="text-gray-400">${suffix}</span>`;
secondary.textContent = secondary.dataset[`${period}UsdNote`] || '—';
} else {
priceWrap.innerHTML = `<span class="text-4xl font-bold">Rp ${amount}</span><span class="text-gray-400">${suffix}</span>`;
secondary.textContent = secondary.dataset[`${period}IdrNote`] || '—';
}
const canPaypal = (period === 'monthly' ? payBtn.dataset.paypalEnabled === 'true' : payBtn.dataset.paypalAnnualEnabled === 'true');
const canQris = payBtn.dataset.qrisEnabled === 'true';
let disabled = false;
let label = 'Start Personal';
let note = '';
if (currency === 'USD') {
disabled = !canPaypal;
label = 'Start Personal';
note = canPaypal ? '' : 'PayPal is not configured for this plan.';
payBtn.classList.remove('bg-brand-sun', 'hover:bg-brand-sunSoft', 'text-black');
payBtn.classList.add('bg-brand-ocean', 'hover:bg-brand-oceanSoft', 'text-white');
payBtn.classList.remove('text-white');
} else {
disabled = !canQris;
label = 'Start Personal';
note = canQris ? '' : 'QRIS is not available right now.';
payBtn.classList.remove('bg-brand-ocean', 'hover:bg-brand-oceanSoft', 'text-white');
payBtn.classList.add('bg-brand-sun', 'hover:bg-brand-sunSoft', 'text-black');
}
setButtonLabel(payBtn, label, currency === 'USD' ? paypalIcon : qrisIcon);
payBtn.disabled = disabled;
payBtn.classList.toggle('opacity-60', disabled);
payBtn.classList.toggle('pointer-events-none', disabled);
if (payNote) {
payNote.textContent = note;
payNote.classList.toggle('hidden', note === '');
}
const lifetimeAmount = currency === 'USD'
? lifetimePrice.dataset.lifetimeUsd
: lifetimePrice.dataset.lifetimeIdr;
if (currency === 'USD') {
lifetimePrice.innerHTML = `<span class="text-4xl font-bold">$${lifetimeAmount}</span>`;
lifetimeSecondary.textContent = lifetimeSecondary.dataset.lifetimeUsdNote || '—';
} else {
lifetimePrice.innerHTML = `<span class="text-4xl font-bold">Rp ${lifetimeAmount}</span>`;
lifetimeSecondary.textContent = lifetimeSecondary.dataset.lifetimeIdrNote || '—';
}
const canLifetimePaypal = lifetimePay.dataset.paypalEnabled === 'true';
const canLifetimeQris = lifetimePay.dataset.qrisEnabled === 'true';
let lifetimeDisabled = false;
let lifetimeLabel = 'Get Lifetime Access';
let lifetimeHint = '';
if (currency === 'USD') {
lifetimeDisabled = !canLifetimePaypal;
lifetimeLabel = 'Get Lifetime Access';
lifetimeHint = canLifetimePaypal ? '' : 'PayPal is not configured.';
lifetimePay.classList.remove('border-brand-sun/60', 'text-brand-sun', 'hover:bg-brand-sun/10');
lifetimePay.classList.add('border-brand-ocean/60', 'text-brand-ocean', 'hover:bg-brand-ocean/10');
} else {
lifetimeDisabled = !canLifetimeQris;
lifetimeLabel = 'Get Lifetime Access';
lifetimeHint = canLifetimeQris ? '' : 'QRIS is not available right now.';
lifetimePay.classList.remove('border-brand-ocean/60', 'text-brand-ocean', 'hover:bg-brand-ocean/10');
lifetimePay.classList.add('border-brand-sun/60', 'text-brand-sun', 'hover:bg-brand-sun/10');
}
setButtonLabel(lifetimePay, lifetimeLabel, currency === 'USD' ? paypalIcon : qrisIcon);
lifetimePay.disabled = lifetimeDisabled;
lifetimePay.classList.toggle('opacity-60', lifetimeDisabled);
lifetimePay.classList.toggle('pointer-events-none', lifetimeDisabled);
if (lifetimeNote) {
lifetimeNote.textContent = lifetimeHint;
lifetimeNote.classList.toggle('hidden', lifetimeHint === '');
}
};
periodButtons.forEach((btn) => {
btn.addEventListener('click', () => {
period = btn.dataset.period;
setActive(periodButtons, period);
updatePrice();
});
});
currencyButtons.forEach((btn) => {
btn.addEventListener('click', () => {
currency = btn.dataset.currency;
setActive(currencyButtons, currency);
updatePrice();
});
});
payBtn.addEventListener('click', async () => {
if (payBtn.disabled) return;
const plan = period === 'monthly' ? 'personal_monthly' : 'personal_annual';
if (currency === 'USD') {
const button = document.querySelector(`[data-paypal-plan="${plan}"]`);
button?.click();
return;
}
const button = document.querySelector(`[data-qris-plan="${plan}"]`);
button?.click();
});
lifetimePay.addEventListener('click', async () => {
if (lifetimePay.disabled) return;
if (currency === 'USD') {
const paypal = document.querySelector('[data-paypal-lifetime="true"]');
paypal?.click();
return;
}
const button = document.querySelector('[data-qris-plan="personal_lifetime"]');
button?.click();
});
setActive(periodButtons, period);
setActive(currencyButtons, currency);
updatePrice();
})();
</script>
<script>
(() => {
const buttons = document.querySelectorAll('[data-qris-plan]');
if (!buttons.length) return;
const csrf = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
const isAuthed = @json(auth()->check());
const modal = document.getElementById('qris-modal');
const qrTarget = document.getElementById('qris-code');
const qrText = document.getElementById('qris-text');
const qrAmount = document.getElementById('qris-amount');
const qrExpiry = document.getElementById('qris-expiry');
const cancelBtn = document.getElementById('qris-cancel');
let currentOrderId = null;
let modalOpen = false;
const openModal = () => {
if (!modal) return;
modal.classList.remove('hidden');
modal.classList.add('flex');
modalOpen = true;
};
const closeModal = () => {
if (!modal) return;
modal.classList.add('hidden');
modal.classList.remove('flex');
modalOpen = false;
};
cancelBtn?.addEventListener('click', async () => {
const ok = await window.dewemojiConfirm('Cancel this QRIS payment? You will need to start a new checkout.', {
title: 'Cancel payment',
okText: 'Cancel payment',
});
if (!ok) return;
try {
await fetch("{{ route('billing.pakasir.cancel') }}", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
...(csrf ? { 'X-CSRF-TOKEN': csrf } : {}),
},
body: JSON.stringify({ order_id: currentOrderId }),
});
} catch (e) {
// best-effort cancel
} finally {
currentOrderId = null;
closeModal();
}
});
document.addEventListener('keydown', (event) => {
if (!modalOpen) return;
if (event.key === 'Escape') {
event.preventDefault();
}
});
const formatExpiry = (value) => {
if (!value) return null;
const parsed = new Date(value);
if (Number.isNaN(parsed.getTime())) return null;
return new Intl.DateTimeFormat('id-ID', {
dateStyle: 'medium',
timeStyle: 'short',
}).format(parsed);
};
buttons.forEach((btn) => {
btn.addEventListener('click', async () => {
const plan = btn.dataset.qrisPlan;
if (!plan) return;
if (!isAuthed) {
window.location.href = "{{ route('login') }}";
return;
}
const original = btn.dataset.original || btn.textContent;
btn.disabled = true;
btn.textContent = 'Generating QR...';
try {
const res = await fetch("{{ route('billing.pakasir.create') }}", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
...(csrf ? { 'X-CSRF-TOKEN': csrf } : {}),
},
body: JSON.stringify({ plan_code: plan }),
});
const data = await res.json().catch(() => null);
if (!res.ok || !data?.payment_number) {
alert('Could not generate QRIS. Please try again.');
btn.disabled = false;
btn.textContent = original;
return;
}
currentOrderId = data.order_id || null;
if (qrTarget) qrTarget.innerHTML = '';
if (qrTarget && window.QRCode) {
new QRCode(qrTarget, {
text: data.payment_number,
width: 220,
height: 220,
colorDark: '#0b0b0f',
colorLight: '#ffffff',
});
}
if (qrText) qrText.textContent = data.payment_number;
if (qrAmount) qrAmount.textContent = `Rp ${Number(data.total_payment || data.amount || 0).toLocaleString('id-ID')}`;
if (qrExpiry) {
const formatted = formatExpiry(data.expired_at);
qrExpiry.textContent = formatted ? `Expires ${formatted}` : 'Complete within 30 minutes';
}
openModal();
btn.disabled = false;
btn.textContent = original;
} catch (e) {
alert('QRIS request failed. Please try again.');
btn.disabled = false;
btn.textContent = original;
}
});
});
})();
</script>
@endpush