# frozen_string_literal: true module Spree class PaymentMethod class StripeCreditCard < Spree::PaymentMethod::CreditCard preference :secret_key, :string preference :publishable_key, :string preference :stripe_country, :string preference :v3_elements, :boolean preference :v3_intents, :boolean CARD_TYPE_MAPPING = { 'American Express' => 'american_express', 'Diners Club' => 'diners_club', 'Visa' => 'visa' } def stripe_config(order) { id: id, publishable_key: preferred_publishable_key }.tap do |config| config.merge!( payment_request: { country: preferred_stripe_country, currency: order.currency.downcase, label: "Payment for order #{order.number}", amount: (order.total * 100).to_i } ) if payment_request? end end def partial_name 'stripe' end def v3_elements? !!preferred_v3_elements end def payment_request? v3_intents? && preferred_stripe_country.present? end def v3_intents? !!preferred_v3_intents end def gateway_class if v3_intents? ActiveMerchant::Billing::StripePaymentIntentsGateway else ActiveMerchant::Billing::StripeGateway end end def payment_profiles_supported? true end def create_intent(*args) gateway.create_intent(*args) end def confirm_intent(*args) gateway.confirm_intent(*args) end def purchase(money, creditcard, transaction_options) gateway.purchase(*options_for_purchase_or_auth(money, creditcard, transaction_options)) end def authorize(money, creditcard, transaction_options) gateway.authorize(*options_for_purchase_or_auth(money, creditcard, transaction_options)) end def capture(money, response_code, transaction_options) gateway.capture(money, response_code, transaction_options) end def credit(money, _creditcard, response_code, _transaction_options) gateway.refund(money, response_code, {}) end def void(response_code, _creditcard, _transaction_options) gateway.void(response_code, {}) end def cancel(response_code) gateway.void(response_code, {}) end def create_profile(payment) return unless payment.source.gateway_customer_profile_id.nil? options = { email: payment.order.email, login: preferred_secret_key, }.merge! address_for(payment) source = update_source!(payment.source) if source.number.blank? && source.gateway_payment_profile_id.present? if v3_intents? creditcard = ActiveMerchant::Billing::StripeGateway::StripePaymentToken.new('id' => source.gateway_payment_profile_id) else creditcard = source.gateway_payment_profile_id end else creditcard = source end response = gateway.store(creditcard, options) if response.success? if v3_intents? payment.source.update!( cc_type: payment.source.cc_type, gateway_customer_profile_id: response.params['customer'], gateway_payment_profile_id: response.params['id'] ) else payment.source.update!( cc_type: payment.source.cc_type, gateway_customer_profile_id: response.params['id'], gateway_payment_profile_id: response.params['default_source'] || response.params['default_card'] ) end else payment.send(:gateway_error, response.message) end end private # In this gateway, what we call 'secret_key' is the 'login' def options options = super options.merge(login: preferred_secret_key) end def options_for_purchase_or_auth(money, creditcard, transaction_options) options = {} options[:description] = "Spree Order ID: #{transaction_options[:order_id]}" options[:currency] = transaction_options[:currency] if customer = creditcard.gateway_customer_profile_id options[:customer] = customer end if token_or_card_id = creditcard.gateway_payment_profile_id # The Stripe ActiveMerchant gateway supports passing the token directly as the creditcard parameter # The Stripe ActiveMerchant gateway supports passing the customer_id and credit_card id # https://github.com/Shopify/active_merchant/issues/770 creditcard = token_or_card_id end [money, creditcard, options] end def address_for(payment) {}.tap do |options| if address = payment.order.bill_address options[:address] = { address1: address.address1, address2: address.address2, city: address.city, zip: address.zipcode } if country = address.country options[:address][:country] = country.name end if state = address.state options[:address].merge!(state: state.name) end end end end def update_source!(source) source.cc_type = CARD_TYPE_MAPPING[source.cc_type] if CARD_TYPE_MAPPING.include?(source.cc_type) source end end end end