Add integration for accepting payments with Authorize.net. https://developer.authorize.net/hello_world.html
126 lines
4.2 KiB
Ruby
126 lines
4.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# https://sandbox.authorize.net/
|
|
# https://developer.authorize.net/hello_world.html
|
|
# https://developer.authorize.net/api/reference/index.html
|
|
# https://developer.authorize.net/api/reference/features/accept_hosted.html
|
|
# https://developer.authorize.net/hello_world/testing_guide.html
|
|
class PaymentTransaction::AuthorizeNet < PaymentTransaction
|
|
extend Memoist
|
|
|
|
class InvalidWebhookError < StandardError; end
|
|
|
|
def create!(country: "US", allow_promotion_codes: false)
|
|
# https://developer.authorize.net/api/reference/index.html#accept-suite-get-an-accept-payment-page
|
|
response = api_client.get_hosted_payment_page(
|
|
reference_id: user_upgrade.id,
|
|
transactionType: "authCaptureTransaction",
|
|
amount: user_upgrade.price,
|
|
customer: {
|
|
id: user_upgrade.purchaser.id,
|
|
email: user_upgrade.purchaser.email_address&.address,
|
|
},
|
|
settings: {
|
|
button: {
|
|
text: "Pay",
|
|
},
|
|
order: {
|
|
show: false,
|
|
merchantName: Danbooru.config.canonical_app_name,
|
|
},
|
|
payment: {
|
|
cardCodeRequired: true,
|
|
showCreditCard: true,
|
|
showBankAccount: false,
|
|
},
|
|
customer: {
|
|
showEmail: true, requiredEmail: true, addPaymentProfile: false
|
|
},
|
|
billing_address: {
|
|
show: true,
|
|
required: false,
|
|
},
|
|
shipping_address: {
|
|
show: false,
|
|
required: false,
|
|
},
|
|
style: { bgColor: "blue" },
|
|
return: {
|
|
url: Routes.user_upgrade_url(user_upgrade),
|
|
cancelUrl: Routes.new_user_upgrade_url(user_id: recipient.id),
|
|
urlText: "Continue",
|
|
cancelUrlText: "Cancel",
|
|
showReceipt: true,
|
|
},
|
|
}
|
|
)
|
|
|
|
[api_client.payment_page_url, response[:token]]
|
|
end
|
|
|
|
def refund!(reason = nil)
|
|
raise NotImplementedError
|
|
end
|
|
|
|
concerning :WebhookMethods do
|
|
class_methods do
|
|
# https://developer.authorize.net/api/reference/features/webhooks.html#Event_Types_and_Payloads
|
|
def receive_webhook(request)
|
|
verify_webhook!(request)
|
|
|
|
case request.params[:eventType]
|
|
when "net.authorize.payment.authcapture.created"
|
|
payment_completed(request)
|
|
end
|
|
end
|
|
|
|
# https://developer.authorize.net/api/reference/features/webhooks.html#Verifying_the_Notification
|
|
private def verify_webhook!(request)
|
|
payload = request.body.read
|
|
actual_signature = request.headers["X-Anet-Signature"].to_s
|
|
calculated_signature = "sha512=" + OpenSSL::HMAC.digest("sha512", Danbooru.config.authorize_net_signature_key, payload).unpack1("H*").upcase
|
|
raise InvalidWebhookError unless ActiveSupport::SecurityUtils::secure_compare(actual_signature, calculated_signature)
|
|
|
|
request
|
|
end
|
|
|
|
private def payment_completed(request)
|
|
# Authorize.net's shitty API sends a real request with fake values when you trigger a test webhook.
|
|
# The only way to detect a test webhook is to check for these hardcoded fake values.
|
|
if request.params.dig(:payload, :authAmount) == 12.5 && request.params.dig(:payload, :id) == "245" && request.params.dig(:payload, :authCode) == "572"
|
|
return
|
|
end
|
|
|
|
user_upgrade_id = request.params.dig(:payload, :merchantReferenceId)
|
|
transaction_id = request.params.dig(:payload, :id)
|
|
user_upgrade = UserUpgrade.find(user_upgrade_id)
|
|
user_upgrade.update!(transaction_id: transaction_id)
|
|
user_upgrade.process_upgrade!("paid")
|
|
end
|
|
|
|
private def register_webhook
|
|
raise NotImplementedError
|
|
end
|
|
end
|
|
end
|
|
|
|
def receipt_url
|
|
# "https://sandbox.authorize.net/ui/themes/sandbox/Transaction/TransactionReceipt.aspx?transid=#{transaction_id}" if transaction_id.present?
|
|
end
|
|
|
|
def payment_url
|
|
# "https://sandbox.authorize.net/ui/themes/sandbox/transaction/transactiondetail.aspx?transID=40092238841" if transaction_id.present?
|
|
end
|
|
|
|
def transaction
|
|
return nil if user_upgrade.transaction_id.nil?
|
|
api_client.get_transaction(user_upgrade.transaction_id)
|
|
end
|
|
|
|
def api_client
|
|
AuthorizeNetClient.new
|
|
end
|
|
|
|
memoize :api_client, :transaction
|
|
end
|