klarna 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. data/.gitignore +9 -0
  2. data/.travis.yml +13 -0
  3. data/Gemfile +14 -0
  4. data/Guardfile +16 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.textile +139 -0
  7. data/Rakefile +22 -0
  8. data/TODO +30 -0
  9. data/examples/Gemfile +8 -0
  10. data/examples/config/initializer.rb +15 -0
  11. data/examples/console.rb +71 -0
  12. data/examples/public/images/klarna.png +0 -0
  13. data/examples/public/images/ruby.png +0 -0
  14. data/examples/views/_address.haml +22 -0
  15. data/examples/views/_articles.haml +21 -0
  16. data/examples/views/checkout_page_example.haml +2 -0
  17. data/examples/views/essential/add_transaction/_form.haml +32 -0
  18. data/examples/views/essential/add_transaction/result.haml +7 -0
  19. data/examples/views/essential/calculate_monthly_cost/_form.haml +16 -0
  20. data/examples/views/essential/calculate_monthly_cost/result.haml +7 -0
  21. data/examples/views/essential/get_addresses/_form.haml +9 -0
  22. data/examples/views/essential/get_addresses/result.haml +8 -0
  23. data/examples/views/index.haml +296 -0
  24. data/examples/views/layout.haml +48 -0
  25. data/examples/views/payment_terms_example.haml +102 -0
  26. data/examples/views/product_page_example.haml +2 -0
  27. data/examples/views/reservation/activate_reservation/_form.haml +54 -0
  28. data/examples/views/reservation/activate_reservation/result.haml +7 -0
  29. data/examples/views/reservation/cancel_reservation/_form.haml +8 -0
  30. data/examples/views/reservation/cancel_reservation/result.haml +7 -0
  31. data/examples/views/reservation/change_reservation/_form.haml +10 -0
  32. data/examples/views/reservation/change_reservation/result.haml +7 -0
  33. data/examples/views/reservation/reserve_amount/_form.haml +58 -0
  34. data/examples/views/reservation/reserve_amount/result.haml +7 -0
  35. data/examples/views/reservation/reserve_ocr_numbers/_form.haml +8 -0
  36. data/examples/views/reservation/reserve_ocr_numbers/result.haml +7 -0
  37. data/examples/views/reservation/split_reservation/_form.haml +14 -0
  38. data/examples/views/reservation/split_reservation/result.haml +7 -0
  39. data/examples/views/special/get_pclasses/_form.haml +8 -0
  40. data/examples/views/special/get_pclasses/result.haml +7 -0
  41. data/examples/views/special/invoice_address/_form.haml +8 -0
  42. data/examples/views/special/invoice_address/result.haml +7 -0
  43. data/examples/views/special/invoice_amount/_form.haml +8 -0
  44. data/examples/views/special/invoice_amount/result.haml +7 -0
  45. data/examples/views/special/is_invoice_paid/_form.haml +8 -0
  46. data/examples/views/special/is_invoice_paid/result.haml +7 -0
  47. data/examples/views/special/update_charge_amount/_form.haml +15 -0
  48. data/examples/views/special/update_charge_amount/result.haml +7 -0
  49. data/examples/views/special/update_goods_quantity/_form.haml +17 -0
  50. data/examples/views/special/update_goods_quantity/result.haml +7 -0
  51. data/examples/views/special/update_order_number/_form.haml +10 -0
  52. data/examples/views/special/update_order_number/result.haml +7 -0
  53. data/examples/views/useful/activate_invoice/_form.haml +9 -0
  54. data/examples/views/useful/activate_invoice/result.haml +7 -0
  55. data/examples/views/useful/credit_invoice/_form.haml +11 -0
  56. data/examples/views/useful/credit_invoice/result.haml +7 -0
  57. data/examples/views/useful/delete_invoice/_form.haml +9 -0
  58. data/examples/views/useful/delete_invoice/result.haml +7 -0
  59. data/examples/views/useful/email_invoice/_form.haml +9 -0
  60. data/examples/views/useful/email_invoice/result.haml +7 -0
  61. data/examples/views/useful/has_account/_form.haml +9 -0
  62. data/examples/views/useful/has_account/result.haml +7 -0
  63. data/examples/views/useful/return_amount/_form.haml +15 -0
  64. data/examples/views/useful/return_amount/result.haml +7 -0
  65. data/examples/views/useful/send_invoice/_form.haml +9 -0
  66. data/examples/views/useful/send_invoice/result.haml +7 -0
  67. data/examples/web.rb +349 -0
  68. data/klarna.gemspec +34 -0
  69. data/lib/klarna.rb +175 -0
  70. data/lib/klarna/api.rb +170 -0
  71. data/lib/klarna/api/client.rb +128 -0
  72. data/lib/klarna/api/constants.rb +638 -0
  73. data/lib/klarna/api/errors.rb +154 -0
  74. data/lib/klarna/api/methods.rb +16 -0
  75. data/lib/klarna/api/methods/cost_calculations.rb +134 -0
  76. data/lib/klarna/api/methods/invoicing.rb +304 -0
  77. data/lib/klarna/api/methods/reservation.rb +149 -0
  78. data/lib/klarna/api/methods/standard.rb +123 -0
  79. data/lib/klarna/version.rb +5 -0
  80. data/test/fixtures/api/companies.yml +97 -0
  81. data/test/fixtures/api/pclasses.yml +37 -0
  82. data/test/fixtures/api/persons.yml +144 -0
  83. data/test/fixtures/api/stores.yml +6 -0
  84. data/test/fixtures/klarna.yml +10 -0
  85. data/test/klarna/api/client_test.rb +272 -0
  86. data/test/klarna/api/errors_test.rb +46 -0
  87. data/test/klarna/api/methods/cost_calculations_test.rb +78 -0
  88. data/test/klarna/api/methods/invoicing_test.rb +409 -0
  89. data/test/klarna/api/methods/reservation_test.rb +66 -0
  90. data/test/klarna/api/methods/standard_test.rb +244 -0
  91. data/test/klarna/api_test.rb +137 -0
  92. data/test/klarna_test.rb +204 -0
  93. data/test/support/assertions_helper.rb +40 -0
  94. data/test/test_helper.rb +55 -0
  95. metadata +312 -0
data/lib/klarna.rb ADDED
@@ -0,0 +1,175 @@
1
+ # encoding: utf-8
2
+ # require 'rubygems'
3
+ # require 'bundler'
4
+ # Bundler.require
5
+ require 'i18n'
6
+ require 'active_support/all'
7
+
8
+ module Klarna
9
+
10
+ autoload :API, 'klarna/api'
11
+ autoload :VERSION, 'klarna/version'
12
+
13
+ module API
14
+ autoload :Client, 'klarna/api/client'
15
+ autoload :Constants, 'klarna/api/constants'
16
+ autoload :Errors, 'klarna/api/errors'
17
+
18
+ module Methods
19
+ autoload :Standard, 'klarna/api/methods/standard'
20
+ autoload :Invoicing, 'klarna/api/methods/invoicing'
21
+ autoload :Reservation, 'klarna/api/methods/reservation'
22
+ autoload :CostCalculations, 'klarna/api/methods/cost_calculations'
23
+ end
24
+ end
25
+
26
+ class KlarnaConfigError < ::StandardError
27
+ end
28
+
29
+ DEFAULT_STORE_CONFIG_FILE = File.join(ENV['HOME'], '.klarna.yml') unless defined?(::Klarna::DEFAULT_STORE_CONFIG_FILE)
30
+ VALID_COUNTRIES = [:SE, :NO, :FI, :DK] unless defined?(::Klarna::VALID_COUNTRIES)
31
+ DEFAULT_COUNTRY = VALID_COUNTRIES.first unless defined?(::Klarna::DEFAULT_COUNTRY)
32
+ DEFAULT_MODE = :test unless defined?(::Klarna::DEFAULT_MODE)
33
+
34
+ # Specifies running mode: +:test+ (alla actions gets virtual) or +:production+ (live)
35
+ # Default: +:test+
36
+ mattr_accessor :mode
37
+ @@mode = :test
38
+
39
+ # Country used to ensure that params sent to Klarna service is correct.
40
+ # Default: +:SE+
41
+ mattr_accessor :country
42
+ @@country = ::Klarna::DEFAULT_COUNTRY
43
+
44
+ # Klarna e-store ID (a.k.a. "eid") used for Klarna API authentication.
45
+ # Default: +nil+
46
+ # NOTE: If +nil+, Klarna will look for credentials in file specified by +Devise.store_config_file+.
47
+ mattr_accessor :store_id
48
+ @@store_id = nil
49
+
50
+ # Klarna e-store shared secret token used for Klarna API authentication.
51
+ # Default: +nil+
52
+ # NOTE: If +nil+, Klarna will look for credentials in file specified by +Devise.credentials_file+.
53
+ mattr_accessor :store_secret
54
+ @@store_secret = nil
55
+
56
+ # Klarna e-store-specific pclasses (a.k.a. campaigns).
57
+ # Default: +nil+
58
+ #
59
+ # NOTE:
60
+ # * Required for campaigns
61
+ # * This should maybe be initialized from database to make more dynamic (e.g. admin inteface).
62
+ #
63
+ mattr_accessor :store_pclasses
64
+ @@store_pclasses = nil
65
+
66
+ # Path to a YAML file containing API credentials - used only if +store_id+ and +store_secret+ are not set.
67
+ # Default: +"~/.klarna.yml"+
68
+ mattr_accessor :store_config_file
69
+ @@store_config_file = ::Klarna::DEFAULT_STORE_CONFIG_FILE
70
+
71
+ # The logger to use in log mode.
72
+ # Default: +::Logger.new(::STDOUT)+
73
+ mattr_accessor :logger
74
+ @@logger = ::Logger.new(::STDOUT)
75
+
76
+ # Sets if activity should be logged or not - for high-level debugging.
77
+ # Default: +false+
78
+ mattr_accessor :logging
79
+ @@logging = false
80
+
81
+ # Sets if Net::HTTP activity should be logged or not - for low-level debugging.
82
+ # Default: +false+
83
+ mattr_accessor :http_logging
84
+ @@http_logging = false
85
+
86
+ class << self
87
+
88
+ # Configuration DSL helper method.
89
+ #
90
+ # == Usage/Example:
91
+ #
92
+ # Klarna::Setup do |config|
93
+ # config.country = :SE
94
+ # # etc.
95
+ # end
96
+ #
97
+ def setup
98
+ yield self
99
+ self.load_credentials_from_file unless self.store_id || self.store_secret
100
+ end
101
+ alias :configure :setup
102
+
103
+ # Reset to defaults - mostly usable in specs.
104
+ #
105
+ def reset!
106
+ self.mode = ::Klarna::DEFAULT_MODE
107
+ self.country = ::Klarna::DEFAULT_COUNTRY
108
+ self.store_id = nil
109
+ self.store_secret = nil
110
+ self.store_pclasses = nil
111
+ self.store_config_file = ::Klarna::DEFAULT_STORE_CONFIG_FILE
112
+ self.logger = ::Logger.new(::STDOUT)
113
+ self.logging = false
114
+ self.http_logging = false
115
+ end
116
+
117
+ # Logging helper for debugging purposes.
118
+ #
119
+ def log(message, level = :info)
120
+ return unless self.logging?
121
+ level = :info if level.blank?
122
+ self.logger ||= ::Logger.new(::STDOUT)
123
+ self.logger.send(level.to_sym, "[klarna:] #{level.to_s.upcase} #{message}")
124
+ end
125
+
126
+ # Logging helper for debugging "log return value"-cases.
127
+ #
128
+ def log_result(label, &block)
129
+ result = block.call
130
+ self.log label % result.inspect
131
+ result
132
+ end
133
+
134
+ # Optional: Try to load credentials from a system file.
135
+ #
136
+ def load_credentials_from_file(force = false)
137
+ begin
138
+ store_config = File.open(self.store_config_file) { |file| YAML.load(file).with_indifferent_access }
139
+ self.store_id = store_config[self.mode][:store_id] if force || self.store_id.nil?
140
+ self.store_secret = store_config[self.mode][:store_secret] if force || self.store_secret.nil?
141
+ self.store_pclasses = store_config[self.mode][:store_pclasses] if force || self.store_pclasses.nil?
142
+ rescue
143
+ raise KlarnaConfigError, "Could not load store details from: #{self.store_config_file.inspect}"
144
+ end
145
+ end
146
+
147
+ def store_id=(value)
148
+ @@store_id = value ? value.to_i : value
149
+ end
150
+
151
+ def store_secret=(value)
152
+ @@store_secret = value ? value.to_s.strip : value
153
+ end
154
+
155
+ def country=(value)
156
+ @@country = value.to_s.upcase.to_sym rescue ::Klarna::DEFAULT_COUNTRY
157
+ end
158
+
159
+ def mode=(value)
160
+ @@mode = value.to_s.downcase.to_sym rescue ::Klarna::DEFAULT_MODE
161
+ end
162
+
163
+ def valid_countries
164
+ ::Klarna::VALID_COUNTRIES
165
+ end
166
+
167
+ def default_country
168
+ ::Klarna::DEFAULT_COUNTRY
169
+ end
170
+
171
+ alias :logging? :logging
172
+ alias :http_logging? :http_logging
173
+ end
174
+
175
+ end
data/lib/klarna/api.rb ADDED
@@ -0,0 +1,170 @@
1
+ # encoding: utf-8
2
+ require 'iconv'
3
+ require 'digest/md5'
4
+ require 'digest/sha2'
5
+ require 'xmlrpc/base64'
6
+
7
+ require 'klarna/api/constants'
8
+ require 'klarna/api/errors'
9
+
10
+ module Klarna
11
+ module API
12
+
13
+ # == Reference:
14
+ #
15
+ # * http://integration.klarna.com/api-functions
16
+ #
17
+ # == Note:
18
+ #
19
+ # All input data strings should be coded in accordance with ISO-8859-1.
20
+ #
21
+ # This implementation is not 1-to-1 implementation of the Klarna API functions.
22
+ # The methods are refactored/cleaned up to meet Ruby code standards; avoid
23
+ # unessecary code-repetition and spaghetti-code like the other implementations.
24
+ # Same functionality can be achieved though; see the RDocs.
25
+ #
26
+ # To obtain pclasses - or download the pclasses file:
27
+ #
28
+ # 1. Log in to Kreditor Online.
29
+ # 2. Click on "Display store"
30
+ # 3. Click on "Click here to view campaigns".
31
+ #
32
+ # ...or use the API-method +::Klarna::API.get_pclasses+.
33
+ #
34
+
35
+ include ::Klarna::API::Constants
36
+ include ::Klarna::API::Errors
37
+
38
+ @@client = nil
39
+
40
+ class << self
41
+
42
+ # Re-use or (re-)initialize a new Klarna XML-RPC API client.
43
+ #
44
+ def client(force_new = false)
45
+ begin
46
+ if force_new || @@client.nil?
47
+ @@client = ::Klarna::API::Client.new
48
+ end
49
+ rescue => e
50
+ ::Klarna.log e, :error
51
+ end
52
+ @@client
53
+ end
54
+
55
+ def key_for(kind, value)
56
+ begin
57
+ kind = validated_kind(kind)
58
+ rescue => e
59
+ raise e
60
+ end
61
+
62
+ begin
63
+ is_correct_format = !(value.to_s =~ /^\d+$/)
64
+ constants = ::Klarna::API.const_get(kind)
65
+ key = is_correct_format ? value : constants.invert[value.to_i] # BUG: Should lookup the value's key.
66
+ rescue
67
+ raise ::Klarna::API::KlarnaArgumentError, "Invalid '#{kind}': #{value.inspect}"
68
+ end
69
+
70
+ key ? key.to_sym : nil
71
+ end
72
+
73
+ def id_for(kind, value)
74
+ begin
75
+ kind = validated_kind(kind)
76
+ rescue => e
77
+ raise e
78
+ end
79
+
80
+ begin
81
+ is_correct_format = (value.to_s =~ /^\d+$/)
82
+ constants = ::Klarna::API.const_get(kind)
83
+ id = is_correct_format ? value.to_i : constants[value.to_s.upcase.to_sym]
84
+ rescue
85
+ raise ::Klarna::API::KlarnaArgumentError, "Invalid '#{kind}': #{value.inspect}"
86
+ end
87
+
88
+ id ? id.to_i : nil
89
+ end
90
+
91
+ # Validate if specified +kind+ is a valid constant.
92
+ #
93
+ def validated_kind(kind)
94
+ valid_kinds = [:country, :currency, :language, :pno_format, :address_format, :shipment_type, :pclass, :mobile, :invoice, :goods, :monthly_cost]
95
+ valid_kinds.collect! do |valid_kind|
96
+ [valid_kind.to_s.singularize.to_sym, valid_kind.to_s.pluralize.to_sym]
97
+ end
98
+ valid_kinds.flatten!
99
+ kind = kind.to_s.pluralize.to_sym
100
+
101
+ unless kind.is_a?(String) || kind.is_a?(Symbol)
102
+ raise ::Klarna::API::KlarnaArgumentError, "Not a valid kind: #{kind.inspect}. Expects a symbol or a string: #{valid_kinds.join(', ')}"
103
+ end
104
+
105
+ unless valid_kinds.include?(kind.to_s.downcase.to_sym)
106
+ raise ::Klarna::API::KlarnaArgumentError, "Not a valid kind: #{kind.inspect}. Valid kinds: #{valid_kinds.join(', ')}"
107
+ end
108
+
109
+ kind.to_s.upcase.to_sym
110
+ end
111
+
112
+ # Parse, validate, and cast a method argument before RPC-call.
113
+ #
114
+ def validate_arg(value, cast_to, format_expression, strip_expression = nil, &block)
115
+ raise ::Klarna::API::KlarnaArgumentError,
116
+ "Argument cast_to should be Symbol, but was #{cast_to.class.name}." unless cast_to.is_a?(Symbol)
117
+ raise ::Klarna::API::KlarnaArgumentError,
118
+ "Argument regexp should be Regexp, but was #{format_expression.class.name}." unless format_expression.is_a?(Regexp)
119
+ raise ::Klarna::API::KlarnaArgumentError,
120
+ "Argument strip should be Regexp, but was #{strip_expression.class.name}." unless strip_expression.is_a?(Regexp)
121
+
122
+ value = value.to_s.gsub(strip_expression, '') if strip_expression
123
+
124
+ unless value.to_s =~ format_expression
125
+ raise ::Klarna::API::KlarnaArgumentError, "Invalid argument: #{value.inspect}. Expected format: #{format_expression.inspect}"
126
+ end
127
+
128
+ # Pass value to block - for type casting, etc. - if given.
129
+ value = block.call(value) if block_given?
130
+
131
+ value.tap do |v|
132
+ case cast_to
133
+ when :string then v.to_s
134
+ when :integer then v.to_i
135
+ when :decimal then v.to_f # number of decimals?
136
+ when :date then v # TODO
137
+ else
138
+ raise ::Klarna::API::KlarnaArgumentError, "Invalid cast_to value: #{cast_to.inspect}. "
139
+ end
140
+ end
141
+ end
142
+
143
+ def parse_flags(constant_name, flags)
144
+ if flags.is_a?(Hash)
145
+ flags = flags.sum { |k,v|
146
+ ::Klarna::API.const_get(constant_name.to_s.upcase.to_sym)[k.to_s.upcase.to_sym]
147
+ }
148
+ end
149
+ flags.to_i
150
+ end
151
+
152
+ def digest(*args)
153
+ string = args.join(':')
154
+ iso_value = self.encode(string)
155
+
156
+
157
+ hex_md5_digest = [*::Digest::MD5.hexdigest(iso_value)].pack('H*')
158
+ base64_digest = ::XMLRPC::Base64.encode(hex_md5_digest).strip
159
+ hex_sha512_digest = [*Digest::SHA512.hexdigest(iso_value)].pack('H*')
160
+ base64_digest = ::XMLRPC::Base64.encode(hex_sha512_digest).strip
161
+ end
162
+
163
+ def encode(string, from_encoding = 'utf-8')
164
+ ::Iconv.conv(::Klarna::API::PROTOCOL_ENCODING, from_encoding, string)
165
+ end
166
+
167
+ end
168
+
169
+ end
170
+ end
@@ -0,0 +1,128 @@
1
+ # encoding: utf-8
2
+ require 'xmlrpc/client'
3
+
4
+ require 'klarna/api/methods'
5
+
6
+ module Klarna
7
+ module API
8
+ class Client < ::XMLRPC::Client
9
+
10
+ include ::Klarna::API::Methods
11
+
12
+ attr_accessor :store_id,
13
+ :store_secret,
14
+ :mode,
15
+ :timeout,
16
+ :last_request_headers,
17
+ :last_request_params,
18
+ :last_response,
19
+ :client_ip,
20
+ :protocol,
21
+ :host,
22
+ :port
23
+
24
+ def initialize(*args)
25
+ self.store_id = args.shift || ::Klarna.store_id
26
+ self.store_secret = args.shift || ::Klarna.store_secret
27
+
28
+ if self.store_id.blank? || self.store_secret.blank?
29
+ raise ::Klarna::API::KlarnaCredentialsError, "Both STORE_ID or STORE_SECRET must be set."
30
+ end
31
+
32
+ options = args.extract_options!
33
+ self.client_ip = options[:client_ip].presence || '127.0.0.1'
34
+ self.mode = options.key?(:mode) ? options[:mode] : ::Klarna.mode
35
+ self.timeout = options.key?(:timeout) ? options[:timeout] : 10 # seconds
36
+
37
+ unless ::Klarna::API::END_POINT.keys.include?(self.mode)
38
+ raise "No such mode: #{self.mode.inspect}. " <<
39
+ "Valid modes: #{::Klarna::API::END_POINT.keys.collect(&:inspect).join(', ')}"
40
+ end
41
+
42
+ begin
43
+ ::Klarna.log "Endpoint URI: %s" % self.endpoint_uri.inspect
44
+
45
+ super(self.host, '/', self.port, nil, nil, nil, nil, self.use_ssl?, self.timeout)
46
+
47
+ self.http_header_extra ||= {}
48
+ self.http_header_extra.merge!(self.content_type_headers)
49
+
50
+ self.instance_variable_get(:@http).set_debug_output(::Klarna.logger) if ::Klarna.http_logging?
51
+ rescue ::XMLRPC::FaultException => e
52
+ raise ::Klarna::API::KlarnaServiceError.new(e.faultCode, e.faultString)
53
+ end
54
+ end
55
+
56
+ def call(service_method, *args)
57
+ args.collect! { |arg| arg.is_a?(String) ? ::Iconv.conv('utf-8', ::Klarna::API::PROTOCOL_ENCODING, arg) : arg }
58
+ ::Klarna.log "Method: #{service_method}"
59
+ ::Klarna.log "Params: %s" % self.add_meta_params(*args).inspect
60
+
61
+ self.last_request_headers = http_header_extra
62
+
63
+ begin
64
+ ::Klarna.log_result("Result: %s") do
65
+ params = self.add_meta_params(*args)
66
+ self.last_request_params = params
67
+ self.last_response = super(service_method, *params)
68
+ end
69
+ rescue ::XMLRPC::FaultException => e
70
+ raise ::Klarna::API::KlarnaServiceError.new(e.faultCode, e.faultString)
71
+ rescue ::Timeout::Error => e
72
+ raise ::Klarna::API::KlarnaServiceError.new(-1, e.message)
73
+ end
74
+ end
75
+
76
+ def ssl?
77
+ self.protocol == 'https'
78
+ end
79
+ alias :use_ssl? :ssl?
80
+
81
+ def protocol
82
+ @protocol = ::Klarna::API::END_POINT[self.mode][:protocol]
83
+ end
84
+
85
+ def host
86
+ @host = ::Klarna::API::END_POINT[self.mode][:host]
87
+ end
88
+
89
+ def port
90
+ @port = ::Klarna::API::END_POINT[self.mode][:port]
91
+ end
92
+
93
+ def endpoint_uri
94
+ @endpoint_uri = "#{self.protocol}://#{self.host}:#{self.port}"
95
+ end
96
+
97
+ protected
98
+
99
+ # Request content-type headers.
100
+ #
101
+ def content_type_headers
102
+ {
103
+ :'Accept-Encoding' => 'deflate,gzclient_ip',
104
+ :'Content-Type' => "text/xml;charset=#{::Klarna::API::PROTOCOL_ENCODING}",
105
+ :'Accept-Charset' => 'iso-8859-1', # REVISIT: 'UTF-8,ISO-8859-1,US-ASCII',
106
+ :'Connection' => 'close',
107
+ :'User-Agent' => 'ruby/xmlrpc' # Note: Default "User-Agent" gives 400-error.
108
+ }.with_indifferent_access
109
+ end
110
+
111
+ # Ensure that the required client info params get sent with each Klarna API request.
112
+ # Without these the Klarna API will get a service error response.
113
+ #
114
+ def add_meta_params(*args)
115
+ args.unshift *[::Klarna::API::PROTOCOL_VERSION, ::XMLRPC::Client::USER_AGENT]
116
+ args
117
+ end
118
+
119
+ # Pass additional required digest args to the raw digest method.
120
+ #
121
+ def digest(*args)
122
+ options = args.extract_options!
123
+ ::Klarna::API.digest(*[(self.store_id unless options[:store_id] == false), args, self.store_secret].compact.flatten)
124
+ end
125
+
126
+ end
127
+ end
128
+ end