fix back card
This commit is contained in:
parent
bd7ed0c30e
commit
153e700b8a
248
app/Http/Controllers/WisePaymentController.php
Normal file
248
app/Http/Controllers/WisePaymentController.php
Normal file
@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Payment;
|
||||
use Illuminate\Http\Request;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
|
||||
class WisePaymentController extends Controller
|
||||
{
|
||||
private $wiseClient;
|
||||
private $wiseApiKey;
|
||||
private $profileId;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->wiseApiKey = env('WISE_API_KEY');
|
||||
$this->profileId = env('WISE_PROFILE_ID'); // Your Wise business/profile ID
|
||||
|
||||
$this->wiseClient = new Client([
|
||||
'base_uri' => 'https://api.wise.com/v1/',
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer ' . $this->wiseApiKey,
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function createTransfer(Request $request)
|
||||
{
|
||||
// Validate the incoming request data
|
||||
$validatedData = $request->validate([
|
||||
'amount' => 'required|numeric|min:0.01',
|
||||
'source_currency' => 'required|string|size:3',
|
||||
'target_currency' => 'required|string|size:3',
|
||||
'recipient_name' => 'required|string|max:255',
|
||||
'recipient_email' => 'required|email',
|
||||
'recipient_account_number' => 'required|string',
|
||||
'recipient_bank_code' => 'sometimes|string', // Needed for some countries
|
||||
'recipient_address' => 'sometimes|array',
|
||||
'reason' => 'sometimes|string|max:255',
|
||||
]);
|
||||
|
||||
try {
|
||||
// Create a new Payment record
|
||||
$payment = new Payment([
|
||||
'amount' => $validatedData['amount'],
|
||||
'currency' => $validatedData['source_currency'],
|
||||
'target_currency' => $validatedData['target_currency'],
|
||||
'recipient_name' => $validatedData['recipient_name'],
|
||||
'recipient_email' => $validatedData['recipient_email'],
|
||||
'status' => 'pending',
|
||||
'client_session_id' => $request->client_session_id ?? uniqid('wise_', true)
|
||||
]);
|
||||
$payment->save();
|
||||
|
||||
// Create recipient account
|
||||
$recipient = $this->createRecipient($validatedData);
|
||||
|
||||
// Create quote
|
||||
$quote = $this->createQuote($validatedData);
|
||||
|
||||
// Create transfer
|
||||
$transfer = $this->createTransferWise($quote, $recipient, $validatedData);
|
||||
|
||||
// Fund the transfer
|
||||
$this->fundTransfer($transfer['id']);
|
||||
|
||||
// Update the Payment record
|
||||
$payment->wise_transfer_id = $transfer['id'];
|
||||
$payment->wise_recipient_id = $recipient['id'];
|
||||
$payment->wise_quote_id = $quote['id'];
|
||||
$payment->status = 'processing';
|
||||
$payment->save();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Transfer initiated successfully.',
|
||||
'transfer_id' => $transfer['id'],
|
||||
'payment_id' => $payment->id
|
||||
]);
|
||||
|
||||
} catch (ClientException $e) {
|
||||
$response = $e->getResponse();
|
||||
$body = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
Log::error('Wise Client Error:', [
|
||||
'code' => $e->getCode(),
|
||||
'message' => $e->getMessage(),
|
||||
'details' => $body
|
||||
]);
|
||||
|
||||
if (isset($payment)) {
|
||||
$payment->status = 'failed';
|
||||
$payment->error_message = $e->getMessage();
|
||||
$payment->save();
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Wise API failed to process the request.',
|
||||
'error' => $body['errors'] ?? $e->getMessage()
|
||||
], 400);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('General Wise API Error:', [
|
||||
'message' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
if (isset($payment)) {
|
||||
$payment->status = 'failed';
|
||||
$payment->error_message = $e->getMessage();
|
||||
$payment->save();
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'An unexpected error occurred.',
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function handleWebhook(Request $request)
|
||||
{
|
||||
// Verify the webhook signature if needed
|
||||
$payload = $request->all();
|
||||
|
||||
Log::info('Wise Webhook Received:', $payload);
|
||||
|
||||
if (isset($payload['data']['resource']['id'])) {
|
||||
$transferId = $payload['data']['resource']['id'];
|
||||
|
||||
// Find the payment by transfer ID
|
||||
$payment = Payment::where('wise_transfer_id', $transferId)->first();
|
||||
|
||||
if ($payment) {
|
||||
$payment->status = $payload['data']['current_state'] ?? 'unknown';
|
||||
$payment->save();
|
||||
|
||||
// You might want to trigger other actions based on status change
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['status' => 'ok']);
|
||||
}
|
||||
|
||||
// Helper methods to interact with Wise API
|
||||
private function createRecipient($data)
|
||||
{
|
||||
$response = $this->wiseClient->post('accounts', [
|
||||
'json' => [
|
||||
'profile' => $this->profileId,
|
||||
'accountHolderName' => $data['recipient_name'],
|
||||
'currency' => $data['target_currency'],
|
||||
'type' => 'email', // or 'sort_code', 'aba', 'iban' etc. based on country
|
||||
'details' => [
|
||||
'email' => $data['recipient_email'],
|
||||
// Add more details based on account type and country
|
||||
'legalType' => 'PRIVATE',
|
||||
// 'accountNumber' => $data['recipient_account_number'],
|
||||
// 'bankCode' => $data['recipient_bank_code'] ?? null,
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
private function createQuote($data)
|
||||
{
|
||||
$response = $this->wiseClient->post('quotes', [
|
||||
'json' => [
|
||||
'profile' => $this->profileId,
|
||||
'source' => $data['source_currency'],
|
||||
'target' => $data['target_currency'],
|
||||
'rateType' => 'FIXED', // or 'FLOAT'
|
||||
'sourceAmount' => $data['amount'],
|
||||
'type' => 'BALANCE_PAYOUT' // or 'BALANCE_CONVERSION'
|
||||
],
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
private function createTransferWise($quote, $recipient, $data)
|
||||
{
|
||||
$response = $this->wiseClient->post('transfers', [
|
||||
'json' => [
|
||||
'targetAccount' => $recipient['id'],
|
||||
'quote' => $quote['id'],
|
||||
'customerTransactionId' => uniqid('cti_', true),
|
||||
'details' => [
|
||||
'reference' => $data['reason'] ?? 'Payment for services',
|
||||
'transferPurpose' => $data['reason'] ?? 'Payment for services',
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
private function fundTransfer($transferId)
|
||||
{
|
||||
// Check if the transfer requires funding
|
||||
$transfer = $this->wiseClient->get("transfers/{$transferId}");
|
||||
$transferData = json_decode($transfer->getBody(), true);
|
||||
|
||||
if ($transferData['status'] === 'pending') {
|
||||
// Fund the transfer from your balance
|
||||
$this->wiseClient->post("transfers/{$transferId}/payments", [
|
||||
'json' => [
|
||||
'type' => 'BALANCE',
|
||||
'profile' => $this->profileId
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Additional helper method to check transfer status
|
||||
public function checkTransferStatus($paymentId)
|
||||
{
|
||||
$payment = Payment::findOrFail($paymentId);
|
||||
|
||||
try {
|
||||
$response = $this->wiseClient->get("transfers/{$payment->wise_transfer_id}");
|
||||
$transferData = json_decode($response->getBody(), true);
|
||||
|
||||
$payment->status = $transferData['status'];
|
||||
$payment->save();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'status' => $transferData['status']
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Failed to check transfer status'
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,7 +22,7 @@ class Payment extends Model
|
||||
'draw_count',
|
||||
'status',
|
||||
'cards',
|
||||
'appointment_date'
|
||||
'appointment_date',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"guzzlehttp/guzzle": "^7.10",
|
||||
"inertiajs/inertia-laravel": "^2.0",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/sanctum": "^4.0",
|
||||
|
||||
2
composer.lock
generated
2
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "7a72790164b9b6dc081f7cbfde7e67d5",
|
||||
"content-hash": "748c4d7177ae376a830b7c336264affd",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
|
||||
55809
public/back-card.svg
Normal file
55809
public/back-card.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 2.8 MiB |
@ -61,14 +61,7 @@
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
}
|
||||
/*
|
||||
The default border color has changed to `currentColor` in Tailwind CSS v4,
|
||||
so we've added these compatibility styles to make sure everything still
|
||||
looks the same as it did with Tailwind CSS v3.
|
||||
|
||||
If we ever want to remove these styles, we need to add an explicit border
|
||||
color utility to any element that depends on these defaults.
|
||||
*/
|
||||
@layer base {
|
||||
*,
|
||||
::after,
|
||||
|
||||
@ -75,28 +75,8 @@ defineExpose({ setDrawnCards });
|
||||
>
|
||||
<div v-for="i in 8" :key="i" class="card" :style="{ transform: `rotate(${-3 + i}deg) translateZ(-${10 * i}px);` }">
|
||||
<div class="card-back">
|
||||
<div class="card-back-design">
|
||||
<svg
|
||||
class="h-16 w-16 text-[var(--subtle-gold)] opacity-80"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M12 4v16m8-8H4" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"></path>
|
||||
<path
|
||||
d="M14.828 7.172a4 4 0 015.656 5.656l-5.656 5.657a4 4 0 01-5.657-5.657l5.657-5.656z"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="0.5"
|
||||
></path>
|
||||
<path
|
||||
d="M9.172 7.172a4 4 0 00-5.657 5.656l5.657 5.657a4 4 0 005.656-5.657L9.172 7.172z"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="0.5"
|
||||
></path>
|
||||
</svg>
|
||||
<div class="card-inner-content">
|
||||
<img src="cards/1.png" alt="Card Back" class="card-back-image" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -108,43 +88,14 @@ defineExpose({ setDrawnCards });
|
||||
<div v-for="(card, index) in drawnCards" :key="index" class="card-result-wrapper">
|
||||
<div class="result-card" :class="{ flipped: isFlipped[index] }" @click="flipCard(index)">
|
||||
<div class="card-face card-unknown-front">
|
||||
<div class="card-back-design">
|
||||
<svg
|
||||
class="h-16 w-16 text-[var(--subtle-gold)] opacity-80"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M12 4v16m8-8H4" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"></path>
|
||||
<path
|
||||
d="M14.828 7.172a4 4 0 015.656 5.656l-5.656 5.657a4 4 0 01-5.657-5.657l5.657-5.656z"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="0.5"
|
||||
></path>
|
||||
<path
|
||||
d="M9.172 7.172a4 4 0 00-5.657 5.656l5.657 5.657a4 4 0 005.656-5.657L9.172 7.172z"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="0.5"
|
||||
></path>
|
||||
</svg>
|
||||
<div class="card-inner-content">
|
||||
<img src="cards/1.png" alt="Card Back" class="card-back-image" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-face card-known-back">
|
||||
<img :src="card.image_url!" :alt="card.name" class="card-image" />
|
||||
<div class="card-description-overlay">
|
||||
<h3>{{ card.name }}</h3>
|
||||
<p class="description">{{ card.description }}</p>
|
||||
<p v-if="card.orientation" class="orientation">
|
||||
{{ card.orientation === 'reversed' ? 'Inversée' : 'Droite' }}
|
||||
</p>
|
||||
<div v-if="card.symbolism" class="symbolism">
|
||||
<p><strong>Numéro:</strong> {{ card.symbolism.numéro }}</p>
|
||||
<p><strong>Planète:</strong> {{ card.symbolism.planète }}</p>
|
||||
<p><strong>Élément:</strong> {{ card.symbolism.élément }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -158,6 +109,7 @@ defineExpose({ setDrawnCards });
|
||||
<span class="truncate">Retourner à la sélection des cartes</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="clientSessionId"
|
||||
@click="goToResult"
|
||||
class="mt-8 flex h-12 max-w-[480px] min-w-[200px] cursor-pointer items-center justify-center overflow-hidden rounded-full bg-[var(--midnight-blue)] px-8 text-base font-bold tracking-wide text-[var(--pure-white)] transition-all duration-300 hover:bg-[var(--spiritual-earth)] hover:shadow-[var(--spiritual-earth)]/30 hover:shadow-lg disabled:cursor-not-allowed disabled:bg-gray-400 disabled:hover:shadow-none"
|
||||
>
|
||||
@ -273,11 +225,28 @@ defineExpose({ setDrawnCards });
|
||||
height: 100%;
|
||||
backface-visibility: hidden;
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
display: flex; /* Keep flexbox for centering if needed */
|
||||
align-items: center; /* For centering .card-inner-content if it's smaller */
|
||||
justify-content: center; /* For centering .card-inner-content if it's smaller */
|
||||
background-color: var(--subtle-gold);
|
||||
}
|
||||
|
||||
.card-inner-content {
|
||||
width: calc(100% - 10px); /* Adjust 10px to control border thickness (e.g., 5px border on each side) */
|
||||
height: calc(100% - 10px); /* Adjust this value as well */
|
||||
background-color: white; /* Or whatever color you want inside the gold border */
|
||||
border-radius: 14px; /* Slightly smaller to match outer radius with padding */
|
||||
display: flex; /* Use flex to center the image if it's an <img> tag */
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--subtle-gold);
|
||||
background: radial-gradient(circle, var(--midnight-blue) 0%, #121a2c 100%);
|
||||
overflow: hidden; /* Ensure image doesn't bleed out */
|
||||
}
|
||||
|
||||
.card-back-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover; /* Or 'cover' depending on how you want the image to fill the inner content */
|
||||
border-radius: 14px; /* Match inner content border-radius */
|
||||
}
|
||||
|
||||
.card-result-wrapper {
|
||||
@ -324,16 +293,22 @@ defineExpose({ setDrawnCards });
|
||||
}
|
||||
|
||||
.card-unknown-front {
|
||||
background: radial-gradient(circle, var(--midnight-blue) 0%, #121a2c 100%);
|
||||
display: flex;
|
||||
background-color: var(--subtle-gold);
|
||||
display: flex; /* Keep flexbox for centering its *own* content */
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--subtle-gold);
|
||||
}
|
||||
|
||||
.card-known-back {
|
||||
background-color: var(--subtle-gold);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
transform: rotateY(180deg); /* This face starts rotated, so it's hidden */
|
||||
position: relative;
|
||||
border-radius: 16px; /* Ensure this matches the outer card radius */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-back-design::before {
|
||||
@ -410,7 +385,11 @@ defineExpose({ setDrawnCards });
|
||||
}
|
||||
|
||||
.card-back-info {
|
||||
background: linear-gradient(145deg, var(--midnight-blue), #121a2c);
|
||||
/* Replace the existing background property */
|
||||
background-image: url('back-card.svg'); /* background-size: cover; /* This makes the image cover the entire element */
|
||||
background-position: center; /* This centers the image in the element */
|
||||
background-repeat: no-repeat; /* This prevents the image from repeating */
|
||||
|
||||
color: var(--pure-white);
|
||||
transform: rotateY(180deg);
|
||||
text-align: center;
|
||||
@ -444,22 +423,39 @@ defineExpose({ setDrawnCards });
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.6); /* Semi-transparent overlay for readability */
|
||||
color: white;
|
||||
|
||||
/* Adjust background for a brighter, more subtle overlay */
|
||||
background: rgba(255, 255, 255, 0.2); /* Lighter, more transparent white overlay */
|
||||
/* You could also use a subtle gradient if you prefer: */
|
||||
/* background: linear-gradient(to top, rgba(0,0,0,0.5) 0%, rgba(255,255,255,0) 50%); */
|
||||
|
||||
color: white; /* Keep text white for contrast */
|
||||
padding: 1rem;
|
||||
|
||||
/* Center the content (just the name) in the middle of the card */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end; /* Align content to the bottom */
|
||||
align-items: center;
|
||||
justify-content: center; /* Center vertically */
|
||||
align-items: center; /* Center horizontally */
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
pointer-events: none; /* Make overlay non-interactive so clicks go through to flip */
|
||||
}
|
||||
|
||||
.card-description-overlay h3,
|
||||
.card-description-overlay p {
|
||||
margin: 0.2rem 0;
|
||||
.card-description-overlay h3 {
|
||||
margin: 0; /* Remove default margin */
|
||||
font-size: 1.8rem; /* Make the name larger */
|
||||
font-weight: bold;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7); /* Add subtle shadow for readability */
|
||||
color: var(--pure-white); /* Ensure it's clearly white */
|
||||
letter-spacing: 1px; /* Add some letter spacing */
|
||||
}
|
||||
/* Hide these elements completely */
|
||||
.card-description-overlay p.description,
|
||||
.card-description-overlay p.orientation,
|
||||
.card-description-overlay div.symbolism {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.orientation {
|
||||
font-style: italic;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
170
resources/js/pages/Checkout.vue
Normal file
170
resources/js/pages/Checkout.vue
Normal file
@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div class="wise-payment-form">
|
||||
<form @submit.prevent="submitTransfer">
|
||||
<div class="form-group">
|
||||
<label for="amount">Amount</label>
|
||||
<input type="number" id="amount" v-model="formData.amount" step="0.01" min="0.01" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="source_currency">Source Currency</label>
|
||||
<select id="source_currency" v-model="formData.source_currency" required>
|
||||
<option value="USD">USD</option>
|
||||
<option value="EUR">EUR</option>
|
||||
<option value="GBP">GBP</option>
|
||||
<!-- Add more currencies as needed -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="target_currency">Target Currency</label>
|
||||
<select id="target_currency" v-model="formData.target_currency" required>
|
||||
<option value="USD">USD</option>
|
||||
<option value="EUR">EUR</option>
|
||||
<option value="GBP">GBP</option>
|
||||
<!-- Add more currencies as needed -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="recipient_name">Recipient Name</label>
|
||||
<input type="text" id="recipient_name" v-model="formData.recipient_name" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="recipient_email">Recipient Email</label>
|
||||
<input type="email" id="recipient_email" v-model="formData.recipient_email" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="recipient_account_number">Account Number</label>
|
||||
<input type="text" id="recipient_account_number" v-model="formData.recipient_account_number" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="reason">Payment Reason</label>
|
||||
<input type="text" id="reason" v-model="formData.reason" />
|
||||
</div>
|
||||
|
||||
<button type="submit" :disabled="loading">
|
||||
{{ loading ? 'Processing...' : 'Create Transfer' }}
|
||||
</button>
|
||||
|
||||
<div v-if="error" class="error-message">
|
||||
{{ error }}
|
||||
</div>
|
||||
|
||||
<div v-if="success" class="success-message">Transfer initiated successfully! Transfer ID: {{ transferId }}</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
error: null,
|
||||
success: false,
|
||||
transferId: null,
|
||||
formData: {
|
||||
amount: '',
|
||||
source_currency: 'USD',
|
||||
target_currency: 'EUR',
|
||||
recipient_name: '',
|
||||
recipient_email: '',
|
||||
recipient_account_number: '',
|
||||
reason: 'Payment for services',
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async submitTransfer() {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
this.success = false;
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/wise/transfer', this.formData);
|
||||
|
||||
if (response.data.success) {
|
||||
this.success = true;
|
||||
this.transferId = response.data.transfer_id;
|
||||
// You might want to redirect or show additional info
|
||||
} else {
|
||||
this.error = response.data.message || 'An error occurred';
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
this.error = error.response.data.message || 'An error occurred';
|
||||
|
||||
// Display detailed errors if available
|
||||
if (error.response.data.error) {
|
||||
if (Array.isArray(error.response.data.error)) {
|
||||
this.error += ': ' + error.response.data.error.map((e) => e.message).join(', ');
|
||||
} else if (typeof error.response.data.error === 'object') {
|
||||
this.error += ': ' + JSON.stringify(error.response.data.error);
|
||||
} else {
|
||||
this.error += ': ' + error.response.data.error;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.error = 'Network error or server unavailable';
|
||||
}
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.wise-payment-form {
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: #cccccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #d9534f;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.success-message {
|
||||
color: #5cb85c;
|
||||
margin-top: 15px;
|
||||
}
|
||||
</style>
|
||||
@ -10,3 +10,7 @@ Route::get('/user', function (Request $request) {
|
||||
|
||||
Route::get('/validate-payment', [App\Http\Controllers\StripeController::class, 'validatePayment']);
|
||||
Route::get('/get-cards', [App\Http\Controllers\StripeController::class, 'getCards']);
|
||||
|
||||
|
||||
Route::post('/wise/transfer', [App\Http\Controllers\WisePaymentController::class, 'createTransfer']);
|
||||
Route::post('/wise/webhook', [App\Http\Controllers\WisePaymentController::class, 'handleWebhook']);
|
||||
|
||||
@ -27,6 +27,11 @@ Route::get('/rendez-vous', [App\Http\Controllers\AppointmentController::class, '
|
||||
|
||||
Route::get('/resultat', [App\Http\Controllers\CardController::class, 'cartResult']);
|
||||
|
||||
Route::get('paiement', function () {
|
||||
return Inertia::render('Checkout');
|
||||
})->name('paiement');
|
||||
|
||||
|
||||
Route::get('/success', function (Request $request) {
|
||||
$clientSessionId = $request->query('client_session_id');
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user