From 0856f7b35994c28873008d6c69ac8f1e9af985fa Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Mon, 23 Mar 2026 10:16:06 -0400 Subject: [PATCH 1/4] Refactor payments controller to use current_user and enhance payment specs Updated the payments controller to utilize `current_user` instead of `@current_user` in the `make_payment` method. Enhanced request specs to support full and partial payment scenarios, ensuring accurate redirection based on payment amounts. Added helper text in the payment view to clarify partial payment options for users. --- app/controllers/payments_controller.rb | 2 +- app/views/payments/payment_show.html.erb | 3 ++ spec/requests/payments_spec.rb | 36 +++++++++++++++++++----- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/app/controllers/payments_controller.rb b/app/controllers/payments_controller.rb index a455b8f..00062f5 100644 --- a/app/controllers/payments_controller.rb +++ b/app/controllers/payments_controller.rb @@ -41,7 +41,7 @@ def payment_receipt end def make_payment - processed_url = generate_hash(@current_user, params['amount']) + processed_url = generate_hash(current_user, params['amount']) redirect_to processed_url, allow_other_host: true end diff --git a/app/views/payments/payment_show.html.erb b/app/views/payments/payment_show.html.erb index f51887e..73f0a59 100644 --- a/app/views/payments/payment_show.html.erb +++ b/app/views/payments/payment_show.html.erb @@ -65,6 +65,9 @@ within: 1..2000, required: true, class: 'form-control' %> <%= f.submit "Pay Now", class: 'btn btn-sm btn-success ml-2' %> + + The amount is prefilled with your full balance due, but you can enter a different amount to make a partial payment. + <% end %> diff --git a/spec/requests/payments_spec.rb b/spec/requests/payments_spec.rb index 1884763..95112a8 100644 --- a/spec/requests/payments_spec.rb +++ b/spec/requests/payments_spec.rb @@ -33,16 +33,22 @@ before do sign_in user allow_any_instance_of(PaymentsController).to receive(:user_has_payments?).and_return(true) + allow_any_instance_of(PaymentsController).to receive(:payments_open?).and_return(true) + allow_any_instance_of(PaymentsController).to receive(:current_application) do |controller| + controller.instance_variable_set(:@current_application, application) + end + create(:lodging, description: application.lodging_selection, cost: 100.0) + application.update!(partner_registration: create(:partner_registration, cost: 0.0), subscription: false) + create(:payment, :current_conference, user: user, transaction_status: '1', total_amount: '3000') - mock_application = instance_double(Application, - lodging_selection: "Standard", - partner_registration: instance_double(PartnerRegistration, cost: 0.0), - subscription: false + allow_any_instance_of(PaymentsController).to receive(:current_application_settings).and_return( + double(subscription_cost: 25.0, payments_directions: 'Payment instructions', allow_payments: true) ) - allow_any_instance_of(PaymentsController).to receive(:current_application).and_return(mock_application) + end - allow(Payment).to receive_message_chain(:current_conference_payments, :where, :pluck).and_return([1000, 2000]) - allow_any_instance_of(PaymentsController).to receive(:current_application_settings).and_return(double(subscription_cost: 25.0)) + it "renders helper text explaining partial payments" do + get all_payments_path + expect(response.body).to include("The amount is prefilled with your full balance due, but you can enter a different amount to make a partial payment.") end end end @@ -65,6 +71,22 @@ post make_payment_path, params: { amount: "100" } expect(response).to redirect_to("https://payment-url.example.com") end + + it "supports paying the full balance amount" do + expect_any_instance_of(PaymentsController) + .to receive(:generate_hash).with(user, "200").and_return("https://payment-url.example.com") + + post make_payment_path, params: { amount: "200" } + expect(response).to redirect_to("https://payment-url.example.com") + end + + it "supports paying a partial balance amount" do + expect_any_instance_of(PaymentsController) + .to receive(:generate_hash).with(user, "75").and_return("https://payment-url.example.com") + + post make_payment_path, params: { amount: "75" } + expect(response).to redirect_to("https://payment-url.example.com") + end end end From dba48b09827f32c09489f1324025af1eda1a3b3c Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Mon, 23 Mar 2026 10:47:10 -0400 Subject: [PATCH 2/4] Enhance payment validation and error handling in PaymentsController and specs Refactor the `make_payment` method in the PaymentsController to include a new `validated_payment_amount` method for improved validation of payment amounts. Implement checks for missing, non-numeric, non-positive, and excessive amounts, redirecting users with appropriate alerts. Update request and controller specs to cover these new validation scenarios, ensuring robust error handling for various payment inputs. --- app/controllers/payments_controller.rb | 36 ++++++++++++++++++- spec/controllers/payments_controller_spec.rb | 16 ++++++--- spec/requests/payments_spec.rb | 37 ++++++++++++++++++-- 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/app/controllers/payments_controller.rb b/app/controllers/payments_controller.rb index 00062f5..c24731e 100644 --- a/app/controllers/payments_controller.rb +++ b/app/controllers/payments_controller.rb @@ -2,6 +2,8 @@ require 'time' class PaymentsController < ApplicationController + MAX_PAYMENT_AMOUNT = 2000 + protect_from_forgery with: :exception skip_before_action :verify_authenticity_token, only: [:payment_receipt] before_action :verify_payment_callback, only: [:payment_receipt] @@ -41,7 +43,13 @@ def payment_receipt end def make_payment - processed_url = generate_hash(current_user, params['amount']) + amount = validated_payment_amount(params['amount']) + if amount.nil? + redirect_to all_payments_path, alert: 'Please enter a valid payment amount.' + return + end + + processed_url = generate_hash(current_user, amount) redirect_to processed_url, allow_other_host: true end @@ -115,6 +123,32 @@ def generate_hash(current_user, amount=100) final_url = connection_hash[url_to_use] + '?' + url_for_payment + 'hash=' + encoded_hash end + def validated_payment_amount(raw_amount) + amount = Integer(raw_amount, exception: false) + return nil if amount.nil? || amount <= 0 + + balance_due = current_balance_due + return nil if balance_due <= 0 + + max_amount = [balance_due.floor, MAX_PAYMENT_AMOUNT].min + return nil if amount > max_amount + + amount + end + + def current_balance_due + current_application + return 0.0 if @current_application.nil? + + cost_lodging = Lodging.find_by(description: @current_application.lodging_selection)&.cost.to_f + cost_partner = @current_application.partner_registration&.cost.to_f + has_subscription = @current_application.subscription + cost_subscription = current_application_settings.subscription_cost.to_f + total_cost = cost_lodging + cost_partner + (has_subscription ? cost_subscription : 0) + total_paid = Payment.current_conference_payments.where(user_id: current_user, transaction_status: '1').pluck(:total_amount).map(&:to_f).sum / 100 + total_cost - total_paid + end + def url_params params.permit(:amount, :transactionType, :transactionStatus, :transactionId, :transactionTotalAmount, :transactionDate, :transactionAcountType, :transactionResultCode, :transactionResultMessage, :orderNumber, :timestamp, :hash, :conf_year) end diff --git a/spec/controllers/payments_controller_spec.rb b/spec/controllers/payments_controller_spec.rb index 4a4faff..fea37df 100644 --- a/spec/controllers/payments_controller_spec.rb +++ b/spec/controllers/payments_controller_spec.rb @@ -168,14 +168,22 @@ end context 'with different amounts' do - it 'handles decimal amounts correctly' do + it 'rejects decimal amounts' do post :make_payment, params: { amount: '50.50' } - expect(response.location).to include('amountDue=5000') # 50.50.to_i * 100 = 50 * 100 + expect(response).to redirect_to(all_payments_path) + expect(flash[:alert]).to eq('Please enter a valid payment amount.') end - it 'handles zero amount' do + it 'rejects zero amount' do post :make_payment, params: { amount: '0' } - expect(response.location).to include('amountDue=0') + expect(response).to redirect_to(all_payments_path) + expect(flash[:alert]).to eq('Please enter a valid payment amount.') + end + + it 'rejects amount above current balance due' do + post :make_payment, params: { amount: '500' } + expect(response).to redirect_to(all_payments_path) + expect(flash[:alert]).to eq('Please enter a valid payment amount.') end end end diff --git a/spec/requests/payments_spec.rb b/spec/requests/payments_spec.rb index 95112a8..faaf305 100644 --- a/spec/requests/payments_spec.rb +++ b/spec/requests/payments_spec.rb @@ -64,6 +64,15 @@ context "when user is signed in" do before do sign_in user + allow_any_instance_of(PaymentsController).to receive(:current_application) do |controller| + controller.instance_variable_set(:@current_application, application) + end + create(:lodging, description: application.lodging_selection, cost: 300.0) + application.update!(partner_registration: create(:partner_registration, cost: 0.0), subscription: false) + allow_any_instance_of(PaymentsController).to receive(:current_application_settings).and_return( + double(subscription_cost: 25.0) + ) + allow_any_instance_of(PaymentsController).to receive(:current_balance_due).and_return(300.0) allow_any_instance_of(PaymentsController).to receive(:generate_hash).and_return("https://payment-url.example.com") end @@ -74,7 +83,7 @@ it "supports paying the full balance amount" do expect_any_instance_of(PaymentsController) - .to receive(:generate_hash).with(user, "200").and_return("https://payment-url.example.com") + .to receive(:generate_hash).with(user, 200).and_return("https://payment-url.example.com") post make_payment_path, params: { amount: "200" } expect(response).to redirect_to("https://payment-url.example.com") @@ -82,11 +91,35 @@ it "supports paying a partial balance amount" do expect_any_instance_of(PaymentsController) - .to receive(:generate_hash).with(user, "75").and_return("https://payment-url.example.com") + .to receive(:generate_hash).with(user, 75).and_return("https://payment-url.example.com") post make_payment_path, params: { amount: "75" } expect(response).to redirect_to("https://payment-url.example.com") end + + it "rejects missing amount input" do + post make_payment_path, params: { amount: "" } + expect(response).to redirect_to(all_payments_path) + expect(flash[:alert]).to eq("Please enter a valid payment amount.") + end + + it "rejects non-numeric amount input" do + post make_payment_path, params: { amount: "abc" } + expect(response).to redirect_to(all_payments_path) + expect(flash[:alert]).to eq("Please enter a valid payment amount.") + end + + it "rejects non-positive amount input" do + post make_payment_path, params: { amount: "0" } + expect(response).to redirect_to(all_payments_path) + expect(flash[:alert]).to eq("Please enter a valid payment amount.") + end + + it "rejects amount above balance due" do + post make_payment_path, params: { amount: "400" } + expect(response).to redirect_to(all_payments_path) + expect(flash[:alert]).to eq("Please enter a valid payment amount.") + end end end From 77a5f5218619561251d0199f15abb2a25467e9a5 Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Mon, 23 Mar 2026 11:23:32 -0400 Subject: [PATCH 3/4] Refactor payment amount handling in PaymentsController and view Updated the payment view to use a dynamic maximum payment amount based on the balance due, enhancing user experience. Introduced a new method in the PaymentsController to calculate the maximum payment amount, ensuring consistent validation logic across the application. --- app/controllers/payments_controller.rb | 7 ++++++- app/views/payments/payment_show.html.erb | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/controllers/payments_controller.rb b/app/controllers/payments_controller.rb index c24731e..8e13430 100644 --- a/app/controllers/payments_controller.rb +++ b/app/controllers/payments_controller.rb @@ -63,6 +63,7 @@ def payment_show @cost_subscription = current_application_settings.subscription_cost.to_f @total_cost = cost_lodging + cost_partner + (@has_subscription ? @cost_subscription : 0) @balance_due = @total_cost - @ttl_paid + @max_payment_amount = max_payment_amount_for(@balance_due) end def delete_manual_payment @@ -130,12 +131,16 @@ def validated_payment_amount(raw_amount) balance_due = current_balance_due return nil if balance_due <= 0 - max_amount = [balance_due.floor, MAX_PAYMENT_AMOUNT].min + max_amount = max_payment_amount_for(balance_due) return nil if amount > max_amount amount end + def max_payment_amount_for(balance_due) + [balance_due.floor, MAX_PAYMENT_AMOUNT].min + end + def current_balance_due current_application return 0.0 if @current_application.nil? diff --git a/app/views/payments/payment_show.html.erb b/app/views/payments/payment_show.html.erb index 73f0a59..0432cb2 100644 --- a/app/views/payments/payment_show.html.erb +++ b/app/views/payments/payment_show.html.erb @@ -61,8 +61,8 @@
$
- <%= f.number_field :amount, value: "#{@balance_due.to_i}", - within: 1..2000, required: true, class: 'form-control' %> + <%= f.number_field :amount, value: @max_payment_amount, + within: 1..@max_payment_amount, required: true, class: 'form-control' %> <%= f.submit "Pay Now", class: 'btn btn-sm btn-success ml-2' %> From 7d8b592e5d82ecc67525204172611343164f8bb9 Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Mon, 23 Mar 2026 11:33:54 -0400 Subject: [PATCH 4/4] Add test for payment amount validation exceeding maximum limit Implemented a new test case in the payments request specs to ensure that payment amounts exceeding the defined maximum limit are correctly rejected, even when the balance due is higher. This enhances the validation coverage for payment processing scenarios. --- spec/requests/payments_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/requests/payments_spec.rb b/spec/requests/payments_spec.rb index faaf305..99a008e 100644 --- a/spec/requests/payments_spec.rb +++ b/spec/requests/payments_spec.rb @@ -120,6 +120,15 @@ expect(response).to redirect_to(all_payments_path) expect(flash[:alert]).to eq("Please enter a valid payment amount.") end + + it "rejects amount above MAX_PAYMENT_AMOUNT even when balance due is higher" do + allow_any_instance_of(PaymentsController).to receive(:current_balance_due).and_return(3000.0) + + post make_payment_path, params: { amount: "2001" } + + expect(response).to redirect_to(all_payments_path) + expect(flash[:alert]).to eq("Please enter a valid payment amount.") + end end end