From 23b4732c0cf85fb72185980c003e1b78d09c97c0 Mon Sep 17 00:00:00 2001 From: Jonathan Rosenbaum Date: Sat, 31 Jan 2026 21:06:33 +0000 Subject: [PATCH] Implement PayPal fallback option and update payment method localization for improved user experience - now only two buttons show in absense of PayPal. Fixes #9 --- .../_payment_type.html.haml | 12 ++- config/initializers/paypal_fallback.rb | 98 ++++++++++++++----- config/locales/en.yml | 2 + config/locales/es.yml | 1 + config/locales/fr.yml | 3 +- 5 files changed, 86 insertions(+), 30 deletions(-) diff --git a/app/views/registration_steps/_payment_type.html.haml b/app/views/registration_steps/_payment_type.html.haml index f974d92..52d2379 100644 --- a/app/views/registration_steps/_payment_type.html.haml +++ b/app/views/registration_steps/_payment_type.html.haml @@ -6,11 +6,17 @@ .options - paypal_configured = @this_conference.paypal_email_address.present? && @this_conference.paypal_username.present? && @this_conference.paypal_password.present? && @this_conference.paypal_signature.present? - @payment_methods.each do |option| - - if !paypal_configured && option.to_sym == :paypal + - if option.to_sym == :paypal + - if paypal_configured + - class_name = [:unstyled] + - class_name << :selected if @payment_method.present? && @payment_method.to_sym == option + = button "payment_#{option}".to_sym, value: option, class: class_name + - elsif option.to_sym == :on_arrival - class_name = [:unstyled] - class_name << :selected if @payment_method.present? && @payment_method.to_sym == option - = button "payment_paypal_fallback".to_sym, value: option, class: class_name - - elsif option.to_sym != :paypal || paypal_configured + - label_key = paypal_configured ? "payment_on_arrival" : "payment_on_arrival_with_link" + = button label_key.to_sym, value: option, class: class_name + - elsif option.to_sym != :paypal - class_name = [:unstyled] - class_name << :selected if @payment_method.present? && @payment_method.to_sym == option = button "payment_#{option}".to_sym, value: option, class: class_name diff --git a/config/initializers/paypal_fallback.rb b/config/initializers/paypal_fallback.rb index 86388b2..dc4410e 100644 --- a/config/initializers/paypal_fallback.rb +++ b/config/initializers/paypal_fallback.rb @@ -1,45 +1,91 @@ # Monkey-patch for bikecollectives_core gem to gracefully handle missing PayPal credentials -# This allows the payment flow to work without PayPal configured +# This allows users to select "I can pay via host provided link" when PayPal isn't configured if defined?(RegistrationControllerHelper) module PayPalFallback def paypal_configured?(conference) conference.paypal_email_address.present? && - valid_paypal_credential?(conference.paypal_username) && - valid_paypal_credential?(conference.paypal_password) && - valid_paypal_credential?(conference.paypal_signature) + conference.paypal_username.present? && + conference.paypal_password.present? && + conference.paypal_signature.present? end - def valid_paypal_credential?(value) - value.present? && value.strip.present? - end - def payment_type_step_update(registration, params) + + def payment_type_step(registration) conference = registration.conference - payment_type = params[:value].try(:to_sym) - if !paypal_configured?(conference) && payment_type == :paypal - registration.data ||= {} - registration.data['payment_method'] = 'paypal' - registration.save! - return { status: :complete } + payment_method = (registration.data || {})['payment_method'] + + available_methods = ConferenceRegistration.all_payment_methods.dup + available_methods.delete(:paypal) unless paypal_configured?(conference) + + if !paypal_configured?(conference) && payment_method == 'paypal' + payment_method = 'host_payment' end + + { + payment_method: payment_method, + payment_methods: available_methods + } + end + + def payment_type_step_update(registration, params) + return { status: :complete } if params[:button] == 'back' + + conference = registration.conference + payment_type = params[:button].try(:to_sym) + + available_methods = ConferenceRegistration.all_payment_methods.dup + available_methods.delete(:paypal) unless paypal_configured?(conference) + + unless available_methods.include?(payment_type) + raise "Invalid payment type #{params[:button]}" + end + + registration.data ||= {} + registration.data['payment_method'] = payment_type.to_s + registration.save! + { status: :complete } + end + + def payment_form_step(registration) + payment_method = (registration.data || {})['payment_method'].present? ? registration.data['payment_method'].to_sym : nil + + if payment_method == :host_payment + return { + method: payment_method, + amount: (registration.data || {})['payment_amount'], + amounts: registration.conference.payment_amounts || Conference.default_payment_amounts, + currencies: [registration.conference.default_currency], + currency: registration.conference.default_currency, + no_ajax: true, + show_amount_form: true + } + end + super end + def payment_form_step_update(registration, params) - conference = registration.conference - username = conference.paypal_username - password = conference.paypal_password - signature = conference.paypal_signature - unless valid_paypal_credential?(username) && valid_paypal_credential?(password) && valid_paypal_credential?(signature) - Rails.logger.warn "[PayPalFallback] Missing or invalid PayPal credentials for conference #{conference.id}. Username: #{username.present?}, Password: #{password.present?}, Signature: #{signature.present?}" + payment_method = (registration.data || {})['payment_method'].to_sym + + if payment_method == :host_payment + if params[:button] == 'back' || params[:button] == 'review' + return { status: :complete } + end + value = (params[:value] || params[:custom_value] || 0).to_f - currency = conference.default_currency + unless value > 0 + return { + status: :error, + message: 'amount_required' + } + end + registration.data ||= {} registration.data['payment_amount'] = value - registration.payment_info = { - amount: value, - currency: currency - }.to_yaml + registration.data['payment_status'] = 'pending_manual' registration.save! - return { status: :complete } + return { status: :complete, message: 'manual_payment_pending' } end + super end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 064e761..4dce817 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2442,7 +2442,9 @@ en: organization_none: None of the above payment_paypal: I can pay now online payment_paypal_fallback: I can pay via host provided link + payment_host_payment: I can pay via host provided link payment_on_arrival: I can pledge to pay on arrival + payment_on_arrival_with_link: I can pledge to pay on arrival or via host provided link payment_none: Not now food_meat: I eat meat and dairy food_vegetarian: I am vegetarian diff --git a/config/locales/es.yml b/config/locales/es.yml index a606f78..aa2989b 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -2010,6 +2010,7 @@ es: organization_none: Ningunx de lxs anteriores payment_none: Por ahora no payment_on_arrival: Puedo comprometerme a pagar a mi llegada + payment_on_arrival_with_link: Puedo comprometerme a pagar a mi llegada o mediante un enlace proporcionado por el anfitrión payment_paypal: Puedo pagar ahora con Paypal payment_paypal_fallback: Puedo pagar mediante un enlace proporcionado por el anfitrión reopen_registration: Re-abrir mi registro diff --git a/config/locales/fr.yml b/config/locales/fr.yml index ff0d6b0..c46c140 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1860,7 +1860,8 @@ fr: back: Retour 'yes': Oui review: Revoir - payment_on_arrival: Je m’engage à payer après mon arrivée + payment_on_arrival: Je m'engage à payer après mon arrivée + payment_on_arrival_with_link: Je m'engage à payer à mon arrivée ou via un lien fourni par l'hôte organization_none: Aucune housing_tent: Je voudrais un emplacement pour planter ma tente housing_none: Je n’ai pas besoin d’un hébergement