Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
getfitter-core / lib / core / utils / payments.rb
Size: Mime:
module Core
  module Utils
    # An encapsulation of different payment errors, allowing a JSON output.
    class PaymentError < StandardError
      attr_reader :message
      attr_reader :id
      attr_reader :url
      attr_reader :http_status

      def initialize(message, id, url, http_status)
        @message = message
        @id = id
        @url = url
        @http_status = http_status
      end

      def json_body
        { id: @id, message: @message, url: @url }
      end
    end

    # Payments handling.
    class Payments
      attr_reader :user, :source

      # Create a new Payments object for processing payments.
      #
      # @param [User] user the user object to work with.
      # @param [String/Hash] source a 'Stripe Token' or a Hash of card details.
      # @return [Payments] a payment object that can be used to process
      #   payments for a given user.
      def initialize(user, source = nil)
        @user = user
        @source = source

        # configure Stripe
        Stripe.api_key = Core.configuration.stripe_token
      end

      # Store a user's details.
      #
      # This is intended to be used when a payment isn't processed, but a
      # customer should be created.
      #
      # @return [String] a Stripe customer identifier.
      def store_details
        update_or_create_customer_id(@user)
      end

      # Process a payment.
      #
      # @param [Integer] amount value to charge (in pence).
      # @param [String] description explanation which shows up on the user's
      #   receipt.
      # @return [Stripe::Charge] a Stripe charge object representing the new
      #   charge.
      #
      # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
      def process(amount, description)
        begin
          customer_id = update_or_create_customer_id(@user)

          charge = Stripe::Charge.create(
            amount: amount,
            description: description,
            currency: 'gbp',
            customer: customer_id
          )
        rescue Stripe::CardError => e
          body = e.json_body
          err = body[:error]

          raise PaymentError.new(err[:message], 'card_error', '', 400)
        rescue Stripe::InvalidRequestError => e
          body = e.json_body
          err = body[:error]

          raise PaymentError.new(err[:message], 'invalid_request', '', 400)
        rescue Stripe::AuthenticationError => e
          body = e.json_body
          err = body[:error]

          raise PaymentError.new(err[:message], 'authentication_error', '', 500)
        rescue Stripe::StripeError => e
          body = e.json_body
          err = body[:error]

          raise PaymentError.new(err[:message], 'stripe_error', '', 500)
        rescue  => e
          body = e.json_body
          err = body[:error]

          raise PaymentError.new(err[:message], 'unknown_error', '', 500)
        end

        charge
      end
      # rubocop:enable Metrics/AbcSize, Metrics/MethodLength

      protected

      def update_or_create_customer_id(user)
        # if we've already got a customer and the source exists
        if user.stripe_customer_id?
          replace_customer_default_source(user, @source) if @source
        else
          create_new_customer(user, @source)
        end

        user.stripe_customer_id
      end

      def create_new_customer(user, source)
        customer = Stripe::Customer.create(
          card: source, description: user.id, email: user.email
        )

        user.stripe_customer_id = customer.id
        user.save

        customer
      end

      def replace_customer_default_source(user, new_source)
        customer = Stripe::Customer.retrieve(user.stripe_customer_id)

        # remove the current, add the new
        customer.sources.first.delete
        customer.save
        new_source[:object] = card if new_source.is_a? Hash

        customer.sources.create(source: new_source)
        customer.save

        customer
      end
    end
  end
end