- Removed unused JavaScript controllers (shadcn_test, featured_event, event_form, ticket_type_form) - Removed unused React components (button.jsx and utils.js) - Removed duplicate env.example file - Removed unused Alpine.js dependencies from package.json - Updated controller registrations and dependency files - Added REFACTORING_SUMMARY.md with details of changes - Added new file: app/controllers/api/v1/orders_controller.rb
280 lines
9.9 KiB
Ruby
280 lines
9.9 KiB
Ruby
# API controller for order management
|
|
# Provides RESTful endpoints for order operations
|
|
|
|
module Api
|
|
module V1
|
|
class OrdersController < ApiController
|
|
before_action :authenticate_user!
|
|
before_action :set_order, only: [ :show, :checkout, :retry_payment, :increment_payment_attempt ]
|
|
before_action :set_event, only: [ :new, :create ]
|
|
|
|
# GET /api/v1/orders/new
|
|
# Returns data needed for new order form
|
|
def new
|
|
cart_data = params[:cart_data] || session[:pending_cart] || {}
|
|
|
|
if cart_data.empty?
|
|
render json: { error: "Veuillez d'abord sélectionner vos billets sur la page de l'événement" }, status: :bad_request
|
|
return
|
|
end
|
|
|
|
tickets_needing_names = []
|
|
cart_data.each do |ticket_type_id, item|
|
|
ticket_type = @event.ticket_types.find_by(id: ticket_type_id)
|
|
next unless ticket_type
|
|
|
|
quantity = item["quantity"].to_i
|
|
next if quantity <= 0
|
|
|
|
quantity.times do |i|
|
|
tickets_needing_names << {
|
|
ticket_type_id: ticket_type.id,
|
|
ticket_type_name: ticket_type.name,
|
|
ticket_type_price: ticket_type.price_cents,
|
|
index: i
|
|
}
|
|
end
|
|
end
|
|
|
|
render json: { tickets_needing_names: tickets_needing_names }, status: :ok
|
|
end
|
|
|
|
# POST /api/v1/orders
|
|
# Creates a new order with tickets
|
|
def create
|
|
cart_data = params[:cart_data] || session[:pending_cart] || {}
|
|
|
|
if cart_data.empty?
|
|
render json: { error: "Aucun billet sélectionné" }, status: :bad_request
|
|
return
|
|
end
|
|
|
|
success = false
|
|
|
|
ActiveRecord::Base.transaction do
|
|
@order = current_user.orders.create!(event: @event, status: "draft")
|
|
|
|
order_params[:tickets_attributes]&.each do |index, ticket_attrs|
|
|
next if ticket_attrs[:first_name].blank? || ticket_attrs[:last_name].blank?
|
|
|
|
ticket_type = @event.ticket_types.find(ticket_attrs[:ticket_type_id])
|
|
|
|
ticket = @order.tickets.build(
|
|
ticket_type: ticket_type,
|
|
first_name: ticket_attrs[:first_name],
|
|
last_name: ticket_attrs[:last_name],
|
|
status: "draft"
|
|
)
|
|
|
|
unless ticket.save
|
|
render json: { error: "Erreur lors de la création des billets: #{ticket.errors.full_messages.join(', ')}" }, status: :unprocessable_entity
|
|
raise ActiveRecord::Rollback
|
|
end
|
|
end
|
|
|
|
if @order.tickets.present?
|
|
@order.calculate_total!
|
|
success = true
|
|
else
|
|
render json: { error: "Aucun billet valide créé" }, status: :unprocessable_entity
|
|
raise ActiveRecord::Rollback
|
|
end
|
|
end
|
|
|
|
if success
|
|
session[:draft_order_id] = @order.id
|
|
session.delete(:pending_cart)
|
|
render json: { order: @order, redirect_to: checkout_order_path(@order) }, status: :created
|
|
end
|
|
rescue => e
|
|
error_message = e.message.present? ? e.message : "Erreur inconnue"
|
|
render json: { error: "Une erreur est survenue: #{error_message}" }, status: :internal_server_error
|
|
end
|
|
|
|
# GET /api/v1/orders/:id
|
|
# Returns order summary
|
|
def show
|
|
tickets = @order.tickets.includes(:ticket_type)
|
|
render json: { order: @order, tickets: tickets }, status: :ok
|
|
end
|
|
|
|
# GET /api/v1/orders/:id/checkout
|
|
# Returns checkout data for an order
|
|
def checkout
|
|
if @order.expired?
|
|
@order.expire_if_overdue!
|
|
render json: { error: "Votre commande a expiré. Veuillez recommencer." }, status: :gone
|
|
return
|
|
end
|
|
|
|
tickets = @order.tickets.includes(:ticket_type)
|
|
total_amount = @order.total_amount_cents
|
|
expiring_soon = @order.expiring_soon?
|
|
|
|
checkout_session = nil
|
|
if Rails.application.config.stripe[:secret_key].present?
|
|
begin
|
|
checkout_session = create_stripe_session
|
|
rescue => e
|
|
error_message = e.message.present? ? e.message : "Erreur Stripe inconnue"
|
|
Rails.logger.error "Stripe checkout session creation failed: #{error_message}"
|
|
render json: { error: "Erreur lors de la création de la session de paiement" }, status: :internal_server_error
|
|
return
|
|
end
|
|
end
|
|
|
|
render json: {
|
|
order: @order,
|
|
tickets: tickets,
|
|
total_amount: total_amount,
|
|
expiring_soon: expiring_soon,
|
|
checkout_session: checkout_session
|
|
}, status: :ok
|
|
end
|
|
|
|
# PATCH /api/v1/orders/:id/increment_payment_attempt
|
|
# Increments payment attempt counter
|
|
def increment_payment_attempt
|
|
@order.increment_payment_attempt!
|
|
render json: { success: true, attempts: @order.payment_attempts }, status: :ok
|
|
end
|
|
|
|
# POST /api/v1/orders/:id/retry_payment
|
|
# Allows retrying payment for failed orders
|
|
def retry_payment
|
|
unless @order.can_retry_payment?
|
|
render json: { error: "Cette commande ne peut plus être payée" }, status: :forbidden
|
|
return
|
|
end
|
|
|
|
render json: { redirect_to: checkout_order_path(@order) }, status: :ok
|
|
end
|
|
|
|
# GET /api/v1/orders/payment_success
|
|
# Handles successful payment confirmation
|
|
def payment_success
|
|
session_id = params[:session_id]
|
|
|
|
stripe_configured = Rails.application.config.stripe[:secret_key].present?
|
|
Rails.logger.debug "Payment success - Stripe configured: #{stripe_configured}"
|
|
|
|
unless stripe_configured
|
|
render json: { error: "Le système de paiement n'est pas correctement configuré. Veuillez contacter l'administrateur." }, status: :service_unavailable
|
|
return
|
|
end
|
|
|
|
begin
|
|
stripe_session = Stripe::Checkout::Session.retrieve(session_id)
|
|
|
|
if stripe_session.payment_status == "paid"
|
|
order_id = stripe_session.metadata["order_id"]
|
|
|
|
unless order_id.present?
|
|
render json: { error: "Informations de commande manquantes" }, status: :bad_request
|
|
return
|
|
end
|
|
|
|
@order = current_user.orders.includes(tickets: :ticket_type).find(order_id)
|
|
@order.mark_as_paid!
|
|
|
|
begin
|
|
StripeInvoiceGenerationJob.perform_later(@order.id)
|
|
Rails.logger.info "Scheduled Stripe invoice generation for order #{@order.id}"
|
|
rescue => e
|
|
Rails.logger.error "Failed to schedule invoice generation for order #{@order.id}: #{e.message}"
|
|
end
|
|
|
|
@order.tickets.each do |ticket|
|
|
begin
|
|
TicketMailer.purchase_confirmation(ticket).deliver_now
|
|
rescue => e
|
|
Rails.logger.error "Failed to send confirmation email for ticket #{ticket.id}: #{e.message}"
|
|
end
|
|
end
|
|
|
|
session.delete(:pending_cart)
|
|
session.delete(:ticket_names)
|
|
session.delete(:draft_order_id)
|
|
|
|
render json: { order: @order, tickets: @order.tickets }, status: :ok
|
|
else
|
|
render json: { error: "Le paiement n'a pas été complété avec succès" }, status: :payment_required
|
|
end
|
|
rescue Stripe::StripeError => e
|
|
error_message = e.message.present? ? e.message : "Erreur Stripe inconnue"
|
|
render json: { error: "Erreur lors du traitement de votre confirmation de paiement : #{error_message}" }, status: :bad_request
|
|
rescue => e
|
|
error_message = e.message.present? ? e.message : "Erreur inconnue"
|
|
Rails.logger.error "Payment success error: #{e.class} - #{error_message}"
|
|
render json: { error: "Une erreur inattendue s'est produite : #{error_message}" }, status: :internal_server_error
|
|
end
|
|
end
|
|
|
|
# POST /api/v1/orders/payment_cancel
|
|
# Handles payment cancellation
|
|
def payment_cancel
|
|
order_id = params[:order_id] || session[:draft_order_id]
|
|
|
|
if order_id.present?
|
|
order = current_user.orders.find_by(id: order_id, status: "draft")
|
|
|
|
if order&.can_retry_payment?
|
|
render json: { message: "Le paiement a été annulé. Vous pouvez réessayer.", redirect_to: checkout_order_path(order) }, status: :ok
|
|
else
|
|
session.delete(:draft_order_id)
|
|
render json: { message: "Le paiement a été annulé et votre commande a expiré." }, status: :gone
|
|
end
|
|
else
|
|
render json: { message: "Le paiement a été annulé" }, status: :ok
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def set_order
|
|
@order = current_user.orders.includes(:tickets, :event).find(params[:id])
|
|
rescue ActiveRecord::RecordNotFound
|
|
render json: { error: "Commande non trouvée" }, status: :not_found
|
|
end
|
|
|
|
def set_event
|
|
@event = Event.includes(:ticket_types).find(params[:id])
|
|
rescue ActiveRecord::RecordNotFound
|
|
render json: { error: "Événement non trouvé" }, status: :not_found
|
|
end
|
|
|
|
def order_params
|
|
params.permit(tickets_attributes: [ :ticket_type_id, :first_name, :last_name ])
|
|
end
|
|
|
|
def create_stripe_session
|
|
line_items = @order.tickets.map do |ticket|
|
|
{
|
|
price_data: {
|
|
currency: "eur",
|
|
product_data: {
|
|
name: "#{@order.event.name} - #{ticket.ticket_type.name}",
|
|
description: ticket.ticket_type.description
|
|
},
|
|
unit_amount: ticket.price_cents
|
|
},
|
|
quantity: 1
|
|
}
|
|
end
|
|
|
|
Stripe::Checkout::Session.create(
|
|
payment_method_types: [ "card" ],
|
|
line_items: line_items,
|
|
mode: "payment",
|
|
success_url: order_payment_success_url + "?session_id={CHECKOUT_SESSION_ID}",
|
|
cancel_url: order_payment_cancel_url,
|
|
metadata: {
|
|
order_id: @order.id,
|
|
user_id: current_user.id
|
|
}
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|