class Docdata::Response
Object
representing a “response” with attributes provided by Docdata
@example
:create_success=>{ :success=>"Operation successful.", :key=>"A7B623A3A7DB5949316F82049450C3F3" }
Attributes
@return [Integer] the captured amount in cents
@return [String] Currency (“EUR”, “GBP”, “USD”, etc.)
@return [String] Payment
key for future correspondence about this transaction
@return [String] Response
message from DocData
@return [Boolean]
@return [Boolean]
@return [Docdata::Payment] object
@return [Hash] The parsed report node of the reponse-xml
@return [String] the status of this response (capture response)
@return [Boolean] true/false, depending of the API response
@return [Boolean] true/false, depending of the API response
@return [String] the return URL
@return [String] The raw XML returned by the API
Public Class Methods
Initializer to transform a Hash
into an Response
object
@param [Hash] args
# File lib/docdata/response.rb, line 53 def initialize(args=nil) @report = {} return if args.nil? args.each do |k,v| instance_variable_set("@#{k}", v) unless v.nil? end end
Parses the returned response hash and turns it into a new Docdata::Response
object
@param [String] method_name (name of the method: create, start, cancel, etc.) @param [Hash] response
# File lib/docdata/response.rb, line 90 def self.parse(method_name, response) body, xml = self.response_body(response) if body["#{method_name}_response".to_sym] && body["#{method_name}_response".to_sym]["#{method_name}_error".to_sym] raise DocdataError.new(response), body["#{method_name}_response".to_sym]["#{method_name}_error".to_sym][:error] else m = body["#{method_name}_response".to_sym]["#{method_name}_success".to_sym] r = self.new(key: m[:key], message: m[:success], success: true) r.xml = xml #save the raw xml # puts m[:report] if m[:report] r.report = m[:report] end r.set_attributes return r end end
@return [Hash] the body of the response. In the test environment, this uses plain XML files, in normal use, it uses a ‘Savon::Response`
# File lib/docdata/response.rb, line 109 def self.response_body(response) if response.is_a?(File) parser = Nori.new(:convert_tags_to => lambda { |tag| tag.snakecase.to_sym }) xml = response.read body = parser.parse(xml).first.last.first.last else body = response.body.to_hash xml = response.xml end return body, xml end
Private Class Methods
Sometimes a single response has multiple payment nodes. When a payment fails first and succeeds later, for example. In that case, always use the newest (== highest id) node. UPDATE (2/3/2015) this is not always the case: a payment can receive a ‘canceled’ payment later on with a higher ID, but the order is still paid.
# File lib/docdata/response.rb, line 264 def self.payment_node(hash) if hash[:payment] && hash[:payment].is_a?(Hash) hash[:payment] elsif hash[:payment] && hash[:payment].is_a?(Array) # use the node with the highest ID, for it is the newest list = hash[:payment].sort_by { |k| k[:id] } return list.last else false end end
Public Instance Methods
- Integer
-
the amount to set, calculated from the multiple payment nodes
# File lib/docdata/response.rb, line 63 def amount_to_set # if (report && Response.payment_node(report) && Response.payment_node(report)[:authorization] && Response.payment_node(report)[:authorization][:amount].present?) if (report && Response.payment_node(report) && Response.payment_node(report)[:authorization] && Response.payment_node(report)[:authorization][:amount].present?) if canceled return Response.payment_node(report)[:authorization][:amount].to_i else return total_acquirer_pending + total_acquirer_approved end else return false end end
@return [Boolean]
# File lib/docdata/response.rb, line 219 def canceled (payment_status && payment_status == "CANCELED") || (capture_status && capture_status == "CANCELED") end
@return [String] the status of the capture, if exists
# File lib/docdata/response.rb, line 226 def capture_status if report && Response.payment_node(report) && Response.payment_node(report)[:authorization] && Response.payment_node(report)[:authorization][:capture] Response.payment_node(report)[:authorization][:capture][:status] else nil end end
@return [String] the currency if this transaction
# File lib/docdata/response.rb, line 236 def currency_to_set if status_xml && status_xml.xpath("//amount").any? status_xml.xpath("//amount").first.attributes["currency"].value else nil end end
@return [Nokogiri::XML::Document] object
# File lib/docdata/response.rb, line 245 def doc # remove returns and whitespaces between tags xml_string = xml.gsub("\n", "").gsub(/>\s+</, "><") # return Nokogiri::XML::Document @doc ||= Nokogiri.XML(xml_string) end
@return [Boolean] true/false, depending wether this payment is considered paid. @note Docdata
doesn’t explicitly say ‘paid’ or ‘not paid’, this is a little bit a gray area. There are several approaches to determine if a payment is paid, some slow and safe, other quick and unreliable. The reason for this is that some payment methods have a much longer processing time. For each payment method a different ‘paid’. @note This method is never 100% reliable. If you need to finetune this, please implement your own method, using the available data (total_captured, total_registered, etc.)
from the Docs: Safe route:
The safest route to check whether all payments were made is for the merchants to refer to the “Total captured” amount to see whether this equals the “Total registered amount”. While this may be the safest indicator, the downside is that it can sometimes take a long time for acquirers or shoppers to actually have the money transferred and it can be captured.
Quick route:
Another option is to see whether the sum of “total shopper pending”, “total acquirer pending” and “total acquirer authorized” matches the “total registered sum”. This implies that everyone responsible has indicated that they are going to make the payment and that the merchant is trusting that everyone will indeed make this. While this route will be faster, it does also have the risk that some payments will actually not have been made.
Balanced route:
Depending on the merchant’s situation, it can be a good option to only refer to certain totals. For instance, if the merchant only makes use of credit card payments it could be a good route to only look at “Total acquirer approved”, since this will be rather safe but quicker than looking at the captures.
# File lib/docdata/response.rb, line 185 def is_paid? if payment_method case payment_method # ideal (dutch) when "IDEAL" (total_registered == total_captured) ## && (capture_status == "CAPTURED") # creditcard when "MASTERCARD", "VISA", "AMEX" (total_registered == total_acquirer_approved) # sofort überweisung (german) when "SOFORT_UEBERWEISUNG" (total_registered == total_acquirer_approved) # podium giftcard (dutch) when "PODIUM_GIFTCARD" (total_registered == total_captured) # fallback: if total_registered equals total_caputured, # we can assume that this order is paid. No 100% guarantee. else total_registered == total_acquirer_approved end else false end end
@return [String] the payment method of this transaction
# File lib/docdata/response.rb, line 129 def payment_method begin if report && Response.payment_node(report).present? && Response.payment_node(report)[:payment_method].present? Response.payment_node(report)[:payment_method].to_s else nil end rescue nil end end
@return [String] the status string provided by the API. One of [AUTHORIZED, CANCELED]
# File lib/docdata/response.rb, line 143 def payment_status if report && Response.payment_node(report) && Response.payment_node(report)[:authorization] Response.payment_node(report)[:authorization][:status] else nil end end
@return [String] the PID of the transaction
# File lib/docdata/response.rb, line 152 def pid if report && Response.payment_node(report) && Response.payment_node(report)[:id] Response.payment_node(report)[:id] else nil end end
Set the attributes based on the API response
# File lib/docdata/response.rb, line 77 def set_attributes self.paid = is_paid? self.amount = amount_to_set if amount_to_set self.status = capture_status if capture_status self.currency = currency_to_set end
@return [Nokogiri::XML::Document] object, containing only the status section @note This is a fix for Nokogiri’s trouble finding xpath elements after ‘xlmns’ attribute in a node.
# File lib/docdata/response.rb, line 254 def status_xml @status_xml ||= Nokogiri.XML(doc.xpath("//S:Body").first.children.first.children.first.to_xml) end