class SpookAndPay::Providers::Spreedly

Constants

CARD_FIELDS

A mapping from the names used by Spreedly to the names SpookAndPay uses internally.

ERRORS

Maps the error types from Spreedly's to the names used internally.

FORM_FIELD_NAMES

The map of generic field names to what is specifically required by Spreedly.

Attributes

currency_code[R]

Currency code. Spreedly defaults to USD, but we default to AUD.

@attr_reader String

gateway_token[R]

The which refers to a specific gateway.

@attr_reader String

spreedly[R]

An instance of the spreedly spreedly.

@attr_reader Spreedly::Environment

Public Class Methods

new(env, config) click to toggle source

Generate a new instance of the Spreedly provider.

@param Hash config @option config String :environment_key @option config String :access_secret @option config String :gateway_token @option config String :currency_code

Calls superclass method SpookAndPay::Providers::Base::new
# File lib/spook_and_pay/providers/spreedly.rb, line 36
def initialize(env, config)
  @gateway_token = config[:gateway_token]
  @currency_code = config[:currency_code] || 'AUD'
  @spreedly = ::Spreedly::Environment.new(
    config[:environment_key],
    config[:access_secret],
    :currency_code => currency_code
  )

  super(env, config)
end

Public Instance Methods

authorize_via_credit_card(id, amount) click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 155
def authorize_via_credit_card(id, amount)
  result = spreedly.authorize_on_gateway(gateway_token, credit_card_id(id), (amount.to_f * 100).to_i)
  coerce_result(result)
end
capture_transaction(id) click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 102
def capture_transaction(id)
  result = spreedly.capture_transaction(transaction_id(id))
  coerce_result(result)
end
confirm_payment_submission(query_string, opts) click to toggle source

Confirms the submission of payment details to Spreedly Core.

@param String query_string @param Hash opts @option opts [String, Numeric] :amount @option opts [:purchase, :authorize, :store] :execute @return SpookAndPay::Result

# File lib/spook_and_pay/providers/spreedly.rb, line 72
def confirm_payment_submission(query_string, opts)
  token = Rack::Utils.parse_nested_query(query_string)["token"]
  card = credit_card(token)

  if card.valid?
    case opts[:execute]
    when :authorize then card.authorize!(opts[:amount])
    when :purchase  then card.purchase!(opts[:amount])
    else                 SpookAndPay::Result.new(true, nil, :credit_card => card)
    end
  else
    SpookAndPay::Result.new(false, nil, :credit_card => card, :errors => extract_card_errors(card.raw))
  end
end
credit_card(id) click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 87
def credit_card(id)
  result = spreedly.find_payment_method(id)
  coerce_credit_card(result)
end
credit_card_from_transaction(id) click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 92
def credit_card_from_transaction(id)
  result = spreedly.find_transaction(transaction_id(id))
  coerce_credit_card(result.payment_method)
end
delete_credit_card(id) click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 165
def delete_credit_card(id)
  result = spreedly.redact_payment_method(credit_card_id(id))
  coerce_result(result)
end
partially_refund_transaction(id, amount) click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 112
def partially_refund_transaction(id, amount)
  result = spreedly.refund_transaction(transaction_id(id), :amount => (amount.to_f * 100).to_i)
  coerce_result(result)
end
prepare_payment_submission(redirect_url, opts = {}) click to toggle source

@param String redirect_url @param Hash opts @return Hash

# File lib/spook_and_pay/providers/spreedly.rb, line 51
def prepare_payment_submission(redirect_url, opts = {})
  config = {
    :url            => spreedly.transparent_redirect_form_action,
    :field_names    => self.class::FORM_FIELD_NAMES,
    :hidden_fields  => {:redirect_url => redirect_url, :environment_key => spreedly.key}
  }

  if opts[:token]
    config[:hidden_fields][:payment_method_token] = opts[:token]
  end

  config
end
purchase_via_credit_card(id, amount) click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 160
def purchase_via_credit_card(id, amount)
  result = spreedly.purchase_on_gateway(gateway_token, credit_card_id(id), (amount.to_f * 100).to_i)
  coerce_result(result)
end
refund_transaction(id) click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 107
def refund_transaction(id)
  result = spreedly.refund_transaction(transaction_id(id))
  coerce_result(result)
end
retain_credit_card(id) click to toggle source

Calls the retain action on the provider's vault.

@param [SpookAndPay::CreditCard, Integer, String] id @return SpookAndPay::Result @api private

# File lib/spook_and_pay/providers/spreedly.rb, line 175
def retain_credit_card(id)
  result = spreedly.retain_payment_method(credit_card_id(id))
  coerce_result(result)
end
supports_authorize?() click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 145
def supports_authorize?
  check_support_for('supports_authorize')
end
supports_capture?() click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 141
def supports_capture?
  check_support_for('supports_capture')
end
supports_credit?() click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 137
def supports_credit?
  false
end
supports_delete?() click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 149
def supports_delete?
  # This does not check the gateway, since the redaction is specific to
  # Spreedly's store, not the gateway.
  true
end
supports_purchase?() click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 129
def supports_purchase?
  check_support_for('supports_purchase')
end
supports_void?() click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 133
def supports_void?
  check_support_for('supports_void')
end
transaction(id) click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 97
def transaction(id)
  result = spreedly.find_transaction(id)
  coerce_transaction(result)
end
void_transaction(id) click to toggle source
# File lib/spook_and_pay/providers/spreedly.rb, line 117
def void_transaction(id)
  result = spreedly.void_transaction(transaction_id(id))
  coerce_result(result)
end

Private Instance Methods

check_support_for(path) click to toggle source

Retrieves the gateway from Spreedly and then inspects the response to see what features it supports. This is a helper for the supports_*? predicates.

@param String path @return [true, false]

# File lib/spook_and_pay/providers/spreedly.rb, line 188
def check_support_for(path)
  gateway = spreedly.find_gateway(gateway_token)
  node = Nokogiri::XML::DocumentFragment.parse(gateway.characteristics)
  node.xpath(".//#{path}").inner_html.strip == 'true'
end
coerce_credit_card(card) click to toggle source

Takes the response generated by the Spreedly lib and coerces it into a SpookAndPay::CreditCard

@param Spreedly::CreditCard @return SpookAndPay::CreditCard

# File lib/spook_and_pay/providers/spreedly.rb, line 280
def coerce_credit_card(card)
  expired = !card.errors.select {|e| e[:key] == 'errors.expired'}.empty?

  fields = {
    :card_type        => card.card_type,
    :number           => card.number,
    :name             => card.full_name,
    :expiration_month => card.month,
    :expiration_year  => card.year,
    :valid            => card.valid?,
    :expired          => expired
  }

  SpookAndPay::CreditCard.new(self, card.token, fields, card)
end
coerce_result(result) click to toggle source

Takes the result of running a transaction against a Spreedly gateway and coerces it into a SpookAndPay::Result

@param Spreedly::Transaction result @return SpookAndPay::Result

# File lib/spook_and_pay/providers/spreedly.rb, line 199
def coerce_result(result)
  opts = {
    :transaction  => coerce_transaction(result),
    :errors       => extract_transaction_errors(result)
  }

  if result.respond_to?(:payment_method)
    opts[:credit_card] = coerce_credit_card(result.payment_method)
  end

  SpookAndPay::Result.new(result.succeeded, result, opts)
end
coerce_transaction(transaction) click to toggle source

Takes a transaction generated by the Spreedly lib and coerces it into a SpookAndPay::Transaction

@param Spreedly::Transaction transaction @return SpookAndPay::Transaction @todo extract created_at and status from the transaction.response

# File lib/spook_and_pay/providers/spreedly.rb, line 302
def coerce_transaction(transaction)
  fields = {}

  fields[:type], status = case transaction
  when ::Spreedly::Authorization        then [:authorize, :authorized]
  when ::Spreedly::Purchase             then [:purchase, :settled]
  when ::Spreedly::Capture              then [:capture, :settled]
  when ::Spreedly::Refund               then [:credit, :refunded]
  when ::Spreedly::Void                 then [:void, :voided]
  when ::Spreedly::RetainPaymentMethod  then [:retain, :retained]
  end

  if transaction.respond_to?(:amount)
    fields[:amount] = transaction.amount
  end

  SpookAndPay::Transaction.new(self, transaction.token, status, transaction, fields)
end
extract_card_errors(result) click to toggle source

Extracts/coerces errors from a Spreedly response into SubmissionError instances.

@param Spreedly::CreditCard result @return Array<SpookAndPay::SubmissionError> @todo If the Spreedly API behaves later, the check for first/last

name might not be needed anymore.
# File lib/spook_and_pay/providers/spreedly.rb, line 236
def extract_card_errors(result)
  # This gnarly bit of code transforms errors on the first_name or
  # last_name attributes into an error on full_name. This is because
  # Spreedly accepts input for full_name, but propogates errors to the
  # separate attributes.
  errors = result.errors.map do |e|
    case e[:attribute]
    when 'first_name' then e.merge(:attribute => "full_name")
    when 'last_name' then nil
    else e
    end
  end.compact

  errors.map do |e|
    name = CARD_FIELDS[e[:attribute]]
    error = ERRORS[e[:key]]

    if name and error
      SubmissionError.new(:credit_card, error, name, e)
    else
      SubmissionError.new(:unknown, :unknown, :unknown, e)
    end
  end
end
extract_transaction_errors(result) click to toggle source

Extracts/coerces errors from a Spreedly transaction into SubmissionError instances.

@param Spreedly::Transaction result @return Array<SpookAndPay::SubmissionError> @todo Expand this to handle different errors

# File lib/spook_and_pay/providers/spreedly.rb, line 267
def extract_transaction_errors(result)
  if result.succeeded
    []
  else
    [SubmissionError.new(:transaction, :card_declined, :transaction, result.message)]
  end
end