pero-ict-solutions/spree_mollie

View on GitHub
app/services/mollie_payment_service.rb

Summary

Maintainability
B
4 hrs
Test Coverage
require 'mollie'

class MolliePaymentService
  class StatusObject
    attr_reader :transaction_id, :redirect_url, :errors, :payment_url, :response_status

    def initialize(response={})
      @response_status = response['status']
      @errors = []
      @transaction_id = response['id']
      @redirect_url = response['redirectUrl']
      @payment_url = response['links']['paymentUrl'] if response['links']
      @mollie_error = false
    end

    def open?
      @response_status == 'open'
    end

    def refunded?
      @response_status == 'refunded'
    end

    def has_error?
      !@errors.empty?
    end

    def mollie_error=(val)
      @mollie_error = val
    end

    def mollie_error?
      @mollie_error
    end

    def add_error(error = '')
      @errors << error
      self
    end
  end

  def initialize(params = {})
    @payment_id = params[:payment_id]
    @order = params[:order]
    @redirect_url = params[:redirect_url]
    @payment_method = params[:payment_method]
    @payment = params[:payment]
    @method = params[:method]
    @issuer = params[:issuer] if @method
  end

  def update_payment_status
    response = payment_status_in_mollie(@payment_id)

    payment = Spree::Payment.find_by_response_code(response['id'])

    unless payment.completed? || payment.failed?
      case response['status']
        when 'cancelled', 'expired'
          payment.failure! unless payment.failed?
        when 'pending'
          payment.pend! unless payment.pending?
        when 'paid', 'paidout'
          payment.complete! unless payment.completed?
          payment.order.next! unless payment.order.complete?
      end
    end if payment

  end

  def create_payment
    amount = @order.total
    description = "Order #{@order.number}"
    response = mollie_client.prepare_payment(amount, description, @redirect_url, {order_id: @order.number}, @method, {issuer: @issuer, locale: "en"})
    status_object = StatusObject.new(response)
    if status_object.open?
      payment = @order.payments.build(
          payment_method_id: @payment_method.id,
          amount: @order.total,
          state: 'checkout',
          response_code: status_object.transaction_id
      )

      unless payment.save
        status_object.add_error(payment.errors.full_messages.join("\n"))
      end

      payment.pend!
    else
      status_object.mollie_error = true
    end

    status_object
  end

  def refund_payment
    response = mollie_client.refund_payment(@payment.transaction_id)

    status = response['error'] ? response['error']['message'] : response['payment']['status']
    status_object = StatusObject.new('status' => status)

    if status_object.refunded?
      default_reason = Spree::RefundReason.find_or_create_by(name: Spree.t(:default_refund_reason, scope: 'mollie'))
      @payment.refunds.create!(transaction_id: response[:id], amount: @payment.amount, reason: default_reason)
      @payment.void!
    else
      status_object.add_error(status_object.response_status)
    end

    status_object
  end

  def payment_methods
    mollie_client.payment_methods['data']
  end

  def issuers
    mollie_client.issuers
  end

  private

    def payment_status_in_mollie(payment_id)
      mollie_client.payment_status(payment_id)
    end

    def mollie_client
      @client ||= begin
        api_key = Spree::PaymentMethod::Mollie.first.get_preference(:api_key)
        Mollie::Client.new(api_key)
      end
    end
end