«Я знаю это, но пользователь будет использовать кнопку возврата назад, если в клиенте есть какой-то кэш, пользователь все равно может что-то увидеть и повторно отправить, это не то, что я ожидаю» [в этом был смысл?]
Если вы хотите предотвратить повторную отправку пользователем того же запроса, вы можете добавить скрытое поле (просто что-то случайное) и сохранить его в сеансе после обработки запроса:
if params[:token] == session[:used_token]
render_already_processed_notice
return
end
if @cart.save
session[:used_token] = params[:token]
....
Если вы используете идентификатор корзины в запросе, вы можете использовать статус модели корзины:
@cart = Cart.find(params[:id])
render_some_notice and return if @cart.done?
....
@cart.done = true
@cart.save
Лично я бы не стал создавать страницы оформления заказа и order_success.Я бы просто создал одну страницу - статус корзины, и в зависимости от статуса модели я просто отобразил бы другое содержимое.
Если какое-то действие может быть выполнено только один раз (например, закрытие корзины или завершение транзакции), естьнет проблем: render_something and return if @cart.already_closed?
На страницах, где возможна многократная отправка, но не всегда приветствуется (например, добавление товара в корзину - пользователь может добавить два идентичных товара), вы можете сделать две вещи:
1) Используйте упомянутый токен, который будет определять, когда пользователь просто нажал F5, и спросит его, действительно ли действие должно быть выполнено дважды, или
2) Просто примите запросы, но всегда предоставьтепользователь с помощью методов «отката» действий (позволяет ему удалять товары из корзины) и обеспечения проверки содержимого корзины до окончательного принятия.