Factor out the Stripe code from the UserUpgrade class. Introduce a new PaymentTransaction abstract class that represents a payment with some payment processor, and a PaymentTransaction::Stripe class that implements transactions with Stripe. Note that we can't completely eliminate Stripe even though we no longer accept payments with it because we still need to be able to look up old payments in Stripe.
191 lines
5.6 KiB
Ruby
191 lines
5.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class PaymentTransaction::Stripe < PaymentTransaction
|
|
delegate :stripe_id, to: :user_upgrade
|
|
|
|
def self.enabled?
|
|
Danbooru.config.stripe_secret_key.present? && Danbooru.config.stripe_publishable_key.present? && Danbooru.config.stripe_webhook_secret.present?
|
|
end
|
|
|
|
def create!(country: "US", allow_promotion_codes: false)
|
|
checkout_session = ::Stripe::Checkout::Session.create(
|
|
mode: "payment",
|
|
success_url: Routes.user_upgrade_url(user_upgrade),
|
|
cancel_url: Routes.new_user_upgrade_url(user_id: recipient.id),
|
|
client_reference_id: "user_upgrade_#{user_upgrade.id}",
|
|
customer_email: purchaser.email_address&.address,
|
|
payment_method_types: payment_method_types(country),
|
|
allow_promotion_codes: allow_promotion_codes.presence,
|
|
line_items: [{
|
|
price: upgrade_price_id(country),
|
|
quantity: 1,
|
|
}],
|
|
discounts: [{
|
|
coupon: promotion_discount_id,
|
|
}],
|
|
metadata: {
|
|
user_upgrade_id: user_upgrade.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: user_upgrade.is_gift?,
|
|
level: user_upgrade.level,
|
|
}
|
|
)
|
|
|
|
user_upgrade.update!(stripe_id: checkout_session.id)
|
|
checkout_session
|
|
end
|
|
|
|
def refund!(reason = nil)
|
|
::Stripe::Refund.create(payment_intent: payment_intent.id, reason: reason)
|
|
end
|
|
|
|
concerning :WebhookMethods do
|
|
class_methods do
|
|
def receive_webhook(request)
|
|
event = build_event(request)
|
|
|
|
case event.type
|
|
when "checkout.session.completed"
|
|
checkout_session_completed(event.data.object)
|
|
when "charge.dispute.created"
|
|
charge_dispute_created(event.data.object)
|
|
when "radar.early_fraud_warning.created"
|
|
radar_early_fraud_warning_created(event.data.object)
|
|
end
|
|
end
|
|
|
|
private def build_event(request)
|
|
payload = request.body.read
|
|
signature = request.headers["Stripe-Signature"]
|
|
::Stripe::Webhook.construct_event(payload, signature, Danbooru.config.stripe_webhook_secret)
|
|
end
|
|
|
|
private def checkout_session_completed(checkout)
|
|
user_upgrade = UserUpgrade.find(checkout.metadata.user_upgrade_id)
|
|
user_upgrade.process_upgrade!(checkout.payment_status)
|
|
end
|
|
|
|
private def charge_dispute_created(dispute)
|
|
Dmail.create_automated(to: User.owner, title: "Stripe Dispute", body: <<~EOS)
|
|
Dispute: https://stripe.com/payments/#{dispute.charge}
|
|
EOS
|
|
end
|
|
|
|
private def radar_early_fraud_warning_created(fraud_warning)
|
|
Dmail.create_automated(to: User.owner, title: "Stripe Early Fraud Warning", body: <<~EOS)
|
|
Charge: https://stripe.com/payments/#{fraud_warning.charge}
|
|
EOS
|
|
end
|
|
|
|
private def register_webhook
|
|
webhook = ::Stripe::WebhookEndpoint.create({
|
|
url: Routes.webhook_user_upgrade_url(source: "stripe"),
|
|
enabled_events: [
|
|
"checkout.session.completed",
|
|
"checkout.session.async_payment_failed",
|
|
"checkout.session.async_payment_succeeded",
|
|
"charge.dispute.created",
|
|
"radar.early_fraud_warning.created",
|
|
],
|
|
})
|
|
|
|
webhook.secret
|
|
end
|
|
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 amount
|
|
payment_intent.amount
|
|
end
|
|
|
|
def currency
|
|
payment_intent.currency
|
|
end
|
|
|
|
private def checkout_session
|
|
return nil if stripe_id.nil?
|
|
@checkout_session ||= ::Stripe::Checkout::Session.retrieve(stripe_id)
|
|
end
|
|
|
|
private def payment_intent
|
|
return nil if checkout_session.nil?
|
|
@payment_intent ||= ::Stripe::PaymentIntent.retrieve(checkout_session.payment_intent)
|
|
end
|
|
|
|
private def charge
|
|
payment_intent.charges.data.first
|
|
end
|
|
|
|
private 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
|
|
|
|
private 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
|
|
|
|
private def upgrade_price_id(country)
|
|
case [upgrade_type, preferred_currency(country)]
|
|
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
|
|
|
|
private def promotion_discount_id
|
|
if Danbooru.config.is_promotion?
|
|
Danbooru.config.stripe_promotion_discount_id
|
|
end
|
|
end
|
|
end
|