user upgrades: add ability to refund upgrades.

This commit is contained in:
evazion
2020-12-29 03:50:43 -06:00
parent 87af02f689
commit 4b171bf97e
8 changed files with 111 additions and 10 deletions

View File

@@ -27,6 +27,14 @@ class UserUpgradesController < ApplicationController
respond_with(@user_upgrade)
end
def refund
@user_upgrade = authorize UserUpgrade.find(params[:id])
@user_upgrade.refund!
flash[:notice] = "Upgrade refunded"
respond_with(@user_upgrade)
end
def receipt
@user_upgrade = authorize UserUpgrade.find(params[:id])
redirect_to @user_upgrade.receipt_url

View File

@@ -11,7 +11,8 @@ class UserUpgrade < ApplicationRecord
enum status: {
pending: 0,
processing: 10,
complete: 20
complete: 20,
refunded: 30,
}
scope :gifted, -> { where("recipient_id != purchaser_id") }
@@ -62,6 +63,19 @@ class UserUpgrade < ApplicationRecord
end
end
def previous_level
case upgrade_type
when "gold"
User::Levels::MEMBER
when "platinum"
User::Levels::MEMBER
when "gold_to_platinum"
User::Levels::GOLD
else
raise NotImplementedError
end
end
def upgrade_price
case upgrade_type
when "gold"
@@ -120,7 +134,7 @@ class UserUpgrade < ApplicationRecord
concerning :UpgradeMethods do
def process_upgrade!(payment_status)
recipient.with_lock do
return if status == "complete"
return unless pending? || processing?
if payment_status == "paid"
upgrade_recipient!
@@ -198,24 +212,38 @@ class UserUpgrade < ApplicationRecord
checkout
end
def refund!(reason: nil)
with_lock do
return if refunded?
Stripe::Refund.create(payment_intent: payment_intent.id, reason: reason)
recipient.update!(level: previous_level)
update!(status: "refunded")
end
end
def receipt_url
return nil if pending? || stripe_id.nil?
checkout_session = Stripe::Checkout::Session.retrieve(stripe_id)
payment_intent = Stripe::PaymentIntent.retrieve(checkout_session.payment_intent)
charge = payment_intent.charges.data.first
charge.receipt_url
end
def payment_url
return nil if pending? || stripe_id.nil?
checkout_session = Stripe::Checkout::Session.retrieve(stripe_id)
payment_intent = Stripe::PaymentIntent.retrieve(checkout_session.payment_intent)
"https://dashboard.stripe.com/payments/#{payment_intent.id}"
end
def checkout_session
@checkout_session ||= Stripe::Checkout::Session.retrieve(stripe_id)
end
def payment_intent
@payment_intent ||= Stripe::PaymentIntent.retrieve(checkout_session.payment_intent)
end
def charge
payment_intent.charges.data.first
end
def has_receipt?
!pending?
end

View File

@@ -11,6 +11,10 @@ class UserUpgradePolicy < ApplicationPolicy
record.recipient == user || record.purchaser == user || user.is_owner?
end
def refund?
user.is_owner? && record.complete?
end
def receipt?
(record.purchaser == user || user.is_owner?) && record.has_receipt?
end

View File

@@ -40,6 +40,9 @@
<% if policy(user_upgrade).payment? %>
| <%= link_to "Payment", payment_user_upgrade_path(user_upgrade), target: "_blank" %>
<% end %>
<% if policy(user_upgrade).refund? %>
| <%= link_to "Refund", refund_user_upgrade_path(user_upgrade), remote: true, method: :put, "data-confirm": "Are you sure you want to refund this payment?" %>
<% end %>
<% end %>
<% end %>

View File

@@ -0,0 +1 @@
location.reload();

View File

@@ -257,6 +257,7 @@ Rails.application.routes.draw do
resources :user_upgrades, only: [:new, :create, :show, :index] do
get :receipt, on: :member
get :payment, on: :member
put :refund, on: :member
end
resources :user_feedbacks, except: [:destroy]
resources :user_name_change_requests, only: [:new, :create, :show, :index]

View File

@@ -183,6 +183,51 @@ class UserUpgradesControllerTest < ActionDispatch::IntegrationTest
end
end
context "refund action" do
mock_stripe!
context "for a self upgrade" do
context "to Gold" do
should_eventually "refund the upgrade" do
@user_upgrade = create(:self_gold_upgrade, recipient: create(:gold_user), status: "complete")
@user_upgrade.create_checkout!
put_auth refund_user_upgrade_path(@user_upgrade), create(:owner_user), xhr: true
assert_response :success
assert_equal("refunded", @user_upgrade.reload.status)
assert_equal(User::Levels::MEMBER, @user_upgrade.recipient.level)
end
end
end
context "for a gifted upgrade" do
context "to Platinum" do
should_eventually "refund the upgrade" do
@user_upgrade = create(:gift_platinum_upgrade, recipient: create(:platinum_user), status: "complete")
@user_upgrade.create_checkout!
put_auth refund_user_upgrade_path(@user_upgrade), create(:owner_user), xhr: true
assert_response :success
assert_equal("refunded", @user_upgrade.reload.status)
assert_equal(User::Levels::MEMBER, @user_upgrade.recipient.level)
end
end
end
should "not allow unauthorized users to create a refund" do
@user_upgrade = create(:self_gold_upgrade, recipient: create(:gold_user), status: "complete")
@user_upgrade.create_checkout!
put_auth refund_user_upgrade_path(@user_upgrade), @user_upgrade.purchaser, xhr: true
assert_response 403
assert_equal("complete", @user_upgrade.reload.status)
assert_equal(User::Levels::GOLD, @user_upgrade.recipient.level)
end
end
context "create action" do
mock_stripe!

View File

@@ -82,5 +82,16 @@ class UserUpgradeTest < ActiveSupport::TestCase
end
end
end
context "the #refund! method" do
should_eventually "refund a Gold upgrade" do
@user_upgrade = create(:self_gold_upgrade, recipient: create(:gold_user), status: "complete")
@user_upgrade.create_checkout!
@user_upgrade.refund!
assert_equal("refunded", @user_upgrade.reload.status)
assert_equal(User::Levels::MEMBER, @user_upgrade.recipient.level)
end
end
end
end