klarna 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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