Switching Payment Methods for Orders

Problem

When a customer needs to switch from Credit Card to Purchase Order (PO) payment, simply updating the company settings or sending an invoice isn't enough. The order itself has payment configuration that controls whether it will be automatically charged by the RecurringCardChargeJob.

How Auto-Charging Works

The RecurringCardChargeJob (runs daily at 8am) automatically charges orders that meet these criteria:

# From app/jobs/recurring_card_charge_job.rb
Order.active_rentals
     .where(is_recurring: true)
     .where(payment_method: "credit_card")  # ← KEY FILTER
     .where("next_billing_date <= ?", Date.current)

Key takeaway: If payment_method is "credit_card", the order will be auto-charged regardless of invoices sent or customer requests.

Solution: Switch Order Payment Method

Step 1: Identify the Order

ssh root@5.78.141.175 'docker exec $(docker ps --filter label=service=quarry_rentals --filter label=role=web --format "{{.Names}}" | head -1) bin/rails runner "
o = Order.find(ORDER_ID)
puts \"Current payment_method: #{o.payment_method}\"
puts \"Current payment_terms: #{o.payment_terms}\"
puts \"Current payment_status: #{o.payment_status}\"
"'

Step 2: Switch to Purchase Order

ssh root@5.78.141.175 'docker exec $(docker ps --filter label=service=quarry_rentals --filter label=role=web --format "{{.Names}}" | head -1) bin/rails runner "
o = Order.find(ORDER_ID)

# Update payment configuration
o.payment_method = \"purchase_order\"
o.payment_terms = \"net_30\"  # or net_15, net_60, on_receipt
o.payment_profile_id = nil   # Clear credit card reference
o.payment_status = \"unpaid\"  # Invoice sent, awaiting payment

o.save!
puts \"✓ Order switched to Purchase Order payment\"
"'

Step 3: Verify

ssh root@5.78.141.175 'docker exec $(docker ps --filter label=service=quarry_rentals --filter label=role=web --format "{{.Names}}" | head -1) bin/rails runner "
o = Order.find(ORDER_ID)
puts \"payment_method: #{o.payment_method}\"
puts \"Will be auto-charged: #{o.payment_method == \"credit_card\" ? \"YES\" : \"NO\"}\"
"'

Available Payment Methods

From app/models/order.rb:

PAYMENT_METHODS = %w[
  credit_card
  purchase_order
].freeze

Available Payment Terms (for Purchase Orders)

From app/models/order.rb:

PAYMENT_TERMS = %w[
  on_receipt
  net_15
  net_30
  net_60
].freeze

Quick Reference Script

Save this as scripts/switch_order_to_po.rb:

# Usage: bin/rails runner scripts/switch_order_to_po.rb ORDER_ID [PAYMENT_TERMS]
order_id = ARGV[0] || raise("Order ID required")
payment_terms = ARGV[1] || "net_30"

o = Order.find(order_id)

puts "Switching Order #{order_id} from #{o.payment_method} to Purchase Order"
puts "="*60

puts "BEFORE:"
puts "  payment_method: #{o.payment_method}"
puts "  payment_terms: #{o.payment_terms}"
puts "  payment_status: #{o.payment_status}"
puts ""

o.payment_method = "purchase_order"
o.payment_terms = payment_terms
o.payment_profile_id = nil
o.payment_status = "unpaid"

if o.save
  puts "✓ Successfully updated order #{order_id}"
  puts ""
  puts "AFTER:"
  puts "  payment_method: #{o.payment_method}"
  puts "  payment_terms: #{o.payment_terms}"
  puts "  payment_status: #{o.payment_status}"
  puts ""
  puts "✓ Order will NO LONGER be auto-charged"
  puts "✓ Customer will receive invoices instead"
else
  puts "✗ ERROR: #{o.errors.full_messages.join('; ')}"
  exit 1
end

Production Usage

# On production server
kamal app exec -i "bin/rails runner scripts/switch_order_to_po.rb 223 net_30"

Or remotely via SSH:

ssh root@5.78.141.175 'docker exec $(docker ps --filter label=service=quarry_rentals --filter label=role=web --format "{{.Names}}" | head -1) bin/rails runner "
o = Order.find(223)
o.payment_method = \"purchase_order\"
o.payment_terms = \"net_30\"
o.payment_profile_id = nil
o.payment_status = \"unpaid\"
o.save!
"'

Common Scenarios

1. Customer Called to Switch to PO

2. Credit Card Repeatedly Declining

3. New Customer Wants Invoicing Instead of CC

Troubleshooting

Order still being auto-charged after switch

Check that payment_method was actually updated:

Order.find(ORDER_ID).payment_method
# Should return "purchase_order", not "credit_card"

Validation error: "payment_terms must be specified"

Purchase orders require payment terms:

o.payment_terms = "net_30"  # Required!

Company settings vs Order settings

Related Files