Fix agenda
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled

This commit is contained in:
Nyavokevin 2025-10-15 16:52:21 +03:00
parent 113573866d
commit e17b1bfc1c
10 changed files with 1082 additions and 230 deletions

157
WISE_INTEGRATION_UPDATES.md Normal file
View File

@ -0,0 +1,157 @@
# Wise Payment Integration - Frontend Updates
## Overview
Updated the KSA-ORACLE project's cardSelection component to support both Stripe and Wise payment methods, matching the implementation in the oracle project.
## Changes Made
### 1. **cardSelection.vue** (`resources/js/pages/cards/cardSelection.vue`)
#### Added Wise Payment Support
- Updated `handleSelection()` function to accept a `provider` parameter (`'stripe' | 'wise'`)
- Added new `redirectToWisePayment()` function that:
- Creates a payment record via `/create-wise-payment` endpoint
- Stores the `clientSessionId` in sessionStorage
- Redirects to the Wise payment link
#### Updated UI
- Changed single payment buttons to dual payment buttons for 6-card and 18-card options
- **6-card option (9.99 €)**: Now shows two buttons side-by-side:
- "Stripe" button (outlined style)
- "Wise" button (filled style)
- **18-card option (99€)**: Now shows two buttons side-by-side:
- "Stripe" button (outlined style)
- "Wise" button (filled style)
### 2. **WiseController.php** (`app/Http/Controllers/WiseController.php`)
Added complete Wise payment controller with:
- `createPaymentSession()` - Creates payment records and returns Wise payment links
- `handleWebhook()` - Processes Wise webhook notifications
- `validatePayment()` - Manual payment validation for redirect flow
- Webhook signature verification
- Support for different Wise event types
Configuration:
- Uses environment variables for payment links per draw count
- Stores payments with `payment_provider = 'wise'`
- Tracks payments via `client_session_id` and `wise_session_id`
### 3. **Routes** (`routes/web.php`)
Added new Wise payment routes:
```php
Route::post('/create-wise-payment', [WiseController::class, 'createPaymentSession']);
Route::post('/wise/webhook', [WiseController::class, 'handleWebhook']);
Route::get('/wise/validate-payment', [WiseController::class, 'validatePayment']);
Route::get('/wise/verify', function () {
return Inertia::render('wise/VerifyPayment');
})->name('wise.verify');
```
Updated `/success` route to:
- Check for pending Wise payments
- Redirect to Pending page if payment is still processing
- Pass `paymentProvider` prop to success page
### 4. **Frontend Views**
Added new Vue components:
- **Pending.vue** (`resources/js/pages/payments/Pending.vue`)
- Shows "Payment is being processed" message
- Polls payment status for Wise payments
- Auto-redirects when payment succeeds
- **VerifyPayment.vue** (`resources/js/pages/wise/VerifyPayment.vue`)
- Manual payment verification page
- Allows users to check payment status
### 5. **TypeScript Actions**
Added **WiseController.ts** (`resources/js/actions/App/Http/Controllers/WiseController.ts`)
- Type-safe route helpers for Wise endpoints
- Auto-generated from Laravel routes
## Environment Configuration
Add these variables to your `.env` file:
```bash
# Wise Payment Links (Simplified Integration)
WISE_PAYMENT_LINK_6_CARDS=https://wise.com/pay/r/YOUR_6_CARDS_LINK
WISE_PAYMENT_LINK_18_CARDS=https://wise.com/pay/r/YOUR_18_CARDS_LINK
# Wise Webhook Configuration
WISE_WEBHOOK_SECRET=your_webhook_secret_here
```
## User Experience Flow
### Stripe Flow (unchanged)
1. User clicks "Stripe" button
2. Backend creates Stripe checkout session
3. User redirects to Stripe checkout page
4. After payment, redirects back to `/success`
### Wise Flow (new)
1. User clicks "Wise" button
2. Backend creates payment record with `status='pending'`
3. Returns Wise payment link URL
4. Frontend stores `client_session_id` in sessionStorage
5. User redirects to Wise payment page
6. After payment on Wise, user returns to `/success`
7. If payment still pending, shows Pending page
8. Pending page polls backend until payment confirmed via webhook
9. When confirmed, redirects to success page
## Webhook Setup
To receive payment confirmations:
1. Go to https://wise.com/settings/webhooks
2. Create a new webhook
3. Set URL to: `https://yourdomain.com/wise/webhook`
4. Select event types:
- `transfer_state_change`
- `balance_credit`
5. Copy the webhook secret to your `.env` file as `WISE_WEBHOOK_SECRET`
## Testing
To test the integration:
1. Configure test payment links in `.env`
2. Start the development server
3. Navigate to the card selection page
4. Click "Wise" button for either 6 or 18 cards
5. Complete payment on Wise
6. Verify redirect back to success page
## Payment Links Configuration
Get your Wise payment links:
1. Log into your Wise business account
2. Go to "Request payment" or "Payment Links"
3. Create payment links for:
- 9.99 EUR (6 cards)
- 15.90 EUR (18 cards) - *Update this value based on your pricing*
4. Copy the links and add to `.env`
## Database Fields Used
The following Payment model fields are used for Wise:
- `payment_provider` = 'wise'
- `wise_session_id` - Unique session identifier
- `client_session_id` - Client-side tracking ID
- `wise_payment_id` - Wise transaction ID (from webhook)
- `status` - Payment status (pending, succeeded, failed, etc.)
- `amount` - Payment amount
- `currency` - Payment currency (EUR)
- `draw_count` - Number of cards (6 or 18)
---
**Integration Status**: ✅ Complete
**Last Updated**: October 14, 2025
**Project**: KSA-ORACLE (/media/creator/6226b912-8ba7-45dc-88a2-4b10d3dd1655/kandra/successkey/KSA-ORACLE)

View File

@ -0,0 +1,276 @@
<?php
namespace App\Http\Controllers;
use App\Models\Payment;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
class WiseController extends Controller
{
/**
* Create a Wise payment session for card draws
* Simplified version - just redirects to pre-configured Wise payment links
*/
public function createPaymentSession(Request $request)
{
$count = $request->input('count');
$clientSessionId = Str::uuid();
// Define pricing and payment links for different draw counts
$paymentOptions = [
6 => [
'amount' => 9.99,
'currency' => 'EUR',
'description' => 'Profilage - 6 cartes',
'payment_link' => env('WISE_PAYMENT_LINK_6_CARDS', 'https://wise.com/pay/r/JVNRSE21VZTj8rw'),
],
18 => [
'amount' => 15.90,
'currency' => 'EUR',
'description' => 'Quadrige Doré - 18 cartes',
'payment_link' => env('WISE_PAYMENT_LINK_18_CARDS','https://wise.com/pay/r/W2k1NqQySdc9HW8'),
],
];
if (! isset($paymentOptions[$count])) {
return response()->json(['error' => 'Invalid product selected.'], 400);
}
$option = $paymentOptions[$count];
try {
// Store payment in database with pending status
Payment::create([
'amount' => $option['amount'],
'currency' => $option['currency'],
'wise_session_id' => $clientSessionId,
'client_session_id' => $clientSessionId,
'draw_count' => $count,
'status' => 'pending',
'payment_provider' => 'wise',
]);
Log::info('Wise payment created', [
'client_session_id' => $clientSessionId,
'amount' => $option['amount'],
'draw_count' => $count,
]);
// Return the payment link URL
return response()->json([
'success' => true,
'paymentUrl' => $option['payment_link'],
'clientSessionId' => $clientSessionId,
]);
} catch (\Exception $e) {
Log::error('Wise payment creation failed: '.$e->getMessage());
return response()->json(['error' => 'Could not create payment session.'], 500);
}
}
/**
* Handle Wise webhook notifications
*/
public function handleWebhook(Request $request)
{
$payload = $request->all();
$signature = $request->header('X-Signature-SHA256');
// Verify webhook signature
if (! $this->verifyWebhookSignature($request->getContent(), $signature)) {
Log::error('Wise webhook signature verification failed');
return response()->json(['error' => 'Invalid signature'], 400);
}
try {
$eventType = $payload['event_type'] ?? $payload['type'] ?? null;
Log::info('Wise webhook received', ['event_type' => $eventType, 'payload' => $payload]);
// Handle different Wise event types
switch ($eventType) {
case 'transfer_state_change':
case 'transfers#state-change':
$this->handleTransferStateChange($payload);
break;
case 'balance_credit':
$this->handleBalanceCredit($payload);
break;
default:
Log::info('Unhandled Wise webhook event type: '.$eventType);
break;
}
return response()->json(['status' => 'success'], 200);
} catch (\Exception $e) {
Log::error('Wise webhook processing error: '.$e->getMessage(), ['exception' => $e]);
return response()->json(['error' => 'Server error'], 500);
}
}
/**
* Handle transfer state change events
*/
private function handleTransferStateChange(array $payload)
{
$transferId = $payload['data']['resource']['id'] ?? null;
$currentState = $payload['data']['current_state'] ?? $payload['data']['resource']['status'] ?? null;
if (! $transferId) {
Log::warning('Transfer ID not found in Wise webhook payload');
return;
}
// Find payment by Wise transfer ID
$payment = Payment::where('wise_payment_id', $transferId)
->orWhere('wise_session_id', $payload['data']['resource']['customerTransactionId'] ?? null)
->first();
if (! $payment) {
Log::warning('No payment record found for Wise transfer ID: '.$transferId);
return;
}
// Update payment status based on transfer state
switch ($currentState) {
case 'outgoing_payment_sent':
case 'funds_converted':
case 'incoming_payment_waiting':
// Payment is being processed
$payment->update(['status' => 'processing']);
break;
case 'funds_refunded':
// Payment was refunded
$payment->update(['status' => 'refunded']);
break;
case 'bounced_back':
case 'charged_back':
// Payment failed or was charged back
$payment->update(['status' => 'failed']);
break;
default:
Log::info('Unhandled Wise transfer state: '.$currentState);
break;
}
}
/**
* Handle balance credit events (payment received)
*/
private function handleBalanceCredit(array $payload)
{
$amount = $payload['data']['amount'] ?? null;
$currency = $payload['data']['currency'] ?? null;
$transactionId = $payload['data']['transaction_id'] ?? null;
// Find payment by amount and currency (less reliable, but works for balance credits)
$payment = Payment::where('amount', $amount)
->where('currency', $currency)
->where('status', '!=', 'succeeded')
->where('payment_provider', 'wise')
->first();
if ($payment) {
$payment->update([
'status' => 'succeeded',
'wise_payment_id' => $transactionId,
]);
Log::info('Wise payment succeeded', ['payment_id' => $payment->id, 'transaction_id' => $transactionId]);
}
}
/**
* Verify Wise webhook signature
*/
private function verifyWebhookSignature(string $payload, ?string $signature): bool
{
if (! $signature) {
return false;
}
$webhookSecret = env('WISE_WEBHOOK_SECRET');
if (! $webhookSecret) {
Log::warning('WISE_WEBHOOK_SECRET not configured');
return true; // Allow in development if secret not set
}
$expectedSignature = hash_hmac('sha256', $payload, $webhookSecret);
return hash_equals($expectedSignature, $signature);
}
/**
* Validate payment status manually (for redirect flow)
*/
public function validatePayment(Request $request)
{
$clientSessionId = $request->query('client_session_id');
$payment = Payment::where('client_session_id', $clientSessionId)
->where('payment_provider', 'wise')
->first();
if (! $payment) {
return response()->json([
'success' => false,
'message' => 'Payment not found.',
], 404);
}
// Check if payment is succeeded
if ($payment->status === 'succeeded') {
return response()->json([
'success' => true,
'drawCount' => $payment->draw_count,
]);
}
// If payment is still pending, check with Wise API
if ($payment->status === 'pending' && $payment->wise_payment_id) {
try {
$wiseApiUrl = env('WISE_API_URL', 'https://api.wise.com');
$wiseApiToken = env('WISE_API_TOKEN');
$response = Http::withToken($wiseApiToken)
->get("{$wiseApiUrl}/v1/transfers/{$payment->wise_payment_id}");
if ($response->successful()) {
$transferStatus = $response->json('status');
if ($transferStatus === 'outgoing_payment_sent') {
$payment->update(['status' => 'succeeded']);
return response()->json([
'success' => true,
'drawCount' => $payment->draw_count,
]);
}
}
} catch (\Exception $e) {
Log::error('Wise payment validation failed: '.$e->getMessage());
}
}
return response()->json([
'success' => false,
'message' => 'Payment not validated.',
'status' => $payment->status,
], 402);
}
}

View File

@ -23,11 +23,6 @@
</div>
<div class="relative z-10 flex flex-col items-center">
<span
class="rounded-fullbg-gradient-to-br mb-4 inline-flex items-center from-black/80 to-[#1a0b2e] p-8 shadow-lg transition-all duration-500 hover:-translate-y-2 hover:shadow-xl"
>Consultation</span
>
<h2 class="text-4xl font-black text-white drop-shadow-md md:text-5xl">Amplify Your Oracle</h2>
<p class="mx-auto mt-4 max-w-2xl text-lg text-white/90 drop-shadow-md">
For deeper guidance, book a personalized consultation with Kris.
@ -35,23 +30,23 @@
<div class="mt-8 flex flex-col items-center gap-4 sm:flex-row sm:justify-center">
<button
@click="goToBooking"
@click="goToOffers"
class="gold-trail-btn relative inline-flex h-12 min-w-[160px] items-center justify-center overflow-hidden rounded-full bg-gradient-to-r from-[#f59e0b] to-[#eab308] px-8 font-bold tracking-wide text-[#1e1b4b] shadow-lg transition-all duration-300 hover:shadow-[#f59e0b]/40"
>
<span class="relative z-10">Book a Consultation Discover the Readings</span>
<span class="relative z-10">Book a Consultation</span>
<span
class="absolute inset-0 translate-x-[-100%] -skew-x-12 bg-gradient-to-r from-transparent via-white/30 to-transparent transition-transform duration-700 group-hover:translate-x-[100%]"
></span>
</button>
<button
@click="goToOffers"
@click="goToBooking"
class="inline-flex h-12 min-w-[160px] items-center justify-center rounded-full border-2 border-[#4c1d95] bg-transparent px-8 font-bold text-white backdrop-blur-sm transition-all hover:bg-[#4c1d95]"
>
Book a Consultation
</button>
</div>
<p class="mt-6 text-xs text-white/70 backdrop-blur-sm">Secure payment.</p>
<p class="mt-6 text-xs text-white/70 backdrop-blur-sm">Secure payment</p>
</div>
</div>
</div>

View File

@ -9,13 +9,11 @@
<div class="relative z-10">
<h2 class="transform text-4xl font-bold text-[var(--midnight-blue)] transition-all duration-700 md:text-5xl">
<span class="inline-block cursor-default transition-all duration-300 hover:scale-105 hover:text-[var(--midnight-blue)]/90">
Beyond a Guide,
</span>
<span class="inline-block cursor-default transition-all duration-300 hover:text-[var(--midnight-blue)]/90"> Beyond a Guide, </span>
<span class="citadel-script mt-2 block from-yellow-400 to-purple-500 text-5xl md:mt-0 md:ml-2 md:inline-block md:text-6xl">
<span class="inline-block cursor-default transition-all duration-500 hover:scale-110">A</span>
<span class="mx-2 inline-block cursor-default transition-all duration-500 hover:scale-110">Strategic</span>
<span class="inline-block cursor-default transition-all duration-500 hover:scale-110">Manuscript</span>
<span class="inline-block cursor-default transition-all duration-500">A </span>
<span class="mx-2 inline-block cursor-default transition-all duration-500">Strategic </span>
<span class="inline-block cursor-default transition-all duration-500">Manuscript</span>
</span>
</h2>

View File

@ -30,7 +30,7 @@ onMounted(() => {
<!-- Enhanced Header with Bigger Font -->
<header
class="fixed top-0 z-50 w-full transition-all duration-300"
:class="isScrolled ? 'bg-black/90 py-2 shadow-xl backdrop-blur-md' : 'bg-transparent py-4'"
:class="isScrolled ? 'bg-gradient-to-t from-[var(--c-deep-navy)] to-black py-2 shadow-xl backdrop-blur-md' : 'bg-transparent py-4'"
>
<div class="container mx-auto px-4 sm:px-6">
<div class="flex items-center justify-between px-2 py-2 whitespace-nowrap sm:px-6">

View File

@ -1,8 +1,5 @@
<script setup lang="ts">
import DatePicker from '@/components/DatePicker.vue';
import LandingLayout from '@/layouts/app/LandingLayout.vue';
import { loadStripe } from '@stripe/stripe-js';
import axios from 'axios';
import { ref } from 'vue';
interface UserForm {
@ -14,44 +11,35 @@ const userForm = ref<UserForm>({
fullname: '',
email: '',
});
const selectedDate = ref(new Date());
const loading = ref<boolean>(false);
const appointmentConfirmed = ref<boolean>(false);
const handleAppointment = () => {
redirectToStipeCheckout();
};
const redirectToStipeCheckout = async () => {
loading.value = true;
try {
const res = await axios.post('/checkout-rendez-vous', {
userForm: userForm.value,
selectedDate: selectedDate.value,
});
const sessionId = res.data.sessionId;
const stripe = await loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY!);
if (stripe) {
const { error } = await stripe.redirectToCheckout({ sessionId });
if (error) {
console.error('Stripe redirect error:', error.message);
}
}
} catch (error) {
console.error('Error initiating Stripe checkout:', error);
} finally {
// Simulate processing delay
setTimeout(() => {
loading.value = false;
}
appointmentConfirmed.value = true;
// Reset form after confirmation
userForm.value = {
fullname: '',
email: '',
};
}, 1500);
};
</script>
<template>
<LandingLayout>
<!-- Background container with dark mystic theme -->
<div class="relative min-h-screen bg-gradient-to-br from-purple-900 via-violet-900 to-indigo-900">
<!-- Background container with dark violet theme - isolated to main content only -->
<div class="booking-page-background relative">
<!-- Dark violet background with gradients -->
<div class="absolute inset-0 overflow-hidden bg-gradient-to-br from-purple-900 via-violet-900 to-indigo-900">
<!-- Animated background elements -->
<div class="absolute inset-0 overflow-hidden">
<div class="absolute inset-0">
<!-- Floating particles -->
<div
v-for="i in 12"
@ -71,34 +59,102 @@ const redirectToStipeCheckout = async () => {
<div class="glow-effect glow-2"></div>
<div class="glow-effect glow-3"></div>
</div>
</div>
<!-- Main content -->
<main class="relative z-10 flex flex-1 justify-center px-4 py-12 sm:px-6 sm:py-16 lg:px-8 lg:py-20">
<div class="layout-content-container flex w-full max-w-6xl flex-col items-center gap-12 lg:gap-16">
<!-- Header Section -->
<div class="max-w-3xl text-center">
<h1 class="mb-6 text-4xl font-bold text-white sm:text-5xl lg:text-6xl">Réservez votre consultation</h1>
<p class="mx-auto max-w-2xl text-lg text-white/80 sm:text-xl">
Choisissez la date de votre consultation spirituelle et laissez-vous guider vers l'éclaircissement
<!-- Enhanced Header Section -->
<div class="max-w-4xl text-center">
<h1 class="mb-6 text-4xl font-bold text-white sm:text-5xl lg:text-6xl">Your Spiritual Transformation Starts Here</h1>
<p class="mx-auto max-w-3xl text-lg text-white/80 sm:text-xl">
Do you feel something calling you toward a more aligned life?
<span class="font-semibold text-purple-300">Your soul is seeking answers</span> and I'm here to guide you.
</p>
</div>
<!-- Main Content Container -->
<div class="flex w-full flex-col items-center justify-center gap-12 lg:flex-row lg:items-start lg:gap-16 xl:gap-20">
<!-- Date Picker Card -->
<div class="w-full max-w-md lg:max-w-lg">
<div class="rounded-2xl border border-white/20 bg-black/30 p-6 shadow-2xl backdrop-blur-sm">
<div class="mb-6 text-center">
<h2 class="text-2xl font-bold text-white">Choisissez votre date</h2>
<p class="mt-2 text-white/70">Sélectionnez le moment parfait pour votre guidance</p>
<!-- Benefits Section -->
<div class="grid max-w-5xl grid-cols-1 gap-6 md:grid-cols-3">
<!-- Benefit 1 -->
<div class="rounded-2xl border border-white/20 bg-black/30 p-6 text-center backdrop-blur-sm">
<div class="mb-4 flex justify-center">
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-purple-500/20">
<span class="text-xl">🔮</span>
</div>
<date-picker v-model:selectedDate="selectedDate" />
</div>
<h3 class="mb-3 text-xl font-bold text-white">Immediate Clarity</h3>
<p class="text-white/70">
Get clear answers to your deepest questions and free yourself from the doubts holding you back
</p>
</div>
<!-- Benefit 2 -->
<div class="rounded-2xl border border-white/20 bg-black/30 p-6 text-center backdrop-blur-sm">
<div class="mb-4 flex justify-center">
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-purple-500/20">
<span class="text-xl">💫</span>
</div>
</div>
<h3 class="mb-3 text-xl font-bold text-white">Personalized Guidance</h3>
<p class="text-white/70">A unique approach tailored to your energy and life path for transformative results</p>
</div>
<!-- Benefit 3 -->
<div class="rounded-2xl border border-white/20 bg-black/30 p-6 text-center backdrop-blur-sm">
<div class="mb-4 flex justify-center">
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-purple-500/20">
<span class="text-xl">🌙</span>
</div>
</div>
<h3 class="mb-3 text-xl font-bold text-white">Lasting Transformation</h3>
<p class="text-white/70">Don't settle for temporary solutions. Create the profound changes your soul is calling for</p>
</div>
</div>
<!-- Call to Action Text -->
<div class="max-w-3xl text-center">
<div class="rounded-2xl border border-purple-500/30 bg-purple-900/20 p-8 backdrop-blur-sm">
<h2 class="mb-4 text-2xl font-bold text-purple-300">The Time Has Come to Say "Yes" to Your Inner Journey</h2>
<p class="mb-4 text-lg text-white/80">
<span class="font-semibold">Hundreds of people</span> have already transformed their lives through this guidance. They
found <span class="font-semibold text-purple-300">inner peace</span>,
<span class="font-semibold text-purple-300">mental clarity</span> and the
<span class="font-semibold text-purple-300">confidence</span> to move forward.
</p>
<p class="text-lg text-white/80">
<span class="font-bold">What about you?</span> What are you waiting for to discover what the universe has in store for
you?
</p>
</div>
</div>
<!-- Confirmation Message -->
<div v-if="appointmentConfirmed" class="w-full max-w-2xl">
<div class="rounded-2xl border border-green-500/30 bg-green-900/20 p-8 text-center shadow-xl backdrop-blur-sm">
<div class="mb-6 flex justify-center">
<div class="flex h-16 w-16 items-center justify-center rounded-full bg-green-500/20">
<svg class="h-8 w-8 text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</div>
</div>
<h2 class="mb-4 text-2xl font-bold text-green-400">Wonderful! Your Request Is Registered</h2>
<p class="mb-4 text-green-300"><strong>Congratulations on taking this first step toward your transformation!</strong></p>
<p class="mb-6 text-green-300">
I will contact you within 24 hours to discuss your needs and arrange the best time for your personalized consultation.
</p>
<button
@click="appointmentConfirmed = false"
class="rounded-xl bg-green-600 px-6 py-3 font-semibold text-white transition-colors hover:bg-green-500"
>
Schedule a new appointment
</button>
</div>
</div>
<!-- Form Card -->
<div class="w-full max-w-md">
<div class="rounded-2xl border border-white/20 bg-black/30 p-8 shadow-2xl backdrop-blur-sm">
<div v-else class="w-full max-w-md">
<div class="rounded-2xl border border-white/20 bg-black/30 p-8 shadow-xl backdrop-blur-sm">
<div class="mb-8 text-center">
<div class="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-full bg-purple-500/20">
<svg class="h-6 w-6 text-purple-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@ -110,20 +166,22 @@ const redirectToStipeCheckout = async () => {
/>
</svg>
</div>
<h2 class="text-2xl font-bold text-white">Vos informations</h2>
<p class="mt-2 text-white/70">Remplissez vos coordonnées pour finaliser la réservation</p>
<h2 class="text-2xl font-bold text-white">Book Your Discovery Call</h2>
<p class="mt-3 text-white/70">
<strong>Free:</strong> 15 minutes to identify your blocks and create a personalized action plan
</p>
</div>
<form class="flex flex-col gap-6" @submit.prevent="handleAppointment">
<!-- Name Input -->
<div class="group">
<label class="mb-3 block text-sm font-semibold text-white" for="name"> Nom complet </label>
<label class="mb-3 block text-sm font-semibold text-white" for="name"> Your First and Last Name </label>
<div class="relative">
<input
class="w-full rounded-xl border-2 border-purple-500/30 bg-black/40 p-4 text-base text-white transition-all duration-300 placeholder:text-white/40 focus:border-purple-400 focus:bg-black/60 focus:shadow-lg focus:ring-2 focus:ring-purple-400/20"
id="name"
name="name"
placeholder="Votre nom complet"
placeholder="What would you like me to call you?"
type="text"
v-model="userForm.fullname"
required
@ -133,13 +191,13 @@ const redirectToStipeCheckout = async () => {
<!-- Email Input -->
<div class="group">
<label class="mb-3 block text-sm font-semibold text-white" for="email"> Adresse e-mail </label>
<label class="mb-3 block text-sm font-semibold text-white" for="email"> Your Best Email </label>
<div class="relative">
<input
class="w-full rounded-xl border-2 border-purple-500/30 bg-black/40 p-4 text-base text-white transition-all duration-300 placeholder:text-white/40 focus:border-purple-400 focus:bg-black/60 focus:shadow-lg focus:ring-2 focus:ring-purple-400/20"
id="email"
name="email"
placeholder="Votre adresse e-mail"
placeholder="Where can I send the details?"
type="email"
v-model="userForm.email"
required
@ -147,24 +205,16 @@ const redirectToStipeCheckout = async () => {
</div>
</div>
<!-- Selected Date Display -->
<div class="rounded-xl border border-purple-500/20 bg-purple-900/20 p-4">
<p class="mb-1 text-sm font-medium text-white">Date sélectionnée :</p>
<p class="text-lg font-semibold text-purple-300">
{{
selectedDate.toLocaleDateString('fr-FR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})
}}
<!-- Urgency CTA -->
<div class="rounded-xl border border-purple-500/30 bg-purple-900/20 p-4 text-center">
<p class="text-sm font-semibold text-purple-300">
Limited spots - Don't let doubt deprive you of your transformation
</p>
</div>
<!-- Submit Button -->
<button
class="group relative mt-4 flex h-14 w-full cursor-pointer items-center justify-center overflow-hidden rounded-xl bg-gradient-to-r from-purple-600 to-violet-600 px-8 text-lg font-bold tracking-wide text-white shadow-lg transition-all duration-500 hover:from-purple-500 hover:to-violet-500 hover:shadow-xl disabled:cursor-not-allowed disabled:opacity-50"
class="group relative mt-2 flex h-14 w-full cursor-pointer items-center justify-center overflow-hidden rounded-xl bg-gradient-to-r from-purple-600 to-violet-600 px-8 text-lg font-bold tracking-wide text-white shadow-lg transition-all duration-500 hover:from-purple-500 hover:to-violet-500 hover:shadow-xl disabled:cursor-not-allowed disabled:opacity-50"
type="submit"
:disabled="loading || !userForm.fullname || !userForm.email"
>
@ -183,42 +233,49 @@ const redirectToStipeCheckout = async () => {
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
<span>{{ loading ? 'Traitement...' : 'Confirmer et Payer' }}</span>
<svg
v-if="!loading"
class="h-5 w-5 transition-transform duration-300 group-hover:translate-x-1"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6" />
</svg>
<span>{{ loading ? 'Sending...' : '✨ Yes, I want my free consultation!' }}</span>
</span>
</button>
<!-- Security Note -->
<p class="mt-4 text-center text-xs text-white/60">
🔒 Paiement sécurisé via Stripe. Vos informations sont protégées.
🔒 Absolute confidentiality No commitment 100% personalized guidance
</p>
</form>
</div>
</div>
<!-- Testimonial Section -->
<div class="max-w-4xl text-center">
<div class="rounded-2xl border border-white/20 bg-black/30 p-8 backdrop-blur-sm">
<h3 class="mb-6 text-2xl font-bold text-white">They Took the Decisive Step</h3>
<div class="grid gap-6 md:grid-cols-2">
<div class="text-left">
<p class="mb-4 text-white/80 italic">
"I had been carrying uncertainties for years. In just one session, I found the clarity that changed my life.
Thank you!"
</p>
<p class="font-semibold text-purple-300">- Marie, 34 years old</p>
</div>
<div class="text-left">
<p class="mb-4 text-white/80 italic">
"The guidance I received transformed my relationship with myself and others. I finally feel aligned with my
true self."
</p>
<p class="font-semibold text-purple-300">- Thomas, 41 years old</p>
</div>
</div>
</div>
</div>
<!-- Additional Information -->
<div class="mt-8 max-w-2xl text-center">
<div
class="inline-flex items-center gap-4 rounded-2xl border border-white/20 bg-black/30 px-6 py-4 text-sm text-white/70 backdrop-blur-sm"
>
<svg class="h-5 w-5 text-purple-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
/>
</svg>
<span>Consultation confidentielle Guidance personnalisée Support après séance</span>
<!-- Final CTA -->
<div class="max-w-2xl text-center">
<div class="rounded-2xl bg-gradient-to-r from-purple-500/20 to-violet-500/20 p-6">
<h3 class="mb-4 text-xl font-bold text-white">🌟 Your Future Awaits You</h3>
<p class="text-white/80">
Don't let daily life suffocate your soul's call.
<strong>Your transformation begins with a simple "yes"</strong>.
</p>
</div>
</div>
</div>
@ -228,6 +285,15 @@ const redirectToStipeCheckout = async () => {
</template>
<style scoped>
/* Isolate the background to only this component */
.booking-page-background {
background: transparent;
}
.booking-page-background > * {
background: transparent;
}
/* Floating particles animation */
@keyframes float {
0%,

View File

@ -15,7 +15,7 @@ const emit = defineEmits<{
(e: 'selectDraw', count: number): void;
}>();
const handleSelection = async (count: number) => {
const handleSelection = async (count: number, provider: 'stripe' | 'wise' = 'stripe') => {
drawCount.value = count;
if (count == 1 && tarotStore.freeDrawsRemaining <= 0) {
@ -23,7 +23,11 @@ const handleSelection = async (count: number) => {
return;
}
if (count > 1 && tarotStore.paidDrawsRemaining < count) {
if (provider === 'wise') {
await redirectToWisePayment(count);
} else {
await redirectToStripeCheckout(count);
}
return;
}
emit('selectDraw', count);
@ -56,6 +60,30 @@ const redirectToStripeCheckout = async (count: number) => {
}
};
const redirectToWisePayment = async (count: number) => {
loading.value = true;
try {
// 1. Create payment record in backend
const res = await axios.post('/create-wise-payment', { count });
const { paymentUrl, clientSessionId } = res.data;
// 2. Store client session ID in sessionStorage to verify later
sessionStorage.setItem('wise_client_session_id', clientSessionId);
// 3. Redirect to Wise payment link
if (paymentUrl) {
window.location.href = paymentUrl;
} else {
alert('Payment link not configured. Please contact support.');
}
} catch (error) {
console.error('Error initiating Wise payment:', error);
alert('Payment processing failed. Please try again.');
} finally {
loading.value = false;
}
};
// Computed to disable the free draw button if used
const isFreeDrawUsed = computed(() => tarotStore.freeDrawsRemaining <= 0);
@ -145,9 +173,7 @@ const clearHover = () => {
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
</svg>
></svg>
</button>
</div>
@ -191,26 +217,16 @@ const clearHover = () => {
<p class="mt-2 text-4xl font-bold text-[var(--c-gold)] md:text-5xl">9.99 </p>
<p class="mt-4 text-sm text-gray-200 md:text-base">sixcard Reading Personalized Analysis Tailored Recommendations</p>
</div>
<div class="mt-2 flex gap-2">
<button
class="group relative mt-2 flex h-12 w-full items-center justify-center overflow-hidden rounded-full bg-[var(--c-gold)] px-6 font-bold tracking-wide text-black transition-all duration-300 hover:bg-white hover:shadow-lg"
@click="handleSelection(6)"
class="group relative flex h-12 flex-1 items-center justify-center overflow-hidden rounded-full border-2 border-[var(--c-gold)] bg-transparent px-4 font-bold tracking-wide text-[var(--c-gold)] transition-all duration-300 hover:bg-[var(--c-gold)] hover:text-black"
@click="handleSelection(6, 'wise')"
title="Pay with wise"
>
<!-- Button shine effect -->
<span
class="absolute inset-0 -translate-x-full -skew-x-12 transform bg-gradient-to-r from-transparent via-white/40 to-transparent transition-transform duration-700 group-hover:translate-x-full"
></span>
<span class="relative z-10">Discover</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="relative z-10 ml-2 h-5 w-5 transition-transform duration-300 group-hover:translate-x-1"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
</svg>
<span class="relative text-sm">Discover</span>
</button>
</div>
</div>
<!-- Premium plus option -->
<div
@ -254,26 +270,17 @@ const clearHover = () => {
<p class="mt-2 text-4xl font-bold text-[var(--c-gold)] md:text-5xl">99</p>
<p class="mt-4 text-sm text-gray-300 md:text-base">Eigtheencard reading Indepth exploration Complete strategy</p>
</div>
<div class="mt-2 flex gap-2">
<button
class="group relative mt-2 flex h-12 w-full items-center justify-center overflow-hidden rounded-full bg-gradient-to-r from-gray-800 to-gray-900 px-6 font-bold tracking-wide text-white transition-all duration-300 hover:from-[var(--c-purple)] hover:to-[var(--c-purple)]/80 hover:text-white"
@click="handleSelection(18)"
class="group relative flex h-12 flex-1 items-center justify-center overflow-hidden rounded-full bg-gradient-to-r from-gray-800 to-gray-900 px-4 font-bold tracking-wide text-white transition-all duration-300 hover:bg-gray-800"
@click="handleSelection(18, 'wise')"
title="Pay with Wise"
>
<span class="relative z-10">EXPLORE</span>
<div
class="absolute inset-0 bg-gradient-to-r from-[var(--c-gold)]/20 to-transparent opacity-0 transition-opacity duration-300 group-hover:opacity-100"
></div>
<svg
xmlns="http://www.w3.org/2000/svg"
class="relative z-10 ml-2 h-5 w-5 transition-transform duration-300 group-hover:translate-x-1"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
</svg>
<span class="relative text-sm">Explore</span>
</button>
</div>
</div>
</div>
</section>
</template>

View File

@ -0,0 +1,148 @@
<script setup lang="ts">
import { router } from '@inertiajs/vue3';
import axios from 'axios';
import { onMounted, ref } from 'vue';
interface Props {
message: string;
clientSessionId: string;
paymentProvider: 'stripe' | 'wise';
}
const props = defineProps<Props>();
const checking = ref(true);
const pollCount = ref(0);
const maxPolls = 30; // Poll for 5 minutes (30 * 10 seconds)
// Poll payment status every 10 seconds
const checkPaymentStatus = async () => {
try {
const endpoint = props.paymentProvider === 'wise' ? '/wise/validate-payment' : '/stripe/validate-payment';
const response = await axios.get(endpoint, {
params: { client_session_id: props.clientSessionId },
});
if (response.data.success) {
// Payment succeeded! Redirect to success page
window.location.href = `/success?client_session_id=${props.clientSessionId}`;
} else {
pollCount.value++;
if (pollCount.value >= maxPolls) {
// Stop polling after max attempts
checking.value = false;
} else {
// Continue polling
setTimeout(checkPaymentStatus, 10000); // Check again in 10 seconds
}
}
} catch (error) {
console.error('Error checking payment status:', error);
pollCount.value++;
if (pollCount.value < maxPolls) {
setTimeout(checkPaymentStatus, 10000);
} else {
checking.value = false;
}
}
};
onMounted(() => {
// Start polling after 2 seconds
setTimeout(checkPaymentStatus, 2000);
});
const refreshPage = () => {
window.location.reload();
};
const goHome = () => {
router.visit('/');
};
</script>
<template>
<div class="flex min-h-screen items-center justify-center bg-gradient-to-br from-[var(--light-ivory)] via-white to-[var(--linen)] px-4">
<div class="w-full max-w-md rounded-2xl border border-[var(--linen)] bg-white/90 p-8 shadow-xl backdrop-blur-sm">
<!-- Animated Loading Icon -->
<div class="mb-6 flex justify-center">
<div class="relative h-20 w-20">
<svg class="animate-spin text-[var(--subtle-gold)]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
</div>
</div>
<!-- Title -->
<h1 class="mb-4 text-center text-2xl font-bold text-[var(--midnight-blue)] md:text-3xl">Payment en cours</h1>
<!-- Message -->
<p class="mb-6 text-center text-base text-[var(--spiritual-earth)]">
{{ message }}
</p>
<!-- Provider Info -->
<div class="mb-6 rounded-lg bg-[var(--light-ivory)] p-4">
<p class="text-center text-sm text-[var(--midnight-blue)]">
<span class="font-semibold">Fournisseur de paiement:</span>
<span class="ml-2 capitalize">{{ paymentProvider }}</span>
</p>
<p v-if="checking" class="mt-2 text-center text-xs text-[var(--spiritual-earth)]">
Vérification automatique en cours... ({{ pollCount }}/{{ maxPolls }})
</p>
</div>
<!-- Status Message -->
<div v-if="!checking" class="mb-6 rounded-lg border border-yellow-300 bg-yellow-50 p-4">
<p class="text-center text-sm text-yellow-800">
Le paiement prend plus de temps que prévu. Cela peut prendre quelques minutes pour que {{ paymentProvider }} traite votre
paiement.
</p>
</div>
<!-- Action Buttons -->
<div class="flex flex-col gap-3">
<button
v-if="!checking"
@click="refreshPage"
class="w-full rounded-full bg-[var(--subtle-gold)] px-6 py-3 font-semibold text-[var(--midnight-blue)] shadow-md transition-all duration-300 hover:shadow-lg"
>
Vérifier à nouveau
</button>
<button
@click="goHome"
class="w-full rounded-full border-2 border-[var(--midnight-blue)] bg-transparent px-6 py-3 font-semibold text-[var(--midnight-blue)] transition-all duration-300 hover:bg-[var(--midnight-blue)] hover:text-white"
>
Retour à l'accueil
</button>
</div>
<!-- Help Text -->
<p class="mt-6 text-center text-xs text-[var(--spiritual-earth)]">
Si le problème persiste, veuillez contacter notre support avec votre ID de session:
<br />
<code class="mt-1 block rounded bg-gray-100 px-2 py-1 text-[10px]">{{ clientSessionId }}</code>
</p>
</div>
</div>
</template>
<style scoped>
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.animate-spin {
animation: spin 1s linear infinite;
}
</style>

View File

@ -0,0 +1,185 @@
<script setup lang="ts">
import { router } from '@inertiajs/vue3';
import axios from 'axios';
import { onMounted, ref } from 'vue';
const checking = ref(true);
const paymentStatus = ref<'pending' | 'succeeded' | 'failed' | null>(null);
const errorMessage = ref('');
const clientSessionId = ref('');
// Check payment status
const checkPayment = async () => {
try {
// Get client session ID from sessionStorage
const storedSessionId = sessionStorage.getItem('wise_client_session_id');
if (!storedSessionId) {
errorMessage.value = 'Session non trouvée. Veuillez recommencer le processus de paiement.';
checking.value = false;
return;
}
clientSessionId.value = storedSessionId;
// Check payment status via API
const response = await axios.get('/wise/validate-payment', {
params: { client_session_id: storedSessionId },
});
if (response.data.success) {
paymentStatus.value = 'succeeded';
// Clear session storage
sessionStorage.removeItem('wise_client_session_id');
// Redirect to success page after 2 seconds
setTimeout(() => {
window.location.href = `/success?client_session_id=${storedSessionId}`;
}, 2000);
} else {
paymentStatus.value = response.data.status || 'pending';
errorMessage.value = response.data.message || 'Le paiement est en cours de vérification...';
}
} catch (error: any) {
console.error('Error checking payment:', error);
paymentStatus.value = 'failed';
errorMessage.value = error.response?.data?.message || 'Erreur lors de la vérification du paiement.';
} finally {
checking.value = false;
}
};
const retryCheck = () => {
checking.value = true;
paymentStatus.value = null;
errorMessage.value = '';
setTimeout(checkPayment, 1000);
};
const goHome = () => {
sessionStorage.removeItem('wise_client_session_id');
router.visit('/');
};
onMounted(() => {
// Check payment status after 1 second
setTimeout(checkPayment, 1000);
});
</script>
<template>
<div class="flex min-h-screen items-center justify-center bg-gradient-to-br from-[var(--light-ivory)] via-white to-[var(--linen)] px-4">
<div class="w-full max-w-md rounded-2xl border border-[var(--linen)] bg-white/90 p-8 shadow-xl backdrop-blur-sm">
<!-- Loading State -->
<div v-if="checking" class="text-center">
<div class="mb-6 flex justify-center">
<div class="relative h-20 w-20">
<svg class="animate-spin text-[var(--subtle-gold)]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
</div>
</div>
<h1 class="mb-4 text-2xl font-bold text-[var(--midnight-blue)]">Vérification du paiement...</h1>
<p class="text-[var(--spiritual-earth)]">Veuillez patienter pendant que nous vérifions votre paiement Wise.</p>
</div>
<!-- Success State -->
<div v-else-if="paymentStatus === 'succeeded'" class="text-center">
<div class="mb-6 flex justify-center">
<div class="flex h-20 w-20 items-center justify-center rounded-full bg-green-100">
<svg class="h-12 w-12 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</div>
</div>
<h1 class="mb-4 text-2xl font-bold text-green-600">Paiement réussi ! 🎉</h1>
<p class="text-[var(--spiritual-earth)]">Votre paiement a été confirmé. Vous allez être redirigé vers vos cartes...</p>
</div>
<!-- Pending State -->
<div v-else-if="paymentStatus === 'pending'" class="text-center">
<div class="mb-6 flex justify-center">
<div class="flex h-20 w-20 items-center justify-center rounded-full bg-yellow-100">
<svg class="h-12 w-12 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
</div>
<h1 class="mb-4 text-2xl font-bold text-yellow-600">Paiement en cours</h1>
<p class="mb-6 text-[var(--spiritual-earth)]">
{{ errorMessage || 'Votre paiement est en cours de traitement. Cela peut prendre quelques minutes.' }}
</p>
<div class="flex flex-col gap-3">
<button
@click="retryCheck"
class="w-full rounded-full bg-[var(--subtle-gold)] px-6 py-3 font-semibold text-[var(--midnight-blue)] shadow-md transition-all duration-300 hover:shadow-lg"
>
Vérifier à nouveau
</button>
<button
@click="goHome"
class="w-full rounded-full border-2 border-[var(--midnight-blue)] bg-transparent px-6 py-3 font-semibold text-[var(--midnight-blue)] transition-all duration-300 hover:bg-[var(--midnight-blue)] hover:text-white"
>
Retour à l'accueil
</button>
</div>
</div>
<!-- Failed State -->
<div v-else-if="paymentStatus === 'failed'" class="text-center">
<div class="mb-6 flex justify-center">
<div class="flex h-20 w-20 items-center justify-center rounded-full bg-red-100">
<svg class="h-12 w-12 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
</div>
<h1 class="mb-4 text-2xl font-bold text-red-600">Erreur de paiement</h1>
<p class="mb-6 text-[var(--spiritual-earth)]">
{{ errorMessage || 'Une erreur est survenue lors de la vérification du paiement.' }}
</p>
<div class="flex flex-col gap-3">
<button
@click="retryCheck"
class="w-full rounded-full bg-[var(--subtle-gold)] px-6 py-3 font-semibold text-[var(--midnight-blue)] shadow-md transition-all duration-300 hover:shadow-lg"
>
Réessayer
</button>
<button
@click="goHome"
class="w-full rounded-full border-2 border-[var(--midnight-blue)] bg-transparent px-6 py-3 font-semibold text-[var(--midnight-blue)] transition-all duration-300 hover:bg-[var(--midnight-blue)] hover:text-white"
>
Retour à l'accueil
</button>
</div>
</div>
<!-- Session ID Info -->
<div v-if="clientSessionId && !checking" class="mt-6 rounded-lg bg-gray-50 p-3">
<p class="text-center text-xs text-gray-600">
ID de session:
<br />
<code class="block font-mono text-[10px] break-all">{{ clientSessionId }}</code>
</p>
</div>
</div>
</div>
</template>
<style scoped>
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.animate-spin {
animation: spin 1s linear infinite;
}
</style>

View File

@ -24,6 +24,14 @@ Route::post('/checkout-rendez-vous', [App\Http\Controllers\StripeController::cla
Route::post('/stripe/webhook', [App\Http\Controllers\WebhookController::class, 'handleWebhook']);
// Wise payment routes
Route::post('/create-wise-payment', [App\Http\Controllers\WiseController::class, 'createPaymentSession']);
Route::post('/wise/webhook', [App\Http\Controllers\WiseController::class, 'handleWebhook']);
Route::get('/wise/validate-payment', [App\Http\Controllers\WiseController::class, 'validatePayment']);
Route::get('/wise/verify', function () {
return Inertia::render('wise/VerifyPayment');
})->name('wise.verify');
Route::get('/rendez-vous', [App\Http\Controllers\AppointmentController::class, 'index']);
Route::get('/resultat', [App\Http\Controllers\CardController::class, 'cartResult']);
@ -46,7 +54,19 @@ Route::get('/success', function (Request $request) {
if ($payment) {
return Inertia::render('payments/Success', [
'paymentSuccess' => true,
'drawCount' => $payment->draw_count
'drawCount' => $payment->draw_count,
'paymentProvider' => $payment->payment_provider ?? 'stripe'
]);
}
// If payment not found as succeeded, check if it's pending (especially for Wise)
$pendingPayment = Payment::where('client_session_id', $clientSessionId)->first();
if ($pendingPayment && $pendingPayment->status === 'pending') {
return Inertia::render('payments/Pending', [
'message' => 'Payment is being processed. Please wait...',
'clientSessionId' => $clientSessionId,
'paymentProvider' => $pendingPayment->payment_provider ?? 'stripe'
]);
}