- Added AddressForm and CheckoutForm components for user input during checkout. - Implemented validation using Zod and VeeValidate for billing address fields. - Created OrderSummary and MockPayPalButton components for order confirmation and payment simulation. - Updated CartSheet and CartSidebar to navigate to the new checkout page at '/kasse'. - Introduced new API endpoints for validating checkout data and creating orders. - Enhanced user experience with responsive design and error handling. These changes complete the checkout functionality, allowing users to enter billing information, simulate payment, and confirm orders.
150 lines
5.4 KiB
Vue
150 lines
5.4 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* MockPayPalButton Component
|
|
*
|
|
* Simulates a PayPal payment button for MVP testing.
|
|
* NO real PayPal API integration - just UI simulation.
|
|
*
|
|
* Features:
|
|
* - PayPal-like button styling (gold/blue gradient)
|
|
* - Simulates redirect to PayPal (shows URL flash)
|
|
* - Auto-returns after 2 seconds with "success"
|
|
* - Emits @success event when "payment" completes
|
|
*/
|
|
|
|
interface Props {
|
|
/**
|
|
* Order ID for the payment
|
|
*/
|
|
orderId: string
|
|
/**
|
|
* Total amount to display
|
|
*/
|
|
amount?: number | string
|
|
/**
|
|
* Loading state (disables button)
|
|
*/
|
|
loading?: boolean
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
|
|
const emit = defineEmits<{
|
|
success: []
|
|
}>()
|
|
|
|
const isProcessing = ref(false)
|
|
const showPayPalSimulation = ref(false)
|
|
|
|
// Format currency
|
|
const formatCurrency = (amount: number | string) => {
|
|
const value = typeof amount === 'string' ? Number.parseFloat(amount) : amount
|
|
return new Intl.NumberFormat('de-DE', {
|
|
style: 'currency',
|
|
currency: 'EUR',
|
|
}).format(value)
|
|
}
|
|
|
|
// Simulate PayPal payment flow
|
|
async function handlePayment() {
|
|
isProcessing.value = true
|
|
showPayPalSimulation.value = true
|
|
|
|
// Simulate redirect to PayPal (show fake URL for 1 second)
|
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
|
|
// Simulate PayPal processing (1 second)
|
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
|
|
// "Payment" successful
|
|
showPayPalSimulation.value = false
|
|
isProcessing.value = false
|
|
|
|
// Emit success event
|
|
emit('success')
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="space-y-4">
|
|
<!-- PayPal Button (Mock) -->
|
|
<button
|
|
v-if="!showPayPalSimulation"
|
|
type="button"
|
|
:disabled="loading || isProcessing"
|
|
@click="handlePayment"
|
|
class="w-full rounded-lg px-6 py-4 text-lg font-bold text-white shadow-lg transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
:class="{
|
|
'bg-gradient-to-r from-[#0070ba] to-[#003087] hover:from-[#003087] hover:to-[#001c64]':
|
|
!isProcessing,
|
|
'bg-gray-600 cursor-wait': isProcessing,
|
|
}"
|
|
>
|
|
<span v-if="!isProcessing" class="flex items-center justify-center gap-2">
|
|
<svg class="w-6 h-6" viewBox="0 0 24 24" fill="currentColor">
|
|
<path
|
|
d="M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944 3.72a.77.77 0 0 1 .76-.633h8.433c2.76 0 4.633.578 5.575 1.716.94 1.136 1.052 2.73.337 4.745-.713 2.015-1.936 3.533-3.639 4.515-1.703.98-3.818 1.47-6.286 1.47H8.316c-.438 0-.823.314-.906.74l-.97 4.742a.641.641 0 0 1-.633.522h-.73z"
|
|
/>
|
|
<path
|
|
d="M19.175 7.715c-.027.183-.057.364-.09.546-1.06 5.485-4.678 7.381-9.303 7.381H7.737a.914.914 0 0 0-.9.781l-1.238 7.854a.491.491 0 0 0 .485.567h3.42c.383 0 .71-.275.77-.648l.032-.165.611-3.878.039-.213c.06-.373.387-.648.77-.648h.485c4.042 0 7.205-1.642 8.127-6.393.385-1.986.186-3.645-.816-4.812a4.024 4.024 0 0 0-1.037-.85z"
|
|
opacity=".7"
|
|
/>
|
|
</svg>
|
|
<span>Mit PayPal bezahlen</span>
|
|
<span v-if="amount" class="text-white/90">({{ formatCurrency(amount) }})</span>
|
|
</span>
|
|
<span v-else class="flex items-center justify-center gap-2">
|
|
<div
|
|
class="animate-spin rounded-full h-5 w-5 border-2 border-white/20 border-t-white"
|
|
/>
|
|
Verbinde mit PayPal...
|
|
</span>
|
|
</button>
|
|
|
|
<!-- PayPal Simulation Overlay -->
|
|
<div
|
|
v-if="showPayPalSimulation"
|
|
class="rounded-lg border-2 border-[#0070ba] bg-white p-8 text-center space-y-4"
|
|
>
|
|
<div class="flex items-center justify-center gap-2 mb-4">
|
|
<svg class="w-12 h-12 text-[#0070ba]" viewBox="0 0 24 24" fill="currentColor">
|
|
<path
|
|
d="M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944 3.72a.77.77 0 0 1 .76-.633h8.433c2.76 0 4.633.578 5.575 1.716.94 1.136 1.052 2.73.337 4.745-.713 2.015-1.936 3.533-3.639 4.515-1.703.98-3.818 1.47-6.286 1.47H8.316c-.438 0-.823.314-.906.74l-.97 4.742a.641.641 0 0 1-.633.522h-.73z"
|
|
/>
|
|
<path
|
|
d="M19.175 7.715c-.027.183-.057.364-.09.546-1.06 5.485-4.678 7.381-9.303 7.381H7.737a.914.914 0 0 0-.9.781l-1.238 7.854a.491.491 0 0 0 .485.567h3.42c.383 0 .71-.275.77-.648l.032-.165.611-3.878.039-.213c.06-.373.387-.648.77-.648h.485c4.042 0 7.205-1.642 8.127-6.393.385-1.986.186-3.645-.816-4.812a4.024 4.024 0 0 0-1.037-.85z"
|
|
opacity=".7"
|
|
/>
|
|
</svg>
|
|
<span class="text-2xl font-bold text-[#0070ba]">PayPal</span>
|
|
</div>
|
|
|
|
<div class="animate-pulse">
|
|
<div
|
|
class="animate-spin rounded-full h-12 w-12 border-4 border-[#0070ba]/20 border-t-[#0070ba] mx-auto mb-4"
|
|
/>
|
|
<p class="text-lg font-semibold text-gray-800">Zahlung wird verarbeitet...</p>
|
|
<p class="text-sm text-gray-600 mt-2">
|
|
Du wirst zu PayPal weitergeleitet und kehrst automatisch zurück.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Mock PayPal URL -->
|
|
<div class="mt-4 p-2 bg-gray-100 rounded text-xs font-mono text-gray-600">
|
|
https://www.paypal.com/checkoutnow?token=EC-MOCK{{ Date.now() }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Info Text -->
|
|
<div class="text-center space-y-2">
|
|
<p class="text-sm text-white/60">
|
|
<strong class="text-experimenta-accent">Hinweis (MVP):</strong> Dies ist eine
|
|
Test-Zahlung. Es wird kein echtes Geld abgebucht.
|
|
</p>
|
|
<p class="text-xs text-white/40">
|
|
Die echte PayPal-Integration erfolgt in Phase 7 des Projekts.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</template>
|