From 0201274182fcb6999982140c8317eabbee39574c Mon Sep 17 00:00:00 2001
From: CIFO Dev <dev@local.test>
Date: Mon, 10 Nov 2025 14:50:55 +0700
Subject: [PATCH] payments: konsistenkan varian tombol dan penataan VA

- Ubah semua Button varian 'ghost' menjadi 'outline' di CardPanel, GoPayPanel, CStorePanel, InlinePaymentStatus untuk konsistensi tipe dan styling\n- Hilangkan logo per-bank dari BankTransferPanel; hanya tampilkan nama bank\n- Perbaiki tampilan Nomor VA: pindah ke baris baru, wrap (reak-all), ukuran font responsif, kurangi tracking\n- Tampilkan gambar 'logo semua bank' di header metode Transfer bank (kanan) dan panel pilihan di Step 2\n- Perbesar ikon logo agar jelas dan seragam di PaymentMethodList\n- Penyesuaian ringan di PaymentSheet untuk CTA status
---
 public/logos/logo-semua-bank.PNG              | Bin 0 -> 17217 bytes
 .../payments/components/BankTransferPanel.tsx |  18 ++--
 .../payments/components/CStorePanel.tsx       |   6 +-
 .../payments/components/CardPanel.tsx         |   7 +-
 .../payments/components/GoPayPanel.tsx        |  32 +++---
 .../components/InlinePaymentStatus.tsx        | 102 ++++++++++++++++++
 .../payments/components/PaymentLogos.tsx      |  55 +++++++---
 .../payments/components/PaymentMethodList.tsx |  45 +++++---
 .../payments/components/PaymentSheet.tsx      |  23 ++--
 src/pages/CheckoutPage.tsx                    |  28 +++--
 10 files changed, 244 insertions(+), 72 deletions(-)
 create mode 100644 public/logos/logo-semua-bank.PNG
 create mode 100644 src/features/payments/components/InlinePaymentStatus.tsx

diff --git a/public/logos/logo-semua-bank.PNG b/public/logos/logo-semua-bank.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..093f7185c0d46717fb98a64ad4500495b7a93af3
GIT binary patch
diff --git a/src/features/payments/components/BankTransferPanel.tsx b/src/features/payments/components/BankTransferPanel.tsx
index 457a5eb..61cc295 100644
--- a/src/features/payments/components/BankTransferPanel.tsx
+++ b/src/features/payments/components/BankTransferPanel.tsx
@@ -4,9 +4,10 @@ import React from 'react'
 import { PaymentInstructions } from './PaymentInstructions'
 import { BcaInstructionList } from './BcaInstructionList'
 import { TrustStrip } from './TrustStrip'
-import { BankLogosRow, BankLogo, type BankKey } from './PaymentLogos'
+import { type BankKey } from './PaymentLogos'
 import { postCharge } from '../../../services/api'
 import { Alert } from '../../../components/alert/Alert'
+import { InlinePaymentStatus } from './InlinePaymentStatus'
 
 // Global guard to prevent duplicate auto-charge across StrictMode double-mounts
 const attemptedChargeKeys = new Set<string>()
@@ -137,11 +138,9 @@ export function BankTransferPanel({ orderId, amount, locked, onChargeInitiated,
   return (
     <div className="space-y-3">
       <div className="font-medium">Transfer bank</div>
-      <BankLogosRow compact />
       {selected && (
         <div className="flex items-center gap-2 text-base">
           <span className="text-black/60 dark:text-white/60">Bank:</span>
-          <BankLogo bank={selected} compact />
           <span className="text-black/80 dark:text-white/80 font-semibold">{selected.toUpperCase()}</span>
         </div>
       )}
@@ -155,7 +154,10 @@ export function BankTransferPanel({ orderId, amount, locked, onChargeInitiated,
             <div className="text-sm font-medium mb-2">Virtual Account</div>
             <div className="text-sm text-black/70 dark:text-white/70">
               {vaCode ? (
-                <span>Nomor VA: <span className="font-mono text-2xl md:text-3xl font-semibold tracking-wider text-black dark:text-white">{vaCode}</span></span>
+                <span>
+                  Nomor VA:
+                  <span className="block break-all mt-1 font-mono text-xl sm:text-2xl md:text-3xl font-semibold tracking-normal text-black dark:text-white">{vaCode}</span>
+                </span>
               ) : (
                 <span className="inline-flex items-center gap-2" role="status" aria-live="polite">
                   {busy && <span className="h-3 w-3 animate-spin rounded-full border-2 border-black/40 dark:border-white/40 border-t-transparent" aria-hidden />}
@@ -176,6 +178,10 @@ export function BankTransferPanel({ orderId, amount, locked, onChargeInitiated,
           </div>
         </div>
       )}
+      {/* Status inline dengan polling otomatis */}
+      {selected && (
+        <InlinePaymentStatus orderId={orderId} method="bank_transfer" compact />
+      )}
       {selected && (
         <div className="pt-2">
           {selected === 'bca' ? (
@@ -281,12 +287,12 @@ export function BankTransferPanel({ orderId, amount, locked, onChargeInitiated,
           </Button>
         )}
         <Button
-          variant="secondary"
+          variant="outline"
           className="w-full"
           disabled={busy || (!locked && !vaCode && !billKey)}
           onClick={() => nav.toStatus(orderId, 'bank_transfer')}
         >
-          Cek Status Pembayaran
+          Buka halaman status
         </Button>
       </div>
       <TrustStrip />
diff --git a/src/features/payments/components/CStorePanel.tsx b/src/features/payments/components/CStorePanel.tsx
index ae8cb49..8c524c2 100644
--- a/src/features/payments/components/CStorePanel.tsx
+++ b/src/features/payments/components/CStorePanel.tsx
@@ -4,6 +4,7 @@ import React from 'react'
 import { PaymentInstructions } from './PaymentInstructions'
 import { TrustStrip } from './TrustStrip'
 import { postCharge } from '../../../services/api'
+import { InlinePaymentStatus } from './InlinePaymentStatus'
 
 type StoreKey = 'alfamart' | 'indomaret'
 
@@ -115,13 +116,14 @@ export function CStorePanel({ orderId, amount, locked, onChargeInitiated, defaul
           <div className="mt-2"><Button variant="outline" className="w-full sm:w-auto" onClick={() => copy(paymentCode, 'Kode pembayaran')} disabled={!paymentCode || busy}>Copy Kode</Button></div>
         </div>
         <Button
-          variant="secondary"
+          variant="outline"
           className="w-full"
           disabled={busy || (!locked && !paymentCode)}
           onClick={() => nav.toStatus(orderId, 'cstore')}
         >
-          Cek Status Pembayaran
+          Buka halaman status
         </Button>
+        <InlinePaymentStatus orderId={orderId} method="cstore" />
       </div>
       <TrustStrip />
     </div>
diff --git a/src/features/payments/components/CardPanel.tsx b/src/features/payments/components/CardPanel.tsx
index d0286e9..a7940c6 100644
--- a/src/features/payments/components/CardPanel.tsx
+++ b/src/features/payments/components/CardPanel.tsx
@@ -8,6 +8,7 @@ import { ensureMidtrans3ds, getCardToken, authenticate3ds } from '../lib/midtran
 import { Logger } from '../../../lib/logger'
 import { Env } from '../../../lib/env'
 import { postCharge } from '../../../services/api'
+import { InlinePaymentStatus } from './InlinePaymentStatus'
 
 export function CardPanel({ orderId, amount, locked, onChargeInitiated }: { orderId: string; amount: number; locked?: boolean; onChargeInitiated?: () => void }) {
   const nav = usePaymentNavigation()
@@ -145,14 +146,16 @@ export function CardPanel({ orderId, amount, locked, onChargeInitiated }: { orde
           ) : 'Bayar sekarang'}
         </Button>
         <Button
-          variant="secondary"
+          variant="outline"
           className="w-full"
           disabled={busy || (!charged && !locked)}
           onClick={() => nav.toStatus(orderId, 'credit_card')}
         >
-          Cek Status Pembayaran
+          Buka halaman status
         </Button>
       </div>
+      {/* Status inline dengan polling otomatis */}
+      <InlinePaymentStatus orderId={orderId} method="credit_card" compact />
       <TrustStrip />
     </div>
   )
diff --git a/src/features/payments/components/GoPayPanel.tsx b/src/features/payments/components/GoPayPanel.tsx
index d7856d6..bdc6b6e 100644
--- a/src/features/payments/components/GoPayPanel.tsx
+++ b/src/features/payments/components/GoPayPanel.tsx
@@ -5,6 +5,7 @@ import { PaymentInstructions } from './PaymentInstructions'
 import { TrustStrip } from './TrustStrip'
 import { GoPayLogosRow } from './PaymentLogos'
 import { postCharge } from '../../../services/api'
+import { InlinePaymentStatus } from './InlinePaymentStatus'
 
 // Global guards/tasks to stabilize QR generation across StrictMode remounts
 const attemptedChargeKeys = new Set<string>()
@@ -135,20 +136,23 @@ export function GoPayPanel({ orderId, amount, locked, onChargeInitiated }: { ord
         <div className="text-xs text-black/60 dark:text-white/60">Metode terkunci. Gunakan QR/deeplink untuk menyelesaikan pembayaran.</div>
       )}
       <div className="pt-2">
-        <Button
-          variant="secondary"
-          className="w-full"
-          aria-busy={busy}
-          disabled={busy || (!locked && actions.length === 0)}
-          onClick={() => { setBusy(true); onChargeInitiated?.(); setTimeout(() => { nav.toStatus(orderId, mode) ; setBusy(false) }, 250) }}
-        >
-          {busy ? (
-            <span className="inline-flex items-center justify-center gap-2" role="status" aria-live="polite">
-              <span className="h-4 w-4 animate-spin rounded-full border-2 border-black/40 dark:border-white/60 border-t-transparent" aria-hidden />
-              Menuju status…
-            </span>
-          ) : 'Cek Status Pembayaran'}
-        </Button>
+        <InlinePaymentStatus orderId={orderId} method={mode} />
+        <div className="mt-2">
+          <Button
+            variant="outline"
+            className="w-full"
+            aria-busy={busy}
+            disabled={busy || (!locked && actions.length === 0)}
+            onClick={() => { setBusy(true); onChargeInitiated?.(); setTimeout(() => { nav.toStatus(orderId, mode) ; setBusy(false) }, 250) }}
+          >
+            {busy ? (
+              <span className="inline-flex items-center justify-center gap-2" role="status" aria-live="polite">
+                <span className="h-4 w-4 animate-spin rounded-full border-2 border-black/40 dark:border-white/60 border-t-transparent" aria-hidden />
+                Menuju status…
+              </span>
+            ) : 'Buka halaman status'}
+          </Button>
+        </div>
       </div>
       <TrustStrip />
     </div>
diff --git a/src/features/payments/components/InlinePaymentStatus.tsx b/src/features/payments/components/InlinePaymentStatus.tsx
new file mode 100644
index 0000000..d70ff4c
--- /dev/null
+++ b/src/features/payments/components/InlinePaymentStatus.tsx
@@ -0,0 +1,102 @@
+import React from 'react'
+import { Button } from '../../../components/ui/button'
+import { usePaymentNavigation } from '../lib/navigation'
+import { usePaymentStatus } from '../lib/usePaymentStatus'
+import type { PaymentStatusResponse } from '../lib/midtrans'
+
+function formatIDR(amount?: string) {
+  if (!amount) return ''
+  const n = Number(amount)
+  if (Number.isNaN(n)) return amount
+  return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(Math.round(n))
+}
+
+export function InlinePaymentStatus({ orderId, method, compact }: { orderId: string; method?: string; compact?: boolean }) {
+  const nav = usePaymentNavigation()
+  const { data, isLoading, error, refetch, isRefetching } = usePaymentStatus(orderId)
+  const status = (data?.status ?? 'pending') as PaymentStatusResponse['status']
+  const isFinal = ['settlement', 'capture', 'expire', 'cancel', 'deny', 'refund', 'chargeback'].includes(status)
+  const isSuccess = status === 'settlement' || status === 'capture'
+  const isFailure = ['deny', 'cancel', 'expire', 'refund', 'chargeback'].includes(status)
+
+  return (
+    <div className={`rounded border ${compact ? 'p-2' : 'p-3'} border-black/10 dark:border-white/10 bg-white dark:bg-black/20`} aria-live="polite">
+      {/* Header minimal tanpa detail teknis */}
+      <div className="text-sm font-medium">Status pembayaran</div>
+
+      {/* Konten berdasarkan status */}
+      {isLoading ? (
+        <div className="mt-2 text-sm">
+          <span className="inline-flex items-center gap-2" role="status">
+            <span className="h-4 w-4 animate-spin rounded-full border-2 border-black/40 dark:border-white/60 border-t-transparent" aria-hidden />
+            Mengecek pembayaran…
+          </span>
+          <div className="mt-1 text-[11px] text-black/60 dark:text-white/60">Kami memeriksa otomatis setiap 3 detik.</div>
+        </div>
+      ) : error ? (
+        <div className="mt-2 text-sm text-brand-600">Gagal memuat status. Coba refresh.</div>
+      ) : isSuccess ? (
+        <div className="mt-2">
+          <div className="flex items-center gap-2">
+            <span className="inline-flex h-6 w-6 items-center justify-center rounded-full bg-green-500/15 text-green-600">
+              {/* check icon */}
+              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" className="animate-[pop_200ms_ease-out]">
+                <path d="M20 6L9 17L4 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
+              </svg>
+            </span>
+            <div className="text-base font-semibold">Pembayaran berhasil</div>
+          </div>
+          {data?.grossAmount ? (
+            <div className="mt-1 text-sm text-black/70 dark:text-white/70">Total dibayar: {formatIDR(data.grossAmount)}</div>
+          ) : null}
+          <div className="mt-1 text-xs text-black/60 dark:text-white/60">Terima kasih! Pesanan Anda sedang diproses.</div>
+          <div className="mt-3 flex flex-wrap gap-2">
+            <Button className="w-full sm:w-auto" onClick={() => nav.toHistory()}>Lihat riwayat pembayaran</Button>
+            <Button variant="outline" className="w-full sm:w-auto" onClick={() => nav.toCheckout()}>Kembali ke checkout</Button>
+            <Button variant="outline" className="w-full sm:w-auto" onClick={() => nav.toStatus(orderId, method)}>Lihat detail status</Button>
+          </div>
+        </div>
+      ) : isFailure ? (
+        <div className="mt-2">
+          <div className="flex items-center gap-2">
+            <span className="inline-flex h-6 w-6 items-center justify-center rounded-full bg-red-500/15 text-red-600">
+              {/* x icon */}
+              <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
+                <path d="M18 6L6 18" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
+                <path d="M6 6L18 18" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
+              </svg>
+            </span>
+            <div className="text-base font-semibold">Pembayaran belum berhasil</div>
+          </div>
+          <div className="mt-1 text-xs text-black/60 dark:text-white/60">Silakan coba lagi atau pilih metode lain.</div>
+          <div className="mt-3 flex flex-wrap gap-2">
+            <Button className="w-full sm:w-auto" onClick={() => nav.toCheckout()}>Coba lagi</Button>
+            <Button variant="outline" className="w-full sm:w-auto" onClick={() => nav.toStatus(orderId, method)}>Lihat detail status</Button>
+          </div>
+        </div>
+      ) : (
+        <div className="mt-2">
+          <div className="flex items-center gap-2">
+            <span className="inline-flex h-6 w-6 items-center justify-center rounded-full bg-yellow-500/15 text-yellow-700 dark:text-yellow-300">
+              {/* hourglass/spinner icon */}
+              <span className="h-4 w-4 animate-spin rounded-full border-2 border-yellow-600/50 dark:border-yellow-300/80 border-t-transparent" aria-hidden />
+            </span>
+            <div className="text-base font-semibold">Menunggu pembayaran</div>
+          </div>
+          <div className="mt-1 text-[11px] text-black/60 dark:text-white/60">Kami memeriksa otomatis setiap 3 detik sampai selesai.</div>
+          <div className="mt-3 flex flex-wrap gap-2">
+            <Button variant="outline" className="w-full sm:w-auto" onClick={() => refetch()} aria-busy={isRefetching} disabled={isRefetching}>
+              {isRefetching ? (
+                <span className="inline-flex items-center gap-2" role="status" aria-live="polite">
+                  <span className="h-3 w-3 animate-spin rounded-full border-2 border-black/40 dark:border-white/60 border-t-transparent" aria-hidden />
+                  Memuat…
+                </span>
+              ) : 'Refresh sekarang'}
+            </Button>
+            <Button variant="outline" className="w-full sm:w-auto" onClick={() => nav.toStatus(orderId, method)}>Buka halaman status</Button>
+          </div>
+        </div>
+      )}
+    </div>
+  )
+}
\ No newline at end of file
diff --git a/src/features/payments/components/PaymentLogos.tsx b/src/features/payments/components/PaymentLogos.tsx
index c0482d1..f3a5c79 100644
--- a/src/features/payments/components/PaymentLogos.tsx
+++ b/src/features/payments/components/PaymentLogos.tsx
@@ -6,13 +6,13 @@ function toProxy(url: string) {
   return `https://images.weserv.nl/?url=${encodeURIComponent(url)}`
 }
 
-function BrandImg({ src, alt, compact = false, fallbackSrc }: { src: string; alt: string; compact?: boolean; fallbackSrc?: string }) {
-  const sizeClass = compact ? 'h-5' : 'h-6'
+function BrandImg({ src, alt, compact = false, size, fallbackSrc }: { src: string; alt: string; compact?: boolean; size?: 'xs' | 'sm' | 'md'; fallbackSrc?: string }) {
+  const sizeClass = size ? (size === 'xs' ? 'h-4' : size === 'sm' ? 'h-5' : 'h-6') : (compact ? 'h-5' : 'h-6')
   return (
     <img
       src={src}
       alt={alt}
-      className={`${sizeClass} inline-block object-contain`}
+      className={`${sizeClass} inline-block object-contain max-w-full`}
       loading="lazy"
       referrerPolicy="no-referrer"
       onError={(e) => {
@@ -45,23 +45,23 @@ const BANK_LOGOS: Record<BankKey, { alt: string; src: string; fb: string }> = {
   permata: { alt: 'PermataBank', src: '/logos/permata Bank.png', fb: 'https://upload.wikimedia.org/wikipedia/commons/4/42/PermataBank_logo.svg' },
 }
 
-export function BankLogo({ bank, compact = false }: { bank: BankKey; compact?: boolean }) {
+export function BankLogo({ bank, compact = false, size }: { bank: BankKey; compact?: boolean; size?: 'xs' | 'sm' | 'md' }) {
   const cfg = BANK_LOGOS[bank]
-  return <BrandImg src={cfg.src} alt={cfg.alt} compact={compact} fallbackSrc={cfg.fb} />
+  return <BrandImg src={cfg.src} alt={cfg.alt} compact={compact} size={size} fallbackSrc={cfg.fb} />
 }
 
-export function BankLogosRow({ compact = false }: { compact?: boolean }) {
+export function BankLogosRow({ compact = false, size }: { compact?: boolean; size?: 'xs' | 'sm' | 'md' }) {
   const all: BankKey[] = ['bca', 'bni', 'bri', 'cimb', 'mandiri', 'permata']
   return (
     <div className="flex items-center gap-2">
       {all.map((k) => (
-        <BankLogo key={k} bank={k} compact={compact} />
+        <BankLogo key={k} bank={k} compact={compact} size={size} />
       ))}
     </div>
   )
 }
 
-export function CardLogosRow({ compact = false }: { compact?: boolean }) {
+export function CardLogosRow({ compact = false, size }: { compact?: boolean; size?: 'xs' | 'sm' | 'md' }) {
   const logos = [
     { alt: 'Visa', src: '/logos/visa.svg', fb: 'https://upload.wikimedia.org/wikipedia/commons/0/04/Visa.svg' },
     { alt: 'Mastercard', src: '/logos/mastercard.svg', fb: 'https://upload.wikimedia.org/wikipedia/commons/2/2a/Mastercard-logo.svg' },
@@ -71,25 +71,48 @@ export function CardLogosRow({ compact = false }: { compact?: boolean }) {
   return (
     <div className="flex items-center gap-2">
       {logos.map((l) => (
-        <BrandImg key={l.alt} src={l.src} alt={l.alt} compact={compact} fallbackSrc={l.fb} />
+        <BrandImg key={l.alt} src={l.src} alt={l.alt} compact={compact} size={size} fallbackSrc={l.fb} />
       ))}
     </div>
   )
 }
 
-export function LogoGoPay({ compact = false }: { compact?: boolean }) {
-  return <BrandImg src="/logos/gopay.svg" fallbackSrc="https://upload.wikimedia.org/wikipedia/commons/1/1e/Logo_GoPay.svg" alt="GoPay" compact={compact} />
+export function LogoGoPay({ compact = false, size }: { compact?: boolean; size?: 'xs' | 'sm' | 'md' }) {
+  // Gunakan file lokal yang tersedia di public/logos/
+  return <BrandImg src="/logos/Gopay_logo.svg" fallbackSrc="https://upload.wikimedia.org/wikipedia/commons/1/1e/Logo_GoPay.svg" alt="GoPay" compact={compact} size={size} />
 }
 
-export function LogoQRIS({ compact = false }: { compact?: boolean }) {
-  return <BrandImg src="/logos/qris.svg" fallbackSrc="https://upload.wikimedia.org/wikipedia/commons/0/0a/QRIS_Logo.svg" alt="QRIS" compact={compact} />
+export function LogoQRIS({ compact = false, size }: { compact?: boolean; size?: 'xs' | 'sm' | 'md' }) {
+  // Gunakan file lokal yang tersedia di public/logos/
+  return <BrandImg src="/logos/Logo_QRIS.svg" fallbackSrc="https://upload.wikimedia.org/wikipedia/commons/0/0a/QRIS_Logo.svg" alt="QRIS" compact={compact} size={size} />
 }
 
-export function GoPayLogosRow({ compact = false }: { compact?: boolean }) {
+export function LogoAlfamart({ compact = false, size }: { compact?: boolean; size?: 'xs' | 'sm' | 'md' }) {
+  return <BrandImg src="/logos/ALFAMART_LOGO_BARU.png" alt="Alfamart" compact={compact} size={size} />
+}
+
+export function LogoIndomaret({ compact = false, size }: { compact?: boolean; size?: 'xs' | 'sm' | 'md' }) {
+  return <BrandImg src="/logos/Logo_Indomaret.png" alt="Indomaret" compact={compact} size={size} />
+}
+
+export function LogoCpay({ compact = false, size }: { compact?: boolean; size?: 'xs' | 'sm' | 'md' }) {
+  // Sumber lokal sesuai permintaan: public/logos/Cifo_cpay.png
+  return <BrandImg src="/logos/Cifo_cpay.png" alt="cPay" compact={compact} size={size} />
+}
+
+export function CStoreLogosRow({ compact = false, size }: { compact?: boolean; size?: 'xs' | 'sm' | 'md' }) {
   return (
     <div className="flex items-center gap-2">
-      <LogoGoPay compact={compact} />
-      <LogoQRIS compact={compact} />
+      <LogoAlfamart compact={compact} size={size} />
+      <LogoIndomaret compact={compact} size={size} />
+    </div>
+  )
+}
+export function GoPayLogosRow({ compact = false, size }: { compact?: boolean; size?: 'xs' | 'sm' | 'md' }) {
+  return (
+    <div className="flex items-center gap-2">
+      <LogoGoPay compact={compact} size={size} />
+      <LogoQRIS compact={compact} size={size} />
     </div>
   )
 }
\ No newline at end of file
diff --git a/src/features/payments/components/PaymentMethodList.tsx b/src/features/payments/components/PaymentMethodList.tsx
index 2f293aa..e62af20 100644
--- a/src/features/payments/components/PaymentMethodList.tsx
+++ b/src/features/payments/components/PaymentMethodList.tsx
@@ -1,8 +1,8 @@
 import React from 'react'
-import { BankLogosRow, CardLogosRow, GoPayLogosRow } from './PaymentLogos'
+import { CardLogosRow, GoPayLogosRow, CStoreLogosRow, LogoCpay } from './PaymentLogos'
 import { Env } from '../../../lib/env'
 
-export type PaymentMethod = 'bank_transfer' | 'credit_card' | 'gopay' | 'cstore'
+export type PaymentMethod = 'bank_transfer' | 'credit_card' | 'gopay' | 'cstore' | 'cpay'
 
 export interface PaymentMethodListProps {
   selected?: PaymentMethod
@@ -13,18 +13,20 @@ export interface PaymentMethodListProps {
 }
 
 const baseItems: Array<{ key: PaymentMethod; title: string; subtitle: string; icon?: React.ReactNode }> = [
-  { key: 'bank_transfer', title: 'Transfer bank', subtitle: 'BCA • BNI • BRI • CIMB • Mandiri • Permata', icon: <BankLogosRow compact /> },
-  { key: 'credit_card', title: 'Kartu kredit/debit', subtitle: 'Visa • MasterCard • JCB • Amex', icon: <CardLogosRow compact /> },
-  { key: 'gopay', title: 'Gopay/QRIS', subtitle: 'Scan & bayar via QR', icon: <GoPayLogosRow compact /> },
-  { key: 'cstore', title: 'Convenience Store', subtitle: 'Alfamart • Indomaret' },
+  { key: 'bank_transfer', title: 'Transfer bank', subtitle: 'BCA • BNI • BRI • CIMB • Mandiri • Permata', icon: <img src="/logos/logo-semua-bank.PNG" alt="Semua bank yang didukung" className="h-6 sm:h-8 object-contain" /> },
+  { key: 'credit_card', title: 'Kartu kredit/debit', subtitle: 'Visa • MasterCard • JCB • Amex', icon: <CardLogosRow compact size="xs" /> },
+  { key: 'gopay', title: 'Gopay/QRIS', subtitle: 'Scan & bayar via QR', icon: <GoPayLogosRow compact size="xs" /> },
+  { key: 'cstore', title: 'Convenience Store', subtitle: '', icon: <CStoreLogosRow compact size="xs" /> },
+  { key: 'cpay', title: 'cPay', subtitle: 'Bayar via aplikasi CIFO Token', icon: <LogoCpay compact size="md" /> },
 ]
 
 export function PaymentMethodList({ selected, onSelect, renderPanel, disabled, enabled }: PaymentMethodListProps) {
-  const enabledMap: Record<PaymentMethod, boolean> = enabled ?? {
-    bank_transfer: Env.ENABLE_BANK_TRANSFER,
-    credit_card: Env.ENABLE_CREDIT_CARD,
-    gopay: Env.ENABLE_GOPAY,
-    cstore: Env.ENABLE_CSTORE,
+  const enabledMap: Record<PaymentMethod, boolean> = {
+    bank_transfer: enabled?.bank_transfer ?? Env.ENABLE_BANK_TRANSFER,
+    credit_card: enabled?.credit_card ?? Env.ENABLE_CREDIT_CARD,
+    gopay: enabled?.gopay ?? Env.ENABLE_GOPAY,
+    cstore: enabled?.cstore ?? Env.ENABLE_CSTORE,
+    cpay: enabled?.cpay ?? Env.ENABLE_CPAY,
   }
   const items = baseItems.filter((it) => enabledMap[it.key])
   return (
@@ -41,14 +43,25 @@ export function PaymentMethodList({ selected, onSelect, renderPanel, disabled, e
               aria-expanded={selected === it.key}
               aria-controls={`panel-${it.key}`}
             >
-              <div>
+              <div className="flex-1">
                 <div className="text-base font-semibold text-black dark:text-white">{it.title}</div>
-                <div className="text-sm text-black/70 dark:text-white/70">{it.subtitle}</div>
+                {it.key === 'bank_transfer' && it.subtitle && (
+                  <div className="mt-1 text-xs text-black/60 dark:text-white/60">
+                    {it.subtitle}
+                  </div>
+                )}
+                {it.key === 'cpay' && it.subtitle && (
+                  <div className="mt-1 text-xs text-black/60 dark:text-white/60">
+                    {it.subtitle}
+                  </div>
+                )}
               </div>
               <div className="flex items-center gap-2">
-                <span className="hidden sm:block" aria-hidden>
-                  {it.icon}
-                </span>
+                {it.icon && (
+                  <span aria-hidden>
+                    {it.icon}
+                  </span>
+                )}
                 <span aria-hidden className={`text-black/60 dark:text-white/60 text-lg transition-transform ${selected === it.key ? 'rotate-90' : ''}`}>›</span>
               </div>
             </button>
diff --git a/src/features/payments/components/PaymentSheet.tsx b/src/features/payments/components/PaymentSheet.tsx
index fbcbb09..2f8d2fe 100644
--- a/src/features/payments/components/PaymentSheet.tsx
+++ b/src/features/payments/components/PaymentSheet.tsx
@@ -25,9 +25,10 @@ export interface PaymentSheetProps {
   amount: number
   expireAt: number // epoch ms
   children?: React.ReactNode
+  showStatusCTA?: boolean
 }
 
-export function PaymentSheet({ merchantName = 'Zara', orderId, amount, expireAt, children }: PaymentSheetProps) {
+export function PaymentSheet({ merchantName = 'Zara', orderId, amount, expireAt, children, showStatusCTA = true }: PaymentSheetProps) {
   const countdown = useCountdown(expireAt)
   const [expanded, setExpanded] = React.useState(true)
   return (
@@ -76,15 +77,17 @@ export function PaymentSheet({ merchantName = 'Zara', orderId, amount, expireAt,
           <TrustStrip location="sheet" />
         </div>
         {/* Sticky CTA (mobile-friendly) */}
-        <div className="sticky bottom-0 bg-white/95 dark:bg-neutral-900/95 backdrop-blur border-t border-black/10 dark:border-white/10 p-3 pb-[env(safe-area-inset-bottom)]">
-          <Link
-            to={`/payments/${orderId}/status`}
-            aria-label="Buka halaman status pembayaran"
-            className="w-full block text-center rounded bg-[#0c1f3f] text-white py-3 text-base font-semibold hover:bg-[#0a1a35] focus:outline-none focus-visible:ring-3 focus-visible:ring-offset-2 focus-visible:ring-[#0c1f3f]"
-          >
-            Cek status pembayaran
-          </Link>
-        </div>
+        {showStatusCTA && (
+          <div className="sticky bottom-0 bg-white/95 dark:bg-neutral-900/95 backdrop-blur border-t border-black/10 dark:border-white/10 p-3 pb-[env(safe-area-inset-bottom)]">
+            <Link
+              to={`/payments/${orderId}/status`}
+              aria-label="Buka halaman status pembayaran"
+              className="w-full block text-center rounded bg-[#0c1f3f] text-white py-3 text-base font-semibold hover:bg-[#0a1a35] focus:outline-none focus-visible:ring-3 focus-visible:ring-offset-2 focus-visible:ring-[#0c1f3f]"
+            >
+              Cek status pembayaran
+            </Link>
+          </div>
+        )}
       </div>
     </div>
   )
diff --git a/src/pages/CheckoutPage.tsx b/src/pages/CheckoutPage.tsx
index 2c91718..00bf98b 100644
--- a/src/pages/CheckoutPage.tsx
+++ b/src/pages/CheckoutPage.tsx
@@ -8,7 +8,7 @@ import { BankTransferPanel } from '../features/payments/components/BankTransferP
 import { CardPanel } from '../features/payments/components/CardPanel'
 import { GoPayPanel } from '../features/payments/components/GoPayPanel'
 import { CStorePanel } from '../features/payments/components/CStorePanel'
-import { BankLogo, type BankKey } from '../features/payments/components/PaymentLogos'
+import { BankLogo, type BankKey, LogoAlfamart, LogoIndomaret } from '../features/payments/components/PaymentLogos'
 import { usePaymentConfig } from '../features/payments/lib/usePaymentConfig'
 import { Logger } from '../lib/logger'
 import React from 'react'
@@ -72,7 +72,7 @@ export function CheckoutPage() {
         </Alert>
       )}
 
-      <PaymentSheet merchantName="Zara" orderId={orderId} amount={amount} expireAt={expireAt}>
+      <PaymentSheet merchantName="Zara" orderId={orderId} amount={amount} expireAt={expireAt} showStatusCTA={currentStep === 3}>
         {/* Wizard 3 langkah: Step 1 (Form Dummy) → Step 2 (Pilih Metode) → Step 3 (Panel Metode) */}
         {currentStep === 1 && (
           <div className="space-y-3">
@@ -142,6 +142,15 @@ export function CheckoutPage() {
                 setSelected(m)
                 if (m === 'bank_transfer' || m === 'cstore') {
                   // Panel akan tampil di bawah item menggunakan renderPanel
+                } else if (m === 'cpay') {
+                  // Redirect ke aplikasi cPay (CIFO Token) di Play Store
+                  try {
+                    Logger.info('cpay.redirect.start')
+                    window.open('https://play.google.com/store/apps/details?id=com.cifo.walanja', '_blank')
+                    Logger.info('cpay.redirect.done')
+                  } catch (e) {
+                    Logger.error('cpay.redirect.error', { message: (e as Error)?.message })
+                  }
                 } else {
                   setIsBusy(true)
                   setTimeout(() => { setCurrentStep(3); setIsBusy(false) }, 300)
@@ -161,6 +170,13 @@ export function CheckoutPage() {
                 if (m === 'bank_transfer') {
                   return (
                     <div className="space-y-2" aria-live="polite">
+                      <div className="flex justify-center">
+                        <img
+                          src="/logos/logo-semua-bank.PNG"
+                          alt="Logo semua bank yang didukung"
+                          className="max-h-20 sm:max-h-24 object-contain"
+                        />
+                      </div>
                       <div className="text-xs text-black/60 dark:text-white/60">Pilih bank untuk membuat Virtual Account</div>
                       <div className={`grid grid-cols-3 gap-2 ${isBusy ? 'pointer-events-none opacity-60' : ''}`}>
                         {(['bca','bni','bri','cimb','mandiri','permata'] as BankKey[]).map((bk) => (
@@ -172,11 +188,10 @@ export function CheckoutPage() {
                               setIsBusy(true)
                               setTimeout(() => { setCurrentStep(3); setIsBusy(false) }, 300)
                             }}
-                            className="rounded border border-black/10 dark:border-white/10 bg-white dark:bg-black/20 p-2 flex flex-col items-center gap-1 hover:bg-black/5 dark:hover:bg-white/10"
+                            className="rounded border border-black/10 dark:border-white/10 bg-white dark:bg-black/20 p-2 flex items-center justify-center overflow-hidden hover:bg-black/5 dark:hover:bg-white/10"
                             aria-label={`Pilih bank ${bk.toUpperCase()}`}
                           >
                             <BankLogo bank={bk} />
-                            <span className="text-xs text-black/70 dark:text-white/70">{bk.toUpperCase()}</span>
                           </button>
                         ))}
                       </div>
@@ -203,10 +218,10 @@ export function CheckoutPage() {
                               setIsBusy(true)
                               setTimeout(() => { setCurrentStep(3); setIsBusy(false) }, 300)
                             }}
-                            className="rounded border border-black/10 dark:border-white/10 bg-white dark:bg-black/20 p-2 flex flex-col items-center gap-1 hover:bg-black/5 dark:hover:bg-white/10"
+                            className="rounded border border-black/10 dark:border-white/10 bg-white dark:bg-black/20 p-2 flex items-center justify-center overflow-hidden hover:bg-black/5 dark:hover:bg-white/10"
                             aria-label={`Pilih toko ${st.toUpperCase()}`}
                           >
-                            <span className="text-sm font-medium">{st.toUpperCase()}</span>
+                            {st === 'alfamart' ? <LogoAlfamart /> : <LogoIndomaret />}
                           </button>
                         ))}
                       </div>
@@ -255,5 +270,6 @@ function defaultEnabled(): Record<PaymentMethod, boolean> {
     credit_card: Env.ENABLE_CREDIT_CARD,
     gopay: Env.ENABLE_GOPAY,
     cstore: Env.ENABLE_CSTORE,
+    cpay: Env.ENABLE_CPAY,
   }
 }
\ No newline at end of file
