General
Payment Method code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/payment-method.blade.php
@props([
'brand' => null,
'last4' => null,
'expMonth' => null,
'expYear' => null,
'expiry' => null,
'name' => null,
'default' => false,
'expired' => false,
'type' => 'card',
'email' => null,
'updateLabel' => 'Update',
'updateHref' => null,
])
@php
/*
| Payment method card — a stored card (or alternative method) with a brand
| mark, masked number, expiry, an optional "Default" badge and an actions
| menu. The companion to <x-ui.invoice> / <x-ui.billing-history>. Use the
| `actions` slot to override the menu.
|
| type: card, paypal, bank
| brand: visa, mastercard, amex, discover, … (shown as a recognisable label)
|
| The brand chip and the default-method accent use the themeable palette
| (neutral surfaces + the `brand-*` ramp) so the card re-themes with the app.
|
| Usage:
| <x-ui.payment-method brand="visa" last4="4242" exp-month="8" exp-year="2027" name="Ada Lovelace" default />
| <x-ui.payment-method type="paypal" email="ada@example.com" />
*/
// Brand chip uses neutral, theme-aware surfaces (mirrors the provider picker)
// rather than fixed brand hexes, so it adapts to light/dark and re-theming.
$chipClass = $default
? 'bg-brand-600 text-white dark:bg-brand-500'
: 'bg-gray-900 text-white dark:bg-gray-700';
$rootClass = implode(' ', [
'flex items-center gap-4 rounded-2xl border bg-white p-4 dark:bg-gray-900 sm:p-5',
$default
? 'border-brand-300 ring-1 ring-brand-500/20 dark:border-brand-800'
: 'border-gray-200 dark:border-gray-800',
]);
$brandIcon = match ($type) {
'paypal' => 'paypal-logo',
'bank' => 'bank',
default => 'credit-card',
};
$brandLabel = $brand
? \Illuminate\Support\Str::of($brand)->title()->toString()
: match ($type) {
'paypal' => 'PayPal',
'bank' => 'Bank account',
default => 'Card',
};
$resolvedExpiry = $expiry
?? ($expMonth && $expYear
? str_pad((string) $expMonth, 2, '0', STR_PAD_LEFT).' / '.mb_substr((string) $expYear, -2)
: null);
$primaryLine = match ($type) {
'paypal' => $email,
'bank' => $last4 ? 'Account ending in '.$last4 : null,
default => $last4 ? '•••• •••• •••• '.$last4 : null,
};
@endphp
<div {{ $attributes->merge(['class' => $rootClass]) }}>
{{-- Brand mark --}}
<div class="flex h-11 w-16 shrink-0 items-center justify-center rounded-lg text-xs font-bold uppercase tracking-wide {{ $chipClass }}">
@if ($type === 'card' && $brand)
<span class="truncate px-1">{{ $brandLabel }}</span>
@else
<x-ui.icon :name="$brandIcon" weight="fill" size="lg" />
@endif
</div>
{{-- Details --}}
<div class="min-w-0 flex-1">
<div class="flex flex-wrap items-center gap-2">
<p class="truncate text-sm font-semibold text-gray-900 dark:text-white">
@if ($type === 'card')
{{ $brandLabel }}@if ($last4) ending in {{ $last4 }}@endif
@else
{{ $brandLabel }}
@endif
</p>
@if ($default)
<x-ui.badge variant="brand" size="sm" icon="check-circle">Default</x-ui.badge>
@endif
@if ($expired)
<x-ui.badge variant="red" size="sm" icon="warning">Expired</x-ui.badge>
@endif
</div>
<div class="mt-0.5 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-sm text-gray-500 dark:text-gray-400">
@if ($primaryLine && $type !== 'card')
<span class="truncate">{{ $primaryLine }}</span>
@endif
@if ($resolvedExpiry)
<span @class(['tabular-nums', 'text-red-500 dark:text-red-400' => $expired])>Expires {{ $resolvedExpiry }}</span>
@endif
@if ($name)
<span class="truncate">{{ $name }}</span>
@endif
</div>
</div>
{{-- Actions --}}
<div class="shrink-0">
@isset($actions)
{{ $actions }}
@else
<x-ui.dropdown align="right" width="48">
<x-slot:trigger>
<x-ui.icon-button icon="dots-three-vertical" size="sm" label="Manage payment method" />
</x-slot:trigger>
@if ($updateHref)
<x-ui.dropdown.item :href="$updateHref" icon="pencil-simple">{{ $updateLabel }}</x-ui.dropdown.item>
@else
<x-ui.dropdown.item icon="pencil-simple">{{ $updateLabel }}</x-ui.dropdown.item>
@endif
@unless ($default)
<x-ui.dropdown.item icon="check-circle">Set as default</x-ui.dropdown.item>
@endunless
<x-ui.dropdown.item icon="trash" variant="danger">Remove</x-ui.dropdown.item>
</x-ui.dropdown>
@endisset
</div>
</div>