681 lines
37 KiB
PHP
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
|