Account upgrades are now logged on the /user_upgrades page, so they no longer need to be recorded as mod actions. The mod actions log should be reserved for privileged actions performed by Builders and above. They also tended to spam the mod actions log.
306 lines
7.6 KiB
Ruby
306 lines
7.6 KiB
Ruby
class UserUpgrade < ApplicationRecord
|
|
belongs_to :recipient, class_name: "User"
|
|
belongs_to :purchaser, class_name: "User"
|
|
|
|
enum upgrade_type: {
|
|
gold: 0,
|
|
platinum: 10,
|
|
gold_to_platinum: 20,
|
|
}, _suffix: "upgrade"
|
|
|
|
enum status: {
|
|
pending: 0,
|
|
processing: 10,
|
|
complete: 20,
|
|
refunded: 30,
|
|
}
|
|
|
|
scope :gifted, -> { where("recipient_id != purchaser_id") }
|
|
scope :self_upgrade, -> { where("recipient_id = purchaser_id") }
|
|
|
|
def self.enabled?
|
|
stripe_secret_key.present? && stripe_publishable_key.present? && stripe_webhook_secret.present?
|
|
end
|
|
|
|
def self.stripe_secret_key
|
|
Danbooru.config.stripe_secret_key
|
|
end
|
|
|
|
def self.stripe_publishable_key
|
|
Danbooru.config.stripe_publishable_key
|
|
end
|
|
|
|
def self.stripe_webhook_secret
|
|
Danbooru.config.stripe_webhook_secret
|
|
end
|
|
|
|
def self.gold_price
|
|
2000
|
|
end
|
|
|
|
def self.platinum_price
|
|
2 * gold_price
|
|
end
|
|
|
|
def self.gold_to_platinum_price
|
|
platinum_price - gold_price
|
|
end
|
|
|
|
def level
|
|
case upgrade_type
|
|
when "gold"
|
|
User::Levels::GOLD
|
|
when "platinum"
|
|
User::Levels::PLATINUM
|
|
when "gold_to_platinum"
|
|
User::Levels::PLATINUM
|
|
else
|
|
raise NotImplementedError
|
|
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 level_string
|
|
User.level_string(level)
|
|
end
|
|
|
|
def is_gift?
|
|
recipient != purchaser
|
|
end
|
|
|
|
def self.visible(user)
|
|
if user.is_owner?
|
|
all
|
|
else
|
|
where(recipient: user).or(where(purchaser: user))
|
|
end
|
|
end
|
|
|
|
def self.search(params)
|
|
q = search_attributes(params, :id, :created_at, :updated_at, :upgrade_type, :status, :stripe_id, :recipient, :purchaser)
|
|
|
|
if params[:is_gifted].to_s.truthy?
|
|
q = q.gifted
|
|
elsif params[:is_gifted].to_s.falsy?
|
|
q = q.self_upgrade
|
|
end
|
|
|
|
q = q.apply_default_order(params)
|
|
q
|
|
end
|
|
|
|
concerning :UpgradeMethods do
|
|
def process_upgrade!(payment_status)
|
|
recipient.with_lock do
|
|
return unless pending? || processing?
|
|
|
|
if payment_status == "paid"
|
|
upgrade_recipient!
|
|
dmail_recipient!
|
|
dmail_purchaser!
|
|
update!(status: :complete)
|
|
else
|
|
update!(status: :processing)
|
|
end
|
|
end
|
|
end
|
|
|
|
def upgrade_recipient!
|
|
recipient.update!(level: level)
|
|
end
|
|
|
|
def dmail_recipient!
|
|
if is_gift?
|
|
body = "Congratulations, your account has been upgraded to #{level_string} by <@#{purchaser.name}>. Enjoy!"
|
|
else
|
|
body = "You are now a #{level_string} user. Thanks for supporting #{Danbooru.config.canonical_app_name}!"
|
|
end
|
|
|
|
title = "You have been upgraded to #{level_string}!"
|
|
Dmail.create_automated(to: recipient, title: title, body: body)
|
|
end
|
|
|
|
def dmail_purchaser!
|
|
return unless is_gift?
|
|
|
|
title = "#{recipient.name} has been upgraded to #{level_string}!"
|
|
body = "<@#{recipient.name}> is now a #{level_string} user. Thanks for supporting #{Danbooru.config.canonical_app_name}!"
|
|
|
|
Dmail.create_automated(to: purchaser, title: title, body: body)
|
|
end
|
|
end
|
|
|
|
concerning :StripeMethods do
|
|
def create_checkout!(country: "US", allow_promotion_codes: false)
|
|
methods = payment_method_types(country)
|
|
currency = preferred_currency(country)
|
|
price_id = upgrade_price_id(currency)
|
|
|
|
checkout = Stripe::Checkout::Session.create(
|
|
mode: "payment",
|
|
success_url: Routes.user_upgrade_url(self),
|
|
cancel_url: Routes.new_user_upgrade_url(user_id: recipient.id),
|
|
client_reference_id: "user_upgrade_#{id}",
|
|
customer_email: purchaser.email_address&.address,
|
|
payment_method_types: methods,
|
|
allow_promotion_codes: allow_promotion_codes,
|
|
line_items: [{
|
|
price: price_id,
|
|
quantity: 1,
|
|
}],
|
|
metadata: {
|
|
user_upgrade_id: id,
|
|
purchaser_id: purchaser.id,
|
|
recipient_id: recipient.id,
|
|
purchaser_name: purchaser.name,
|
|
recipient_name: recipient.name,
|
|
upgrade_type: upgrade_type,
|
|
country: country,
|
|
is_gift: is_gift?,
|
|
level: level,
|
|
}
|
|
)
|
|
|
|
update!(stripe_id: checkout.id)
|
|
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?
|
|
charge.receipt_url
|
|
end
|
|
|
|
def payment_url
|
|
return nil if pending? || stripe_id.nil?
|
|
"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
|
|
|
|
def has_payment?
|
|
!pending?
|
|
end
|
|
|
|
def upgrade_price_id(currency)
|
|
case [upgrade_type, currency]
|
|
when ["gold", "usd"]
|
|
Danbooru.config.stripe_gold_usd_price_id
|
|
when ["gold", "eur"]
|
|
Danbooru.config.stripe_gold_eur_price_id
|
|
when ["platinum", "usd"]
|
|
Danbooru.config.stripe_platinum_usd_price_id
|
|
when ["platinum", "eur"]
|
|
Danbooru.config.stripe_platinum_eur_price_id
|
|
when ["gold_to_platinum", "usd"]
|
|
Danbooru.config.stripe_gold_to_platinum_usd_price_id
|
|
when ["gold_to_platinum", "eur"]
|
|
Danbooru.config.stripe_gold_to_platinum_eur_price_id
|
|
else
|
|
raise NotImplementedError
|
|
end
|
|
end
|
|
|
|
def payment_method_types(country)
|
|
case country.to_s.upcase
|
|
# Austria, https://stripe.com/docs/payments/bancontact
|
|
when "AT"
|
|
["card", "eps"]
|
|
# Belgium, https://stripe.com/docs/payments/eps
|
|
when "BE"
|
|
["card", "bancontact"]
|
|
# Germany, https://stripe.com/docs/payments/giropay
|
|
when "DE"
|
|
["card", "giropay"]
|
|
# Netherlands, https://stripe.com/docs/payments/ideal
|
|
when "NL"
|
|
["card", "ideal"]
|
|
# Poland, https://stripe.com/docs/payments/p24
|
|
when "PL"
|
|
["card", "p24"]
|
|
else
|
|
["card"]
|
|
end
|
|
end
|
|
|
|
def preferred_currency(country)
|
|
case country.to_s.upcase
|
|
# Austria, Belgium, Germany, Netherlands, Poland
|
|
when "AT", "BE", "DE", "NL", "PL"
|
|
"eur"
|
|
else
|
|
"usd"
|
|
end
|
|
end
|
|
|
|
class_methods do
|
|
def register_webhook
|
|
webhook = Stripe::WebhookEndpoint.create({
|
|
url: Routes.webhook_user_upgrade_url(source: "stripe"),
|
|
enabled_events: [
|
|
"payment_intent.created",
|
|
"payment_intent.payment_failed",
|
|
"checkout.session.completed",
|
|
],
|
|
})
|
|
|
|
webhook.secret
|
|
end
|
|
|
|
def receive_webhook(request)
|
|
event = build_event(request)
|
|
|
|
if event.type == "checkout.session.completed"
|
|
checkout_session_completed(event.data.object)
|
|
end
|
|
end
|
|
|
|
def build_event(request)
|
|
payload = request.body.read
|
|
signature = request.headers["Stripe-Signature"]
|
|
Stripe::Webhook.construct_event(payload, signature, stripe_webhook_secret)
|
|
end
|
|
|
|
def checkout_session_completed(checkout)
|
|
user_upgrade = UserUpgrade.find(checkout.metadata.user_upgrade_id)
|
|
user_upgrade.process_upgrade!(checkout.payment_status)
|
|
end
|
|
end
|
|
end
|
|
end
|