dinero_mail_ipn 0.0.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
1
  pkg/*
2
2
  *.gem
3
3
  .bundle
4
+ .yardoc
5
+ doc
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.2-p180@dm --create
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ -t default --markup markdown --no-private
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
- source "http://rubygems.org"
1
+ source :rubygems
2
2
 
3
- # Specify your gem's dependencies in dinero_mail_ipn.gemspec
4
3
  gemspec
data/Gemfile.lock CHANGED
@@ -1,19 +1,30 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dinero_mail_ipn (0.0.1)
5
- httparty (~> 0.7.6)
4
+ dinero_mail_ipn (1.0.1)
5
+ httparty (~> 0.8.1)
6
+ nokogiri (~> 1.5.0)
6
7
 
7
8
  GEM
8
9
  remote: http://rubygems.org/
9
10
  specs:
10
- crack (0.1.8)
11
- httparty (0.7.6)
12
- crack (= 0.1.8)
11
+ fakeweb (1.3.0)
12
+ httparty (0.8.1)
13
+ multi_json
14
+ multi_xml
15
+ multi_json (1.0.4)
16
+ multi_xml (0.4.1)
17
+ nokogiri (1.5.0)
18
+ redcarpet (2.0.1)
19
+ shoulda (2.11.3)
20
+ yard (0.7.4)
13
21
 
14
22
  PLATFORMS
15
23
  ruby
16
24
 
17
25
  DEPENDENCIES
18
26
  dinero_mail_ipn!
19
- httparty (~> 0.7.6)
27
+ fakeweb (~> 1.3.0)
28
+ redcarpet (~> 2.0.1)
29
+ shoulda (~> 2.11.3)
30
+ yard (~> 0.7.4)
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011
1
+ Copyright (c) 2012
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
17
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
19
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,74 @@
1
+ # Dinero Mail IPN Gem
2
+
3
+ Ruby gem para consumir los métodos de IPN 1 y IPN 2.
4
+
5
+ ## Instalación
6
+
7
+ gem install dinero_mail_ipn
8
+
9
+ ### Bundler
10
+
11
+ gem 'dinero_mail_ipn'
12
+
13
+ ## Uso
14
+
15
+ require 'dinero_mail_ipn'
16
+
17
+ ## IPN v1
18
+
19
+ Consulta de pagos en períodos de una semana.
20
+
21
+ client = DineroMailIpn::Client.new(:email => 'EMAILPRUEBA@gmail.com', :account => '17128254', :pin => 'AYCN7IXDTM')
22
+ response = client.consulta_pago(Date.today - 7, Date.today)
23
+ # #<DineroMailIpn::ConsultaPagoResponse:0x5f9600 @hash={"Report"=>{"Email"=>"EMAILPRUEBA@gmail.com",
24
+ # "Acount"=>"17128254", "Pin"=>nil, "StartDate"=>"20110603", "EndDate"=>"20110703", "XML"=>"1",
25
+ # "State"=>"1", "Pays"=>nil, "Collections"=>nil, "Tickets"=>nil, "Receptions"=>nil, "Retreats"=>nil,
26
+ # "Credits"=>nil, "Debits"=>nil}}>
27
+ response.ok?
28
+ # true
29
+ response.state_description
30
+ # "La consulta se realizó correctamente"
31
+ payment = response.payments.first
32
+ payment["Trx_Payment"]
33
+ # "108.98"
34
+
35
+ ### IPN v2
36
+
37
+ Consulta de reportes para transacciones específicas.
38
+
39
+ c = DineroMailIpn::Client.new(:account => '09813581', :password => 'mipassword')
40
+ reporter = c.consulta_transacciones(["31548", "23123"])
41
+ report = reporter.reports.first
42
+ report.id
43
+ # 1
44
+ report.amount
45
+ # 2999
46
+ report.valid?
47
+ # true
48
+
49
+ ## Documentación
50
+
51
+ Ver: [http://rubydoc.info/github/etagwerker/dinero\_mail\_ipn/master/frames](http://rubydoc.info/github/etagwerker/dinero_mail_ipn/master/frames)
52
+
53
+ Si falta algo, por favor reportarlo como issue.
54
+
55
+ ## Roadmap
56
+
57
+ * Agregar más tests que testeen las posibles respuestas de Dinero Mail IPN
58
+ * Agregar más documentación y ejemplos
59
+
60
+ ## Patches/Pull Requests
61
+
62
+ Fork the project.
63
+
64
+ Make your feature addition or bug fix.
65
+
66
+ Add tests for it. This is important so I don't break it in a future version unintentionally.
67
+
68
+ Commit, do not mess with Rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
69
+
70
+ Send me a pull request. Bonus points for topic branches.
71
+
72
+ ## Licencia
73
+
74
+ Dinero Mail IPN is released under the MIT license.
data/Rakefile CHANGED
@@ -1,2 +1,19 @@
1
+ require 'rake'
2
+ require "shoulda/tasks"
3
+ require "rake/testtask"
1
4
  require 'bundler'
5
+
2
6
  Bundler::GemHelper.install_tasks
7
+
8
+ Rake::TestTask.new(:test) do |test|
9
+ test.ruby_opts = ["-rubygems"] if defined? Gem
10
+ test.libs << "lib" << "test"
11
+ test.pattern = "test/**/test_*.rb"
12
+ end
13
+
14
+ task :default => :test
15
+
16
+ desc "Open an irb session preloaded with this library"
17
+ task :console do
18
+ sh "irb -rubygems -I lib -r dinero_mail_ipn.rb"
19
+ end
@@ -14,12 +14,19 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.rubyforge_project = "dinero_mail_ipn"
16
16
 
17
- s.add_dependency 'httparty', '~> 0.7.6'
18
- s.add_dependency 'nokogiri', '~> 1.4.4'
19
-
17
+ s.add_dependency 'httparty', '~> 0.8.1'
18
+ s.add_dependency 'nokogiri', '~> 1.5.0'
20
19
 
21
20
  s.files = `git ls-files`.split("\n")
22
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.test_files = [
22
+ "test/helper.rb",
23
+ "test/test_dinero_mail_ipn.rb"
24
+ ]
23
25
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
26
  s.require_paths = ["lib"]
27
+
28
+ s.add_development_dependency 'shoulda', '~> 2.11.3'
29
+ s.add_development_dependency 'fakeweb', '~> 1.3.0'
30
+ s.add_development_dependency 'yard', '~> 0.7.4'
31
+ s.add_development_dependency 'redcarpet', '~> 2.0.1'
25
32
  end
@@ -1,38 +1,94 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'httparty'
2
4
  require 'nokogiri'
3
5
 
6
+ Dir[File.join(File.dirname(__FILE__),'dinero_mail_ipn', '*')].each {|file| require file }
7
+
4
8
  module DineroMailIpn
9
+
10
+ # # Cliente para Dinero Mail API (v1 y v2)
11
+ #
12
+ # Cliente para consumir los métodos de la API IPN v1 y v2.
13
+ #
14
+ # ## Cliente para IPN v1
15
+ #
16
+ # DineroMailIpn::Client.new(:email => 'EMAILPRUEBA@gmail.com', :account => '17128254', :pin => 'AYCN7IXDTM')
17
+ #
18
+ # ## Cliente para IPN v2
19
+ #
20
+ # DineroMailIpn::Client.new(:email => 'EMAILPRUEBA@gmail.com',
21
+ # :account => '17128254', :pin => 'AYCN7IXDTM', :password => 'ipnv2password')
22
+ #
23
+ # ## Paises
24
+ #
25
+ # Por default el Cliente se crea para una cuenta de Argentina. Para consultar la API de una cuenta en otro país:
26
+ #
27
+ # DineroMailIpn::Client.new(:email => 'EMAILPRUEBA@gmail.com',
28
+ # :account => '17128254', :pin => 'AYCN7IXDTM', :pais => 'brasil')
29
+ #
30
+ # ### Posibilidades
31
+ #
32
+ # * 'argentina'
33
+ # * 'chile'
34
+ # * 'brasil'
35
+ # * 'mexico'
36
+ #
37
+ # @see Client#initialize
5
38
  class Client
6
39
  include HTTParty
7
40
  format :xml
8
41
  attr_reader :email, :account, :pin, :pais, :password
9
42
 
10
- DEFAULT_PAIS = 'argentina'
11
-
43
+ # País por default para consultas
44
+ DEFAULT_COUNTRY = 'argentina'
45
+
46
+ # @param [Hash] opts opciones para crear la instancia
47
+ # @option opts [String] :email Email de la cuenta de Dinero Mail
48
+ # @option opts [String] :account Número de cuenta de Dinero Mail (sin /)
49
+ # @option opts [String] :pin PIN de la cuenta de Dinero Mail
50
+ # @option opts [String] :password Contraseña para consultas de IPN v2
51
+ # @option opts [String] :pais Pais de la cuenta. Por default: 'argentina'
12
52
  def initialize(opts)
13
53
  @email = opts[:email]
14
54
  @account = opts[:account]
15
55
  @pin = opts[:pin]
16
56
  @password = opts[:password]
17
- @pais = opts[:pais] || DEFAULT_PAIS
57
+ @pais = opts[:pais] || DEFAULT_COUNTRY
18
58
  end
19
59
 
20
- # devuelve una response
60
+ # Devuelve una {ConsultaPagoResponse}.
61
+ #
62
+ # @param [Date] Comienzo del período
63
+ # @param [Date] Fin del período
64
+ # @return [ConsultaPagoResponse] Respuesta
21
65
  def consulta_pago(start_date, end_date)
22
66
  params = default_params.merge({:StartDate => format_date(start_date), :EndDate => format_date(end_date)})
23
- self.class.get("https://#{@pais}.dineromail.com/vender/ConsultaPago.asp", :query => params).parsed_response
67
+ response = self.class.get("https://#{@pais}.dineromail.com/vender/ConsultaPago.asp", :query => params)
68
+ ConsultaPagoResponse.new(
69
+ response.parsed_response)
24
70
  end
25
71
 
26
- def default_params
27
- {:XML => 1, :Acount => @account, :Pin => @pin, :Email => @email}
28
- end
29
-
30
- # formatea una date a 20110201
72
+ # Devuelve un {String} con formato YYYYMMDD. (Por ej.: "20110201")
73
+ #
74
+ # @param [Date] Una fecha
75
+ # @return [String] Fecha en formato Dinero Mail
31
76
  def format_date(a_date)
32
77
  a_date.strftime("%Y%m%d")
33
78
  end
34
79
 
35
- def consulta_transacciones(*transacciones)
80
+ # Devuelve un objeto [Reporter] con un array de objetos tipo [Report].
81
+ #
82
+ # Los objetos de tipo [Report] están asociados a los ID de transacciones.
83
+ #
84
+ # Ejemplo:
85
+ #
86
+ # client.consulta_transacciones([1,2])
87
+ #
88
+ # @param [Array] Array de transacciones
89
+ #
90
+ # @return [Reporter] Con todos los [Report] correspondientes
91
+ def consulta_transacciones(transacciones = [])
36
92
  xml_builder = Nokogiri::XML::Builder.new do |xml|
37
93
  xml.REPORTE {
38
94
  xml.NROCTA @account
@@ -41,8 +97,8 @@ module DineroMailIpn
41
97
  xml.CLAVE @password
42
98
  xml.TIPO 1
43
99
  xml.OPERACIONES {
44
- transacciones.each do |transaccion|
45
- xml.ID transaccion
100
+ transacciones.each do |transaction_id|
101
+ xml.ID transaction_id
46
102
  end
47
103
  }
48
104
  }
@@ -53,11 +109,16 @@ module DineroMailIpn
53
109
  body.sub!("<?xml version=\"1.0\"?>", "")
54
110
  body.gsub!(/\s/, '')
55
111
 
56
- self.class.post("http://#{@pais}.dineromail.com/Vender/Consulta_IPN.asp", :body => "DATA=#{body}",
57
- :headers => {"Content-type" => "application/x-www-form-urlencoded", "Content-length" => "#{body.length}" }).parsed_response
112
+ response = self.class.post("http://#{@pais}.dineromail.com/Vender/Consulta_IPN.asp", :body => "DATA=#{body}",
113
+ :headers => {"Content-type" => "application/x-www-form-urlencoded", "Content-length" => "#{body.length}" }).response.body
114
+ DineroMailIpn::Reporter.new(response)
115
+ end
116
+
117
+ private
118
+
119
+ def default_params
120
+ {:XML => 1, :Acount => @account, :Pin => @pin, :Email => @email}
58
121
  end
59
122
  end
60
123
 
61
124
  end
62
-
63
- # DineroMail::Ipn.consulta_ipn()
@@ -0,0 +1,105 @@
1
+ # encoding: utf-8
2
+ module DineroMailIpn
3
+
4
+ # # Respuesta a Consulta IPN v1
5
+ #
6
+ # Clase que agrega información de valor para entender la respuesta de Dinero Mail IPN v1.
7
+ #
8
+ # Para más información ver:
9
+ # [https://github.com/etagwerker/dinero_mail_ipn/blob/master/resources/IPN_es.pdf](https://github.com/etagwerker/dinero_mail_ipn/blob/master/resources/IPN_es.pdf)
10
+ #
11
+ # ## Ejemplos:
12
+ #
13
+ # response = ConsultaPagoResponse.new({"Report"=>{"Email"=>"ernesto@ombushop.com", "Acount"=>"17128254",
14
+ # "Pin"=>nil, "StartDate"=>"20110603", "EndDate"=>"20110703", "XML"=>"1",
15
+ # "State"=>"1", "Pays"=>nil, "Collections"=>nil, "Tickets"=>nil, "Receptions"=>nil,
16
+ # "Retreats"=>nil, "Credits"=>nil, "Debits"=>nil}})
17
+ # response.state
18
+ # # 1
19
+ # response.ok?
20
+ # # true
21
+ # response.state_description
22
+ # # 'La consulta se realizó correctamente'
23
+ #
24
+ # ## Nota
25
+ #
26
+ # Por ahora las collections de la consulta solo se pueden consultar por un hash (response.hash)
27
+ #
28
+ # ## TODO
29
+ #
30
+ # Agregar más métodos de 'negocio' que tengan métodos útiles.
31
+ class ConsultaPagoResponse
32
+
33
+ attr_accessor :hash
34
+
35
+ STATE_DESCRIPTIONS = {0 => 'Los parámetros no respetan el formato requerido', 1 => 'La consulta se realizó correctamente', 2 => 'Los valores son incorrectos para realizar la consulta'}
36
+
37
+ # Crea una ConsultaPagoResponse usando un hash en base a la respuesta de Dinero Mail.
38
+ #
39
+ # Por ejemplo:
40
+ #
41
+ # ConsultaPagoResponse.new({"Report"=>{"Email"=>"ernesto@ombushop.com", "Acount"=>"17128254",
42
+ # "Pin"=>nil, "StartDate"=>"20110603", "EndDate"=>"20110703", "XML"=>"1",
43
+ # "State"=>"1", "Pays"=>nil, "Collections"=>nil, "Tickets"=>nil, "Receptions"=>nil,
44
+ # "Retreats"=>nil, "Credits"=>nil, "Debits"=>nil}})
45
+ def initialize(_hash)
46
+ self.hash = _hash
47
+ end
48
+
49
+ # Devuelve true si la respuesta no devolvió error
50
+ #
51
+ # @return [Boolean]
52
+ def ok?
53
+ state == "1"
54
+ end
55
+
56
+ # Devuelve true si la respuesta devolvió error
57
+ #
58
+ # @return [Boolean]
59
+ def error?
60
+ !ok?
61
+ end
62
+
63
+ # Devuelve un {Array} de objetos tipo {Payment}
64
+ #
65
+ # @return [Array] Objetos de tipo {Payment}
66
+ def payments
67
+ return @payments if @payments
68
+
69
+ @payments = []
70
+ begin
71
+ pays = self.hash["Report"]["Pays"]
72
+
73
+ if pays
74
+ pays.each do |key, payment_hash|
75
+ @payments << Payment.new(payment_hash)
76
+ end
77
+ end
78
+ rescue Exception => e
79
+ raise MalformedResponseError.new("Error procesando pagos: #{e.message}")
80
+ end
81
+ @payments
82
+ end
83
+
84
+ # Devuelve el código de estado de la respuesta.
85
+ #
86
+ # Posibilidades: "0", "1" o "2".
87
+ #
88
+ # @raise [MalformedResponseError] Si la respuesta no tiene /Report/State
89
+ def state
90
+ begin
91
+ self.hash["Report"]["State"]
92
+ rescue Exception => e
93
+ raise MalformedResponseError.new("No se encontró /Report/State en el XML de respuesta")
94
+ end
95
+ end
96
+
97
+ # Devuelve la descripción del código de respuesta
98
+ # según la documentación
99
+ def state_description
100
+ STATE_DESCRIPTIONS[state.to_i]
101
+ end
102
+
103
+ end
104
+
105
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ module DineroMailIpn
4
+
5
+ # # Item (IPN v1)
6
+ #
7
+ # Cada {Payment} puede estar asociado con uno o muchos items.
8
+ #
9
+ # Para acceder a estos objetos:
10
+ #
11
+ # payment.items
12
+ #
13
+ # Para acceder a los atributos de estos objetos:
14
+ #
15
+ # item["Item_Code"]
16
+ # # "SKU123"
17
+ # item["Item_Payment"]
18
+ # # "68.00"
19
+ #
20
+ # @see Payment#items
21
+ class Item
22
+
23
+ # @private
24
+ def initialize(hash)
25
+ @hash = hash
26
+ end
27
+
28
+ # Devuelve un {String} o nil
29
+ # @return [String]
30
+ def [](key)
31
+ @hash[key]
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,6 @@
1
+ module DineroMailIpn
2
+
3
+ # La respuesta enviada desde Dinero Mail IPN tiene un formato inesperado.
4
+ class MalformedResponseError < StandardError; end
5
+
6
+ end
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ module DineroMailIpn
4
+
5
+ # # Payment (IPN v1)
6
+ #
7
+ # Cada {ConsultaPagoResponse} puede tener 0 o muchos pagos,
8
+ # dependiendo del período.
9
+ #
10
+ # Un pago puede tener muchos objetos de tipo {Item} describiendo los
11
+ # artículos pagados.
12
+ #
13
+ # Para acceder a estos objetos:
14
+ #
15
+ # response.payments
16
+ #
17
+ # Para acceder a los atributos de cada pago:
18
+ #
19
+ # payment["Trx_Email"]
20
+ # # ernesto@ombushop.com
21
+ # payment["trx_id"]
22
+ # # 12345441
23
+ #
24
+ # @see ConsultaPagoResponse#payments
25
+ class Payment
26
+
27
+ # @private
28
+ def initialize(hash)
29
+ @hash = hash
30
+ end
31
+
32
+ # Devuelve un {String} o nil
33
+ # @return [String]
34
+ def [](key)
35
+ @hash[key]
36
+ end
37
+
38
+ # Devuelve un {Array} de objetos tipo {Item}
39
+ # @return [Array]
40
+ def items
41
+ return @items if @items
42
+
43
+ @items = []
44
+
45
+ unless @hash["Items"].nil?
46
+ @hash["Items"].each do |key, hash|
47
+ @items << Item.new(hash)
48
+ end
49
+ end
50
+
51
+ return @items
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,55 @@
1
+ # encoding: UTF-8
2
+ module DineroMailIpn
3
+
4
+ # # Reporte por transacción
5
+ #
6
+ # Nos dice el estado de la transacción, el ID y el monto de la misma.
7
+ #
8
+ # ## Posibles estados
9
+ #
10
+ # * Pendiente (Report::PENDING_STATUS)
11
+ # * Cancelado (Report::CANCELLED_STATUS)
12
+ # * Completado (Report::COMPLETED_STATUS)
13
+ #
14
+ class Report
15
+
16
+ # Posibles estados de transacciones
17
+ PENDING_STATUS = 1
18
+ COMPLETED_STATUS = 2
19
+ CANCELLED_STATUS = 3
20
+
21
+ attr_reader :id, :amount
22
+
23
+ # @param [Hash] opts opciones para crear la instancia
24
+ # @option opts [String] :id ID de transacción
25
+ # @option opts [String] :amount Monto de transacción (Por ej. "12.45")
26
+ # @option opts [String] :state Uno de los posibles estados
27
+ def initialize(opts)
28
+ @id = opts[:id]
29
+ @amount = opts[:amount]
30
+ @state = opts[:state]
31
+ end
32
+
33
+ # Devuelve true si la transacción fue completada
34
+ #
35
+ # @return [Boolean]
36
+ def transaction_completed?
37
+ @state == COMPLETED_STATUS
38
+ end
39
+
40
+ # Devuelve true si la transacción está pendiente
41
+ #
42
+ # @return [Boolean]
43
+ def transaction_pending?
44
+ @state == PENDING_STATUS
45
+ end
46
+
47
+ # Devuelve true si la transacción fue cancelada
48
+ #
49
+ # @return [Boolean]
50
+ def transaction_cancelled?
51
+ @state == CANCELLED_STATUS
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+
3
+ module DineroMailIpn
4
+ class Reporter
5
+
6
+ # Report status list
7
+ VALID_REPORT_STATUS = 1
8
+ MALFORMED_REPORT_STATUS = 2
9
+ INVALID_ACCOUNT_NUMBER_REPORT_STATUS = 3
10
+ INVALID_PASSWORD_REPORT_STATUS = 4
11
+ INVALID_REQUEST_TYPE_STATUS = 5
12
+ INVALID_TRANSACTION_ID_REQUEST_STATUS = 6
13
+ INVALID_PASSWORD_OR_ACCOUNT_NUMBER_REQUEST_STATUS = 7
14
+ TRANSACTION_NOT_FOUND_REQUEST_STATUS = 8
15
+
16
+ attr_reader :reports
17
+
18
+ def initialize(xml_response)
19
+ @doc = Nokogiri::XML(xml_response.downcase)
20
+ @reports = build_reports
21
+ end
22
+
23
+ def valid?
24
+ @doc.xpath("//estadoreporte").first.content.to_i == VALID_REPORT_STATUS
25
+ end
26
+
27
+ private
28
+
29
+ def build_reports
30
+ return [] if @doc.xpath("//operacion").empty?
31
+ @doc.xpath("//operacion").map do |report_xml|
32
+ DineroMailIpn::Report.new :id => report_xml.xpath("//id").first.content.to_i,
33
+ :amount => report_xml.xpath("//monto").first.content.to_i,
34
+ :state => report_xml.xpath("//estado").first.content.to_i
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -1,3 +1,3 @@
1
1
  module DineroMailIpn
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.1"
3
3
  end
Binary file
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="ISO-8859-1"?>
2
+ <Report>
3
+ <Email>ernesto@ombushop.com</Email>
4
+ <Acount>17128254</Acount>
5
+ <Pin>AYCN7IXDTM</Pin>
6
+ <StartDate>20110620</StartDate>
7
+ <EndDate>20110703</EndDate>
8
+ <XML>1</XML>
9
+ <State>2</State>
10
+ <Pays />
11
+ <Collections />
12
+ <Tickets />
13
+ <Reception />
14
+ <Retreats />
15
+ <Credits />
16
+ <Debits />
17
+ </Report>
@@ -0,0 +1,42 @@
1
+ <?xml version="1.0" encoding="ISO-8859-1"?>
2
+ <Report>
3
+ <Email>ernesto@ombushop.com</Email>
4
+ <Acount>17128254</Acount>
5
+ <Pin>AYCN7IXDTM</Pin>
6
+ <StartDate>20110620</StartDate>
7
+ <EndDate>20110703</EndDate>
8
+ <XML>1</XML>
9
+ <State>1</State>
10
+ <Pays>
11
+ <Pay trx_id="R163000000">
12
+ <Trx_Date>2011-12-20 05:44:56</Trx_Date>
13
+ <Trx_NombreComprador>Juan Perez</Trx_NombreComprador>
14
+ <Trx_Email>juanperez@ombushop.com</Trx_Email>
15
+ <Trx_Payment>108.00</Trx_Payment>
16
+ <Trx_ShipAddress></Trx_ShipAddress>
17
+ <Trx_Comment></Trx_Comment>
18
+ <Trx_MontoNeto>100.97</Trx_MontoNeto>
19
+ <Trx_SellerDocumentType>DNI</Trx_SellerDocumentType>
20
+ <Trx_SellerDocumentNumber>26.000.000</Trx_SellerDocumentNumber>
21
+ <Trx_Number>12345678</Trx_Number>
22
+ <Trx_PaymentMethod>Codigo de Barras</Trx_PaymentMethod>
23
+ <Trx_PaymentMean>Pago Facil</Trx_PaymentMean>
24
+ <Trx_BuyerDocumentType>DNI</Trx_BuyerDocumentType>
25
+ <Trx_BuyerDocumentNumber>26000000</Trx_BuyerDocumentNumber>
26
+ <Trx_BuyerPhone>+540351844232</Trx_BuyerPhone>
27
+ <Items>
28
+ <Item Item_Code="SKU123">
29
+ <Item_Quantity>1</Item_Quantity>
30
+ <Item_Description>Zapatos de Cuero</Item_Description>
31
+ <Item_Payment>68.00</Item_Payment>
32
+ </Item>
33
+ </Items>
34
+ </Pay>
35
+ </Pays>
36
+ <Collections />
37
+ <Tickets />
38
+ <Reception />
39
+ <Retreats />
40
+ <Credits />
41
+ <Debits />
42
+ </Report>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="ISO-8859-1"?>
2
+ <Report>
3
+ <Email>ernesto@ombushop.com</Email>
4
+ <Acount>17128254</Acount>
5
+ <Pin>AYCN7IXDTM</Pin>
6
+ <StartDate>20110620</StartDate>
7
+ <EndDate>20110703</EndDate>
8
+ <XML>1</XML>
9
+ <State>1</State>
10
+ <Pays />
11
+ <Collections />
12
+ <Tickets />
13
+ <Reception />
14
+ <Retreats />
15
+ <Credits />
16
+ <Debits />
17
+ </Report>
@@ -0,0 +1,50 @@
1
+ <?xml version="1.0" encoding="ISO-8859-1"?>
2
+ <REPORTE>
3
+ <ESTADOREPORTE>1</ESTADOREPORTE>
4
+ <DETALLE>
5
+ <OPERACIONES>
6
+ <OPERACION>
7
+ <ID>1</ID>
8
+ <FECHA>12/29/2011 7:38:00 PM</FECHA>
9
+ <ESTADO>2</ESTADO>
10
+ <COMPRADOR>
11
+ <EMAIL>email@customer.com</EMAIL>
12
+ <DIRECCION/>
13
+ <COMENTARIO/>
14
+ </COMPRADOR>
15
+ <MONTO>1010</MONTO>
16
+ <MONTONETO>510</MONTONETO>
17
+ <METODOPAGO>3</METODOPAGO>
18
+ <ITEMS>
19
+ <ITEM>
20
+ <DESCRIPCION>Camiseta amarilla</DESCRIPCION>
21
+ <MONEDA>1</MONEDA>
22
+ <PRECIOUNITARIO>1010</PRECIOUNITARIO>
23
+ <CANTIDAD>1</CANTIDAD>
24
+ </ITEM>
25
+ </ITEMS>
26
+ </OPERACION>
27
+ <OPERACION>
28
+ <ID>2</ID>
29
+ <FECHA>12/30/2011 17:38:00 PM</FECHA>
30
+ <ESTADO>2</ESTADO>
31
+ <COMPRADOR>
32
+ <EMAIL>other-email@other-customer.com</EMAIL>
33
+ <DIRECCION/>
34
+ <COMENTARIO/>
35
+ </COMPRADOR>
36
+ <MONTO>2000</MONTO>
37
+ <MONTONETO>710</MONTONETO>
38
+ <METODOPAGO>3</METODOPAGO>
39
+ <ITEMS>
40
+ <ITEM>
41
+ <DESCRIPCION>Sombrero rojo</DESCRIPCION>
42
+ <MONEDA>1</MONEDA>
43
+ <PRECIOUNITARIO>1000</PRECIOUNITARIO>
44
+ <CANTIDAD>2</CANTIDAD>
45
+ </ITEM>
46
+ </ITEMS>
47
+ </OPERACION>
48
+ </OPERACIONES>
49
+ </DETALLE>
50
+ </REPORTE>
@@ -0,0 +1,29 @@
1
+ <?xml version="1.0" encoding="ISO-8859-1"?>
2
+ <REPORTE>
3
+ <ESTADOREPORTE>1</ESTADOREPORTE>
4
+ <DETALLE>
5
+ <OPERACIONES>
6
+ <OPERACION>
7
+ <ID>1</ID>
8
+ <FECHA>12/29/2011 7:38:00 PM</FECHA>
9
+ <ESTADO>2</ESTADO>
10
+ <COMPRADOR>
11
+ <EMAIL>email@customer.com</EMAIL>
12
+ <DIRECCION/>
13
+ <COMENTARIO/>
14
+ </COMPRADOR>
15
+ <MONTO>1010</MONTO>
16
+ <MONTONETO>510</MONTONETO>
17
+ <METODOPAGO>3</METODOPAGO>
18
+ <ITEMS>
19
+ <ITEM>
20
+ <DESCRIPCION>Camiseta amarilla</DESCRIPCION>
21
+ <MONEDA>1</MONEDA>
22
+ <PRECIOUNITARIO>1010</PRECIOUNITARIO>
23
+ <CANTIDAD>1</CANTIDAD>
24
+ </ITEM>
25
+ </ITEMS>
26
+ </OPERACION>
27
+ </OPERACIONES>
28
+ </DETALLE>
29
+ </REPORTE>
data/test/helper.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'test/unit'
2
+ require 'shoulda'
3
+ require 'fakeweb'
4
+ require File.expand_path('../../lib/dinero_mail_ipn', __FILE__)
5
+
6
+ FakeWeb.allow_net_connect = false
7
+
8
+ def fixture_file(filename)
9
+ return '' if filename == ''
10
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/' + filename)
11
+ File.read(file_path)
12
+ end
13
+
14
+ def dinero_mail_url(path, options = {})
15
+ options[:https] = true if options[:https].nil?
16
+ options[:country] ||= 'argentina'
17
+ protocol = (options[:https] == true) ? 'https' : 'http'
18
+ "#{protocol}://#{options[:country]}.dineromail.com#{path}"
19
+ end
20
+
21
+ def stub_get(path, filename, options={})
22
+ opts = {:body => fixture_file(filename)}.merge(options)
23
+
24
+ FakeWeb.register_uri(:get, dinero_mail_url(path), opts)
25
+ end
26
+
27
+ def stub_post(path, filename, options={})
28
+ opts = {:body => fixture_file(filename)}.merge(options)
29
+
30
+ FakeWeb.register_uri(:post, dinero_mail_url(path, options[:url]), opts)
31
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+ require File.expand_path('../helper', __FILE__)
3
+
4
+ class TestDineroMailIpn < Test::Unit::TestCase
5
+
6
+ context "when I query the Dinero Mail API via GET" do
7
+
8
+ should "return OK for the query with valid Pin and Account parameters" do
9
+ stub_get("/vender/ConsultaPago.asp?XML=1&Acount=17128254&Pin=AYCN7IXDTM&Email=ernesto%40ombushop.com&StartDate=20110702&EndDate=20110703",
10
+ "ConsultaPagoOkWithoutCollections.xml")
11
+ client = DineroMailIpn::Client.new(:email => 'ernesto@ombushop.com',
12
+ :account => '17128254',
13
+ :pin => 'AYCN7IXDTM')
14
+ response = client.consulta_pago(Date.new(2011,7,2), Date.new(2011,7,3))
15
+ assert_equal response.state, "1"
16
+ assert response.ok?
17
+ assert_equal response.state_description, "La consulta se realizó correctamente"
18
+ end
19
+
20
+ should "return OK with a collection of payments" do
21
+ stub_get("/vender/ConsultaPago.asp?XML=1&Acount=17128254&Pin=AYCN7IXDTM&Email=ernesto%40ombushop.com&StartDate=20110702&EndDate=20110703",
22
+ "ConsultaPagoOkWithPays.xml")
23
+ client = DineroMailIpn::Client.new(:email => 'ernesto@ombushop.com',
24
+ :account => '17128254',
25
+ :pin => 'AYCN7IXDTM')
26
+ response = client.consulta_pago(Date.new(2011,7,2), Date.new(2011,7,3))
27
+ assert_equal response.state, "1"
28
+ assert response.ok?
29
+ assert_equal response.state_description, "La consulta se realizó correctamente"
30
+ assert !response.payments.empty?
31
+ payment = response.payments.first
32
+ assert_equal payment["Trx_Email"], "juanperez@ombushop.com"
33
+ assert_equal payment["trx_id"], "R163000000"
34
+ assert payment["Trx_Numbr"].nil?
35
+ assert !payment.items.empty?
36
+ item = payment.items.first
37
+ assert_equal item["Item_Code"], "SKU123"
38
+ assert_equal item["Item_Payment"], "68.00"
39
+ assert item["item_quantity"].nil?
40
+ end
41
+
42
+ should "return Error for the query" do
43
+ stub_get("/vender/ConsultaPago.asp?XML=1&Acount=123&Pin=UasdM&Email=ernesto%40ombushop.com&StartDate=20110702&EndDate=20110703",
44
+ "ConsultaPagoErrorBadCredentials.xml")
45
+ client = DineroMailIpn::Client.new(:email => 'ernesto@ombushop.com',
46
+ :account => '123',
47
+ :pin => 'UasdM')
48
+ response = client.consulta_pago(Date.new(2011,7,2), Date.new(2011,7,3))
49
+ assert_equal response.state, "2"
50
+ assert response.error?
51
+ assert_equal response.state_description, "Los valores son incorrectos para realizar la consulta"
52
+ end
53
+
54
+ end
55
+
56
+ context "when I query for some transactions" do
57
+
58
+ should "return a new report object" do
59
+ stub_post("/Vender/Consulta_IPN.asp", "ConsultaTransacciones1-2Success.xml",
60
+ {:url => {:https => false}})
61
+
62
+ client = DineroMailIpn::Client.new(:password => 'fuckingipnpasswordinplaintext', :account => '17128254')
63
+ reporter = client.consulta_transacciones([1,2])
64
+ assert reporter.kind_of?(DineroMailIpn::Reporter)
65
+ assert_equal 2, reporter.reports.size
66
+ end
67
+
68
+ end
69
+
70
+ context "when I query for a transactions" do
71
+
72
+ should "return a new report object" do
73
+ stub_post("/Vender/Consulta_IPN.asp", "ConsultaTransacciones1Success.xml",
74
+ {:url => {:https => false}})
75
+
76
+ client = DineroMailIpn::Client.new(:password => 'fuckingipnpasswordinplaintext', :account => '17128254')
77
+ reporter = client.consulta_transacciones([1])
78
+ assert reporter.kind_of?(DineroMailIpn::Reporter)
79
+ assert_equal 1, reporter.reports.size
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../helper', __FILE__)
4
+
5
+ class TestDineromailIpnReport < Test::Unit::TestCase
6
+
7
+ def test_report_id
8
+ report = DineroMailIpn::Report.new(:id => 33)
9
+ assert_equal 33, report.id
10
+ end
11
+
12
+ def test_report_amount
13
+ report = DineroMailIpn::Report.new(:amount => 3003)
14
+ assert_equal 3003, report.amount
15
+ end
16
+
17
+ def test_transaction_completed?
18
+ report = DineroMailIpn::Report.new(:state => 2)
19
+ assert report.transaction_completed?
20
+ end
21
+
22
+ def test_transaction_pending?
23
+ report = DineroMailIpn::Report.new(:state => 1)
24
+ assert report.transaction_pending?
25
+ end
26
+
27
+ def test_transaction_cancelled?
28
+ report = DineroMailIpn::Report.new(:state => 3)
29
+ assert report.transaction_cancelled?
30
+ end
31
+
32
+ end
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../helper', __FILE__)
4
+
5
+ class TestDineromailIpnReporter < Test::Unit::TestCase
6
+
7
+ def test_invalid_report_should_return_invalid
8
+ xml_response = <<-XML
9
+ <?xml version="1.0" encoding="ISO-8859-1"?>
10
+ <REPORTE>
11
+ <ESTADOREPORTE>2</ESTADOREPORTE>
12
+ </REPORTE>
13
+ XML
14
+ reporter = DineroMailIpn::Reporter.new(xml_response)
15
+ assert !reporter.valid?
16
+ end
17
+
18
+ def test_invalid_report_should_has_no_reports
19
+ xml_response = <<-XML
20
+ <?xml version="1.0" encoding="ISO-8859-1"?>
21
+ <REPORTE>
22
+ <ESTADOREPORTE>2</ESTADOREPORTE>
23
+ </REPORTE>
24
+ XML
25
+ reporter = DineroMailIpn::Reporter.new(xml_response)
26
+ assert reporter.reports.empty?
27
+ end
28
+
29
+ def test_valid_report_should_be_valid
30
+ xml_response = <<-XML
31
+ <?xml version="1.0" encoding="ISO-8859-1"?>
32
+ <REPORTE>
33
+ <ESTADOREPORTE>1</ESTADOREPORTE>
34
+ </REPORTE>
35
+ XML
36
+ reporter = DineroMailIpn::Reporter.new(xml_response)
37
+ assert reporter.valid?
38
+ end
39
+
40
+ def test_reports_should_be_created_from_multiple_reports_response
41
+ xml_response = fixture_file("ConsultaTransacciones1-2Success.xml")
42
+ reporter = DineroMailIpn::Reporter.new(xml_response)
43
+ assert_equal 2, reporter.reports.size
44
+ assert reporter.reports.kind_of?(Array)
45
+ assert reporter.reports.first.kind_of?(DineroMailIpn::Report)
46
+ end
47
+
48
+ def test_reports_should_be_created_from_single_report_response
49
+ xml_response = fixture_file("ConsultaTransacciones1Success.xml")
50
+ reporter = DineroMailIpn::Reporter.new(xml_response)
51
+ assert_equal 1, reporter.reports.size
52
+ assert reporter.reports.kind_of?(Array)
53
+ assert reporter.reports.first.kind_of?(DineroMailIpn::Report)
54
+ end
55
+
56
+ end
metadata CHANGED
@@ -1,105 +1,144 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: dinero_mail_ipn
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 0
8
- - 1
9
- version: 0.0.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Ernesto Tagwerker
13
9
  - Esteban Pastorino
14
10
  autorequire:
15
11
  bindir: bin
16
12
  cert_chain: []
17
-
18
- date: 2011-04-19 00:00:00 -03:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
13
+ date: 2012-01-16 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
22
16
  name: httparty
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &2804470 !ruby/object:Gem::Requirement
25
18
  none: false
26
- requirements:
19
+ requirements:
27
20
  - - ~>
28
- - !ruby/object:Gem::Version
29
- segments:
30
- - 0
31
- - 7
32
- - 6
33
- version: 0.7.6
21
+ - !ruby/object:Gem::Version
22
+ version: 0.8.1
34
23
  type: :runtime
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: nokogiri
38
24
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: *2804470
26
+ - !ruby/object:Gem::Dependency
27
+ name: nokogiri
28
+ requirement: &2804220 !ruby/object:Gem::Requirement
40
29
  none: false
41
- requirements:
30
+ requirements:
42
31
  - - ~>
43
- - !ruby/object:Gem::Version
44
- segments:
45
- - 1
46
- - 4
47
- - 4
48
- version: 1.4.4
32
+ - !ruby/object:Gem::Version
33
+ version: 1.5.0
49
34
  type: :runtime
50
- version_requirements: *id002
35
+ prerelease: false
36
+ version_requirements: *2804220
37
+ - !ruby/object:Gem::Dependency
38
+ name: shoulda
39
+ requirement: &2819840 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: 2.11.3
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *2819840
48
+ - !ruby/object:Gem::Dependency
49
+ name: fakeweb
50
+ requirement: &2819610 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 1.3.0
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *2819610
59
+ - !ruby/object:Gem::Dependency
60
+ name: yard
61
+ requirement: &2819380 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ version: 0.7.4
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *2819380
70
+ - !ruby/object:Gem::Dependency
71
+ name: redcarpet
72
+ requirement: &2819150 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 2.0.1
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: *2819150
51
81
  description: Dinero Mail IPN stub
52
- email:
82
+ email:
53
83
  - ernesto@etagwerker.com
54
84
  - kito@pinggers.com
55
85
  executables: []
56
-
57
86
  extensions: []
58
-
59
87
  extra_rdoc_files: []
60
-
61
- files:
88
+ files:
62
89
  - .gitignore
90
+ - .rvmrc
91
+ - .yardopts
63
92
  - Gemfile
64
93
  - Gemfile.lock
65
94
  - MIT-LICENSE
66
- - README.rdoc
95
+ - README.markdown
67
96
  - Rakefile
68
97
  - dinero_mail_ipn.gemspec
69
98
  - lib/dinero_mail_ipn.rb
99
+ - lib/dinero_mail_ipn/consulta_pago_response.rb
100
+ - lib/dinero_mail_ipn/item.rb
101
+ - lib/dinero_mail_ipn/malformed_response_error.rb
102
+ - lib/dinero_mail_ipn/payment.rb
103
+ - lib/dinero_mail_ipn/report.rb
104
+ - lib/dinero_mail_ipn/reporter.rb
70
105
  - lib/dinero_mail_ipn/version.rb
106
+ - resources/IPN_es.pdf
71
107
  - script/console
72
- has_rdoc: true
73
- homepage: ""
108
+ - test/fixtures/ConsultaPagoErrorBadCredentials.xml
109
+ - test/fixtures/ConsultaPagoOkWithPays.xml
110
+ - test/fixtures/ConsultaPagoOkWithoutCollections.xml
111
+ - test/fixtures/ConsultaTransacciones1-2Success.xml
112
+ - test/fixtures/ConsultaTransacciones1Success.xml
113
+ - test/helper.rb
114
+ - test/test_dinero_mail_ipn.rb
115
+ - test/test_report.rb
116
+ - test/test_reporter.rb
117
+ homepage: ''
74
118
  licenses: []
75
-
76
119
  post_install_message:
77
120
  rdoc_options: []
78
-
79
- require_paths:
121
+ require_paths:
80
122
  - lib
81
- required_ruby_version: !ruby/object:Gem::Requirement
123
+ required_ruby_version: !ruby/object:Gem::Requirement
82
124
  none: false
83
- requirements:
84
- - - ">="
85
- - !ruby/object:Gem::Version
86
- segments:
87
- - 0
88
- version: "0"
89
- required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
130
  none: false
91
- requirements:
92
- - - ">="
93
- - !ruby/object:Gem::Version
94
- segments:
95
- - 0
96
- version: "0"
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
97
135
  requirements: []
98
-
99
136
  rubyforge_project: dinero_mail_ipn
100
- rubygems_version: 1.3.7
137
+ rubygems_version: 1.7.2
101
138
  signing_key:
102
139
  specification_version: 3
103
140
  summary: Dinero Mail IPN gem
104
- test_files: []
105
-
141
+ test_files:
142
+ - test/helper.rb
143
+ - test/test_dinero_mail_ipn.rb
144
+ has_rdoc:
data/README.rdoc DELETED
@@ -1,17 +0,0 @@
1
- = Dinero Mail IPN Gem
2
-
3
- Sirve para consumir los metodos de IPN 1 y IPN 2.
4
-
5
- Por ejemplo:
6
-
7
- #IPN v1
8
- c = DineroMailIpn::Client.new(:email => 'EMAILPRUEBA@gmail.com', :account => '09813581', :pin => 'URJYRLU2PP')
9
- c.consulta_pago(before,now) # before y now son Dates
10
- #Response
11
- {"Report"=>{"Email"=>"EMAILPRUEBA@gmail.com", "Acount"=>"09813581", "Pin"=>"URJYRLU2PP", "StartDate"=>"20110419", "EndDate"=>"20110419", "XML"=>"1", "State"=>"2", "Pays"=>nil, "Collections"=>nil, "Tickets"=>nil, "Reception"=>nil, "Retreats"=>nil, "Credits"=>nil, "Debits"=>nil}}
12
-
13
- #IPN v2
14
- c = DineroMailIpn::Client.new(:account => '09813581', :password => 'mipassword)
15
- c.consulta_transacciones("31548", "XA5547")
16
- #Response
17
- {"REPORTE"=>{"ESTADOREPORTE"=>"8", "DETALLE"=>{"OPERACIONES"=>nil}}}