From f29b44e558046740f39936b95a2f45cf95b07a0f Mon Sep 17 00:00:00 2001 From: Artsiom Kuts Date: Thu, 28 Jul 2022 18:37:42 +0200 Subject: [PATCH 1/7] refactoring wip --- Gemfile | 4 - Gemfile.lock | 34 -- Rakefile | 2 +- lib/trustly.rb | 34 +- lib/trustly/api.rb | 152 +++++---- lib/trustly/api/signed.rb | 300 ++++++++++-------- lib/trustly/data.rb | 79 ++--- lib/trustly/data/jsonrpc_request.rb | 120 ++++--- lib/trustly/data/jsonrpc_response.rb | 22 +- .../data/jsonrpcnotification_request.rb | 61 ++-- .../data/jsonrpcnotification_response.rb | 78 +++-- lib/trustly/data/request.rb | 33 +- lib/trustly/data/response.rb | 106 +++---- lib/trustly/exception.rb | 3 +- .../exception/authentification_error.rb | 2 +- lib/trustly/exception/configuration_error.rb | 2 + lib/trustly/exception/connection_error.rb | 2 +- lib/trustly/exception/data_error.rb | 3 +- .../exception/jsonrpc_version_error.rb | 3 +- lib/trustly/exception/signature_error.rb | 2 +- lib/trustly/version.rb | 2 +- pkg/trustly-client-ruby-0.0.6.gem | Bin 10240 -> 0 bytes pkg/trustly-client-ruby-0.0.7.gem | Bin 10240 -> 0 bytes pkg/trustly-client-ruby-0.0.8.gem | Bin 10240 -> 0 bytes pkg/trustly-client-ruby-0.0.9.gem | Bin 10240 -> 0 bytes pkg/trustly-client-ruby-0.1.0.gem | Bin 10752 -> 0 bytes pkg/trustly-client-ruby-0.1.1.gem | Bin 10752 -> 0 bytes pkg/trustly-client-ruby-0.1.2.gem | Bin 10752 -> 0 bytes pkg/trustly-client-ruby-0.1.3.gem | Bin 10752 -> 0 bytes pkg/trustly-client-ruby-0.1.4.gem | Bin 10752 -> 0 bytes pkg/trustly-client-ruby-0.1.5.gem | Bin 10752 -> 0 bytes pkg/trustly-client-ruby-0.1.6.gem | Bin 10752 -> 0 bytes pkg/trustly-client-ruby-0.1.7.gem | Bin 10752 -> 0 bytes pkg/trustly-client-ruby-0.1.71.gem | Bin 11776 -> 0 bytes pkg/trustly-client-ruby-0.1.72.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.75.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.78.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.8.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.81.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.85.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.86.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.87.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.88.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.89.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.91.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.92.gem | Bin 12800 -> 0 bytes pkg/trustly-client-ruby-0.1.93.gem | Bin 12800 -> 0 bytes trustly-client-ruby.gemspec | 1 + trustly-client-ruby.gemspec~ | 25 -- 49 files changed, 482 insertions(+), 588 deletions(-) delete mode 100644 Gemfile delete mode 100644 Gemfile.lock create mode 100644 lib/trustly/exception/configuration_error.rb delete mode 100644 pkg/trustly-client-ruby-0.0.6.gem delete mode 100644 pkg/trustly-client-ruby-0.0.7.gem delete mode 100644 pkg/trustly-client-ruby-0.0.8.gem delete mode 100644 pkg/trustly-client-ruby-0.0.9.gem delete mode 100644 pkg/trustly-client-ruby-0.1.0.gem delete mode 100644 pkg/trustly-client-ruby-0.1.1.gem delete mode 100644 pkg/trustly-client-ruby-0.1.2.gem delete mode 100644 pkg/trustly-client-ruby-0.1.3.gem delete mode 100644 pkg/trustly-client-ruby-0.1.4.gem delete mode 100644 pkg/trustly-client-ruby-0.1.5.gem delete mode 100644 pkg/trustly-client-ruby-0.1.6.gem delete mode 100644 pkg/trustly-client-ruby-0.1.7.gem delete mode 100644 pkg/trustly-client-ruby-0.1.71.gem delete mode 100644 pkg/trustly-client-ruby-0.1.72.gem delete mode 100644 pkg/trustly-client-ruby-0.1.75.gem delete mode 100644 pkg/trustly-client-ruby-0.1.78.gem delete mode 100644 pkg/trustly-client-ruby-0.1.8.gem delete mode 100644 pkg/trustly-client-ruby-0.1.81.gem delete mode 100644 pkg/trustly-client-ruby-0.1.85.gem delete mode 100644 pkg/trustly-client-ruby-0.1.86.gem delete mode 100644 pkg/trustly-client-ruby-0.1.87.gem delete mode 100644 pkg/trustly-client-ruby-0.1.88.gem delete mode 100644 pkg/trustly-client-ruby-0.1.89.gem delete mode 100644 pkg/trustly-client-ruby-0.1.91.gem delete mode 100644 pkg/trustly-client-ruby-0.1.92.gem delete mode 100644 pkg/trustly-client-ruby-0.1.93.gem delete mode 100644 trustly-client-ruby.gemspec~ diff --git a/Gemfile b/Gemfile deleted file mode 100644 index dc92c4f..0000000 --- a/Gemfile +++ /dev/null @@ -1,4 +0,0 @@ -source 'https://rubygems.org' - -# Specify your gem's dependencies in trustly-client-ruby.gemspec -gemspec diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 5297065..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,34 +0,0 @@ -PATH - remote: . - specs: - trustly-client-ruby (0.1.95) - rake - -GEM - remote: https://rubygems.org/ - specs: - diff-lcs (1.2.5) - rake (11.2.2) - rspec (3.5.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-core (3.5.2) - rspec-support (~> 3.5.0) - rspec-expectations (3.5.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-mocks (3.5.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-support (3.5.0) - -PLATFORMS - ruby - -DEPENDENCIES - rspec (>= 2.0.0) - trustly-client-ruby! - -BUNDLED WITH - 1.10.6 diff --git a/Rakefile b/Rakefile index a1f5747..9d043d7 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,5 @@ require "bundler/gem_tasks" task :console do - exec "irb -r trustly -r 'active_support/core_ext/object/try' -r 'active_support/core_ext/hash/keys' -r 'JSON' -I ./lib" + exec "irb -r trustly -I ./lib" end diff --git a/lib/trustly.rb b/lib/trustly.rb index de31606..743ae20 100644 --- a/lib/trustly.rb +++ b/lib/trustly.rb @@ -1,22 +1,22 @@ module Trustly end -require "trustly/exception" -require "trustly/exception/authentification_error" -require "trustly/exception/connection_error" -require "trustly/exception/data_error" -require "trustly/exception/jsonrpc_version_error" -require "trustly/exception/signature_error" +require 'trustly/exception' +require 'trustly/exception/authentification_error' +require 'trustly/exception/connection_error' +require 'trustly/exception/data_error' +require 'trustly/exception/configuration_error' +require 'trustly/exception/jsonrpc_version_error' +require 'trustly/exception/signature_error' -require "trustly/data" -require "trustly/data/request" -require "trustly/data/response" -require "trustly/data/jsonrpc_request" -require "trustly/data/jsonrpc_response" -require "trustly/data/jsonrpcnotification_request" -require "trustly/data/jsonrpcnotification_response" - -require "trustly/api" -require "trustly/api/signed" -require "trustly/version" +require 'trustly/data' +require 'trustly/data/request' +require 'trustly/data/response' +require 'trustly/data/jsonrpc_request' +require 'trustly/data/jsonrpc_response' +require 'trustly/data/jsonrpcnotification_request' +require 'trustly/data/jsonrpcnotification_response' +require 'trustly/api' +require 'trustly/api/signed' +require 'trustly/version' diff --git a/lib/trustly/api.rb b/lib/trustly/api.rb index 5860de8..7a7269e 100644 --- a/lib/trustly/api.rb +++ b/lib/trustly/api.rb @@ -1,109 +1,107 @@ - class Trustly::Api - - attr_accessor :api_host, :api_port, :api_is_https,:last_request,:trustly_publickey,:trustly_verifyer - - def serialize_data(object) - serialized = "" - if object.is_a?(Array) - # Its an array - object.each do |obj| - serialized.concat(obj.is_a?(Hash) ? serialize_data(obj) : obj.to_s) - end - elsif object.is_a?(Hash) - # Its a Hash - Hash[object.sort.each{}].each do |key,value| - serialized.concat(key.to_s).concat(serialize_data(value)) - end - else - # Anything else: numbers, symbols, values - serialized.concat object.to_s - end - return serialized - end - - def initialize(host,port,is_https,pem_file) - self.api_host = host - self.api_port = port - self.api_is_https = is_https - - self.load_trustly_publickey + attr_accessor :api_host, + :api_port, + :api_is_https, + :last_request, + :trustly_key + + def initialize(**config) + self.api_host = config.fetch(:host, nil) + self.api_port = config.fetch(:port, nil) + self.api_is_https = config.fetch(:is_https, nil) + + self.load_trustly_key(config[:public_key]) + validate! end - def load_trustly_publickey - self.trustly_publickey = OpenSSL::PKey::RSA.new(ENV['TRUSTLY_PUBLIC_KEY']) + def url_path(_request = nil) + raise NotImplementedError end - def url_path(request=nil) + def handle_response(_request, _http_call) raise NotImplementedError end - def handle_response(request,httpcall) + def insert_credentials(_request) raise NotImplementedError end - def insert_credentials(request) - raise NotImplementedError + def verify_signed_response(response) + method = response.method || '' + uuid = response.uuid || '' + raw_signature = Base64.decode64(response.signature || '') + serial_data = "#{method}#{uuid}#{self.serialize(response.data)}" + trustly_key.public_key.verify( + OpenSSL::Digest::SHA1.new, raw_signature, serial_data + ) end - def verify_trustly_signed_notification(response) - method = response.get_method() - uuid = response.get_uuid() - signature = response.get_signature() - data = response.get_data() - return self._verify_trustly_signed_data(method, uuid, signature, data) + private + + def serialize(object) + serialized = "" + case object + when Array then serialize_array(object, serialized) + when Hash then serialize_hash(object, serialized) + else serialized.concat(object.to_s) + end + serialized end - protected + def serialize_array(object, serialized) + object.each { |value| serialized.concat(serialize(value)) } + end - def _verify_trustly_signed_data(method, uuid, signature, data) - method = '' if method.nil? - uuid = '' if uuid.nil? - serial_data = "#{method}#{uuid}#{self.serialize_data(data)}" - raw_signature = Base64.decode64(signature) - return self.trustly_publickey.public_key.verify(OpenSSL::Digest::SHA1.new, raw_signature, serial_data) + def serialize_hash(object, serialized) + object.sort.each do |key, value| + serialized.concat(key.to_s).concat(serialize(value)) + end end - def verify_trustly_signed_response(response) - method = response.get_method() - uuid = response.get_uuid() - signature = response.get_signature() - data = response.get_data() - return self._verify_trustly_signed_data(method, uuid, signature, data) + def load_trustly_key(pkey) + self.trustly_key = OpenSSL::PKey::RSA.new(pub_key) unless pkey.nil? end + def validate! + return if configuration_errors.empty? + + errors_string = configuration_errors.join('; ') + raise Trustly::Exception::ConfigurationError, errors_string + end - def set_host(host=nil,port=nil,is_https=nil) - self.api_host = host unless host.nil? - self.load_trustly_publickey() unless host.nil? - self.api_port = port unless port.nil? - self.is_https = is_https unless is_https.nil? + def configuration_errors + errors = [] + errors.push 'Api host not specified' if api_host.nil? + errors.push 'Trustly public key not specified' if trustly_key.nil? + errors end def base_url - if self.api_is_https - return (self.api_port == 443) ? "https://#{self.api_host}" : "https://#{self.api_host}:#{self.api_port}" - else - return (self.api_port == 80) ? "http://#{self.api_host}" : "http://#{self.api_host}:#{self.api_port}" - end + schema = api_is_https ? 'https' : 'http' + add_port = (api_is_https && api_port != 443) || api_port != 80 + port = add_port ? ":#{api_port}" : '' + "#{schema}://#{api_host}#{port}" end - def uri(request) - return URI("#{self.base_url}#{self.url_path(request)}") + def url(request) + return URI.parse("#{self.base_url}#{url_path(request)}") end def call_rpc(request) - self.insert_credentials(request) - self.last_request = request - uri = self.uri(request) - http_req = Net::HTTP::Post.new(uri.path, initheader = {'Content-Type' =>'application/json'}) - http_req.body = request.json() - http_res = Net::HTTP.start(uri.hostname, uri.port,{use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE}) { |http| http.request(http_req) } - return self.handle_response(request,http_res) + insert_credentials!(request) + self.last_request = request + request_uri = url(request) + body = request.to_json + response = connection(request_uri).post( + request_uri.path, body, { 'Content-Type' => 'application/json' } + ) + handle_response(request, response) end - - + def connection(request_uri) + Faraday.new(request_uri.origin) do |conn| + conn.response :json + conn.adapter :net_http + end + end end - - diff --git a/lib/trustly/api/signed.rb b/lib/trustly/api/signed.rb index 6af2c39..4844850 100644 --- a/lib/trustly/api/signed.rb +++ b/lib/trustly/api/signed.rb @@ -1,179 +1,213 @@ class Trustly::Api::Signed < Trustly::Api + DEFAULT_API_PATH = '/api/1' + SIGNATURE_ERROR = 'Incoming message signature is not valid' + UUID_MISMATCH = 'Incoming response is not related to the request. UUID mismatch.' - attr_accessor :url_path,:api_username, :api_password, :merchant_privatekey, :is_https + attr_accessor :url_path, + :api_username, + :api_password, + :merchant_key + def initialize(**config) + full_config = default_config.merge(config) - def initialize(_options) - options = { - :host => 'test.trustly.com', - :port => 443, - :is_https => true, - # :private_pem => "#{Rails.root}/certs/trustly/test.merchant.private.pem", - # :public_pem => "#{Rails.root}/certs/trustly/test.trustly.public.pem" - :private_pem => ENV['TRUSTLY_PRIVATE_KEY'], - :public_pem => ENV['TRUSTLY_PUBLIC_KEY'] - }.merge(_options) + super(**full_config.slice(%i[host port is_https public_pem])) + self.api_username = full_config.fetch(:username, nil) + self.api_password = full_config.fetch(:password, nil) + self.url_path = DEFAULT_API_PATH + self.load_merchant_key(full_config[:private_pem]) - # raise Trustly::Exception::SignatureError, "File '#{options[:private_pem]}' does not exist" unless File.file?(options[:private_pem]) - # raise Trustly::Exception::SignatureError, "File '#{options[:public_pem]}' does not exist" unless File.file?(options[:public_pem]) - - super(options[:host],options[:port],options[:is_https],options[:public_pem]) - - self.api_username = options.try(:[],:username) - self.api_password = options.try(:[],:password) - self.is_https = options.try(:[],:is_https) - self.url_path = '/api/1' - - raise Trustly::Exception::AuthentificationError, "Username not valid" if self.api_username.nil? - raise Trustly::Exception::AuthentificationError, "Password not valid" if self.api_password.nil? - - self.load_merchant_privatekey + validate! + end + def load_merchant_key(pkey) + self.merchant_key = OpenSSL::PKey::RSA.new(pkey) if pkey end - # def load_merchant_privatekey(filename) - # self.merchant_privatekey = OpenSSL::PKey::RSA.new(File.read(filename)) - # end + def configuration_errors + errors = super + errors.push 'Username not specified' if api_username.nil? + errors.push 'Password not specified' if api_password.nil? + errors.push 'Merchant private key not specified' if merchant_key.nil? + errors + end - def load_merchant_privatekey - self.merchant_privatekey = OpenSSL::PKey::RSA.new(ENV['TRUSTLY_PRIVATE_KEY']) + def handle_response(request, response) + rcp_response = Trustly::Data::JSONRPCResponse.new(response) + check_response(rcp_response, request) + rpc_response end - def handle_response(request,httpcall) - response = Trustly::Data::JSONRPCResponse.new(httpcall) - raise Trustly::Exception::SignatureError,'Incoming message signature is not valid' unless self.verify_trustly_signed_response(response) - raise Trustly::Exception::DataError, 'Incoming response is not related to request. UUID mismatch.' if response.get_uuid() != request.get_uuid() - return response + def check_response(response, request) + unless self.verify_signed_response(response) + raise Trustly::Exception::SignatureError, SIGNATURE_ERROR + end + if response.uuid != request.uuid + raise Trustly::Exception::DataError, UUID_MISMATCH + end end - def insert_credentials(request) - request.set_data( 'Username' , self.api_username) - request.set_data( 'Password' , self.api_password) - request.set_param('Signature', self.sign_merchant_request(request)) + def insert_credentials!(request) + request.update_data_at('Username', api_username) + request.update_data_at('Password', api_password) + request.signature = sign_merchant_request(request) end - def sign_merchant_request(data) - raise Trustly::Exception::SignatureError, 'No private key has been loaded' if self.merchant_privatekey.nil? - method = data.get_method() - method = '' if method.nil? - uuid = data.get_uuid() - uuid = '' if uuid.nil? - data = data.get_data() - data = {} if data.nil? + def sign_merchant_request(request) + method = request.method || '' + uuid = request.uuid || '' + data = request.data || {} - serial_data = "#{method}#{uuid}#{self.serialize_data(data)}" - sha1hash = OpenSSL::Digest::SHA1.new - signature = self.merchant_privatekey.sign(sha1hash,serial_data) - return Base64.encode64(signature).chop #removes \n + serial_data = "#{method}#{uuid}#{serialize(data)}" + sha1hash = OpenSSL::Digest::SHA1.new + signature = self.merchant_key.sign(sha1hash, serial_data) + Base64.encode64(signature).chop end - def url_path(request=nil) - return '/api/1' + def url_path(_request = nil) + DEFAULT_API_PATH end def call_rpc(request) - request.set_uuid(SecureRandom.uuid) if request.get_uuid().nil? - return super(request) + request.uuid = SecureRandom.uuid if request.uuid.nil? + super(request) end - def void(orderid) - request = Trustly::Data::JSONRPCRequest.new('Void',{"OrderID"=>orderid},nil) - return self.call_rpc(request) - end - - def deposit(_options) - options = { - "Locale" => "es_ES", - "Country" => "ES", - "Currency" => "EUR", - "SuccessURL" => "https://www.trustly.com/success", - "FailURL" => "https://www.trustly.com/fail", - "NotificationURL" => "https://test.trustly.com/demo/notifyd_test", - "Amount" => 0 - }.merge(_options) - - ["Locale","Country","Currency","SuccessURL","FailURL","Amount","NotificationURL","EndUserID","MessageID"].each do |req_attr| - raise Trustly::Exception::DataError, "Option not valid '#{req_attr}'" if options.try(:[],req_attr).nil? + def call_rpc_for_data(method, options, data:, required:, attriubtes: []) + missing_options = required.find_all { |req| options[req].nil? } + unless missing_options.empty? + msg = "Required data is missing: #{missing_options.join('; ')}" + raise Trustly::Exception::DataError, msg end - - raise Trustly::Exception::DataError, "Amount is 0" if options["Amount"].nil? || options["Amount"].to_f <= 0.0 - - attributes = options.slice( - "Locale","Country","Currency", - "SuggestedMinAmount","SuggestedMaxAmount","Amount", - "Currency","Country","IP", - "SuccessURL","FailURL","TemplateURL","URLTarget", - "MobilePhone","Firstname","Lastname","NationalIdentificationNumber", - "ShopperStatement" + request = Trustly::Data::JSONRPCRequest.new( + method: method, + data: options.slice(*data), + attributes: attributes.empty? ? nil : options.slice(*attributes) ) - - data = options.slice("NotificationURL","EndUserID","MessageID") - - # check required fields - request = Trustly::Data::JSONRPCRequest.new('Deposit',data,attributes) - return self.call_rpc(request) - #options["HoldNotifications"] = "1" unless + call_rpc(request) end - def refund(_options) - options = { - "Currency" => "EUR" - }.merge(_options) - - # check for required options - ["OrderID","Amount","Currency"].each{|req_attr| raise Trustly::Exception::DataError, "Option not valid '#{req_attr}'" if options.try(:[],req_attr).nil? } - - request = Trustly::Data::JSONRPCRequest.new('Refund',options,nil) - return self.call_rpc(request) + def void(**options) + required = %w[OrderId] + data = %w[OrderId] + call_rpc_for_data('Void', options, data: data, required: required) end - def select_account(_options) - options = { - "Locale" => "se_SE", - "Country" => "SE", - "SuccessURL" => "https://www.trustly.com/success", - "FailURL" => "https://www.trustly.com/fail", - "NotificationURL" => "https://test.trustly.com/demo/notifyd_test", - }.merge(_options) - - ["Locale","Country","SuccessURL","FailURL","NotificationURL","EndUserID","MessageID"].each do |req_attr| - raise Trustly::Exception::DataError, "Option not valid '#{req_attr}'" if options.try(:[],req_attr).nil? - end - - attributes = options.slice( - "Locale","Country","IP", - "SuccessURL","FailURL","TemplateURL","URLTarget", - "MobilePhone","Firstname","Lastname","NationalIdentificationNumber" + def deposit(**options) + required = %w[ + Locale Country Currency SuccessURL FailURL NotificationURL Amount + EndUserID MessageID Firstname Lastname ShopperStatement + ] + attributes = %w[ + Locale Country Currency SuggestedMinAmount SuggestedMaxAmount Amount + IP SuccessURL FailURL TemplateURL URLTarget MobilePhone ShopperStatement + Firstname Lastname NationalIdentificationNumber Email AccountID + UnchangeableNationalIdentificationNumber ShippingAddressCountry + ShippingAddressPostalCode ShippingAddressLine1 ShippingAddressLine2 + ShippingAddress RequestDirectDebitMandate ChargeAccountID QuickDeposit + URLScheme ExternalReference PSPMerchant PSPMerchantURL + MerchantCategoryCode RecipientInformation + ] + data = %w[NotificationURL EndUserID MessageID] + call_rpc_for_data( + 'Deposit', + options, data: data, attributes: attributes, required: required ) + end - data = options.slice("NotificationURL","EndUserID","MessageID") + def refund(**options) + required = %w[OrderId Amount Currency] + data = %w[OrderId Amount Currency] + attributes = %w[ExternalReference] + call_rpc_for_data( + 'Refund', options, + data: data, attributes: attributes, required: required + ) + end - # check required fields - request = Trustly::Data::JSONRPCRequest.new('SelectAccount',data,attributes) - return self.call_rpc(request) + def select_account(**options) + required = %w[ + Locale Country SuccessURL FailURL NotificationURL EndUserID MessageID + Firstname Lastname + ] + attributes = %w[ + Locale Country Firstname Lastname SuccessURL FailURL Email IP + RequestDirectDebitMandate TemplateURL URLTarget MobilePhone + NationalIdentificationNumber UnchangeableNationalIdentificationNumber + ShopperStatement DateOfBirth URLScheme PSPMerchant PSPMerchantURL + MerchantCategoryCode + ] + data = %w[NotificationURL EndUserID MessageID] + call_rpc_for_data( + 'SelectAccount', options, + data: data, attributes: attributes, required: required + ) end - def account_payout(_options) - options = { - "Currency" => "SEK" - }.merge(_options) + def account_payout(**options) + required = %w[ + NotificationURL AccountID EndUserID MessageID Amount Currency + ShopperStatement + ] + data = %w[ + NotificationURL AccountID EndUserID MessageID Amount Currency + ] + attributes = %w[ + ShopperStatement PSPMerchant PSPMerchantURL + ExternalReference MerchantCategoryCode SenderInformation + ] + call_rpc_for_data( + 'AccountPayout', options, + data: data, attributes: attributes, required: required + ) + end - # check for required options - ["NotificationURL","AccountID","EndUserID","MessageID","Amount","Currency"].each{|req_attr| raise Trustly::Exception::DataError, "Option not valid '#{req_attr}'" if options.try(:[],req_attr).nil? } + def register_account(**options) + required = %w[ + EndUserID ClearingHouse BankNumber AccountNumber Firstname Lastname + ] + data = %w[ + EndUserID ClearingHouse BankNumber AccountNumber Firstname Lastname + ] + attributes = %w[ + DateOfBirth MobilePhone NationalIdentificationNumber AddressCountry + AddressPostalCode AddressCity AddressLine1 AddressLine2 Address Email + ] + call_rpc_for_data( + 'RegisterAccount', options, + data: data, attributes: attributes, required: required + ) + end - request = Trustly::Data::JSONRPCRequest.new('AccountPayout',options,nil) - return self.call_rpc(request) + def get_withdrawals(**options) + data = %w[OrderId] + required = %w[OrderId] + call_rpc_for_data( + 'GetWithdrawals', options, + data: data, required: required + ) end - def notification_response(notification,success=true) - response = Trustly::JSONRPCNotificationResponse.new(notification,success) - response.set_signature(self.sign_merchant_request(response)) - return response + def notification_response(request, success = true) + response = Trustly::JSONRPCNotificationResponse.new( + request: request, success: success + ) + response.signature = sign_merchant_request(response) + response end def withdraw(_options) end + def default_config + { + host: 'test.trustly.com', + port: 443, + is_https: true, + private_pem: ENV['MERCHANT_PRIVATE_KEY'], + public_pem: ENV['TRUSTLY_PUBLIC_KEY'] + } + end end diff --git a/lib/trustly/data.rb b/lib/trustly/data.rb index 9acb20c..c2de828 100644 --- a/lib/trustly/data.rb +++ b/lib/trustly/data.rb @@ -1,65 +1,46 @@ class Trustly::Data - attr_accessor :payload - def initialize + def initialize(**_options) self.payload = {} end - # Vacuum out all keys being set to Nil in the data to be communicated - def vacumm(data) - if data.is_a? Array - ret = [] - data.each do |elem| - unless elem.nil? - v = self.vacumm elem - ret.append(v) unless v.nil? - end - end - return nil if ret.length == 0 - return ret - elsif data.is_a? Hash - ret = {} - data.each do |key,elem| - unless elem.nil? - v = self.vacumm elem - ret[key.to_s] = elem unless v.nil? - end - end - return nil if ret.length == 0 - return ret - else - return data - end - end - def get(name=nil) - return name.nil? ? self.payload.dup : self.payload.try(:[],name) + def to_json + payload.to_json end - def get_from(sub,name) - return nil if sub.nil? || name.nil? || self.payload.try(:[],sub).nil? || self.payload[sub].try(:[],name).nil? - return self.payload[sub][name] - end + private - def set(name,value) - self.payload[name] = value - return value + # Vacuum out all keys being set to nil in the data to be communicated + def vacuum(data) + case data + when Array then vacuum_array_data(data) + when Hash then vacuum_hash_data(data) + else data + end end - def set_in(sub,name,value,parent=nil) - return nil if sub.nil? || name.nil? - self.payload[sub] = {} if self.payload.try(:[],sub).nil? - self.payload[sub][name] = value + def vacuum_array_data(data) + ret = data.each_with_object([]) do |element, acc| + processed_element = vacuum(element) unless element.nil? + next if processed_element.nil? + acc.push(processed_element) + end + ret.length == 0 ? nil : ret end - def pop(name) - v = self.payload.try(:[],name) - delete self.payload[name] unless v.nil? - return v + def vacuum_hash_data(data) + ret = data.each_with_object({}) do |(key, element), acc| + processed_element = vacuum(element) unless element.nil? + next if processed_element.nil? + acc[key] = processed_element + end + ret.length == 0 ? nil : ret end - def json() - self.payload.to_json + def stringify_hash(hash) + hash.each_with_object({}) do |(k, v), acc| + acc[k.to_s] = v.is_a?(Hash) ? stringify_hash(v) : v + end end - -end \ No newline at end of file +end diff --git a/lib/trustly/data/jsonrpc_request.rb b/lib/trustly/data/jsonrpc_request.rb index 0bf2a2e..7f8777e 100644 --- a/lib/trustly/data/jsonrpc_request.rb +++ b/lib/trustly/data/jsonrpc_request.rb @@ -1,92 +1,78 @@ class Trustly::Data::JSONRPCRequest < Trustly::Data::Request + def initialize(**options) + super(**options.slice(:method, :payload)) - def initialize(method=nil,data=nil,attributes=nil) - - if !data.nil? || !attributes.nil? - self.payload = {"params"=>{}} - unless data.nil? - if !data.is_a?(Hash) && !attributes.nil? - raise TypeError, "Data must be a Hash if attributes is provided" - else - self.payload["params"]["Data"] = data - end - else - self.payload["params"]["Data"] = {} - end - - self.payload["params"]["Data"]["Attributes"] = attributes unless attributes.nil? - end - - self.payload['method'] = method unless method.nil? - self.payload['params'] = {} unless self.get('params') - self.payload['version'] = '1.1' + data = options[:data] + attributes = options[:attributes] + payload['params'] ||= {} + payload['version'] ||= '1.1' + initialize_data_and_attributes(data, attributes) end + def params(name) + payload['params'] + end - def get_param(name) - return self.payload['params'].try(:[],name) + def data + params['Data'] end - def get_data(name=nil) - data = self.get_param('Data') - return data if name.nil? - raise KeyError, "Not found #{name} in data" if data.nil? - return data.dup if name.nil? - return data.try(:[],name) + def data_at(name) + params.dig('Data', name) end - def get_attribute(name) - data = self.get_param('Data') - if data.nil? - attributes = nil - else - attributes = data.try(:[],'Attributes') - end - raise KeyError, "Not found 'Attributes' in data" if attributes.nil? - return attributes.dup if name.nil? - return attributes.try(:[],name) + def attribute_at(name) + params.dig('Data', 'Attributes', name) end - def set_param(name,value) - self.payload['params'][name] = value + def update_data_at(name, value) + params['Data'] ||= {} + params['Data'][name] = value end - def set_data(name,value) - unless name.nil? - self.payload['params']['Data'] = {} if self.payload['params'].try(:[],'Data').nil? - self.payload['params']['Data'][name] = value - end - return value + def update_attribute_at(name, value) + params['Data'] ||= {} + params['Data']['Attributes'] ||= {} + params['Data']['Attributes'][name] = value end - def set_attributes(name,value) - unless name.nil? - self.payload['params']['Data'] = {} if self.payload['params'].try(:[],'Data').nil? - self.payload['params']['Data']['Attributes'] = {} if self.payload['params']['Data'].try(:[],'Attributes').nil? - self.payload['params']['Data']['Attributes'][name] = value - end - return value + def signature + params['Signature'] end - def set_uuid(uuid) - return self.set_param('UUID',uuid) + def signature=(value) + params['Signature'] = value end - def get_uuid - return self.get_param('UUID') - rescue KeyError => e - return nil - end + def method=(value) + super + payload['method'] = method + end - def set_method(method) - return self.set('method',method) + def uuid + params['UUID'] end - def get_method() - return self.get('method') - rescue KeyError => e - return nil + def uuid=(value) + params['UUID'] = value end -end \ No newline at end of file + private + + def initialize_data_and_attributes(data, attributes) + return if data.nil? && attributes.nil? + + if data.nil? + payload['params']['Data'] ||= {} + else + if !data.is_a?(Hash) && !attributes.nil? + raise TypeError, 'Data must be a Hash if attributes are provided' + end + payload['params']['Data'] = vacuum(data) + end + return if attributes.nil? + + payload['params']['Data']['Attributes'] ||= vacuum(attributes) + end +end diff --git a/lib/trustly/data/jsonrpc_response.rb b/lib/trustly/data/jsonrpc_response.rb index 9d5b883..77eaf4b 100644 --- a/lib/trustly/data/jsonrpc_response.rb +++ b/lib/trustly/data/jsonrpc_response.rb @@ -1,15 +1,17 @@ class Trustly::Data::JSONRPCResponse < Trustly::Data::Response + VERSION_ERROR = 'JSON RPC Version is not supported' - def initialize(http_response) - super(http_response) - version = self.get("version") - raise Trustly::Exception::JSONRPCVersionError, "JSON RPC Version is not supported" if version != '1.1' + def initialize(**options) + super + version = payload['version'] + if version != '1.1' + raise Trustly::Exception::JSONRPCVersionError, VERSION_ERROR + end end - def get_data(name=nil) - return self.response_result.try(:[],"data") if name.nil? - return Trustly::Exception::DataError, "Data not found or key is null" if self.response_result.try(:[],"data").nil? || name.nil? - return self.response_result["data"][name] + def data_at(name) + return if data.nil? + + data[name] end - -end \ No newline at end of file +end diff --git a/lib/trustly/data/jsonrpcnotification_request.rb b/lib/trustly/data/jsonrpcnotification_request.rb index 0af0126..82f2e81 100644 --- a/lib/trustly/data/jsonrpcnotification_request.rb +++ b/lib/trustly/data/jsonrpcnotification_request.rb @@ -1,52 +1,41 @@ -class Trustly::JSONRPCNotificationRequest < Trustly::Data +class Trustly::JSONRPCNotificationRequest < Trustly::Request + def initialize(**options) + super(payload: notification_body(options[:notification_body])) + return if version == '1.1' - attr_accessor :notification_body, :payload - - def initialize(notification_body) - super() - self.notification_body = notification_body - unless self.notification_body.is_a?(Hash) - begin - self.payload = JSON.parse(self.notification_body) - rescue JSON::ParserError => e - raise Trustly::Exception::DataError, e.message - end - - raise Trustly::Exception::JSONRPCVersionError, 'JSON RPC Version #{(self.get_version()} is not supported' if self.get_version() != '1.1' - else - self.payload = self.notification_body.deep_stringify_keys - end + error_message = "JSON RPC Version #{version} is not supported" + raise Trustly::Exception::JSONRPCVersionError, error_message end - def get_version() - return self.get('version') + def notification_body(body) + return stringify_hash(body) if body.is_a?(Hash) + + JSON.parse(body) + rescue JSON::ParserError => e + raise Trustly::Exception::DataError, e.message end - def get_method() - return self.get('method') + def version + payload['version'] end - def get_uuid() - return self.get_params('uuid') + def method + payload['method'] end - def get_signature() - return self.get_params('signature') + def signature + payload.dig('params', 'signature') end - def get_params(name) - raise KeyError,"#{name} is not present in params" if name.nil? || self.payload.try(:[],"params").nil? || self.payload["params"].try(:[],name).nil? - return self.payload["params"][name] + def uuid + payload.dig('params', 'uuid') end - def get_data(name=nil) - if name.nil? - raise KeyError,"Data not present" if self.payload.try(:[],"params").nil? || self.payload["params"].try(:[],"data").nil? - return self.payload["params"]["data"] - else - raise KeyError,"#{name} is not present in data" if name.nil? || self.payload.try(:[],"params").nil? || self.payload["params"].try(:[],"data").nil? || self.payload["params"]["data"].try(:[],name).nil? - return self.payload["params"]["data"][name] - end + def data_at(key) + payload.dig('params', 'data', key) end + def attribute_at(key) + payload.dig('params', 'data', 'attributes', key) + end end diff --git a/lib/trustly/data/jsonrpcnotification_response.rb b/lib/trustly/data/jsonrpcnotification_response.rb index 2539eef..b29a4b0 100644 --- a/lib/trustly/data/jsonrpcnotification_response.rb +++ b/lib/trustly/data/jsonrpcnotification_response.rb @@ -1,63 +1,59 @@ class Trustly::JSONRPCNotificationResponse < Trustly::Data - - def initialize(request,success=nil) - super() - uuid = request.get_uuid() - method = request.get_method() + def initialize(**options) + super + request = options[:request] + success = options[:success] - self.set('version','1.1') - self.set_result('uuid', uuid) unless uuid.nil? - self.set_result('method', method) unless method.nil? - self.set_data( 'status', (!success.nil? && !success ? 'FAILED' : 'OK' )) + self.version = '1.1' + self.uuid = request.uuid if request.uuid + self.method = request.method if request.method + self.update_data_at('status', success ? 'OK' : 'FAILED') end - def set_signature(signature) - self.set_result('signature',signature) + def signature=(value) + update_result_at('signature', value) end - def set_result(name,value) - return nil if name.nil? || value.nil? - self.payload["result"] = {} if self.payload.try(:[],"result").nil? - self.payload["result"][name] = value + def method=(value) + update_result_at('method', value) end - def set_data(name,value) - return nil if name.nil? || value.nil? - self.payload["result"] = {} if self.payload.try(:[],"result").nil? - self.payload["result"]["data"] = {} if self.payload["result"].try(:[],"data").nil? - self.payload["result"]["data"][name] = value + def uuid=(value) + update_result_at('uuid', value) end - def get_result(name) - raise KeyError,"#{name} is not present in result" if name.nil? || self.payload.try(:[],"result").nil? || self.payload["result"].try(:[],name).nil? - return self.payload["result"][name] + def version=(value) + payload['version'] = value end - def get_data(name=nil) - raise KeyError,"#{name} is not present in data" if name.nil? || self.payload.try(:[],"result").nil? || self.payload["result"].try(:[],"data").nil? || self.payload["result"]["data"].try(:[],name).nil? - return self.payload["result"]["data"][name] + def update_result_at(name, value) + payload['result'] ||= {} + payload['result'][name] = value end - def get_data(name=nil) - if name.nil? - raise KeyError,"Data not present" if self.payload.try(:[],"result").nil? || self.payload["result"].try(:[],"data").nil? - return self.payload["result"]["data"] - else - raise KeyError,"#{name} is not present in data" if name.nil? || self.payload.try(:[],"result").nil? || self.payload["result"].try(:[],"data").nil? || self.payload["result"]["data"].try(:[],name).nil? - return self.payload["result"]["data"][name] - end + def update_data_at(name, value) + payload['result'] ||= {} + payload['result']['data'] ||= {} + payload['result']['data'][name] = value end - def get_method - return self.get_result('method') + def data + payload.dig('result', 'data') end - def get_uuid - return self.get_result('uuid') + def result + payload['result'] end - def get_signature - return self.get_result('signature') + def method + result['method'] end -end \ No newline at end of file + def uuid + result['uuid'] + end + + def signature + result['signature'] + end +end diff --git a/lib/trustly/data/request.rb b/lib/trustly/data/request.rb index f1e26ee..de4fdb8 100644 --- a/lib/trustly/data/request.rb +++ b/lib/trustly/data/request.rb @@ -1,33 +1,12 @@ class Trustly::Data::Request < Trustly::Data - attr_accessor :method - def initialize(method=nil,payload=nil) + def initialize(**options) super - self.payload = self.vacuum(payload) unless payload.nil? - unless method.nil? - self.method = method - else - self.method = self.payload.get('method') + if (new_payload = options[:payload]) + vacuumed_payload = vacuum(new_payload) + self.payload = stringify_hash(vacuumed_payload) end + self.method = options[:method] || payload['method'] end - - def get_method - return self.method - end - - def set_method(method) - self.method = method - return method - end - - def get_uuid - return self.payload.get('uuid') - end - - def set_uuid - self.set('uuid',uuid) - return uuid - end - -end \ No newline at end of file +end diff --git a/lib/trustly/data/response.rb b/lib/trustly/data/response.rb index 8d0f8cc..f383dca 100644 --- a/lib/trustly/data/response.rb +++ b/lib/trustly/data/response.rb @@ -1,80 +1,72 @@ class Trustly::Data::Response < Trustly::Data - attr_accessor :response_status, :response_reason, :response_body, :response_result + attr_accessor :response_status, + :response_reason, + :response_body, + :response_result - def initialize(http_response) #called from Net::HTTP.get_response("trustly.com","/api_path") -> returns Net::HTTPResponse - super() - self.response_status = http_response.code - self.response_reason = http_response.class.name - self.response_body = http_response.body - begin - self.payload = JSON.parse(self.response_body) - rescue JSON::ParserError => e - if self.response_status != 200 - raise Trustly::Exception::ConnectionError, "#{self.response_status}: #{self.response_reason} [#{self.response_body}]" - else - raise Trustly::Exception::DataError, e.message - end - end - - begin - self.response_result = self.get('result') - rescue IndexError::KeyError => e - self.response_result = nil - end - - if self.response_result.nil? - begin - self.response_result = self.payload["error"]["error"] - rescue IndexError::KeyError => e - end - end - raise Trustly::Exception::DataError, "No result or error in response #{self.payload}" if self.response_result.nil? + #called from Net::HTTP.get_response("trustly.com","/api_path") -> returns Net::HTTPResponse + def initialize(**options) + super + http_response = options[:http_response] + process_http_response(http_response) end def error? - return !self.get('error').nil? - rescue IndexError::KeyError => e - return false + !payload['error'].nil? end def error_code - return nil unless self.error? - return self.response_result["data"].try(:[],'code') + return nil unless error? + + response_result.dig('data', 'code') end - def error_msg - return nil unless self.error? - return self.response_result["data"].try(:[],'message') + def error_message + return nil unless error? + + response_result.dig('data', 'message') end def success? - return !self.get('result').nil? - rescue IndexError::KeyError => e - return false + !payload['result'].nil? end - def get_uuid - return self.response_result.try(:[],'uuid') + def data + response_result['data'] end - def get_method - return self.response_result.try(:[],'method') + def uuid + response_result['uuid'] end - def get_signature - return self.response_result.try(:[],"signature") + def method + response_result['method'] end - def get_result - unless name.nil? - if self.response_result.is_a?(Hash) - return self.response_result.try(:[],name) - else - raise StandardError::TypeError, "Result is not a Hash" - end - else - return self.response_result.dup - end + def signature + response_result['signature'] end -end \ No newline at end of file + private + + def process_http_response(http_response) + self.response_status = http_response.code + self.response_reason = http_response.class.name + init_response_result(http_response.body) + end + + def init_response_result(body) + self.payload = JSON.parse(body) + self.response_result = payload['result'] || payload.dig('error', 'error') + return unless response_result.nil? + + message = "No result or error in response #{payload}" + raise Trustly::Exception::DataError, message + rescue JSON::ParserError => e + if response_status != 200 + message = "#{response_status}: #{response_reason} [#{response_body}]" + raise Trustly::Exception::ConnectionError, message + end + raise Trustly::Exception::DataError, e.message + end +end diff --git a/lib/trustly/exception.rb b/lib/trustly/exception.rb index 743e7fb..ea2ecfd 100644 --- a/lib/trustly/exception.rb +++ b/lib/trustly/exception.rb @@ -1,3 +1,2 @@ class Trustly::Exception < Exception - -end \ No newline at end of file +end diff --git a/lib/trustly/exception/authentification_error.rb b/lib/trustly/exception/authentification_error.rb index 75d923d..5571d31 100644 --- a/lib/trustly/exception/authentification_error.rb +++ b/lib/trustly/exception/authentification_error.rb @@ -1,2 +1,2 @@ class Trustly::Exception::AuthentificationError < Exception -end \ No newline at end of file +end diff --git a/lib/trustly/exception/configuration_error.rb b/lib/trustly/exception/configuration_error.rb new file mode 100644 index 0000000..ae62a8d --- /dev/null +++ b/lib/trustly/exception/configuration_error.rb @@ -0,0 +1,2 @@ +class Trustly::Exception::ConfigurationError < Exception +end diff --git a/lib/trustly/exception/connection_error.rb b/lib/trustly/exception/connection_error.rb index 6dbbebd..add0974 100644 --- a/lib/trustly/exception/connection_error.rb +++ b/lib/trustly/exception/connection_error.rb @@ -1,2 +1,2 @@ class Trustly::Exception::ConnectionError < Exception -end \ No newline at end of file +end diff --git a/lib/trustly/exception/data_error.rb b/lib/trustly/exception/data_error.rb index 675397b..3198f10 100644 --- a/lib/trustly/exception/data_error.rb +++ b/lib/trustly/exception/data_error.rb @@ -1,3 +1,2 @@ class Trustly::Exception::DataError < Exception - -end \ No newline at end of file +end diff --git a/lib/trustly/exception/jsonrpc_version_error.rb b/lib/trustly/exception/jsonrpc_version_error.rb index fd504e5..1cc56b2 100644 --- a/lib/trustly/exception/jsonrpc_version_error.rb +++ b/lib/trustly/exception/jsonrpc_version_error.rb @@ -1,3 +1,2 @@ class Trustly::Exception::JSONRPCVersionError < Exception - -end \ No newline at end of file +end diff --git a/lib/trustly/exception/signature_error.rb b/lib/trustly/exception/signature_error.rb index 176485f..a438c69 100644 --- a/lib/trustly/exception/signature_error.rb +++ b/lib/trustly/exception/signature_error.rb @@ -1,2 +1,2 @@ class Trustly::Exception::SignatureError < Exception -end \ No newline at end of file +end diff --git a/lib/trustly/version.rb b/lib/trustly/version.rb index efa1b60..1580d56 100644 --- a/lib/trustly/version.rb +++ b/lib/trustly/version.rb @@ -1,3 +1,3 @@ module Trustly VERSION = "0.1.95" -end \ No newline at end of file +end diff --git a/pkg/trustly-client-ruby-0.0.6.gem b/pkg/trustly-client-ruby-0.0.6.gem deleted file mode 100644 index 009e39eaa9b4da8191934d00e3111d56b3d2a5a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 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 diff --git a/pkg/trustly-client-ruby-0.0.8.gem b/pkg/trustly-client-ruby-0.0.8.gem deleted file mode 100644 index 9bacb0237618125c742587c4dfb94105656aea9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 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 diff --git a/pkg/trustly-client-ruby-0.0.9.gem b/pkg/trustly-client-ruby-0.0.9.gem deleted file mode 100644 index 0b8740f8d0d742ae54cafc145855f6c40d12d964..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 zcmeHsRZu0$vL){B*0@{a?$)?FH0~}98{fD)jYFf2)403SxI2xzyYKzZo%y)$-HDj_ zx-)MgPX1(OWK>1ws;U)Hu^cVGnOc~BGi9~*f%tbB+h4}T#Rc&X|Cjx%XXoHyhhXR6 z=HlYy;^yJxfna0*E8u`2XZv?G^q=?jaQ|lN_E$>)8%s-v|6KT&`2SS@pR@gQxPMvx z-!&zRL4f!%o}>i@J*lnajLnYG^`?_yuWbWI%fgB|bX-}p%>2#+Oj`Vw2W$D?VVmdkd8-!NcSA#^ml43DSuKB{? z*rH0|v@_W>Brci0h|Qq8&{5CO=QStxDc*c8wbPQSkn={XFaAbtE-wP5^b^byeZpk>Bt52$4_Kc7_yN?_Xl#{yW@?13Mn-lVwZlz z0L@`_XJBp0^M5}k-g+nKVxK+EC~rWq2oT|Z@>|xcX7 zTU#!1h&Z7jbajkUIkbzbK+36thNnSLIn1ONc2LMgM-W2ZDP3CDC=~_-C6s-hcZ@=A z4VhbET?HE)rg=gd30T&#e_dW8ipV*PF;*it^4+#tnr0RKaO=y*s+f4@Kw0-)MKxen z8Zv>cmtkkI73(+bLlTcL%>Iep#uPhYT`|(wbKEZaHAhLrSjEhcqWTwY=}~%0E8{Vs zyhQE`x#xTV1*Y}|7p>7GHxBxf&B~CIef_&}%u@2(%)G z;XUC?6eh&UFgkIe@=P8?cbBPFc68guk|iH3o|%<|)3a6dA)^SF1}J;@1n~}8b7J61 zzoyAhPKjHPjei=odsvJQkpRD*r~jr8TAtp|3m=e*3F@3t>Pl(|p7?TvPH6Xe9Tt_lCLS zQ1*qG9BXk`w=+fT;D!jCgx$U8&@hmy&yTJCEL9N5IEm#xofZQwy*!e{eLmw|2zI?=j7$#`d|2;jq`8*|G#mm{>lI6 z!uIZKqiHV_HOHd{_+{#Bo(@N>O+Ofhhp|0LTp*P_$IkjW>|!qwq86#*mv2Ci!lh*D zl{ApbIbK%5c6LykrPGxi)0GC#jlK&fsDg3F8uz;f!jrjHcP7qP(kY^^JsmS2X4U|p z^VP#2P~RDJAh4_9=@8bo`x#AzL`EB;YGaDtBfYo!bUExTMyQC8=Bk~_?NqT0UtK-q z^Mm}ssPP8u)Fd-ylU(jO58&~48F0fn2|Wl~&FyFe@}`QvT64;V4bVSxBfJ}GAp`B> z|HTPvJf_Ss*;b6 zusfd02Bj{^c2SDI`6DoN!GkHV53)P6iUEKEr)QimK~+$QVc=v95@L$N)-G|~hLjr{ zUJ)saReQ(Jm0Ahj*4Xm+Fu-x#^=%Hp3agffE!}8;6ji51&a<3O2-KQ_QtqY9)tbef zVqT_M)}%){7BGU%F6M(wj+oq+jz35aBV+Sv&p^!wH%o?pq|8P<=1aCqy73(MT#*9e zqh?9_0Rz-z?L{pgcbJfo;vKxNx%Vov9Z!NU)$4*haz{N=aJf5)_wcEg9S!qN@N4Z| zMe}!iPFFg&$gyV+rs4oo8t6H-JaX}%igiNDdO&X9tikgd=%?@6rw=D><@O@dU7-_n z+3cJq_@c|%7h7MksVR8t4^#M2GUzp<761nXV=f@~PZ^|ULEN`L1Na<%>mtI^>Zyo4^evCSya(qo0zmeQ8}@G0ZuUn5^b zFJQLq-yu55AvF826+az{l(|gIAQiQSw9BOLaX28`-$r;y%5`o>CXqaEob@7I_LiE2 z28I&tqxiWZvEMk}3nK=&!J0QyO}#&c-gJM0*w+e)^^*_{LUw1?QUpqebuAr^JlC0Bx+BdnJ(ND2w7 zI1nRcUntZz1!>3CBv#461Vg2@X9OWJh-^M#;Q^lyws#jt6IqQs_dVzaAzY8Sxe(bn zGci%-W*2FZ(!1^?*oWtV7Q?K!GdM9JOH|~d1? z1k<0{#+}(0E4Qmyqqs?33(9vBGWLP2!bO&_jk(B7z!<3$<1|{qGmy}1#{B%lSR;qn zjV6nz?;({zCqqX<3Bx2h3kIjIKP@OTu8Oz>ISg*NWGD8a-!r^dy~_BEcK@3$FIuZz z;I@vRGvqFE4!{PHIKahSFI#$+(!tTWviboo`2k9t83TTSX}6n36^Wy*V_H&(#W4itB#3OtW??Y41Tc`P;l-$N>;_5L(~Hv zVfLqs+>rPAk2+A76-M5u2pRh%kQS7GP)ksC%9Tf%ka-9$Coz5{aR}<Rao!{kx z02ii>18Z*2QDBMyDUF;q>~$EC^jp--1{%D@jMQOT;7ms& zq6D_J&ll)o^Ld6Kw%$6!Bj5=EC3WRt1EqGZbctNL(kKNx%F?kSVuUS(>)DaMAVQTO znhJu{2JcD)F>+g`bf9PnpZgL_43oB6O3QT&M z$O2^MQj9twf{vp>$;d-LR5ZXJLukMB;*7*T7f8+*xM6tef zN1p+4epMbJ4kfu%ME?>_c#WYNpO&UV+I$^!xzd6i>Q*zrMLJV&tb{S9IU}a2ni(A= zu5(F|B%CNBWBy02(td0)zgDuhO#gQ;^(QFRGSP%s2Em$^8hsI_hu{cG4Ym3gR}X9` z#KxWpw<1`xrELGiVeTdcy}e0PK>LH)beL)gcZFch2JLb>id-ke69hlwN*OA{HF73J zy&9TEqYG-bH_D^#FfG3^m0?LnP}cA+MNlukB#NZALa1<3yAK=r9Q}m@18StIF3C_+ z`rLPNWhu!du8Y$Y{ALz~i!7|XWW^g#ZQ)wKBs@17UJA3FND`AF zrm=>68Dk6T=gC?IbdqFWw5V;R6!?dbGOle>Sk8A(E&*t=X*~K?0KvLgn0&F z`?qJV(7wZLJs*rgN~U!f;!kKuH{Uh#lmBEd~9k3 zzeGnf%15V8=pN07azKdWGKZTJrpuuya>{v}8JV=IhIRRsVXedWKw`2M>sF5?H)_bm)K_?^ZN4Zn{FZ$ ziPhP!rS?`XgL;)1?*k`#BFeW@*jf6gwKj@+V?s*aB1MZ#?yd4)`XmG`&;`W0v3jRjdaL zUf<=WC2&$-=OpCeBGhGmOKgaeqrsJ~l{FeYOE31jSX~6NTIa~vx~f!S(lFR1HDgxM z-el?HvaO;OGRt)t>eaJnz984*&a{CsJ8yx$_BuuUhl8K0lZGpqeoX#JR1S=QBv1kV z=}i;Zx_+I~_Dhmw(?iK8*E#bMK?(W_S223oNMn_bEmdjYk3LX&*fskFWz&4`nl9+9 zZ^6YV+tq-@Q7A>vn~m7Y-CB)aNV0+nY^Z-mqgHZQ6~jsP>jNU-#5p?F%?|D78)wv&nTt# zhkXYpo6_cq;@?YwyyQW9iCn}=%2;})HHx9~WyHIc*xZhb_|wwu`pz?A>TkJwG-rIQ~Z6j00xOlw~A>70=V(92It(6#+S~v3TNXtf1P>XmyN+VST(2y9J0t-z8 zBL<`9PIDEs9M&Z%oj)95WRid0Edd<0qGwvAM%q&>O7sk(pZqU++b5lD;cDB~8mmAf zyBW?dkT#1S;E=Cfpq4EmzLf>=j{;D{dgev9Uz4-xrm10G&C<`4dI-}XS7}l>$E2qv z>iDDcIbNgsa4(!Yn287S!GX?SN3FMZq74@BVUV5K#J;UZtGSqXjgTcThH}~#_mh_^ z7@nB)!Vgd)tgJ&iaH*~Or*qyuxiY$#4vNB*K#{ThEDb9*d0vd5@2E9p%ObS!$z%<( z`@#edDYXHlW5SlYhNqiSoIwQ-irv1Y@R^( zx+Zr;1J>CMJ*w6}M6U~mk4ZQcE7HHd74V#UiR-V@5>nT7=I$fJmIjH|gxxfi(>_+{ z&ZJ9;GMGNA%fjWKg<;^&xMv>F=4~Bc!Jo02BgHHMVs&!v4E_egz zlnRKSH`K)tIY{ZYMEczj^j;aot(Gvau&5nNN0&m`-*3Dy*QS8yxAT6t9o_S$k>Bt> zmYy}9tT|=b(D=^oNmFu5xQo3BZrHPm9I`MyX4nnF1p%FV%DccV>waAg{9s>Ja81PB zsW|yn`c>UN`lVNS%qB$t;-E-zfoKcHoNJHbsg(_(QHeS>%BQxavoJQu*6SFS4Y4`0m2s~R??pO@^>|w}{ zO%vAIX|kdxkkZOM>0M!?O(`c4NS$ZQjevD0g%K{1q;T9MtA_|ed1TB3ozi5vs5Riq z-rG5Pk{m@RV3!yRe-j&FHAN!P)#^OcmIhS{q6VA}eU2-R&&Hp2H|oep$|0U9gwSr7 z940FK!$g=iW7LwP!+Q@NG%8A)hwE z2vV2AfMYAVzTWbRhU1WLd;ws>{OvgCD<1iP8TZ%RU@^DqoRdn`uc!I3Q_stWn@0@7HBI z2&P?d?%^4)&wCG`)*Jk!f&q@nCV)i@Y~@kA#!7E(sWgP%8L5T+bPK?ki!8K?`f zU$8F#K$gYx2!2}!nrbc1FC{ao_W8SbOvc`z;=2DXS7U*%=8NId851`}DsN%Gw)ffd z8q%Wrj?@iDQH@Kb#A;+V6ERc0L>LV36WM+Ui@1gXU|jApR6Cxm;CF8JFjm;L+ZnWb zh{#D%aQJ^(JhZadc6{~DQe`Ho1=;?7Bax4)KOCRWe{0rpu4!VJ!+NL zFJ1#z%b&tFnfFB+Ax@H3TAvezF!Fz8=M&g-%k{Cb+Z!x-7Cs!M*#o7~s49HHFILHp z-rnj4J%v7XFNuIxm%*U%J`iY%76fd*%X?4==nr7&LZFQ`S7XgzJW0+Tx#^_UPdPSN z`?&X?-YpaIcF*o%XS|{~>6V2aYsKqwd*EBo<+9&#?t^R|lL}W0szJ~y-pu7=g z0qN!ny_jWJR#wp0$U7p?VFx+*nbvH3I@OpByN(gOgDB~ZyTTUd2$DpaohVDu5k<_# zD+$3yOhrbDR$3bDH)hh*v)g{Vft@kj7vef??E@L=2gE+p@cK`jnzQI-&!D$(WRSVm zrgj*TTL8UfJ+mjHJ;mF%QO-u6gap)p+tTj~LE$$A()Vb;TH)Oa0Qo5B=`ypH$DjEk zH3D9ANq-+KR8}iLILW8-YH`Y6`0#uTmNn_WoSFTUO|h>{PO9(tByUo5+n%#dP4;sX zt)ysIGP&c|P)9}egE89GOCdzqlMcaGwUk{!WyLy^n?7BdHmp82i%g{P@o#Rr);<6? z7EIW*@`F=R$bs6p6#rk}85Uzsyu;Hrf60CJr8E*2+QT{8nQS4a_Y`NP-nTsANF93s zNeQr^ejUH$OvPi9m$*_FI^h0(t1f#&Q)v;Y_PKJ6wWHOrdE-tLyN6WNHmG$*42^2M zp}H6E?q~NZ$99M2_pRqj7MT~7!O!Ny@%XTG-?Oh@KjVTmY^PgTF7VffCocVp?zXJ% zCbKtQt}YL+fI?s1dro!U$f;z-cm&^_JHT{u^k5G`;O0lo7Iuto`A2s$9ZYP zV;?=G;E$`1kgiD(m{sNR3K*gR+#*Ej_#g%KIyKBXym!>CUWonyfqSg~fZ?B0$g@Ad zbSq%+#yNPh!JB9L@Wns`{P`a`0vHSpQ7)z?&hxka^IuQu-;@1Q|FKLE)P@#AkbbM8B#w-Lv5_CkmFI;p@Dn+}>M@dqLNCg3dWrwq*m-0_ijC4Dg9b2POCUx( zH#p3NjvZO92hF=T2DgnA#}y}rieVPJCTnpD%^Vd1@z>|X;@&XUV*^bC(pR~7MXTLy z-Z=009&tL=TN!GpVNaCf%=gU`vn_hFy< zAMSnH`|MNoua~y!UaP3_&$NpOK@(Bt6 zc=-f?Kz^VgpO6rMhxbo~2SCH~uL|hDuj}Fd!NToNNM3f<*5Lm>@R$1kZ~Xu6?Vp?b z%k=-$CUq1lpkp*a2OeQeSJio$2ea*1&#vuyC(iQ}8(Fa2E;zqO1kMM@J)>OP;fgNbX7L%JXOZez|3ss8FecgOC%TO5 z9}CRI*ar}XP;PJ7R!~^mY87GZ@~L&G1oaqmg%sqiL#m}{Xk%x-&aiyKZ&UNhuo)9l zdyIluA?Z&d*_GrCH7MT~$d^ZW_}Vd_1rSljL}I@z7*sVbBT{v;>Qu{I3wuLv^9(|E z^}CkdYc=#-$456c^fJ5G%jqDg%>@SIg)skCNup{f8b?7i4Y_bXJ}#LtZRs4~^%!pG zMS2RFUf^1U8SSNb!kLO#*YK{+&XI*>?M0bs(U|&f*vw6Efgat4^6*MW@Axp6eHXEe zI8^)0k!t05IqfC7jl0k#!;CYF@SEAAM{P?7et2CsflPAL->j=x8q-!(GTRdCB{s1g zv}?~4(&M%s#%}uCDTe5XR-3E*%y`e@3AKCb+n#%O7~`och=VS6ChMad_SrQ}uqmP6 zwvSRI`Ws0c^ap=A1CTQwQMl-KiD9%$c)MS$a4I9?)L=ov3RGHU3u5x6>`o-q7xhM8 zk|kSivc=E}By`W1K`OX0_%ce!b!2~V`fkzV@%$KYf&-vG49l-+mPdy-JqXV?x9y)09@ylvw z`|kCAZUBg&E3Ud{?~3h~=zhQt8PE%iZf2c*VXh6Htv^PEw8+-?Ajsg`2m(kVi=U!n#+eu`i+ z>9jymz5x_#I~!GwdZ7r@_0Sp&T74mGKmGO!}fZ2E(VssXK=^i>yKFOM&ts& zcUT@->Eh7L)toP7Au)f+w!qMb7B2>7;&$Um9eoa%n&|@ro*OCoPI9v#b~qx1q71gd zFOO%g`)S(C(sw2TLt$t4%P)PxaN&}zUN54Dx$&OoUROqqUf?g|kX$U09@;}3wpB0N z@2l89JbdN96A!GeRqYUy%pdmzopmcA7w&%wLPRQcvJD?x^FWq&-n4y8J+p^CdDXd1 zi=WeuC%y#y7&7p6Src!0?T9nD*tby*Isx2UNh}}Vsv%1h{jQu5bq46z(Pu+1f-%<{ z)YTKTi$1}v;KYO6W{&T&m;C>iSYTcYRju$Ou|kb?_o<8 zUmpUjH6(e)Deo^Df>jl&uwazgUXS7g+r{gm)Sc@1g%nxL(x4Avl!-r3R%zLG-c3Sm zbv3ei3MS$^n@q#3By|!;qXDQd?IfOfAv;YoZ;*_6#M?%%1=y_j)69} zLWMP>u=CS%`xl5W#MkQ-)-hlkoQ0AH4{iS?;zsFGQ87o;-dv^qM#Jl5g5?44?DUYb zxu|Zw@Yt2uLKN_`?T$yxj`tsUNpbKg5dH=Aq$zP7ioiRKM<5De^8;n;I3kV?uvuIf~P5W zt<_7(3SAY%8rXQ0{eVcP%|=e1$E9yxIkFX@ROE<(ax7w6YV`h1m_lG63GIFIxQ4{V z?EL&Z%UdLuSJL4Rp-~UWqL?9o!Yo>xkZ)F~;^qv26CDYe3w0*pQx9;1{R=eM^Q?$; z_^9Q03WYS1=%~DkTvBO*CAd^5{R_Rw$d_r4zR>{EJKkpn6G*HUfJ0599v5l)lA0vW zJgv`?T?CqphGtJNQT3Md3vuRniLI71CN1(PJL-Mm{q~IiK7a|sinf7?-os-9^ZU-i zY#x`N`gqK79Bo#*!8Yq?8pHVY@T@BQF%H|n?;_$(!y1~ThJ~DaR96ZN;C8cP4Wo=T z`Q_FkR$nvid_l>p72`~|&pwQ^h;ZMET_s+zgGtjC2V1OLH0;d5WNHT7U|UpgY=J*PmzVU_-YT+I_F8~%z22{ zyp0NUHu~n|xShh1B z0>>ikarv?J;RUoHtbYzWzbuJ1VaJ@Ilkh#mRY9*{L6@8JR_VdTizMQRnVQ9+PlWUx zRo)$i&=c)ua!L@yz*B8yaMl#dl>^_r1h0Kpjn82Q6oSMG z=l1q(1Y-OwN)BIM_havlXb%2mESZrx*q$-ULCuzaRAJY_g&C`vfRsE8vBO66Rq#P? z)uLJismSiU68h#tiuiEFO2+W#?vH3c`kk=>3ef#oa%^>@in?_ca7$P9Y5+rxCpU5J*`shEKJ!TRqfbHNF)QMAzN`uv4 zg8nFw^=PWVNQJM%*Rb-P)*T>?8`BS0&ngN-V{A{J8btsF-ALnSb*nI-V`m%1E%^yg z*a2@G6MJhAU;|VsUYUr#!iv~*UZb?Um_NvW*24`yq)XDmOMPGgt6JO*TZfRUcx(rL z^;z+jP`C?I{n4;X!XBqX3tMH`#M5}P{QLpZXrWcEaCScTrTwGl2mvRxwIZ-7(qYI= z#M4P~zhdsf(HBE6DZL0>T5A{8tiL>bwPE&QmkMujrm1Ku+-vqI!%cwY1ed2&mybs= z3_x%+L2orYetcS%GNIYFk$)hQ1cn%|hDgAla@Vcc1*YBDm=(IJo@&l{yZ2-}wJo^Q z&YH&@jk7ln-5^Qhad(PK#G(s2NTWYV*@T0WdI&gII&G)|#Zzh&$LCQ~S#9#Z)p#q= zT=e07Wh*P{&QUNC*QK3iIR&#~g=^?j^wpX&a$M%)fL~%aJkC*sj}vmt_c1Fj(ihyQ8;r~Ky=O!E2fqy zf`hW8Z8q-j+iq+x!XrVkt;ktYhk|lg?|jGr2f=}aAIg1m*C$FOu_1g(2>D}>&~{rf zn+O3XzsEO>Oe798cAN;LDK)=3e=u8eZ2DP}t}#SQMt4wIrX}!vF!Clrr0g~w?KG;N z-uV1{{>gSy{QlGIF;pbZ<4WQU{q97xzx0JpB6xntYMh8OQo33o-A6YqfI9gmJJ>lD z=Io|-@zXk>?;fV>ZVOQ!fk7KHj~`%O_d7F?g?TVmJZuz5On_9FEoeY1k+V012e;Ao z8zG$5ke_U$#0Z#yGA_?vkT&&2H^YCIzbgS``+aC7C4$2gCge|m_X>mu?czK_jbzj%M-1splu~}NtQXdYs4DN3W zV`&16dGu`1amu9bS@nX@69!neDVwJ)B+*^cDjp4wuZF=xZ#egoTl4M0Ps)g8zOKCA z4;}Qk4qu7pOR%2v8_{ogy~Jw@R7fNh79!S^%L<6AWjRvA3gvSbotNh-UtV80ebO%Pt>Hz-K& zh1z-70XUG=^p$Yh+oP{;k$jr>f(NjDI6=0cxleAoz8J}khsG2IN3i2yG7&~wJt9H4 znBCBM;ud7*5d-B};h`@5)H^9&N%g|uBA(H50@jR_(P^qVT*1ki3vkuh{c zn^m}3%&$@~c9cNvi_4)cP>iWSfygt(hKbNjQpnSj*7rahJX4BJul&}_%gbT3jnwVp z!xLIa)6oXwHZvdx`;3)QH&BMMT>dFjuzXBaH?^%%ej&5KVM2qtWje$95-4bPd-t(1&EVJ0bQlmco~zrA?CU|x~-%WGx38(Maqhojq%%Fp35uICg^xNdM-*Dz~h zCBexnPR5Zp-6OwP0Vb&-&|@6#lfhZlW;Is2uOhLzx_6gU--%<|0pTdtP3Jr)jrA0%~z6fQk;DS^$46hvq%w)|AO%X ziB)fA4PU<(!bQ(f{e+Vaa)mdw4?h2{dsRO`6tU~+D4=jw^IPvT5>Kc3Sm>+C9t7YS zTd#x8&o7nT$$AA6^S8(-YCo(!R8}ax z(yf}TabirjpG(>L1QD%1t2N4iE3XpdEQQs)-Xk{#7~Vcjyfm1?mfuqW=WjZQi(#9d zHm%%dr5kZBpL@1nSxVZ-U>5qc=3YJwGe=o35s) zY=-U-q`ieew=NTPqVybg2r6~yU&;p$GFNM8Pv|s09wGS|n?~efvbomJ7S&yg#8lHQ zqP)qM(S(QN46!eoRy5$v)e@c_3JMSu954riRafa5jrFkxxu~lNskAVPV z(TgOoNFI?ko(mh0GN@U4h`ur%E|aqhu6fLis>rrla*Nm-C+oVIoBY048&{~&$K_Up zQExHeK0PcrL=&-l${=qJM0+yejCNKLsL|^SYG-&*MC*x@;scm)hct!clyL~%Kgor4 zyo;OJ!9Gmay<@EX=+t<{%adMiCd*C!10OP=ytDs$tQl!j>&>B2VZ0dmU`K{N{>|$) zZO1-uH$GOn(Ptmd?ACW(#N!~G%DXxUlE_GvUr95E;;ylv@(8tso+X9r zz6w!j4n39r7R(EpvGluRc`W<%=6+rI9wMHlhXe1^;TOvZaT!oHIYGv)WV&U3wd&mn3|CkD;hOvAt+UbD)-bq52 zW#W&|74IEg;iA*6)%7!z+kkukJLqY_Qbj7WBECo&oA8}*WGk*@?1||Bl@l0fCB95> zlOsm>(4k7_4>DdG7M3GIkXyUHuQy5Ib2&7?N0?TO#cms4x^`ceg<&5oFscM1@b}hL zB40U!1@20SJAhwAoJ_?gd`>Op+?*t$;PuA@$=`>6ta_(Ubo_QqE|d--gi!l~pPc_! zsN1IGuribX$>O_hI16fCx&E$S;3)&?v7ijGk4WYb;2}Tu;o+1v;=#0S4z2aTn#p_= zPlEu=D7h99q`)0R;TgUBmRU*CY{H6n`6NZ}6J7|akqj~UM=F39O1jGhE{xiGnUPB1 zc#g~>f;$QmduC!~qK>qMUsJ@rUzV%@jCkKZe$o}bVfR~CaPa7=_+}~m!#f|sddRH{ z5(QZ>vkT7@XNhi=lvyb8j5(OQJ6%)=k_;dLDFzP5`;^2O4VOBn@RMa-sRm)J@9LOV z^nRcx>c1>_PptyPGw?)0iyxx!(6J`eNE0Oc)M#ol-z73}=1Y902Q`A@%UbP%G1 zaVCp*xbn)fPG#VQ)j|;VJyjC0pjb!N$YaqOv1{8Gn=2RqW}H-!Y5ge!&#{rrJ?u=P z*ivQP)THG1Jp+Rkc^JR-tz};s$82$GD9*_EFa63*#z0IGs-Jp+e7%*Krm==n?S)IV z5&_C@G|Q+f)Rp~z{-(Bv{X)rVZy@+_{j@LP`{L{Cdu|SKgqd}|V3d{SG^A(;qMrqq zy|)QbFHXZyi*XRP>oX{9*2G5FQfP|_T%R-=V6P<0*S_FKwXYztce}(3eiPzUKaMLl zDLqTbtuPtdq}6=18!6>%v-5MYUz#(b>@$AZFKTx+t~Q@kIOC0TytJ)BeIosKrb4bk z;z;?+Xxeg8R862ZA4e2k7M#79RY@ z`lb+9M>L`#SjS#UlfFF~!TfUmcn_;mMoP_w0dMv@8X9pt=kC{Y`(ebkfrOrlGVVuL z5y-BD+CC4WuE$$Mo`=8uT%F=i)qkpxk%qKbDJ{$D(u<*1>6e6|KRip2ct&2zu3qmm#h@u^@n~9t3^hm^WRnY6wMGy9gmvuzX; zSNON@H)VM&D5BipD%4U}#A0m)rZQyKr_a7m_!7zQ4ka!>MZERFQH08pY4q10 zB$s_(JS#N7fM8%(>oD;}qx;DgD`7!GAL&M@OX+6_l!g+tv*6tvZy~QAVc`9jvN=eD z=XD3H4pLp`|FX|`3b7xOuY(N<^V#u6N^9Ap6%%f)Q!5(vCd7N*{=CM9TkJ$Zyj1M% z%w=`v!LMP1T*~P95HvaH3f)H$6xi3RtvxY-4b!mI7__-qSOg95G1hg9YiB$6w=wdX z2Yu{jU4x&Sd!I-ynS?%#URiM(WKQBV@}=dvR42C@)4YRxQfSB=i}aLi*}yy=go=si znqAPDdgNL7fnT)+v!5jxA5pUOZ0pn?@LdyA^s+j?Ns)IGzHuniYRj~|V50vzTWLF^ z9&K2b+`*+A32a7YCuMD2&PNnvtBPt?y?PsYVlKkg&d_|U^s{Wq@OkqhhZ|kP8}fOI zP=K|4yO#iih`MZqL643kltatHCtD5sSmj);lA)lJug3V9kOy;_WpQ6AU9&AM$JTD@ zG~>FKdFxjXznUx2nmo+sKwjgNfxlf)A6U;j`D@e?VJ~_9- z1{7If9-@$6&wp5NVQJYE_FP501~U|CT)PKNz6@M%3VXgpK0+WM=o7@<1m?T>oLhLE zo(-epF42dy|9(lufr~u$FtbNbIfWdIgJ3nWFf(r$vDlaD21L-{i@K8W1Rf$s9PH+~ zqrR@A9d_CM2zqQd_qc;W*J0huphtFz)H9gL^Eu?tXJaMw&$H_t>_zDH4K%x}dii`4 z!!t9cLawf5?G&{30-MJ;hdu9|Jv>7oLC@>pr-6^KD$w!7L*2_0CkV#<4|xFwLjX`p zZW;oz{w-p|-%kGH5U{edwtDaG;poogW8nz?*QkL1NR9Y^28HaCQcif^Ki+@TJK8NZfFG(!>>cN55T9AfdOfY!Lk#T^+ujayuMVmUV0dj= zLmqX!V3D&bis^lb59mtB-IOh$Q@ca@4tQgjlkas{EfhW3IhKH19LMe diff --git a/pkg/trustly-client-ruby-0.1.1.gem b/pkg/trustly-client-ruby-0.1.1.gem deleted file mode 100644 index 1c860fde89da3e49e75f16a95a376001f9aa07ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10752 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

8|m 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 G;Qs(9jX{$&c(ZYC~&rQ~UCVPW@Q9se@_fBFB< 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 N8-c$O_#cA6{{ryOV^;tG diff --git a/pkg/trustly-client-ruby-0.1.4.gem b/pkg/trustly-client-ruby-0.1.4.gem deleted file mode 100644 index 1ab76376cd1626e0321639ba8afb453d14c12155..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10752 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 HhQNOSwjY#h diff --git a/pkg/trustly-client-ruby-0.1.6.gem b/pkg/trustly-client-ruby-0.1.6.gem deleted file mode 100644 index 576675c8453271a48be4285876e009b65392056e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10752 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 diff --git a/pkg/trustly-client-ruby-0.1.7.gem b/pkg/trustly-client-ruby-0.1.7.gem deleted file mode 100644 index cd27e460f9260ec1735b60fc49cc6a695bceb6de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10752 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 diff --git a/pkg/trustly-client-ruby-0.1.71.gem b/pkg/trustly-client-ruby-0.1.71.gem deleted file mode 100644 index 3072d12010a186a2e402259a58172b7025b2d28b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11776 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 fbi~;#;lcH?5ca diff --git a/pkg/trustly-client-ruby-0.1.72.gem b/pkg/trustly-client-ruby-0.1.72.gem deleted file mode 100644 index 52b04f3a961fd91d842ce8ff829436581ee38db3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12800 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 diff --git a/pkg/trustly-client-ruby-0.1.75.gem b/pkg/trustly-client-ruby-0.1.75.gem deleted file mode 100644 index 79128e15e93498982f1d25e55e9e2aa8e355ca8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12800 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 diff --git a/pkg/trustly-client-ruby-0.1.8.gem b/pkg/trustly-client-ruby-0.1.8.gem deleted file mode 100644 index b665649d967525c8fa66320baafa5ce3cf321f17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12800 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 G@V@}n=qBa> diff --git a/pkg/trustly-client-ruby-0.1.81.gem b/pkg/trustly-client-ruby-0.1.81.gem deleted file mode 100644 index 35cbc99610a8dc45d49c1dac676414655b8c6fe1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12800 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 IZ;8PF0(IXai2wiq diff --git a/pkg/trustly-client-ruby-0.1.85.gem b/pkg/trustly-client-ruby-0.1.85.gem deleted file mode 100644 index 3d5367ad86a188490352c2210e94ca5e541c798d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12800 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 diff --git a/pkg/trustly-client-ruby-0.1.86.gem b/pkg/trustly-client-ruby-0.1.86.gem deleted file mode 100644 index dc9abd7a3e6966639239394fb1c5fe7e80b01174..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12800 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 b6W~oD7w|uT_TNkVjlkas{EfhW4g&uL0Leua diff --git a/pkg/trustly-client-ruby-0.1.88.gem b/pkg/trustly-client-ruby-0.1.88.gem deleted file mode 100644 index cc9e438993e5399f837536521ba1f0048b6cbf16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12800 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 o*2Q+~1r!#Oj9IrANb diff --git a/pkg/trustly-client-ruby-0.1.91.gem b/pkg/trustly-client-ruby-0.1.91.gem deleted file mode 100644 index 098dc038ae202183675f6dd36723ee41031afb7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12800 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 diff --git a/pkg/trustly-client-ruby-0.1.92.gem b/pkg/trustly-client-ruby-0.1.92.gem deleted file mode 100644 index 36d5f0dd006ba23b651cdf71eb55e70d98750466..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12800 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 diff --git a/pkg/trustly-client-ruby-0.1.93.gem b/pkg/trustly-client-ruby-0.1.93.gem deleted file mode 100644 index f1784507fd2201601595333b20879f85f89ec81f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12800 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 diff --git a/trustly-client-ruby.gemspec b/trustly-client-ruby.gemspec index c2c0b7f..153518b 100644 --- a/trustly-client-ruby.gemspec +++ b/trustly-client-ruby.gemspec @@ -18,6 +18,7 @@ Gem::Specification.new do |gem| gem.add_dependency('rake') + gem.add_dependency('faraday') gem.add_development_dependency('rspec', [">= 2.0.0"]) # ensure the gem is built out of versioned files diff --git a/trustly-client-ruby.gemspec~ b/trustly-client-ruby.gemspec~ deleted file mode 100644 index f0f4367..0000000 --- a/trustly-client-ruby.gemspec~ +++ /dev/null @@ -1,25 +0,0 @@ -# -*- encoding: utf-8 -*- -lib = File.expand_path("../lib", __FILE__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require "trustly/version" - -Gem::Specification.new do |gem| - gem.name = 'trustly-client-ruby' - gem.version = Trustly::VERSION - gem.date = Date.today.to_s - - gem.summary = "Trustly Client Ruby Support" - gem.description = "Support for Ruby use of trustly API" - - gem.authors = ['Jorge Carretie'] - gem.email = 'jorge@carretie.com' - gem.homepage = 'https://github.com/jcarreti/trusty-client-ruby' - gem.license = "MIT" - - - gem.add_dependency('rake') - gem.add_development_dependency('rspec', [">= 2.0.0"]) - - # ensure the gem is built out of versioned files - gem.files = Dir['{lib}/**/*', 'README*', 'LICENSE*'] -end \ No newline at end of file From a26c63b378469b47cdcda94fe415e267dbf84f60 Mon Sep 17 00:00:00 2001 From: Artsiom Kuts Date: Wed, 3 Aug 2022 17:33:37 +0200 Subject: [PATCH 2/7] specs WIP --- .gitignore | 3 +- Gemfile | 4 + Gemfile.lock | 114 +++++++++ bin/console | 6 +- lib/trustly.rb | 5 + lib/trustly/api.rb | 56 +++-- lib/trustly/api/signed.rb | 142 ++++++----- lib/trustly/data/jsonrpc_request.rb | 8 +- .../data/jsonrpcnotification_request.rb | 20 +- lib/trustly/data/response.rb | 13 +- spec/data/account_payout.json | 12 + spec/data/credit_notification.json | 18 ++ spec/data/deposit.json | 12 + spec/data/get_withdrawals.json | 21 ++ spec/data/merchant_private_key.pem | 27 ++ spec/data/merchant_public_key.pem | 9 + spec/data/refund.json | 12 + spec/data/register_account.json | 14 ++ spec/data/select_account.json | 12 + spec/data/trustly_private_key.pem | 27 ++ spec/data/trustly_public_key.pem | 9 + spec/data/void.json | 11 + spec/jsonrpc_request_spec.rb | 233 ++++++++++++++++++ spec/jsonrpc_response_spec.rb | 46 ++++ spec/signed_api_spec.rb | 163 ++++++++++++ spec/spec_helper.rb | 54 ++++ trustly-client-ruby.gemspec | 28 ++- 27 files changed, 953 insertions(+), 126 deletions(-) create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 spec/data/account_payout.json create mode 100644 spec/data/credit_notification.json create mode 100644 spec/data/deposit.json create mode 100644 spec/data/get_withdrawals.json create mode 100644 spec/data/merchant_private_key.pem create mode 100644 spec/data/merchant_public_key.pem create mode 100644 spec/data/refund.json create mode 100644 spec/data/register_account.json create mode 100644 spec/data/select_account.json create mode 100644 spec/data/trustly_private_key.pem create mode 100644 spec/data/trustly_public_key.pem create mode 100644 spec/data/void.json create mode 100644 spec/jsonrpc_request_spec.rb create mode 100644 spec/jsonrpc_response_spec.rb create mode 100644 spec/signed_api_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/.gitignore b/.gitignore index 933ff08..5813d2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /tmp .DS_Store -/.idea/ \ No newline at end of file +/.idea/ +/coverage/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..ffb69a5 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +source 'https://rubygems.org/' +gemspec diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..c87e295 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,114 @@ +PATH + remote: . + specs: + trustly-client-ruby (0.1.95) + faraday + faraday_middleware + rake + +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + ast (2.4.2) + crack (0.4.5) + rexml + debug (1.6.1) + irb (>= 1.3.6) + reline (>= 0.3.1) + diff-lcs (1.5.0) + docile (1.4.0) + faraday (1.10.0) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) + faraday (~> 1.0) + hashdiff (1.0.1) + io-console (0.5.11) + irb (1.4.1) + reline (>= 0.3.0) + json (2.6.2) + multipart-post (2.2.3) + parallel (1.22.1) + parser (3.1.2.0) + ast (~> 2.4.1) + public_suffix (4.0.7) + rainbow (3.1.1) + rake (13.0.6) + regexp_parser (2.5.0) + reline (0.3.1) + io-console (~> 0.5) + rexml (3.2.5) + rspec (3.11.0) + rspec-core (~> 3.11.0) + rspec-expectations (~> 3.11.0) + rspec-mocks (~> 3.11.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.11.0) + rspec-mocks (3.11.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.11.0) + rspec-support (3.11.0) + rubocop (1.32.0) + json (~> 2.3) + parallel (~> 1.10) + parser (>= 3.1.0.0) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.19.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.19.1) + parser (>= 3.1.1.0) + ruby-progressbar (1.11.0) + ruby2_keywords (0.0.5) + simplecov (0.21.2) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.12.3) + simplecov_json_formatter (0.1.4) + unicode-display_width (2.2.0) + webmock (3.15.0) + addressable (>= 2.8.0) + crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) + +PLATFORMS + ruby + +DEPENDENCIES + debug + rspec + rubocop + simplecov + trustly-client-ruby! + webmock + +BUNDLED WITH + 2.3.7 diff --git a/bin/console b/bin/console index 9c65f23..24d1ead 100755 --- a/bin/console +++ b/bin/console @@ -1,7 +1,7 @@ #!/usr/bin/env ruby -require "bundler/setup" -require "trustly/client/ruby" +require 'bundler/setup' +require 'trustly' # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. @@ -10,5 +10,5 @@ require "trustly/client/ruby" # require "pry" # Pry.start -require "irb" +require 'irb' IRB.start diff --git a/lib/trustly.rb b/lib/trustly.rb index 743ae20..9a81dd7 100644 --- a/lib/trustly.rb +++ b/lib/trustly.rb @@ -1,6 +1,11 @@ module Trustly end +require 'base64' +require 'openssl' +require 'faraday' +require 'faraday_middleware' + require 'trustly/exception' require 'trustly/exception/authentification_error' require 'trustly/exception/connection_error' diff --git a/lib/trustly/api.rb b/lib/trustly/api.rb index 7a7269e..76fabb5 100644 --- a/lib/trustly/api.rb +++ b/lib/trustly/api.rb @@ -6,26 +6,14 @@ class Trustly::Api :trustly_key def initialize(**config) - self.api_host = config.fetch(:host, nil) - self.api_port = config.fetch(:port, nil) - self.api_is_https = config.fetch(:is_https, nil) + self.api_host = config[:host] + self.api_port = config[:port] + self.api_is_https = config[:is_https] - self.load_trustly_key(config[:public_key]) + self.load_trustly_key(config[:public_pem]) validate! end - def url_path(_request = nil) - raise NotImplementedError - end - - def handle_response(_request, _http_call) - raise NotImplementedError - end - - def insert_credentials(_request) - raise NotImplementedError - end - def verify_signed_response(response) method = response.method || '' uuid = response.uuid || '' @@ -38,6 +26,18 @@ def verify_signed_response(response) private + def url_path(_request = nil) + raise NotImplementedError + end + + def handle_response(_request, _http_call) + raise NotImplementedError + end + + def insert_credentials(_request) + raise NotImplementedError + end + def serialize(object) serialized = "" case object @@ -59,7 +59,9 @@ def serialize_hash(object, serialized) end def load_trustly_key(pkey) - self.trustly_key = OpenSSL::PKey::RSA.new(pub_key) unless pkey.nil? + self.trustly_key = OpenSSL::PKey::RSA.new(pkey) unless pkey.nil? + rescue OpenSSL::PKey::RSAError + self.trustly_key = nil end def validate! @@ -79,7 +81,7 @@ def configuration_errors def base_url schema = api_is_https ? 'https' : 'http' add_port = (api_is_https && api_port != 443) || api_port != 80 - port = add_port ? ":#{api_port}" : '' + port = add_port && !api_port.nil? ? ":#{api_port}" : '' "#{schema}://#{api_host}#{port}" end @@ -96,6 +98,24 @@ def call_rpc(request) request_uri.path, body, { 'Content-Type' => 'application/json' } ) handle_response(request, response) + rescue Faraday::Error => e + handle_error(e, request, body) + end + + def handle_error(error, request, body) + message = e.message + exception = case error + when Faraday::ParsingError, Trustly::ClientError + Trustly::Exception::DataError + else + Trustly::Exception::ConnectionError + end + unless (response = error.response).nil? + message = " + #{response.status}: #{response.body} - #{request.method}, #{body} + " + end + raise exception, message end def connection(request_uri) diff --git a/lib/trustly/api/signed.rb b/lib/trustly/api/signed.rb index 4844850..188d0ee 100644 --- a/lib/trustly/api/signed.rb +++ b/lib/trustly/api/signed.rb @@ -10,82 +10,11 @@ class Trustly::Api::Signed < Trustly::Api def initialize(**config) full_config = default_config.merge(config) - - super(**full_config.slice(%i[host port is_https public_pem])) - self.api_username = full_config.fetch(:username, nil) self.api_password = full_config.fetch(:password, nil) - self.url_path = DEFAULT_API_PATH self.load_merchant_key(full_config[:private_pem]) - validate! - end - - def load_merchant_key(pkey) - self.merchant_key = OpenSSL::PKey::RSA.new(pkey) if pkey - end - - def configuration_errors - errors = super - errors.push 'Username not specified' if api_username.nil? - errors.push 'Password not specified' if api_password.nil? - errors.push 'Merchant private key not specified' if merchant_key.nil? - errors - end - - def handle_response(request, response) - rcp_response = Trustly::Data::JSONRPCResponse.new(response) - check_response(rcp_response, request) - rpc_response - end - - def check_response(response, request) - unless self.verify_signed_response(response) - raise Trustly::Exception::SignatureError, SIGNATURE_ERROR - end - if response.uuid != request.uuid - raise Trustly::Exception::DataError, UUID_MISMATCH - end - end - - def insert_credentials!(request) - request.update_data_at('Username', api_username) - request.update_data_at('Password', api_password) - request.signature = sign_merchant_request(request) - end - - def sign_merchant_request(request) - method = request.method || '' - uuid = request.uuid || '' - data = request.data || {} - - serial_data = "#{method}#{uuid}#{serialize(data)}" - sha1hash = OpenSSL::Digest::SHA1.new - signature = self.merchant_key.sign(sha1hash, serial_data) - Base64.encode64(signature).chop - end - - def url_path(_request = nil) - DEFAULT_API_PATH - end - - def call_rpc(request) - request.uuid = SecureRandom.uuid if request.uuid.nil? - super(request) - end - - def call_rpc_for_data(method, options, data:, required:, attriubtes: []) - missing_options = required.find_all { |req| options[req].nil? } - unless missing_options.empty? - msg = "Required data is missing: #{missing_options.join('; ')}" - raise Trustly::Exception::DataError, msg - end - request = Trustly::Data::JSONRPCRequest.new( - method: method, - data: options.slice(*data), - attributes: attributes.empty? ? nil : options.slice(*attributes) - ) - call_rpc(request) + super(**full_config.slice(*%i[host port is_https public_pem])) end def void(**options) @@ -197,8 +126,75 @@ def notification_response(request, success = true) response end - def withdraw(_options) + private + + def load_merchant_key(pkey) + self.merchant_key = OpenSSL::PKey::RSA.new(pkey) if pkey + rescue OpenSSL::PKey::RSAError + self.merchant_key = nil + end + + def configuration_errors + errors = super + errors.push 'Username not specified' if api_username.nil? + errors.push 'Password not specified' if api_password.nil? + errors.push 'Merchant private key not specified' if merchant_key.nil? + errors + end + + def handle_response(request, response) + rcp_response = Trustly::Data::JSONRPCResponse.new(response) + check_response(rcp_response, request) + rpc_response + end + + def check_response(response, request) + unless self.verify_signed_response(response) + raise Trustly::Exception::SignatureError, SIGNATURE_ERROR + end + if response.uuid != request.uuid + raise Trustly::Exception::DataError, UUID_MISMATCH + end + end + def insert_credentials!(request) + request.update_data_at('Username', api_username) + request.update_data_at('Password', api_password) + request.signature = sign_merchant_request(request) + end + + def sign_merchant_request(request) + method = request.method || '' + uuid = request.uuid || '' + data = request.data || {} + + serial_data = "#{method}#{uuid}#{serialize(data)}" + sha1hash = OpenSSL::Digest::SHA1.new + signature = self.merchant_key.sign(sha1hash, serial_data) + Base64.encode64(signature).chop + end + + def url_path(_request = nil) + DEFAULT_API_PATH + end + + def call_rpc(request) + request.uuid = SecureRandom.uuid if request.uuid.nil? + super(request) + end + + def call_rpc_for_data(method, options, data:, required:, attriubtes: []) + missing_options = required.find_all { |req| options[req].nil? } + unless missing_options.empty? + msg = "Required data is missing: #{missing_options.join('; ')}" + raise Trustly::Exception::DataError, msg + end + request = Trustly::Data::JSONRPCRequest.new( + method: method, + data: options.slice(*data), + attributes: attributes.empty? ? nil : options.slice(*attributes) + ) + call_rpc(request) end def default_config diff --git a/lib/trustly/data/jsonrpc_request.rb b/lib/trustly/data/jsonrpc_request.rb index 7f8777e..d53fc1f 100644 --- a/lib/trustly/data/jsonrpc_request.rb +++ b/lib/trustly/data/jsonrpc_request.rb @@ -5,12 +5,12 @@ def initialize(**options) data = options[:data] attributes = options[:attributes] payload['params'] ||= {} - payload['version'] ||= '1.1' + payload['version'] ||= 1.1 initialize_data_and_attributes(data, attributes) end - def params(name) + def params payload['params'] end @@ -18,6 +18,10 @@ def data params['Data'] end + def attributes + params.dig('Data', 'Attributes') + end + def data_at(name) params.dig('Data', name) end diff --git a/lib/trustly/data/jsonrpcnotification_request.rb b/lib/trustly/data/jsonrpcnotification_request.rb index 82f2e81..99f55fc 100644 --- a/lib/trustly/data/jsonrpcnotification_request.rb +++ b/lib/trustly/data/jsonrpcnotification_request.rb @@ -1,4 +1,4 @@ -class Trustly::JSONRPCNotificationRequest < Trustly::Request +class Trustly::JSONRPCNotificationRequest < Trustly::Data::Request def initialize(**options) super(payload: notification_body(options[:notification_body])) return if version == '1.1' @@ -7,14 +7,6 @@ def initialize(**options) raise Trustly::Exception::JSONRPCVersionError, error_message end - def notification_body(body) - return stringify_hash(body) if body.is_a?(Hash) - - JSON.parse(body) - rescue JSON::ParserError => e - raise Trustly::Exception::DataError, e.message - end - def version payload['version'] end @@ -38,4 +30,14 @@ def data_at(key) def attribute_at(key) payload.dig('params', 'data', 'attributes', key) end + + private + + def notification_body(body) + return stringify_hash(body) if body.is_a?(Hash) + + JSON.parse(body) + rescue JSON::ParserError => e + raise Trustly::Exception::DataError, e.message + end end diff --git a/lib/trustly/data/response.rb b/lib/trustly/data/response.rb index f383dca..c7a1a57 100644 --- a/lib/trustly/data/response.rb +++ b/lib/trustly/data/response.rb @@ -50,23 +50,16 @@ def signature private def process_http_response(http_response) - self.response_status = http_response.code - self.response_reason = http_response.class.name + self.response_status = http_response.status + self.response_reason = http_response.reason_phrase init_response_result(http_response.body) end def init_response_result(body) - self.payload = JSON.parse(body) + self.payload = body self.response_result = payload['result'] || payload.dig('error', 'error') return unless response_result.nil? message = "No result or error in response #{payload}" raise Trustly::Exception::DataError, message - rescue JSON::ParserError => e - if response_status != 200 - message = "#{response_status}: #{response_reason} [#{response_body}]" - raise Trustly::Exception::ConnectionError, message - end - raise Trustly::Exception::DataError, e.message - end end diff --git a/spec/data/account_payout.json b/spec/data/account_payout.json new file mode 100644 index 0000000..b9f461d --- /dev/null +++ b/spec/data/account_payout.json @@ -0,0 +1,12 @@ +{ + "result": { + "signature": "JNtiLW1S+9SE2KgU/3xFiRS5sIieGAcGuoaPx4fUFTGuHe/MQpw9/1pyF4An\nVwawCM0zeqVcZGyyXq/FBBxt7f1adbVuGBw7QQsfRn6qiKR8abXo8a8VBjG8\n8tN4k4hHkcib90Xuq0ecfT45svje+OtsPRo70lzZzAycJnE0Dz8/gZC8Wy5V\n1lEAAJLXE87C6yCPBvAWroX98Rh4+7e4n8Us7yuAUi6VYQ8YOwTXmHSzI2dH\n0KwF9io30NkbwBtjc9PeoS6M7Ave1ri/yaDAM5DlbbcCLMjZUxledAPI+B4u\nizhiWXCUJcnXsSaQu/AMs7QmwQdutle92T5q6o1aSw==", + "uuid": "258a2184-2842-b485-25ca-293525152425", + "method": "AccountPayout", + "data": { + "orderid": "7653345737", + "result": "1" + } + }, + "version":"1.1" +} diff --git a/spec/data/credit_notification.json b/spec/data/credit_notification.json new file mode 100644 index 0000000..66a90ab --- /dev/null +++ b/spec/data/credit_notification.json @@ -0,0 +1,18 @@ +{ + "method": "credit", + "params": { + "signature": "1Cbl2QjwyX9YN1YerZUuGb1qxGjns/Qdx05eopHYNfsoI2pkAnMw1HfSYpeC\nBuOmwyHaeiva8/Sf3w9FIeRn/bgtW4srRynQRyW3bw4rYPYdqBjFzg2pH9Br\nSVxEbKyshr37U3/dO+WP3LAR3rtl+RqEGxO+gK9/tgYqZABL3xEdF8LAGcBj\nAl6MIWDqOj+ZtgQddzAq/sSzRV5a9LnjHBFeFtEKI4d39Dtakap9caiRubYP\nxVnRvefu9twgk6mssNrrI6m0ZddKnQFUUxTmzA/nFzn+djxMfmWCrv9/d9vI\nBAdoQWLTV2m4f5STFIK+zh1UYQmYBrPqI+ItJJzOKw==", + "uuid": "258a2184-2842-b485-25ca-293525152425", + "data": { + "amount": "902.50", + "currency": "EUR", + "messageid": "98348932", + "orderid": "87654567", + "enduserid": "32123", + "notificationid": "9876543456", + "timestamp": "2010-01-20 14:42:04.675645+01", + "attributes": {} + } + }, + "version": "1.1" +} diff --git a/spec/data/deposit.json b/spec/data/deposit.json new file mode 100644 index 0000000..d93f690 --- /dev/null +++ b/spec/data/deposit.json @@ -0,0 +1,12 @@ +{ + "result": { + "signature": "ywKipJ3zkwimgUtSTp4AEXxIaKhWciNBHeVI+g/1rggJ0mMqXWpsG9MfjqXB\ndeb6pWx7qAfykjkE2oQyBfivLV+TCP9tzqruKEReQvUuDQX1GqLhUcwOkSfk\nFDtMQf5ZqR3+GxacFwPOkKqJ3TU35O8YlaBAf4L0UWvotQQH2KoIoKmxgJtb\nHrarmFqFn6cWxiU6Q/EzNo230qaEdlYpEGACvXSU8v5DnxkUmGQOC/4yIUTX\ncBsxJCD3IWf6DHd7MrSDgeb1LI67bDkSkec2zzBoaxPuJJjCPBLCgIHEbxfr\ny96U5oeNUaVd03FN/V1FHF7PGj1GM9z3AXX5qwznng==", + "uuid": "258a2184-2842-b485-25ca-293525152425", + "method": "Deposit", + "data": { + "orderid": "2190971587", + "url": "https://trustly.com/_/2f6b14fa-446a-4364-92f8-84b738d589ff" + } + }, + "version": "1.1" +} diff --git a/spec/data/get_withdrawals.json b/spec/data/get_withdrawals.json new file mode 100644 index 0000000..a8b9fba --- /dev/null +++ b/spec/data/get_withdrawals.json @@ -0,0 +1,21 @@ +{ + "result": { + "uuid": "cecf1a0e-31f7-0bed-b07f-481447584126", + "method": "GetWithdrawals", + "data": [ + { + "reference": "5000010000", + "modificationdate": "2015-05-12 11:16:30.957975+02", + "orderid": "1436557899", + "datestamp": "2015-05-12 11:14:22.982842+02", + "transferstate": "CONFIRMED", + "amount": "1.00", + "accountid": "1234567890", + "currency": "SEK", + "eta": "2015-05-12 12:00:00.000000+02" + } + ], + "signature": "MGGFeJVvY1J/CoZQFmnk0fEmog+B1/rCzSw17Oq8eiA7PqZRWikeHoeqmE2e\nHYL7PNEMT2/crBqGAxT80n73S/qV7wmSIi8DQxast68pNfiRJ5UjAFmm+IAq\nKBC0MPLsJq8gCmvPOtEZwjgfrlaESzknPSraQNvaiKji3zEtX47e8U+R98o7\nJG8Vz5GfXOn9XVu9eMJGYHOEJ2eGgvTyYNt94MClqjsXGIGVD3HYCWHkQmuE\n9aP8tRtelNIoDF4wJdSvG9RuI1HdZ/IFlrU+6+ip45iNTwYkkox+i8sNoH7q\nVl46Bo9NbjDSDs/98T8NPDsy9NsBq7EUqyb+B9EyaQ==" + }, + "version": "1.1" +} diff --git a/spec/data/merchant_private_key.pem b/spec/data/merchant_private_key.pem new file mode 100644 index 0000000..dd130be --- /dev/null +++ b/spec/data/merchant_private_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA4ZX5APbhU924Yl4qJuOoEVh187ppNDx8F7TOYemt4LuICrXM +V+pBb68ZaK0DtoGjeOu/4Xm5mJIFZPFe1TgraIhKPWZI4sfNA0PVEJv/lu7A81a1 +V95ivj3rMfe84KTAJznrpa7xW00IzOIJp5wgqmm3hki6vQooajkr3HGdtn9lf5gj +6PceJIgHOmiX3W5i6cn3Z7zGrU8erFBwrVFx8IT/IIsnGgepf86smjXB0Ur/qaZF +rC5UQHcYV6aftkjnO0Hibgl5oSWkAyCmRV0Ojy3AVPbWXRafHG57DDwJd16gLVhk +dZEkRIVmNGJqhWlmZ7RosDoxv7Sr4LceBt5n+wIDAQABAoIBAF+Z7UMJXYjrR/74 +JSkCBfID6Uj3USqAD58EUwqPu86n6wmO7iC7+Ctaq4v+9rnbyumuD02BBrSv/XVA +DY6TFWJhkAThWjYxsqKVlrBJTFIssLzvnD620mYJW6l7ciJJ790v4LwAneyxgu9B +RBIySm2uC8bu/6Spr2MFA5+SzuHN46toWEIhqRBMmwTh1yyhOMHWhAVUDYWjcr7W +NPRZgnusPODCOcf9joSmzuwkLElQmDFM8KG3YpP2dn2KVIYd/ndmcQ7gMO2u9Zw1 +CnH2sL498OySEldAXxq7+pfE935tPWDLv7fFNG4A3Q85bx70HfVrJQLhAnT/H0aj +1LJg58ECgYEA/w7J4gz5INjdkfDJw6G3MMO68/Eg+/FfzvRYBLpEH8zE9nK16ArG +SMhV3T+KvpUUcSMKOBrMlhAeUIW2czYm25iwgy6KgY6CvKVCMGBGXn4ArkHnd69C +bv+M5SkjUTZafDlhWVAery/V644C+4aKUps4arWY+2cXdP2crJJtBtECgYEA4mtP +5EzBvAT0Qf0zk5j43YGOSWMHyrfxqt/owRfhQTS0ByiBJKQVUIw2W4V7NUCz+KLE +XrEuYbV9WbXycNCO1y0O1UWkPtNaCeb13chSAOTQ+sAFl1WhsUqpmylkMokgDVVh +iPKiEmPGqPApXKsJzNSHsa2qXKcq3NY1FlAOjQsCgYAh3NfG0EwfJUu9fYd8FrNY +oRPoIUJs0K4UrvIkpoo24pvf0HkANrX+ocJsnmwQQ4C0SJ+ptT0mSzuLG0WO5Eii +bRI6SGqRKteGrjYscAvHrdjvScauaDFcxUbygdSzipDW31NiZTW9so8nN/KDbGhe +8Ua7PCL0dcpyeN1dOA+LkQKBgG/vvb+QcvcRO/CjzSvbJK3drwp4+xEtfzyLFfbg +Z2xlMduYGsCSnjcEGpuEkjTxmAgD8DEgR13m6+G+Ie3ELdoTXJHzrA+jTZA3rrXG +o0Pt26Mb66e1ngqYbuFWxUJ2qHHvFBkwWw/cZAqBMPGvXVj2eV9ODDtiKb6j5/rv ++UGhAoGBAIgE8O4PjToc0ZUqSBrpgpmpmRztNDQYcgvVXVsc3hq7sEXcebfyt9Da +Y0qt0yKP6Tha5oHhzUhVqrK3RHRR9nASM22KLh9Ak81mySiW4oY6x/XjhAvfpcBa +oACbpGv+4lKl5SRIlEGBOUkH3YmobZvB/dFOAwAdvk5zw6ucBXYU +-----END RSA PRIVATE KEY----- diff --git a/spec/data/merchant_public_key.pem b/spec/data/merchant_public_key.pem new file mode 100644 index 0000000..3a85fdd --- /dev/null +++ b/spec/data/merchant_public_key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4ZX5APbhU924Yl4qJuOo +EVh187ppNDx8F7TOYemt4LuICrXMV+pBb68ZaK0DtoGjeOu/4Xm5mJIFZPFe1Tgr +aIhKPWZI4sfNA0PVEJv/lu7A81a1V95ivj3rMfe84KTAJznrpa7xW00IzOIJp5wg +qmm3hki6vQooajkr3HGdtn9lf5gj6PceJIgHOmiX3W5i6cn3Z7zGrU8erFBwrVFx +8IT/IIsnGgepf86smjXB0Ur/qaZFrC5UQHcYV6aftkjnO0Hibgl5oSWkAyCmRV0O +jy3AVPbWXRafHG57DDwJd16gLVhkdZEkRIVmNGJqhWlmZ7RosDoxv7Sr4LceBt5n ++wIDAQAB +-----END PUBLIC KEY----- diff --git a/spec/data/refund.json b/spec/data/refund.json new file mode 100644 index 0000000..9f7bf96 --- /dev/null +++ b/spec/data/refund.json @@ -0,0 +1,12 @@ +{ + "version": "1.1", + "result": { + "signature": "b8PabwcB4ETNAx0hRQWLLamCiLMYKVFmQZ+jZ+f8mRZtJKy0PJ+M+Pmcaqjj\nkLXH6FjEtLIOebIH2lwc4qmnG9Kli7Z4TbkLkk80icI4RSFTyCJnFHE1ERSR\nZ+OAfomR+4dZ6gcgJiG08a7QjpV0kzAeLjCz/1iouCXOivg7r0TbqaxywrjY\n/nf/OROP6YGLie/zu0FjTv1pp/zO1pF1nLTd8S+/KjPP0t9YZM58Zu6yMBgm\ntMSF5jPSIP894+wMC0Bzy4Sg0HF2lrgDSTvCCaiscLl4mX3PaHvIXc5wyNW9\nrbqlXzFUtetSeWT41m0YSLvs79v9TYaU97rN0OFR/Q==", + "method": "Refund", + "data": { + "result": "1", + "orderid": "1187741486" + }, + "uuid": "8bedfbd4-8181-38e1-f0be-f360171aefc6" + } +} diff --git a/spec/data/register_account.json b/spec/data/register_account.json new file mode 100644 index 0000000..479b4bb --- /dev/null +++ b/spec/data/register_account.json @@ -0,0 +1,14 @@ +{ + "result": { + "signature": "4uDNGT+/9dXjMtRBJ/dybFfl0qVFrvncL3WxKze7wbvBzByAg4B63Ar3pRFn\nff18ewAXZOOK+GOOeUZMdv/lXMKtGbXwS+iapbYm/qjKJW8NCFLfqrHjooH3\nES/8XUvrZ8wNyjTZx6nKt4ZWO0SgP+2htUI/2K2GCuB/FLZJ2qICdiJh7ZPv\nPIbocsdbvifKUyrIB3Lgrmdl5OoW5RmfXFCQ4CuHNWlpeioTZ2D6BlL/cPK2\nGso/b+WPT9w5ADw77ucqDvhBFOQrQFuINAaP7AXSvcJRpTRwcTMEf0JsBlex\nldn2JWNsruxzWbU+DBY+0ijz0CKCar7/5tMWgueqNQ==", + "uuid": "258a2184-2842-b485-25ca-293525152425", + "method": "RegisterAccount", + "data": { + "accountid": "7653385737", + "clearinghouse": "SWEDEN", + "bank": "Handelsbanken", + "descriptor": "**706212" + } + }, + "version": "1.1" +} diff --git a/spec/data/select_account.json b/spec/data/select_account.json new file mode 100644 index 0000000..945c8d9 --- /dev/null +++ b/spec/data/select_account.json @@ -0,0 +1,12 @@ +{ + "result": { + "signature": "4FTSgwn7JqKns/vRjmdgyoUOhQQP0BM3SccezsLVThLqX5DXCsu5D0MTRheR\n1943yJ4ahm5122HLYx1a4Ug05u5uxvNv/9/IezICb3DCn/xQP0ucIp4ZENEb\nPxzvf1f1HQ8KW8rA4qqyackFxeV2NHAtYIXbA2fgB2FcQMmfWqPXsoUIW2fl\nkUhMfgIMWkUZKd/lALZIt0a9dG9cDePlFHjZ4HtLJLR2j57uHa+jUQ9FqHho\nLRS+egaCx++U8cLtnbgu6F/IP4moVRCIb7tdpkcxdINkQS+gCnrq/Y3OPNJ5\nZG1xnkPRclW8Q9bBBgl6VOo7F/bldzvcqHh+XHmTNg==", + "uuid": "258a2184-2842-b485-25ca-293525152425", + "method": "SelectAccount", + "data": { + "orderid": "2190971587", + "url": "https://trustly.com/_/bec96a48-d454-448e-a9ba-25fea8eeba3f" + } + }, + "version": "1.1" +} diff --git a/spec/data/trustly_private_key.pem b/spec/data/trustly_private_key.pem new file mode 100644 index 0000000..ca2dfcf --- /dev/null +++ b/spec/data/trustly_private_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA50Rcabjgmkj/iS46bRk+dNfqQpaLLuQcFoPaQ1pxrTMpXLM4 +ZJCjejn//6t3nXSJBPjeypG9ANp6Mx26cTv2G2sc4lQHDOmdRXgJn/LmWCPpgjVj +4EMI0KZaErXisWdz8q5MJ1D3+jzTuRFeVPyMA//uPQ0u367pQq0C6G2t1GtZbvD7 +zaPBgxTcBIbUtERRhmuSC97wuoP7+6BZBPO2dF1cysfAWZX785xdR1fi53qZGQhF +qDwjrhI87lsWkNxDlbZSlozWinXL1vsFHzZ1jZtrm4NLzmjzBg1iBQRNNoTll/d8 +IdeUUhCNtTehoETkYbhWtOUGaXS22kXADPMwiQIDAQABAoIBAQCekuAu2caf49fb +ryf+sKWDpp0JRYJwB5c+1O/u6PAzS3ZcCsNrKUX+xBBFtcPR4hslnqPdECshj6zk +qciyZePtjveCNQ2UjAb7oEAxPXM2EoHFd2hhWHWN49K1K6Qh8oata1fqSXmPSu/9 +4OvmqDg1ceJgWE7Ar4Vf45Ov3ayojhkm9iOyoi3kxgLNhzKZ2A/IgJ3hqXhUYtZv +2ZUBo24GP2l5CZmHRN2EA6qQ9V3iKZ7YqwdrvICBQvJ0LreCfscbLBvohr3LxBDn +PvVGGDaoU0r0odG5voUE+rx6ytpT7t7Bv4O2+qYqRyrzrENwagrI3pPWdNsEoZzh +aOwgQ4IBAoGBAPvSl/0Ze6irGA94Gdt1prH1sXP7oLN15qZZIRM9SgcVP7hkkO0d +qiqIoky3UCRQg8/M2ZrGB81oFQoymcZLt/WnctToosYqDhKyIKbvOKzCGdBun8lA +FwVekaHpxQ9E+dje32Gcpmpe2huR0WCAoLWETogE4rA8qIcO/BdlaMxBAoGBAOsa +eXxwxlpaKsZux3kn8ycHIlKB16Cgu2AFnHD705fVfQsg1ltd+YsC0evZNPSpTKHf +puwQEgGejMiYKnTmU8dWPdhKQV9NwpcQQ+wSwgl8R79IUgV71/0BIue7FngEwe9f +/OpfjnXplrotENc1lq+hslvN616u7uUP/60gHHJJAoGBAOBaW3bvATDgXetKQR84 +zm62Sobeo+m/HOMPfVw6un1c/Qw27LeUOkryuEZI+2mfIhA8nZI65DCojjYrprz4 +MMj3imMNcBfE2AzoDhcsAf5IX99G76zJILlz66OpNhvIhCAnUDUS72DNaNwvKa8k +agnN+nlMgPoq0KqjOw1NF/UBAoGAP3XD+R0PzW+tQCbC3Sc1cQFx+EdoBsmcCk05 +bx3qfX944zoX4k25gBZgx4K30pqoPsF58xpbYeiEI9k/DJLnZlUXGHzirHD254PS +cbSWf6z2SOGikixdnsNhwp8zb24JUy3bvP/SGm3U66gidZTXeczxseohcEtT3Ky2 +3OpgA1ECgYBG4bkEe0P0ojbMWtMHCvNSxt020Fnt5IE+EdmVTZn60Vxb2yxmQw7v +fH2Oj+OShNDo6Ap8YcApQCdUQEGypv17oIcqDyVXh8CH3J/iBLtjRyRNgHbxM+w5 +w+rnbjl9ws8tmeyyXuCQLq5Oo8Zb2isEIp/E7X8vweDjT3bc/s7H+g== +-----END RSA PRIVATE KEY----- diff --git a/spec/data/trustly_public_key.pem b/spec/data/trustly_public_key.pem new file mode 100644 index 0000000..5ec88cb --- /dev/null +++ b/spec/data/trustly_public_key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA50Rcabjgmkj/iS46bRk+ +dNfqQpaLLuQcFoPaQ1pxrTMpXLM4ZJCjejn//6t3nXSJBPjeypG9ANp6Mx26cTv2 +G2sc4lQHDOmdRXgJn/LmWCPpgjVj4EMI0KZaErXisWdz8q5MJ1D3+jzTuRFeVPyM +A//uPQ0u367pQq0C6G2t1GtZbvD7zaPBgxTcBIbUtERRhmuSC97wuoP7+6BZBPO2 +dF1cysfAWZX785xdR1fi53qZGQhFqDwjrhI87lsWkNxDlbZSlozWinXL1vsFHzZ1 +jZtrm4NLzmjzBg1iBQRNNoTll/d8IdeUUhCNtTehoETkYbhWtOUGaXS22kXADPMw +iQIDAQAB +-----END PUBLIC KEY----- diff --git a/spec/data/void.json b/spec/data/void.json new file mode 100644 index 0000000..20b3b21 --- /dev/null +++ b/spec/data/void.json @@ -0,0 +1,11 @@ +{ + "version": "1.1", + "result": { + "signature": "ICPnfczGVjEc24+0DeRhZtQTLEXN2LWXiIQhlhsdB5A8hA4xfzcOOyDGtvcF\ntLT4Psckez1f0/dn+v9VNK2/qBcN0/pVdfIkKgUeM3TwMWJTJsG+z+p30Nwn\n8lsicvXZRsYi7xs1SxOiSe3L937HCQv41YP0bk6SfvnAi4xnJ9jwT+UCr+fj\nh7vcBqlrwsDMuYT1X5qRlnpYJoI9/PfMIhu15vQ3LsLF00s0yzU2cU58NSBQ\nuDP7NSU394Pev4xE6RGHOLvYxh7CXbXdP5rQKMk+xE48SdCUAiLVB1zXHN6r\nMZAXEFJN6deWuqzSjtqF+zVAmBM/2bW4VzYqSErP4Q==", + "method": "Void", + "data": { + "result": "1" + }, + "uuid": "8bedfbd4-8181-38e1-f0be-f360171aefc6" + } +} diff --git a/spec/jsonrpc_request_spec.rb b/spec/jsonrpc_request_spec.rb new file mode 100644 index 0000000..363df97 --- /dev/null +++ b/spec/jsonrpc_request_spec.rb @@ -0,0 +1,233 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Trustly::Data::JSONRPCRequest do + describe '#new' do + subject { described_class.new(**params) } + let(:method) { 'Test' } + let(:data) { { 'A' => 1 } } + let(:attributes) { { 'B' => 2 } } + let(:uuid) { SecureRandom.uuid } + let(:signature) { 'signature' } + + shared_examples_for 'successful initialization' do + it 'has method' do + expect(subject.payload).to include({ + 'method' => method + }) + end + it 'has version' do + expect(subject.payload).to include({ + 'version' => 1.1 + }) + end + it 'initializes payload' do + expect(subject.payload).to include({ + 'params' => a_hash_including({ + 'Data' => { + 'Attributes' => attributes + }.merge(data) + }) + }) + end + it 'initializes attributes' do + expect(subject.attributes).to eq({ 'B' => 2 }) + end + it 'initializes data' do + expect(subject.data).to eq({ 'A' => 1, 'Attributes' => attributes }) + end + it 'reads data' do + expect(subject.data_at('A')).to eq(1) + end + it 'reads attributes' do + expect(subject.attribute_at('B')).to eq(2) + end + end + + context 'with data and attributes' do + context 'with invalid data' do + let(:params) do + { + data: [1,2,3], + attributes: { 'Data' => 'test' } + } + end + it 'raises TypeError' do + expect { subject }.to raise_error( + TypeError, + 'Data must be a Hash if attributes are provided' + ) + end + end + context 'with valid data' do + context 'with cleanup' do + let(:params) do + { + data: data, + attributes: attributes + } + end + let(:data) do + { + 'A' => 1, + 'EmptyArray' => [], + 'EmptyValue' => nil, + 'EmptyNestedHash' => { 'EmptyArray' => [], 'EmptyValue' => nil } + } + end + let(:attributes) do + { + 'B' => 2, + 'EmptyNestedHash' => { 'EmptyArray' => [], 'EmptyValue' => nil } + } + end + + it 'cleans up data' do + expect(subject.data).to eq('A' => 1, 'Attributes' => { 'B' => 2 }) + end + end + context 'without cleanup' do + let(:params) do + { + data: data, + attributes: attributes, + method: method + } + end + include_examples 'successful initialization' + end + end + end + context 'with attributes only' do + let(:params) do + { + attributes: attributes + } + end + + it 'still initializes data' do + expect(subject.data).to eq('Attributes' => attributes) + end + + it 'initializes attributes' do + expect(subject.attributes).to eq(attributes) + end + end + context 'with data only' do + let(:params) do + { + data: data + } + end + + it 'initializes data' do + expect(subject.data).to eq('A' => 1) + end + + it 'does not initialize attributes' do + expect(subject.attributes).to be_nil + end + end + context 'without data and attributes' do + let(:params) do + {} + end + + it 'does not initialize data' do + expect(subject.data).to be_nil + end + + it 'initializes params' do + expect(subject.params).to eq({}) + end + end + context 'with payload' do + let(:params) do + { + payload: { + 'method': method, + 'params': { + 'UUID': uuid, + 'Signature': signature, + 'Data': { + 'Attributes': attributes + }.merge(data) + } + } + } + end + include_examples 'successful initialization' + it 'initializes uuid' do + expect(subject.uuid).to eq(uuid) + end + it 'initializes signature' do + expect(subject.signature).to eq(signature) + end + end + end + describe '#setters' do + subject do + described_class.new(payload: payload) + end + let(:payload) do + { + 'method' => 'Test', + 'params' => { + 'Data' => { + 'A' => 1, + 'Attributes' => { + 'B' => 2 + } + } + } + } + end + + describe '#update_data_at' do + it 'updates data at a specific key' do + subject.update_data_at('A', 10) + expect(subject.data).to include({ 'A' => 10 }) + end + + it 'inserts new data at a specific key' do + subject.update_data_at('C', 30) + expect(subject.data).to include({ 'A' => 1, 'C' => 30 }) + end + end + + describe '#update_attribute_at' do + it 'updates attribute at a specific key' do + subject.update_attribute_at('B', 10) + expect(subject.attributes).to include({ 'B' => 10 }) + end + + it 'inserts a new attribute at a specific key' do + subject.update_attribute_at('C', 30) + expect(subject.attributes).to include({ 'B' => 2, 'C' => 30 }) + end + end + + describe '#signature=' do + it 'updates signature' do + subject.signature = 'signature' + expect(subject.params).to include('Signature' => 'signature') + end + end + + describe '#uuid=' do + it 'updates signature' do + uuid = SecureRandom.uuid + subject.uuid = uuid + expect(subject.params).to include('UUID' => uuid) + end + end + + describe '#method=' do + it 'updates signature' do + subject.method = 'OtherTest' + expect(subject.payload).to include('method' => 'OtherTest') + end + end + end +end diff --git a/spec/jsonrpc_response_spec.rb b/spec/jsonrpc_response_spec.rb new file mode 100644 index 0000000..106ba16 --- /dev/null +++ b/spec/jsonrpc_response_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Trustly::Data::JSONRPCResponse do + describe '#new' do + subject { described_class.new(http_response: response) } + let(:payload) do + { + 'version' => '1.1', + 'result' => { + 'signature' => 'signature', + 'method' => 'Test', + 'data' => { + 'result' => '1', + 'orderid' => '1187741486' + }, + 'uuid' => '8bedfbd4-8181-38e1-f0be-f360171aefc6' + } + } + end + let(:status) { 200 } + let(:phrase) { 'OK' } + let(:response) do + Faraday::Response.new( + status: status, + response_headers: { 'Content-Type': 'application/json' }, + response_body: payload, + method: :post, + reason_phrase: phrase + ) + end + context 'with an invalid API version' do + before do + payload.merge!('version' => '1.0') + end + + it 'fails' do + expect { subject }.to raise_error( + Trustly::Exception::JSONRPCVersionError, + 'JSON RPC Version is not supported' + ) + end + end + end +end diff --git a/spec/signed_api_spec.rb b/spec/signed_api_spec.rb new file mode 100644 index 0000000..3986b4e --- /dev/null +++ b/spec/signed_api_spec.rb @@ -0,0 +1,163 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Trustly::Api::Signed do + let(:basic_params) do + { + username: 'User', + password: 'Password', + private_pem: ENV['MERCHANT_PRIVATE_KEY'], + public_pem: ENV['TRUSTLY_PUBLIC_KEY'], + host: 'test.trustly.com' + } + end + describe '#new' do + context 'successful initialization' do + subject { described_class.new(**params) } + + context 'when private key is not specified in the params' do + let(:params) do + basic_params.except(:private_pem) + end + it 'uses default key' do + expect(subject.merchant_key.to_pem).to eq ENV['MERCHANT_PRIVATE_KEY'] + end + end + + context 'when public key is not specified in the params' do + let(:params) do + basic_params.except(:public_key) + end + it 'uses default key' do + expect(subject.trustly_key.to_pem).to eq ENV['TRUSTLY_PUBLIC_KEY'] + end + end + + context 'when host is not specified in the params' do + let(:params) do + basic_params.except(:host) + end + it 'uses default key' do + expect(subject.api_host).to eq 'test.trustly.com' + end + end + + context 'with valid params' do + let(:params) do + basic_params.merge( + host: 'prod.trustly.com', port: 80, is_https: false + ) + end + it 'initializes correctly' do + expect(subject.api_host).to eq 'prod.trustly.com' + expect(subject.api_port).to eq 80 + expect(subject.api_is_https).to eq false + expect(subject.api_username).to eq 'User' + expect(subject.api_password).to eq 'Password' + end + end + end + + context 'initialization errors' do + shared_examples_for 'incorrect configuration' do + it 'fails' do + expect do + described_class.new(**params) + end.to raise_error( + Trustly::Exception::ConfigurationError, message + ) + end + end + context 'with username not specified' do + let(:params) do + basic_params.except(:username) + end + let(:message) do + 'Username not specified' + end + include_examples 'incorrect configuration' + end + context 'with password not specified' do + let(:params) do + basic_params.except(:password) + end + let(:message) do + 'Password not specified' + end + include_examples 'incorrect configuration' + end + context 'with private key is nil' do + let(:params) do + basic_params.merge(private_pem: nil) + end + let(:message) do + 'Merchant private key not specified' + end + include_examples 'incorrect configuration' + end + context 'with public key is nil' do + let(:params) do + basic_params.merge(public_pem: nil) + end + let(:message) do + 'Trustly public key not specified' + end + include_examples 'incorrect configuration' + end + context 'with public key having incorrect value' do + let(:params) do + basic_params.merge(public_pem: 'test') + end + let(:message) do + 'Trustly public key not specified' + end + include_examples 'incorrect configuration' + end + context 'with multiple errors' do + let(:params) do + basic_params.except(:username).merge(host: nil) + end + let(:message) do + 'Api host not specified; Username not specified' + end + include_examples 'incorrect configuration' + end + end + end + + describe '#verify_signed_response' do + end + + describe 'rpc calls' do + shared_examples_for 'rpc call' do |required_params| + required_params.each do |param| + context "with missing required param #{param}" do + let(:modified_params) do + params.except(param) + end + it 'raises data error' do + expect do + subject.public_send(method, params) + end.to raise_error( + Trustly::Exception::DataError, + "Required data is missing: #{params}" + ) + end + end + context 'with valid data' do + context 'with a successful response' do + before do + expect(Trustly::Data::JSONRPCRequest).to receive(:new). + with(method: method, data: data, attributes: attriubtes). + and_return(double) + end + + it 'makes a JSON RPC request and verifies its response' do + end + end + end + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..9a6bfc0 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'simplecov' +require 'webmock/rspec' +require 'debug' + +WebMock.disable_net_connect!(allow_localhost: false) + +SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter] +SimpleCov.start do + add_filter '/spec/' + minimum_coverage 100 +end + +ENV['MERCHANT_PRIVATE_KEY'] ||= begin + path = File.expand_path('data/merchant_private_key.pem', __dir__) + File.read(path) if File.file?(path) +end + +ENV['MERCHANT_PUBLIC_KEY'] ||= begin + path = File.expand_path('data/merchant_public_key.pem', __dir__) + File.read(path) if File.file?(path) +end + +ENV['TRUSTLY_PRIVATE_KEY'] ||= begin + path = File.expand_path('data/trustly_private_key.pem', __dir__) + File.read(path) if File.file?(path) +end + +ENV['TRUSTLY_PUBLIC_KEY'] ||= begin + path = File.expand_path('data/trustly_public_key.pem', __dir__) + File.read(path) if File.file?(path) +end + +require 'trustly' + +RSpec.configure do |config| + config.expect_with :rspec do |expectations| + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + config.mock_with :rspec do |mocks| + mocks.verify_partial_doubles = true + end + + config.shared_context_metadata_behavior = :apply_to_host_groups + # config.disable_monkey_patching! + # config.default_formatter = 'doc' if config.files_to_run.one? + # config.profile_examples = 10 + config.order = :random + config.color = true + config.tty = true + Kernel.srand config.seed +end diff --git a/trustly-client-ruby.gemspec b/trustly-client-ruby.gemspec index 153518b..d91cb7d 100644 --- a/trustly-client-ruby.gemspec +++ b/trustly-client-ruby.gemspec @@ -1,27 +1,35 @@ # -*- encoding: utf-8 -*- -lib = File.expand_path("../lib", __FILE__) +lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "trustly/version" Gem::Specification.new do |gem| gem.name = 'trustly-client-ruby' gem.version = Trustly::VERSION - gem.date = Date.today.to_s + gem.date = Time.now.strftime('%Y-%m-%d') + gem.required_ruby_version = '>= 2.7' + gem.platform = Gem::Platform::RUBY - gem.summary = "Trustly Client Ruby Support" - gem.description = "Support for Ruby use of trustly API" + gem.summary = 'Trustly Client Ruby Support' + gem.description = 'Support for Ruby use of Trustly API' gem.authors = ['Jorge Carretie'] gem.email = 'jorge@carretie.com' gem.homepage = 'https://github.com/jcarreti/trusty-client-ruby' - gem.license = "MIT" + gem.license = 'MIT' + gem.add_runtime_dependency 'rake' + gem.add_runtime_dependency 'faraday' + gem.add_runtime_dependency 'faraday_middleware' - gem.add_dependency('rake') - gem.add_dependency('faraday') - gem.add_development_dependency('rspec', [">= 2.0.0"]) + gem.add_development_dependency 'rspec' + gem.add_development_dependency 'simplecov' + gem.add_development_dependency 'webmock' + gem.add_development_dependency 'debug' + gem.add_development_dependency 'rubocop' # ensure the gem is built out of versioned files - gem.files = Dir['{lib}/**/*', 'README*', 'LICENSE*'] - gem.require_paths = ["lib"] + gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) + gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) } + gem.require_paths = ['lib'] end \ No newline at end of file From 87c1e48bb4c3f727df24382bf3f6ab4315b81392 Mon Sep 17 00:00:00 2001 From: Artsiom Kuts Date: Mon, 8 Aug 2022 15:42:53 +0200 Subject: [PATCH 3/7] spec, bin, etc. rubocop updates --- Rakefile | 6 ++++-- bin/console | 1 + spec/jsonrpc_request_spec.rb | 34 +++++++++++++++++----------------- spec/jsonrpc_response_spec.rb | 4 ++-- spec/signed_api_spec.rb | 14 +++++++------- trustly-client-ruby.gemspec | 17 +++++++++-------- 6 files changed, 40 insertions(+), 36 deletions(-) diff --git a/Rakefile b/Rakefile index 9d043d7..d247fe8 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,7 @@ -require "bundler/gem_tasks" +# frozen_string_literal: true + +require 'bundler/gem_tasks' task :console do - exec "irb -r trustly -I ./lib" + exec 'irb -r trustly -I ./lib' end diff --git a/bin/console b/bin/console index 24d1ead..3c0f8e2 100755 --- a/bin/console +++ b/bin/console @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true require 'bundler/setup' require 'trustly' diff --git a/spec/jsonrpc_request_spec.rb b/spec/jsonrpc_request_spec.rb index 363df97..8d13f29 100644 --- a/spec/jsonrpc_request_spec.rb +++ b/spec/jsonrpc_request_spec.rb @@ -14,22 +14,22 @@ shared_examples_for 'successful initialization' do it 'has method' do expect(subject.payload).to include({ - 'method' => method - }) + 'method' => method + }) end it 'has version' do expect(subject.payload).to include({ - 'version' => 1.1 - }) + 'version' => 1.1 + }) end it 'initializes payload' do expect(subject.payload).to include({ - 'params' => a_hash_including({ - 'Data' => { - 'Attributes' => attributes - }.merge(data) - }) - }) + 'params' => a_hash_including({ + 'Data' => { + 'Attributes' => attributes + }.merge(data) + }) + }) end it 'initializes attributes' do expect(subject.attributes).to eq({ 'B' => 2 }) @@ -49,7 +49,7 @@ context 'with invalid data' do let(:params) do { - data: [1,2,3], + data: [1, 2, 3], attributes: { 'Data' => 'test' } } end @@ -146,12 +146,12 @@ let(:params) do { payload: { - 'method': method, - 'params': { - 'UUID': uuid, - 'Signature': signature, - 'Data': { - 'Attributes': attributes + 'method' => method, + 'params' => { + 'UUID' => uuid, + 'Signature' => signature, + 'Data' => { + 'Attributes' => attributes }.merge(data) } } diff --git a/spec/jsonrpc_response_spec.rb b/spec/jsonrpc_response_spec.rb index 106ba16..49a9dbd 100644 --- a/spec/jsonrpc_response_spec.rb +++ b/spec/jsonrpc_response_spec.rb @@ -12,8 +12,8 @@ 'signature' => 'signature', 'method' => 'Test', 'data' => { - 'result' => '1', - 'orderid' => '1187741486' + 'result' => '1', + 'orderid' => '1187741486' }, 'uuid' => '8bedfbd4-8181-38e1-f0be-f360171aefc6' } diff --git a/spec/signed_api_spec.rb b/spec/signed_api_spec.rb index 3986b4e..6780c42 100644 --- a/spec/signed_api_spec.rb +++ b/spec/signed_api_spec.rb @@ -7,8 +7,8 @@ { username: 'User', password: 'Password', - private_pem: ENV['MERCHANT_PRIVATE_KEY'], - public_pem: ENV['TRUSTLY_PUBLIC_KEY'], + private_pem: ENV.fetch('MERCHANT_PRIVATE_KEY', nil), + public_pem: ENV.fetch('TRUSTLY_PUBLIC_KEY', nil), host: 'test.trustly.com' } end @@ -21,7 +21,7 @@ basic_params.except(:private_pem) end it 'uses default key' do - expect(subject.merchant_key.to_pem).to eq ENV['MERCHANT_PRIVATE_KEY'] + expect(subject.merchant_key.to_pem).to eq ENV.fetch('MERCHANT_PRIVATE_KEY', nil) end end @@ -30,7 +30,7 @@ basic_params.except(:public_key) end it 'uses default key' do - expect(subject.trustly_key.to_pem).to eq ENV['TRUSTLY_PUBLIC_KEY'] + expect(subject.trustly_key.to_pem).to eq ENV.fetch('TRUSTLY_PUBLIC_KEY', nil) end end @@ -148,9 +148,9 @@ context 'with valid data' do context 'with a successful response' do before do - expect(Trustly::Data::JSONRPCRequest).to receive(:new). - with(method: method, data: data, attributes: attriubtes). - and_return(double) + expect(Trustly::Data::JSONRPCRequest).to receive(:new) + .with(method: method, data: data, attributes: attriubtes) + .and_return(double) end it 'makes a JSON RPC request and verifies its response' do diff --git a/trustly-client-ruby.gemspec b/trustly-client-ruby.gemspec index d91cb7d..3dd34a0 100644 --- a/trustly-client-ruby.gemspec +++ b/trustly-client-ruby.gemspec @@ -1,12 +1,12 @@ -# -*- encoding: utf-8 -*- -lib = File.expand_path('../lib', __FILE__) +# frozen_string_literal: true + +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require "trustly/version" +require 'trustly/version' Gem::Specification.new do |gem| gem.name = 'trustly-client-ruby' gem.version = Trustly::VERSION - gem.date = Time.now.strftime('%Y-%m-%d') gem.required_ruby_version = '>= 2.7' gem.platform = Gem::Platform::RUBY @@ -18,18 +18,19 @@ Gem::Specification.new do |gem| gem.homepage = 'https://github.com/jcarreti/trusty-client-ruby' gem.license = 'MIT' - gem.add_runtime_dependency 'rake' gem.add_runtime_dependency 'faraday' gem.add_runtime_dependency 'faraday_middleware' + gem.add_runtime_dependency 'rake' + gem.add_development_dependency 'debug' gem.add_development_dependency 'rspec' + gem.add_development_dependency 'rubocop' gem.add_development_dependency 'simplecov' gem.add_development_dependency 'webmock' - gem.add_development_dependency 'debug' - gem.add_development_dependency 'rubocop' # ensure the gem is built out of versioned files gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) } gem.require_paths = ['lib'] -end \ No newline at end of file + gem.metadata['rubygems_mfa_required'] = 'true' +end From f9f84a683b661fbf5cef3b4c0bebf0e51677e47a Mon Sep 17 00:00:00 2001 From: Artsiom Kuts Date: Mon, 8 Aug 2022 16:00:59 +0200 Subject: [PATCH 4/7] better nesting for data/api objects --- lib/trustly.rb | 4 +- lib/trustly/api.rb | 127 ------ lib/trustly/api/base.rb | 131 ++++++ lib/trustly/api/signed.rb | 416 +++++++++--------- lib/trustly/data.rb | 46 -- lib/trustly/data/base.rb | 50 +++ lib/trustly/data/jsonrpc_request.rb | 132 +++--- lib/trustly/data/jsonrpc_response.rb | 30 +- .../data/jsonrpcnotification_request.rb | 86 ++-- .../data/jsonrpcnotification_response.rb | 118 ++--- lib/trustly/data/request.rb | 20 +- lib/trustly/data/response.rb | 103 ++--- 12 files changed, 650 insertions(+), 613 deletions(-) delete mode 100644 lib/trustly/api.rb create mode 100644 lib/trustly/api/base.rb delete mode 100644 lib/trustly/data.rb create mode 100644 lib/trustly/data/base.rb diff --git a/lib/trustly.rb b/lib/trustly.rb index 9a81dd7..03d018b 100644 --- a/lib/trustly.rb +++ b/lib/trustly.rb @@ -14,7 +14,7 @@ module Trustly require 'trustly/exception/jsonrpc_version_error' require 'trustly/exception/signature_error' -require 'trustly/data' +require 'trustly/data/base' require 'trustly/data/request' require 'trustly/data/response' require 'trustly/data/jsonrpc_request' @@ -22,6 +22,6 @@ module Trustly require 'trustly/data/jsonrpcnotification_request' require 'trustly/data/jsonrpcnotification_response' -require 'trustly/api' +require 'trustly/api/base' require 'trustly/api/signed' require 'trustly/version' diff --git a/lib/trustly/api.rb b/lib/trustly/api.rb deleted file mode 100644 index 76fabb5..0000000 --- a/lib/trustly/api.rb +++ /dev/null @@ -1,127 +0,0 @@ -class Trustly::Api - attr_accessor :api_host, - :api_port, - :api_is_https, - :last_request, - :trustly_key - - def initialize(**config) - self.api_host = config[:host] - self.api_port = config[:port] - self.api_is_https = config[:is_https] - - self.load_trustly_key(config[:public_pem]) - validate! - end - - def verify_signed_response(response) - method = response.method || '' - uuid = response.uuid || '' - raw_signature = Base64.decode64(response.signature || '') - serial_data = "#{method}#{uuid}#{self.serialize(response.data)}" - trustly_key.public_key.verify( - OpenSSL::Digest::SHA1.new, raw_signature, serial_data - ) - end - - private - - def url_path(_request = nil) - raise NotImplementedError - end - - def handle_response(_request, _http_call) - raise NotImplementedError - end - - def insert_credentials(_request) - raise NotImplementedError - end - - def serialize(object) - serialized = "" - case object - when Array then serialize_array(object, serialized) - when Hash then serialize_hash(object, serialized) - else serialized.concat(object.to_s) - end - serialized - end - - def serialize_array(object, serialized) - object.each { |value| serialized.concat(serialize(value)) } - end - - def serialize_hash(object, serialized) - object.sort.each do |key, value| - serialized.concat(key.to_s).concat(serialize(value)) - end - end - - def load_trustly_key(pkey) - self.trustly_key = OpenSSL::PKey::RSA.new(pkey) unless pkey.nil? - rescue OpenSSL::PKey::RSAError - self.trustly_key = nil - end - - def validate! - return if configuration_errors.empty? - - errors_string = configuration_errors.join('; ') - raise Trustly::Exception::ConfigurationError, errors_string - end - - def configuration_errors - errors = [] - errors.push 'Api host not specified' if api_host.nil? - errors.push 'Trustly public key not specified' if trustly_key.nil? - errors - end - - def base_url - schema = api_is_https ? 'https' : 'http' - add_port = (api_is_https && api_port != 443) || api_port != 80 - port = add_port && !api_port.nil? ? ":#{api_port}" : '' - "#{schema}://#{api_host}#{port}" - end - - def url(request) - return URI.parse("#{self.base_url}#{url_path(request)}") - end - - def call_rpc(request) - insert_credentials!(request) - self.last_request = request - request_uri = url(request) - body = request.to_json - response = connection(request_uri).post( - request_uri.path, body, { 'Content-Type' => 'application/json' } - ) - handle_response(request, response) - rescue Faraday::Error => e - handle_error(e, request, body) - end - - def handle_error(error, request, body) - message = e.message - exception = case error - when Faraday::ParsingError, Trustly::ClientError - Trustly::Exception::DataError - else - Trustly::Exception::ConnectionError - end - unless (response = error.response).nil? - message = " - #{response.status}: #{response.body} - #{request.method}, #{body} - " - end - raise exception, message - end - - def connection(request_uri) - Faraday.new(request_uri.origin) do |conn| - conn.response :json - conn.adapter :net_http - end - end -end diff --git a/lib/trustly/api/base.rb b/lib/trustly/api/base.rb new file mode 100644 index 0000000..2d05d9a --- /dev/null +++ b/lib/trustly/api/base.rb @@ -0,0 +1,131 @@ +module Trustly + module Api + class Base + attr_accessor :api_host, + :api_port, + :api_is_https, + :last_request, + :trustly_key + + def initialize(**config) + self.api_host = config[:host] + self.api_port = config[:port] + self.api_is_https = config[:is_https] + + self.load_trustly_key(config[:public_pem]) + validate! + end + + def verify_signed_response(response) + method = response.method || '' + uuid = response.uuid || '' + raw_signature = Base64.decode64(response.signature || '') + serial_data = "#{method}#{uuid}#{self.serialize(response.data)}" + trustly_key.public_key.verify( + OpenSSL::Digest::SHA1.new, raw_signature, serial_data + ) + end + + private + + def url_path(_request = nil) + raise NotImplementedError + end + + def handle_response(_request, _http_call) + raise NotImplementedError + end + + def insert_credentials(_request) + raise NotImplementedError + end + + def serialize(object) + serialized = "" + case object + when Array then serialize_array(object, serialized) + when Hash then serialize_hash(object, serialized) + else serialized.concat(object.to_s) + end + serialized + end + + def serialize_array(object, serialized) + object.each { |value| serialized.concat(serialize(value)) } + end + + def serialize_hash(object, serialized) + object.sort.each do |key, value| + serialized.concat(key.to_s).concat(serialize(value)) + end + end + + def load_trustly_key(pkey) + self.trustly_key = OpenSSL::PKey::RSA.new(pkey) unless pkey.nil? + rescue OpenSSL::PKey::RSAError + self.trustly_key = nil + end + + def validate! + return if configuration_errors.empty? + + errors_string = configuration_errors.join('; ') + raise Trustly::Exception::ConfigurationError, errors_string + end + + def configuration_errors + errors = [] + errors.push 'Api host not specified' if api_host.nil? + errors.push 'Trustly public key not specified' if trustly_key.nil? + errors + end + + def base_url + schema = api_is_https ? 'https' : 'http' + add_port = (api_is_https && api_port != 443) || api_port != 80 + port = add_port && !api_port.nil? ? ":#{api_port}" : '' + "#{schema}://#{api_host}#{port}" + end + + def url(request) + return URI.parse("#{self.base_url}#{url_path(request)}") + end + + def call_rpc(request) + insert_credentials!(request) + self.last_request = request + request_uri = url(request) + body = request.to_json + response = connection(request_uri).post( + request_uri.path, body, { 'Content-Type' => 'application/json' } + ) + handle_response(request, response) + rescue Faraday::Error => e + handle_error(e, request, body) + end + + def handle_error(error, request, body) + message = e.message + exception = case error + when Faraday::ParsingError, Trustly::ClientError + Trustly::Exception::DataError + else + Trustly::Exception::ConnectionError + end + unless (response = error.response).nil? + message = " + #{response.status}: #{response.body} - #{request.method}, #{body} + " + end + raise exception, message + end + + def connection(request_uri) + Faraday.new(request_uri.origin) do |conn| + conn.response :json + conn.adapter :net_http + end + end + end + end +end diff --git a/lib/trustly/api/signed.rb b/lib/trustly/api/signed.rb index 188d0ee..1355010 100644 --- a/lib/trustly/api/signed.rb +++ b/lib/trustly/api/signed.rb @@ -1,209 +1,213 @@ -class Trustly::Api::Signed < Trustly::Api - DEFAULT_API_PATH = '/api/1' - SIGNATURE_ERROR = 'Incoming message signature is not valid' - UUID_MISMATCH = 'Incoming response is not related to the request. UUID mismatch.' - - attr_accessor :url_path, - :api_username, - :api_password, - :merchant_key - - def initialize(**config) - full_config = default_config.merge(config) - self.api_username = full_config.fetch(:username, nil) - self.api_password = full_config.fetch(:password, nil) - self.load_merchant_key(full_config[:private_pem]) - - super(**full_config.slice(*%i[host port is_https public_pem])) - end - - def void(**options) - required = %w[OrderId] - data = %w[OrderId] - call_rpc_for_data('Void', options, data: data, required: required) - end - - def deposit(**options) - required = %w[ - Locale Country Currency SuccessURL FailURL NotificationURL Amount - EndUserID MessageID Firstname Lastname ShopperStatement - ] - attributes = %w[ - Locale Country Currency SuggestedMinAmount SuggestedMaxAmount Amount - IP SuccessURL FailURL TemplateURL URLTarget MobilePhone ShopperStatement - Firstname Lastname NationalIdentificationNumber Email AccountID - UnchangeableNationalIdentificationNumber ShippingAddressCountry - ShippingAddressPostalCode ShippingAddressLine1 ShippingAddressLine2 - ShippingAddress RequestDirectDebitMandate ChargeAccountID QuickDeposit - URLScheme ExternalReference PSPMerchant PSPMerchantURL - MerchantCategoryCode RecipientInformation - ] - data = %w[NotificationURL EndUserID MessageID] - call_rpc_for_data( - 'Deposit', - options, data: data, attributes: attributes, required: required - ) - end - - def refund(**options) - required = %w[OrderId Amount Currency] - data = %w[OrderId Amount Currency] - attributes = %w[ExternalReference] - call_rpc_for_data( - 'Refund', options, - data: data, attributes: attributes, required: required - ) - end - - def select_account(**options) - required = %w[ - Locale Country SuccessURL FailURL NotificationURL EndUserID MessageID - Firstname Lastname - ] - attributes = %w[ - Locale Country Firstname Lastname SuccessURL FailURL Email IP - RequestDirectDebitMandate TemplateURL URLTarget MobilePhone - NationalIdentificationNumber UnchangeableNationalIdentificationNumber - ShopperStatement DateOfBirth URLScheme PSPMerchant PSPMerchantURL - MerchantCategoryCode - ] - data = %w[NotificationURL EndUserID MessageID] - call_rpc_for_data( - 'SelectAccount', options, - data: data, attributes: attributes, required: required - ) - end - - def account_payout(**options) - required = %w[ - NotificationURL AccountID EndUserID MessageID Amount Currency - ShopperStatement - ] - data = %w[ - NotificationURL AccountID EndUserID MessageID Amount Currency - ] - attributes = %w[ - ShopperStatement PSPMerchant PSPMerchantURL - ExternalReference MerchantCategoryCode SenderInformation - ] - call_rpc_for_data( - 'AccountPayout', options, - data: data, attributes: attributes, required: required - ) - end - - def register_account(**options) - required = %w[ - EndUserID ClearingHouse BankNumber AccountNumber Firstname Lastname - ] - data = %w[ - EndUserID ClearingHouse BankNumber AccountNumber Firstname Lastname - ] - attributes = %w[ - DateOfBirth MobilePhone NationalIdentificationNumber AddressCountry - AddressPostalCode AddressCity AddressLine1 AddressLine2 Address Email - ] - call_rpc_for_data( - 'RegisterAccount', options, - data: data, attributes: attributes, required: required - ) - end - - def get_withdrawals(**options) - data = %w[OrderId] - required = %w[OrderId] - call_rpc_for_data( - 'GetWithdrawals', options, - data: data, required: required - ) - end - - def notification_response(request, success = true) - response = Trustly::JSONRPCNotificationResponse.new( - request: request, success: success - ) - response.signature = sign_merchant_request(response) - response - end - - private - - def load_merchant_key(pkey) - self.merchant_key = OpenSSL::PKey::RSA.new(pkey) if pkey - rescue OpenSSL::PKey::RSAError - self.merchant_key = nil - end - - def configuration_errors - errors = super - errors.push 'Username not specified' if api_username.nil? - errors.push 'Password not specified' if api_password.nil? - errors.push 'Merchant private key not specified' if merchant_key.nil? - errors - end - - def handle_response(request, response) - rcp_response = Trustly::Data::JSONRPCResponse.new(response) - check_response(rcp_response, request) - rpc_response - end - - def check_response(response, request) - unless self.verify_signed_response(response) - raise Trustly::Exception::SignatureError, SIGNATURE_ERROR - end - if response.uuid != request.uuid - raise Trustly::Exception::DataError, UUID_MISMATCH - end - end - - def insert_credentials!(request) - request.update_data_at('Username', api_username) - request.update_data_at('Password', api_password) - request.signature = sign_merchant_request(request) - end - - def sign_merchant_request(request) - method = request.method || '' - uuid = request.uuid || '' - data = request.data || {} - - serial_data = "#{method}#{uuid}#{serialize(data)}" - sha1hash = OpenSSL::Digest::SHA1.new - signature = self.merchant_key.sign(sha1hash, serial_data) - Base64.encode64(signature).chop - end - - def url_path(_request = nil) - DEFAULT_API_PATH - end - - def call_rpc(request) - request.uuid = SecureRandom.uuid if request.uuid.nil? - super(request) - end - - def call_rpc_for_data(method, options, data:, required:, attriubtes: []) - missing_options = required.find_all { |req| options[req].nil? } - unless missing_options.empty? - msg = "Required data is missing: #{missing_options.join('; ')}" - raise Trustly::Exception::DataError, msg +module Trustly + module Api + class Signed < Base + DEFAULT_API_PATH = '/api/1' + SIGNATURE_ERROR = 'Incoming message signature is not valid' + UUID_MISMATCH = 'Incoming response is not related to the request. UUID mismatch.' + + attr_accessor :url_path, + :api_username, + :api_password, + :merchant_key + + def initialize(**config) + full_config = default_config.merge(config) + self.api_username = full_config.fetch(:username, nil) + self.api_password = full_config.fetch(:password, nil) + self.load_merchant_key(full_config[:private_pem]) + + super(**full_config.slice(*%i[host port is_https public_pem])) + end + + def void(**options) + required = %w[OrderId] + data = %w[OrderId] + call_rpc_for_data('Void', options, data: data, required: required) + end + + def deposit(**options) + required = %w[ + Locale Country Currency SuccessURL FailURL NotificationURL Amount + EndUserID MessageID Firstname Lastname ShopperStatement + ] + attributes = %w[ + Locale Country Currency SuggestedMinAmount SuggestedMaxAmount Amount + IP SuccessURL FailURL TemplateURL URLTarget MobilePhone ShopperStatement + Firstname Lastname NationalIdentificationNumber Email AccountID + UnchangeableNationalIdentificationNumber ShippingAddressCountry + ShippingAddressPostalCode ShippingAddressLine1 ShippingAddressLine2 + ShippingAddress RequestDirectDebitMandate ChargeAccountID QuickDeposit + URLScheme ExternalReference PSPMerchant PSPMerchantURL + MerchantCategoryCode RecipientInformation + ] + data = %w[NotificationURL EndUserID MessageID] + call_rpc_for_data( + 'Deposit', + options, data: data, attributes: attributes, required: required + ) + end + + def refund(**options) + required = %w[OrderId Amount Currency] + data = %w[OrderId Amount Currency] + attributes = %w[ExternalReference] + call_rpc_for_data( + 'Refund', options, + data: data, attributes: attributes, required: required + ) + end + + def select_account(**options) + required = %w[ + Locale Country SuccessURL FailURL NotificationURL EndUserID MessageID + Firstname Lastname + ] + attributes = %w[ + Locale Country Firstname Lastname SuccessURL FailURL Email IP + RequestDirectDebitMandate TemplateURL URLTarget MobilePhone + NationalIdentificationNumber UnchangeableNationalIdentificationNumber + ShopperStatement DateOfBirth URLScheme PSPMerchant PSPMerchantURL + MerchantCategoryCode + ] + data = %w[NotificationURL EndUserID MessageID] + call_rpc_for_data( + 'SelectAccount', options, + data: data, attributes: attributes, required: required + ) + end + + def account_payout(**options) + required = %w[ + NotificationURL AccountID EndUserID MessageID Amount Currency + ShopperStatement + ] + data = %w[ + NotificationURL AccountID EndUserID MessageID Amount Currency + ] + attributes = %w[ + ShopperStatement PSPMerchant PSPMerchantURL + ExternalReference MerchantCategoryCode SenderInformation + ] + call_rpc_for_data( + 'AccountPayout', options, + data: data, attributes: attributes, required: required + ) + end + + def register_account(**options) + required = %w[ + EndUserID ClearingHouse BankNumber AccountNumber Firstname Lastname + ] + data = %w[ + EndUserID ClearingHouse BankNumber AccountNumber Firstname Lastname + ] + attributes = %w[ + DateOfBirth MobilePhone NationalIdentificationNumber AddressCountry + AddressPostalCode AddressCity AddressLine1 AddressLine2 Address Email + ] + call_rpc_for_data( + 'RegisterAccount', options, + data: data, attributes: attributes, required: required + ) + end + + def get_withdrawals(**options) + data = %w[OrderId] + required = %w[OrderId] + call_rpc_for_data( + 'GetWithdrawals', options, + data: data, required: required + ) + end + + def notification_response(request, success = true) + response = Trustly::JSONRPCNotificationResponse.new( + request: request, success: success + ) + response.signature = sign_merchant_request(response) + response + end + + private + + def load_merchant_key(pkey) + self.merchant_key = OpenSSL::PKey::RSA.new(pkey) if pkey + rescue OpenSSL::PKey::RSAError + self.merchant_key = nil + end + + def configuration_errors + errors = super + errors.push 'Username not specified' if api_username.nil? + errors.push 'Password not specified' if api_password.nil? + errors.push 'Merchant private key not specified' if merchant_key.nil? + errors + end + + def handle_response(request, response) + rcp_response = Trustly::Data::JSONRPCResponse.new(response) + check_response(rcp_response, request) + rpc_response + end + + def check_response(response, request) + unless self.verify_signed_response(response) + raise Trustly::Exception::SignatureError, SIGNATURE_ERROR + end + if response.uuid != request.uuid + raise Trustly::Exception::DataError, UUID_MISMATCH + end + end + + def insert_credentials!(request) + request.update_data_at('Username', api_username) + request.update_data_at('Password', api_password) + request.signature = sign_merchant_request(request) + end + + def sign_merchant_request(request) + method = request.method || '' + uuid = request.uuid || '' + data = request.data || {} + + serial_data = "#{method}#{uuid}#{serialize(data)}" + sha1hash = OpenSSL::Digest::SHA1.new + signature = self.merchant_key.sign(sha1hash, serial_data) + Base64.encode64(signature).chop + end + + def url_path(_request = nil) + DEFAULT_API_PATH + end + + def call_rpc(request) + request.uuid = SecureRandom.uuid if request.uuid.nil? + super(request) + end + + def call_rpc_for_data(method, options, data:, required:, attriubtes: []) + missing_options = required.find_all { |req| options[req].nil? } + unless missing_options.empty? + msg = "Required data is missing: #{missing_options.join('; ')}" + raise Trustly::Exception::DataError, msg + end + request = Trustly::Data::JSONRPCRequest.new( + method: method, + data: options.slice(*data), + attributes: attributes.empty? ? nil : options.slice(*attributes) + ) + call_rpc(request) + end + + def default_config + { + host: 'test.trustly.com', + port: 443, + is_https: true, + private_pem: ENV['MERCHANT_PRIVATE_KEY'], + public_pem: ENV['TRUSTLY_PUBLIC_KEY'] + } + end end - request = Trustly::Data::JSONRPCRequest.new( - method: method, - data: options.slice(*data), - attributes: attributes.empty? ? nil : options.slice(*attributes) - ) - call_rpc(request) - end - - def default_config - { - host: 'test.trustly.com', - port: 443, - is_https: true, - private_pem: ENV['MERCHANT_PRIVATE_KEY'], - public_pem: ENV['TRUSTLY_PUBLIC_KEY'] - } end end diff --git a/lib/trustly/data.rb b/lib/trustly/data.rb deleted file mode 100644 index c2de828..0000000 --- a/lib/trustly/data.rb +++ /dev/null @@ -1,46 +0,0 @@ -class Trustly::Data - attr_accessor :payload - - def initialize(**_options) - self.payload = {} - end - - def to_json - payload.to_json - end - - private - - # Vacuum out all keys being set to nil in the data to be communicated - def vacuum(data) - case data - when Array then vacuum_array_data(data) - when Hash then vacuum_hash_data(data) - else data - end - end - - def vacuum_array_data(data) - ret = data.each_with_object([]) do |element, acc| - processed_element = vacuum(element) unless element.nil? - next if processed_element.nil? - acc.push(processed_element) - end - ret.length == 0 ? nil : ret - end - - def vacuum_hash_data(data) - ret = data.each_with_object({}) do |(key, element), acc| - processed_element = vacuum(element) unless element.nil? - next if processed_element.nil? - acc[key] = processed_element - end - ret.length == 0 ? nil : ret - end - - def stringify_hash(hash) - hash.each_with_object({}) do |(k, v), acc| - acc[k.to_s] = v.is_a?(Hash) ? stringify_hash(v) : v - end - end -end diff --git a/lib/trustly/data/base.rb b/lib/trustly/data/base.rb new file mode 100644 index 0000000..fbeb7ac --- /dev/null +++ b/lib/trustly/data/base.rb @@ -0,0 +1,50 @@ +module Trustly + module Data + class Base + attr_accessor :payload + + def initialize(**_options) + self.payload = {} + end + + def to_json + payload.to_json + end + + private + + # Vacuum out all keys being set to nil in the data to be communicated + def vacuum(data) + case data + when Array then vacuum_array_data(data) + when Hash then vacuum_hash_data(data) + else data + end + end + + def vacuum_array_data(data) + ret = data.each_with_object([]) do |element, acc| + processed_element = vacuum(element) unless element.nil? + next if processed_element.nil? + acc.push(processed_element) + end + ret.length == 0 ? nil : ret + end + + def vacuum_hash_data(data) + ret = data.each_with_object({}) do |(key, element), acc| + processed_element = vacuum(element) unless element.nil? + next if processed_element.nil? + acc[key] = processed_element + end + ret.length == 0 ? nil : ret + end + + def stringify_hash(hash) + hash.each_with_object({}) do |(k, v), acc| + acc[k.to_s] = v.is_a?(Hash) ? stringify_hash(v) : v + end + end + end + end +end diff --git a/lib/trustly/data/jsonrpc_request.rb b/lib/trustly/data/jsonrpc_request.rb index d53fc1f..353ceb1 100644 --- a/lib/trustly/data/jsonrpc_request.rb +++ b/lib/trustly/data/jsonrpc_request.rb @@ -1,82 +1,86 @@ -class Trustly::Data::JSONRPCRequest < Trustly::Data::Request - def initialize(**options) - super(**options.slice(:method, :payload)) - - data = options[:data] - attributes = options[:attributes] - payload['params'] ||= {} - payload['version'] ||= 1.1 - - initialize_data_and_attributes(data, attributes) - end - - def params - payload['params'] - end +module Trustly + module Data + class JSONRPCRequest < Request + def initialize(**options) + super(**options.slice(:method, :payload)) + + data = options[:data] + attributes = options[:attributes] + payload['params'] ||= {} + payload['version'] ||= 1.1 + + initialize_data_and_attributes(data, attributes) + end - def data - params['Data'] - end + def params + payload['params'] + end - def attributes - params.dig('Data', 'Attributes') - end + def data + params['Data'] + end - def data_at(name) - params.dig('Data', name) - end + def attributes + params.dig('Data', 'Attributes') + end - def attribute_at(name) - params.dig('Data', 'Attributes', name) - end + def data_at(name) + params.dig('Data', name) + end - def update_data_at(name, value) - params['Data'] ||= {} - params['Data'][name] = value - end + def attribute_at(name) + params.dig('Data', 'Attributes', name) + end - def update_attribute_at(name, value) - params['Data'] ||= {} - params['Data']['Attributes'] ||= {} - params['Data']['Attributes'][name] = value - end + def update_data_at(name, value) + params['Data'] ||= {} + params['Data'][name] = value + end - def signature - params['Signature'] - end + def update_attribute_at(name, value) + params['Data'] ||= {} + params['Data']['Attributes'] ||= {} + params['Data']['Attributes'][name] = value + end - def signature=(value) - params['Signature'] = value - end + def signature + params['Signature'] + end - def method=(value) - super - payload['method'] = method - end + def signature=(value) + params['Signature'] = value + end - def uuid - params['UUID'] - end + def method=(value) + super + payload['method'] = method + end - def uuid=(value) - params['UUID'] = value - end + def uuid + params['UUID'] + end - private + def uuid=(value) + params['UUID'] = value + end - def initialize_data_and_attributes(data, attributes) - return if data.nil? && attributes.nil? + private + + def initialize_data_and_attributes(data, attributes) + return if data.nil? && attributes.nil? + + if data.nil? + payload['params']['Data'] ||= {} + else + if !data.is_a?(Hash) && !attributes.nil? + raise TypeError, 'Data must be a Hash if attributes are provided' + end + payload['params']['Data'] = vacuum(data) + end + return if attributes.nil? - if data.nil? - payload['params']['Data'] ||= {} - else - if !data.is_a?(Hash) && !attributes.nil? - raise TypeError, 'Data must be a Hash if attributes are provided' + payload['params']['Data']['Attributes'] ||= vacuum(attributes) end - payload['params']['Data'] = vacuum(data) end - return if attributes.nil? - - payload['params']['Data']['Attributes'] ||= vacuum(attributes) end end diff --git a/lib/trustly/data/jsonrpc_response.rb b/lib/trustly/data/jsonrpc_response.rb index 77eaf4b..eabc7e6 100644 --- a/lib/trustly/data/jsonrpc_response.rb +++ b/lib/trustly/data/jsonrpc_response.rb @@ -1,17 +1,21 @@ -class Trustly::Data::JSONRPCResponse < Trustly::Data::Response - VERSION_ERROR = 'JSON RPC Version is not supported' + module Trustly + module Data + class JSONRPCResponse < Response + VERSION_ERROR = 'JSON RPC Version is not supported' - def initialize(**options) - super - version = payload['version'] - if version != '1.1' - raise Trustly::Exception::JSONRPCVersionError, VERSION_ERROR - end - end + def initialize(**options) + super + version = payload['version'] + if version != '1.1' + raise Trustly::Exception::JSONRPCVersionError, VERSION_ERROR + end + end - def data_at(name) - return if data.nil? - - data[name] + def data_at(name) + return if data.nil? + + data[name] + end + end end end diff --git a/lib/trustly/data/jsonrpcnotification_request.rb b/lib/trustly/data/jsonrpcnotification_request.rb index 99f55fc..92114d4 100644 --- a/lib/trustly/data/jsonrpcnotification_request.rb +++ b/lib/trustly/data/jsonrpcnotification_request.rb @@ -1,43 +1,47 @@ -class Trustly::JSONRPCNotificationRequest < Trustly::Data::Request - def initialize(**options) - super(payload: notification_body(options[:notification_body])) - return if version == '1.1' - - error_message = "JSON RPC Version #{version} is not supported" - raise Trustly::Exception::JSONRPCVersionError, error_message - end - - def version - payload['version'] - end - - def method - payload['method'] - end - - def signature - payload.dig('params', 'signature') - end - - def uuid - payload.dig('params', 'uuid') - end - - def data_at(key) - payload.dig('params', 'data', key) - end - - def attribute_at(key) - payload.dig('params', 'data', 'attributes', key) - end - - private - - def notification_body(body) - return stringify_hash(body) if body.is_a?(Hash) - - JSON.parse(body) - rescue JSON::ParserError => e - raise Trustly::Exception::DataError, e.message +module Trustly + module Data + class JSONRPCNotificationRequest < Request + def initialize(**options) + super(payload: notification_body(options[:notification_body])) + return if version == '1.1' + + error_message = "JSON RPC Version #{version} is not supported" + raise Trustly::Exception::JSONRPCVersionError, error_message + end + + def version + payload['version'] + end + + def method + payload['method'] + end + + def signature + payload.dig('params', 'signature') + end + + def uuid + payload.dig('params', 'uuid') + end + + def data_at(key) + payload.dig('params', 'data', key) + end + + def attribute_at(key) + payload.dig('params', 'data', 'attributes', key) + end + + private + + def notification_body(body) + return stringify_hash(body) if body.is_a?(Hash) + + JSON.parse(body) + rescue JSON::ParserError => e + raise Trustly::Exception::DataError, e.message + end + end end end diff --git a/lib/trustly/data/jsonrpcnotification_response.rb b/lib/trustly/data/jsonrpcnotification_response.rb index b29a4b0..931def4 100644 --- a/lib/trustly/data/jsonrpcnotification_response.rb +++ b/lib/trustly/data/jsonrpcnotification_response.rb @@ -1,59 +1,63 @@ -class Trustly::JSONRPCNotificationResponse < Trustly::Data - def initialize(**options) - super - request = options[:request] - success = options[:success] - - self.version = '1.1' - self.uuid = request.uuid if request.uuid - self.method = request.method if request.method - self.update_data_at('status', success ? 'OK' : 'FAILED') - end - - def signature=(value) - update_result_at('signature', value) - end - - def method=(value) - update_result_at('method', value) - end - - def uuid=(value) - update_result_at('uuid', value) - end - - def version=(value) - payload['version'] = value - end - - def update_result_at(name, value) - payload['result'] ||= {} - payload['result'][name] = value - end - - def update_data_at(name, value) - payload['result'] ||= {} - payload['result']['data'] ||= {} - payload['result']['data'][name] = value - end - - def data - payload.dig('result', 'data') - end - - def result - payload['result'] - end - - def method - result['method'] - end - - def uuid - result['uuid'] - end - - def signature - result['signature'] +module Trustly + module Data + class JSONRPCNotificationResponse < Base + def initialize(**options) + super + request = options[:request] + success = options[:success] + + self.version = '1.1' + self.uuid = request.uuid if request.uuid + self.method = request.method if request.method + self.update_data_at('status', success ? 'OK' : 'FAILED') + end + + def signature=(value) + update_result_at('signature', value) + end + + def method=(value) + update_result_at('method', value) + end + + def uuid=(value) + update_result_at('uuid', value) + end + + def version=(value) + payload['version'] = value + end + + def update_result_at(name, value) + payload['result'] ||= {} + payload['result'][name] = value + end + + def update_data_at(name, value) + payload['result'] ||= {} + payload['result']['data'] ||= {} + payload['result']['data'][name] = value + end + + def data + payload.dig('result', 'data') + end + + def result + payload['result'] + end + + def method + result['method'] + end + + def uuid + result['uuid'] + end + + def signature + result['signature'] + end + end end end diff --git a/lib/trustly/data/request.rb b/lib/trustly/data/request.rb index de4fdb8..af0656f 100644 --- a/lib/trustly/data/request.rb +++ b/lib/trustly/data/request.rb @@ -1,12 +1,16 @@ -class Trustly::Data::Request < Trustly::Data - attr_accessor :method +module Trustly + module Data + class Request < Base + attr_accessor :method - def initialize(**options) - super - if (new_payload = options[:payload]) - vacuumed_payload = vacuum(new_payload) - self.payload = stringify_hash(vacuumed_payload) + def initialize(**options) + super + if (new_payload = options[:payload]) + vacuumed_payload = vacuum(new_payload) + self.payload = stringify_hash(vacuumed_payload) + end + self.method = options[:method] || payload['method'] + end end - self.method = options[:method] || payload['method'] end end diff --git a/lib/trustly/data/response.rb b/lib/trustly/data/response.rb index c7a1a57..45ab9d0 100644 --- a/lib/trustly/data/response.rb +++ b/lib/trustly/data/response.rb @@ -1,65 +1,70 @@ -class Trustly::Data::Response < Trustly::Data - attr_accessor :response_status, - :response_reason, - :response_body, - :response_result +module Trustly + module Data + class Response < Base + attr_accessor :response_status, + :response_reason, + :response_body, + :response_result - #called from Net::HTTP.get_response("trustly.com","/api_path") -> returns Net::HTTPResponse - def initialize(**options) - super - http_response = options[:http_response] - process_http_response(http_response) - end + #called from Net::HTTP.get_response("trustly.com","/api_path") -> returns Net::HTTPResponse + def initialize(**options) + super + http_response = options[:http_response] + process_http_response(http_response) + end - def error? - !payload['error'].nil? - end + def error? + !payload['error'].nil? + end - def error_code - return nil unless error? + def error_code + return nil unless error? - response_result.dig('data', 'code') - end + response_result.dig('data', 'code') + end - def error_message - return nil unless error? + def error_message + return nil unless error? - response_result.dig('data', 'message') - end + response_result.dig('data', 'message') + end - def success? - !payload['result'].nil? - end + def success? + !payload['result'].nil? + end - def data - response_result['data'] - end + def data + response_result['data'] + end - def uuid - response_result['uuid'] - end + def uuid + response_result['uuid'] + end - def method - response_result['method'] - end + def method + response_result['method'] + end - def signature - response_result['signature'] - end + def signature + response_result['signature'] + end - private + private - def process_http_response(http_response) - self.response_status = http_response.status - self.response_reason = http_response.reason_phrase - init_response_result(http_response.body) - end + def process_http_response(http_response) + self.response_status = http_response.status + self.response_reason = http_response.reason_phrase + init_response_result(http_response.body) + end - def init_response_result(body) - self.payload = body - self.response_result = payload['result'] || payload.dig('error', 'error') - return unless response_result.nil? + def init_response_result(body) + self.payload = body + self.response_result = payload['result'] || payload.dig('error', 'error') + return unless response_result.nil? - message = "No result or error in response #{payload}" - raise Trustly::Exception::DataError, message + message = "No result or error in response #{payload}" + raise Trustly::Exception::DataError, message + end + end + end end From 0473f31aa40d510435025cdcf761ae72fcd06d9b Mon Sep 17 00:00:00 2001 From: Artsiom Kuts Date: Wed, 10 Aug 2022 15:13:01 +0200 Subject: [PATCH 5/7] specs and rubocop updates --- .rubocop.yml | 9 + lib/generators/trustly/install_generator.rb | 11 +- lib/trustly.rb | 5 +- lib/trustly/api/base.rb | 66 ++- lib/trustly/api/signed.rb | 50 +- lib/trustly/data/base.rb | 10 +- lib/trustly/data/jsonrpc_request.rb | 19 +- lib/trustly/data/jsonrpc_response.rb | 10 +- .../data/jsonrpcnotification_request.rb | 6 +- .../data/jsonrpcnotification_response.rb | 8 +- lib/trustly/data/request.rb | 2 + lib/trustly/data/response.rb | 10 +- lib/trustly/exception.rb | 2 - .../exception/authentification_error.rb | 8 +- lib/trustly/exception/base.rb | 8 + lib/trustly/exception/configuration_error.rb | 8 +- lib/trustly/exception/connection_error.rb | 8 +- lib/trustly/exception/data_error.rb | 8 +- .../exception/jsonrpc_version_error.rb | 8 +- lib/trustly/exception/signature_error.rb | 8 +- lib/trustly/version.rb | 4 +- spec/jsonrpc_request_spec.rb | 10 +- spec/jsonrpc_response_spec.rb | 159 +++++- spec/jsonrpcnotification_request_spec.rb | 100 ++++ spec/jsonrpcnotification_response_spec.rb | 93 ++++ spec/signed_api_spec.rb | 475 +++++++++++++++++- 26 files changed, 984 insertions(+), 121 deletions(-) create mode 100644 .rubocop.yml delete mode 100644 lib/trustly/exception.rb create mode 100644 lib/trustly/exception/base.rb create mode 100644 spec/jsonrpcnotification_request_spec.rb create mode 100644 spec/jsonrpcnotification_response_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..5b0e1a4 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,9 @@ +AllCops: + TargetRubyVersion: 2.7 + NewCops: enable + SuggestExtensions: false +Style/Documentation: + Enabled: false +Metrics/BlockLength: + Exclude: + - 'spec/**/*.rb' diff --git a/lib/generators/trustly/install_generator.rb b/lib/generators/trustly/install_generator.rb index 821fac0..ece1ecf 100644 --- a/lib/generators/trustly/install_generator.rb +++ b/lib/generators/trustly/install_generator.rb @@ -1,16 +1,17 @@ +# frozen_string_literal: true + require 'rails/generators' module Trustly module Generators class InstallGenerator < ::Rails::Generators::Base desc 'Create cert folder and trustly pem files' - source_root ::File.expand_path('../templates', __FILE__) + source_root ::File.expand_path('templates', __dir__) def copy_files - copy_file "test.trustly.public.pem", "certs/trustly/test.trustly.public.pem" - copy_file "live.trustly.public.pem", "certs/trustly/live.trustly.public.pem" + copy_file 'test.trustly.public.pem', 'certs/trustly/test.trustly.public.pem' + copy_file 'live.trustly.public.pem', 'certs/trustly/live.trustly.public.pem' end - end end -end \ No newline at end of file +end diff --git a/lib/trustly.rb b/lib/trustly.rb index 03d018b..69739e0 100644 --- a/lib/trustly.rb +++ b/lib/trustly.rb @@ -1,12 +1,15 @@ +# frozen_string_literal: true + module Trustly end require 'base64' require 'openssl' +require 'stringio' require 'faraday' require 'faraday_middleware' -require 'trustly/exception' +require 'trustly/exception/base' require 'trustly/exception/authentification_error' require 'trustly/exception/connection_error' require 'trustly/exception/data_error' diff --git a/lib/trustly/api/base.rb b/lib/trustly/api/base.rb index 2d05d9a..b01fb91 100644 --- a/lib/trustly/api/base.rb +++ b/lib/trustly/api/base.rb @@ -1,18 +1,20 @@ +# frozen_string_literal: true + module Trustly module Api - class Base + class Base # rubocop:disable Metrics/ClassLength attr_accessor :api_host, - :api_port, - :api_is_https, - :last_request, - :trustly_key + :api_port, + :api_is_https, + :last_request, + :trustly_key def initialize(**config) self.api_host = config[:host] self.api_port = config[:port] self.api_is_https = config[:is_https] - self.load_trustly_key(config[:public_pem]) + load_trustly_key(config[:public_pem]) validate! end @@ -20,43 +22,49 @@ def verify_signed_response(response) method = response.method || '' uuid = response.uuid || '' raw_signature = Base64.decode64(response.signature || '') - serial_data = "#{method}#{uuid}#{self.serialize(response.data)}" + serial_data = "#{method}#{uuid}#{serialize(response.data)}" trustly_key.public_key.verify( - OpenSSL::Digest::SHA1.new, raw_signature, serial_data + OpenSSL::Digest.new('SHA1'), raw_signature, serial_data ) end private def url_path(_request = nil) + # :nocov: raise NotImplementedError + # :nocov: end def handle_response(_request, _http_call) + # :nocov: raise NotImplementedError + # :nocov: end def insert_credentials(_request) + # :nocov: raise NotImplementedError + # :nocov: end def serialize(object) - serialized = "" + serialized = StringIO.new case object when Array then serialize_array(object, serialized) when Hash then serialize_hash(object, serialized) - else serialized.concat(object.to_s) + else serialized << object.to_s end - serialized + serialized.string end def serialize_array(object, serialized) - object.each { |value| serialized.concat(serialize(value)) } + object.each { |value| serialized << serialize(value) } end def serialize_hash(object, serialized) object.sort.each do |key, value| - serialized.concat(key.to_s).concat(serialize(value)) + serialized << key.to_s << serialize(value) end end @@ -88,7 +96,7 @@ def base_url end def url(request) - return URI.parse("#{self.base_url}#{url_path(request)}") + URI.parse("#{base_url}#{url_path(request)}") end def call_rpc(request) @@ -105,25 +113,33 @@ def call_rpc(request) end def handle_error(error, request, body) - message = e.message - exception = case error - when Faraday::ParsingError, Trustly::ClientError - Trustly::Exception::DataError - else - Trustly::Exception::ConnectionError - end - unless (response = error.response).nil? - message = " - #{response.status}: #{response.body} - #{request.method}, #{body} - " + message = error.message + exception = exception_for_error(error) + unless error.response.nil? + status = error.response_status + error_body = error.response_body + message += <<-MSG.gsub(/\s+/, ' ').rstrip + -> #{status}: #{error_body} - #{request.method}, #{body} + MSG end raise exception, message end + def exception_for_error(error) + case error + when Faraday::ParsingError, Faraday::ClientError + Trustly::Exception::DataError + else + Trustly::Exception::ConnectionError + end + end + def connection(request_uri) Faraday.new(request_uri.origin) do |conn| + # :nocov: conn.response :json conn.adapter :net_http + # :nocov: end end end diff --git a/lib/trustly/api/signed.rb b/lib/trustly/api/signed.rb index 1355010..622cf29 100644 --- a/lib/trustly/api/signed.rb +++ b/lib/trustly/api/signed.rb @@ -1,20 +1,21 @@ +# frozen_string_literal: true + module Trustly module Api - class Signed < Base + class Signed < Base # rubocop:disable Metrics/ClassLength DEFAULT_API_PATH = '/api/1' SIGNATURE_ERROR = 'Incoming message signature is not valid' UUID_MISMATCH = 'Incoming response is not related to the request. UUID mismatch.' - attr_accessor :url_path, - :api_username, - :api_password, - :merchant_key + attr_accessor :api_username, + :api_password, + :merchant_key def initialize(**config) full_config = default_config.merge(config) self.api_username = full_config.fetch(:username, nil) self.api_password = full_config.fetch(:password, nil) - self.load_merchant_key(full_config[:private_pem]) + load_merchant_key(full_config[:private_pem]) super(**full_config.slice(*%i[host port is_https public_pem])) end @@ -25,7 +26,7 @@ def void(**options) call_rpc_for_data('Void', options, data: data, required: required) end - def deposit(**options) + def deposit(**options) # rubocop:disable Metrics/MethodLength required = %w[ Locale Country Currency SuccessURL FailURL NotificationURL Amount EndUserID MessageID Firstname Lastname ShopperStatement @@ -57,7 +58,7 @@ def refund(**options) ) end - def select_account(**options) + def select_account(**options) # rubocop:disable Metrics/MethodLength required = %w[ Locale Country SuccessURL FailURL NotificationURL EndUserID MessageID Firstname Lastname @@ -76,7 +77,7 @@ def select_account(**options) ) end - def account_payout(**options) + def account_payout(**options) # rubocop:disable Metrics/MethodLength required = %w[ NotificationURL AccountID EndUserID MessageID Amount Currency ShopperStatement @@ -94,7 +95,7 @@ def account_payout(**options) ) end - def register_account(**options) + def register_account(**options) # rubocop:disable Metrics/MethodLength required = %w[ EndUserID ClearingHouse BankNumber AccountNumber Firstname Lastname ] @@ -120,8 +121,8 @@ def get_withdrawals(**options) ) end - def notification_response(request, success = true) - response = Trustly::JSONRPCNotificationResponse.new( + def notification_response(request, success: true) + response = Trustly::Data::JSONRPCNotificationResponse.new( request: request, success: success ) response.signature = sign_merchant_request(response) @@ -145,18 +146,14 @@ def configuration_errors end def handle_response(request, response) - rcp_response = Trustly::Data::JSONRPCResponse.new(response) - check_response(rcp_response, request) + rpc_response = Trustly::Data::JSONRPCResponse.new(http_response: response) + check_response(rpc_response, request) rpc_response end def check_response(response, request) - unless self.verify_signed_response(response) - raise Trustly::Exception::SignatureError, SIGNATURE_ERROR - end - if response.uuid != request.uuid - raise Trustly::Exception::DataError, UUID_MISMATCH - end + raise Trustly::Exception::DataError, UUID_MISMATCH if response.uuid != request.uuid + raise Trustly::Exception::SignatureError, SIGNATURE_ERROR unless verify_signed_response(response) end def insert_credentials!(request) @@ -171,8 +168,8 @@ def sign_merchant_request(request) data = request.data || {} serial_data = "#{method}#{uuid}#{serialize(data)}" - sha1hash = OpenSSL::Digest::SHA1.new - signature = self.merchant_key.sign(sha1hash, serial_data) + sha1hash = OpenSSL::Digest.new('SHA1') + signature = merchant_key.sign(sha1hash, serial_data) Base64.encode64(signature).chop end @@ -185,15 +182,14 @@ def call_rpc(request) super(request) end - def call_rpc_for_data(method, options, data:, required:, attriubtes: []) + def call_rpc_for_data(method, options, data:, required:, attributes: []) missing_options = required.find_all { |req| options[req].nil? } unless missing_options.empty? msg = "Required data is missing: #{missing_options.join('; ')}" raise Trustly::Exception::DataError, msg end request = Trustly::Data::JSONRPCRequest.new( - method: method, - data: options.slice(*data), + method: method, data: options.slice(*data), attributes: attributes.empty? ? nil : options.slice(*attributes) ) call_rpc(request) @@ -204,8 +200,8 @@ def default_config host: 'test.trustly.com', port: 443, is_https: true, - private_pem: ENV['MERCHANT_PRIVATE_KEY'], - public_pem: ENV['TRUSTLY_PUBLIC_KEY'] + private_pem: ENV.fetch('MERCHANT_PRIVATE_KEY', nil), + public_pem: ENV.fetch('TRUSTLY_PUBLIC_KEY', nil) } end end diff --git a/lib/trustly/data/base.rb b/lib/trustly/data/base.rb index fbeb7ac..13eb081 100644 --- a/lib/trustly/data/base.rb +++ b/lib/trustly/data/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Trustly module Data class Base @@ -7,7 +9,7 @@ def initialize(**_options) self.payload = {} end - def to_json + def to_json(*_args) payload.to_json end @@ -26,18 +28,20 @@ def vacuum_array_data(data) ret = data.each_with_object([]) do |element, acc| processed_element = vacuum(element) unless element.nil? next if processed_element.nil? + acc.push(processed_element) end - ret.length == 0 ? nil : ret + ret.length.zero? ? nil : ret end def vacuum_hash_data(data) ret = data.each_with_object({}) do |(key, element), acc| processed_element = vacuum(element) unless element.nil? next if processed_element.nil? + acc[key] = processed_element end - ret.length == 0 ? nil : ret + ret.length.zero? ? nil : ret end def stringify_hash(hash) diff --git a/lib/trustly/data/jsonrpc_request.rb b/lib/trustly/data/jsonrpc_request.rb index 353ceb1..bd3ee6e 100644 --- a/lib/trustly/data/jsonrpc_request.rb +++ b/lib/trustly/data/jsonrpc_request.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Trustly module Data class JSONRPCRequest < Request @@ -68,17 +70,24 @@ def uuid=(value) def initialize_data_and_attributes(data, attributes) return if data.nil? && attributes.nil? - + + initialize_data(data, !attributes.nil?) + initialize_attributes(attributes) + end + + def initialize_data(data, with_attributes) if data.nil? payload['params']['Data'] ||= {} else - if !data.is_a?(Hash) && !attributes.nil? - raise TypeError, 'Data must be a Hash if attributes are provided' - end + raise TypeError, 'Data must be a Hash if attributes are provided' if !data.is_a?(Hash) && with_attributes + payload['params']['Data'] = vacuum(data) end + end + + def initialize_attributes(attributes) return if attributes.nil? - + payload['params']['Data']['Attributes'] ||= vacuum(attributes) end end diff --git a/lib/trustly/data/jsonrpc_response.rb b/lib/trustly/data/jsonrpc_response.rb index eabc7e6..7566e34 100644 --- a/lib/trustly/data/jsonrpc_response.rb +++ b/lib/trustly/data/jsonrpc_response.rb @@ -1,4 +1,6 @@ - module Trustly +# frozen_string_literal: true + +module Trustly module Data class JSONRPCResponse < Response VERSION_ERROR = 'JSON RPC Version is not supported' @@ -6,14 +8,12 @@ class JSONRPCResponse < Response def initialize(**options) super version = payload['version'] - if version != '1.1' - raise Trustly::Exception::JSONRPCVersionError, VERSION_ERROR - end + raise Trustly::Exception::JSONRPCVersionError, VERSION_ERROR if version != '1.1' end def data_at(name) return if data.nil? - + data[name] end end diff --git a/lib/trustly/data/jsonrpcnotification_request.rb b/lib/trustly/data/jsonrpcnotification_request.rb index 92114d4..2a0c302 100644 --- a/lib/trustly/data/jsonrpcnotification_request.rb +++ b/lib/trustly/data/jsonrpcnotification_request.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Trustly module Data class JSONRPCNotificationRequest < Request @@ -37,9 +39,9 @@ def attribute_at(key) def notification_body(body) return stringify_hash(body) if body.is_a?(Hash) - + JSON.parse(body) - rescue JSON::ParserError => e + rescue JSON::ParserError => e raise Trustly::Exception::DataError, e.message end end diff --git a/lib/trustly/data/jsonrpcnotification_response.rb b/lib/trustly/data/jsonrpcnotification_response.rb index 931def4..6dfe47a 100644 --- a/lib/trustly/data/jsonrpcnotification_response.rb +++ b/lib/trustly/data/jsonrpcnotification_response.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Trustly module Data class JSONRPCNotificationResponse < Base @@ -9,7 +11,7 @@ def initialize(**options) self.version = '1.1' self.uuid = request.uuid if request.uuid self.method = request.method if request.method - self.update_data_at('status', success ? 'OK' : 'FAILED') + update_data_at('status', success ? 'OK' : 'FAILED') end def signature=(value) @@ -47,6 +49,10 @@ def result payload['result'] end + def version + payload['version'] + end + def method result['method'] end diff --git a/lib/trustly/data/request.rb b/lib/trustly/data/request.rb index af0656f..cd991b3 100644 --- a/lib/trustly/data/request.rb +++ b/lib/trustly/data/request.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Trustly module Data class Request < Base diff --git a/lib/trustly/data/response.rb b/lib/trustly/data/response.rb index 45ab9d0..72c2efa 100644 --- a/lib/trustly/data/response.rb +++ b/lib/trustly/data/response.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + module Trustly module Data class Response < Base attr_accessor :response_status, - :response_reason, - :response_body, - :response_result + :response_reason, + :response_body, + :response_result - #called from Net::HTTP.get_response("trustly.com","/api_path") -> returns Net::HTTPResponse + # called from Net::HTTP.get_response("trustly.com","/api_path") -> returns Net::HTTPResponse def initialize(**options) super http_response = options[:http_response] diff --git a/lib/trustly/exception.rb b/lib/trustly/exception.rb deleted file mode 100644 index ea2ecfd..0000000 --- a/lib/trustly/exception.rb +++ /dev/null @@ -1,2 +0,0 @@ -class Trustly::Exception < Exception -end diff --git a/lib/trustly/exception/authentification_error.rb b/lib/trustly/exception/authentification_error.rb index 5571d31..684bfa8 100644 --- a/lib/trustly/exception/authentification_error.rb +++ b/lib/trustly/exception/authentification_error.rb @@ -1,2 +1,8 @@ -class Trustly::Exception::AuthentificationError < Exception +# frozen_string_literal: true + +module Trustly + module Exception + class AuthentificationError < Base + end + end end diff --git a/lib/trustly/exception/base.rb b/lib/trustly/exception/base.rb new file mode 100644 index 0000000..b501193 --- /dev/null +++ b/lib/trustly/exception/base.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Trustly + module Exception + class Base < StandardError + end + end +end diff --git a/lib/trustly/exception/configuration_error.rb b/lib/trustly/exception/configuration_error.rb index ae62a8d..57e4281 100644 --- a/lib/trustly/exception/configuration_error.rb +++ b/lib/trustly/exception/configuration_error.rb @@ -1,2 +1,8 @@ -class Trustly::Exception::ConfigurationError < Exception +# frozen_string_literal: true + +module Trustly + module Exception + class ConfigurationError < Base + end + end end diff --git a/lib/trustly/exception/connection_error.rb b/lib/trustly/exception/connection_error.rb index add0974..ad7fff2 100644 --- a/lib/trustly/exception/connection_error.rb +++ b/lib/trustly/exception/connection_error.rb @@ -1,2 +1,8 @@ -class Trustly::Exception::ConnectionError < Exception +# frozen_string_literal: true + +module Trustly + module Exception + class ConnectionError < Base + end + end end diff --git a/lib/trustly/exception/data_error.rb b/lib/trustly/exception/data_error.rb index 3198f10..af2a74e 100644 --- a/lib/trustly/exception/data_error.rb +++ b/lib/trustly/exception/data_error.rb @@ -1,2 +1,8 @@ -class Trustly::Exception::DataError < Exception +# frozen_string_literal: true + +module Trustly + module Exception + class DataError < Base + end + end end diff --git a/lib/trustly/exception/jsonrpc_version_error.rb b/lib/trustly/exception/jsonrpc_version_error.rb index 1cc56b2..bc22ee2 100644 --- a/lib/trustly/exception/jsonrpc_version_error.rb +++ b/lib/trustly/exception/jsonrpc_version_error.rb @@ -1,2 +1,8 @@ -class Trustly::Exception::JSONRPCVersionError < Exception +# frozen_string_literal: true + +module Trustly + module Exception + class JSONRPCVersionError < Base + end + end end diff --git a/lib/trustly/exception/signature_error.rb b/lib/trustly/exception/signature_error.rb index a438c69..8651772 100644 --- a/lib/trustly/exception/signature_error.rb +++ b/lib/trustly/exception/signature_error.rb @@ -1,2 +1,8 @@ -class Trustly::Exception::SignatureError < Exception +# frozen_string_literal: true + +module Trustly + module Exception + class SignatureError < Base + end + end end diff --git a/lib/trustly/version.rb b/lib/trustly/version.rb index 1580d56..93b90ce 100644 --- a/lib/trustly/version.rb +++ b/lib/trustly/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Trustly - VERSION = "0.1.95" + VERSION = '0.1.95' end diff --git a/spec/jsonrpc_request_spec.rb b/spec/jsonrpc_request_spec.rb index 8d13f29..5f16ae6 100644 --- a/spec/jsonrpc_request_spec.rb +++ b/spec/jsonrpc_request_spec.rb @@ -7,7 +7,7 @@ subject { described_class.new(**params) } let(:method) { 'Test' } let(:data) { { 'A' => 1 } } - let(:attributes) { { 'B' => 2 } } + let(:attributes) { { 'B' => 2, 'Array' => [1, 2, 3] } } let(:uuid) { SecureRandom.uuid } let(:signature) { 'signature' } @@ -32,7 +32,7 @@ }) end it 'initializes attributes' do - expect(subject.attributes).to eq({ 'B' => 2 }) + expect(subject.attributes).to eq({ 'B' => 2, 'Array' => [1, 2, 3] }) end it 'initializes data' do expect(subject.data).to eq({ 'A' => 1, 'Attributes' => attributes }) @@ -229,5 +229,11 @@ expect(subject.payload).to include('method' => 'OtherTest') end end + + describe '#to_json' do + it 'transforms payload to json' do + expect(subject.to_json).to eq(payload.merge('version' => 1.1).to_json) + end + end end end diff --git a/spec/jsonrpc_response_spec.rb b/spec/jsonrpc_response_spec.rb index 49a9dbd..9ccf127 100644 --- a/spec/jsonrpc_response_spec.rb +++ b/spec/jsonrpc_response_spec.rb @@ -3,32 +3,97 @@ require 'spec_helper' RSpec.describe Trustly::Data::JSONRPCResponse do - describe '#new' do - subject { described_class.new(http_response: response) } - let(:payload) do - { - 'version' => '1.1', - 'result' => { - 'signature' => 'signature', - 'method' => 'Test', - 'data' => { - 'result' => '1', - 'orderid' => '1187741486' - }, - 'uuid' => '8bedfbd4-8181-38e1-f0be-f360171aefc6' - } + let(:uuid) { '8bedfbd4-8181-38e1-f0be-f360171aefc6' } + let(:signature) { 'signature' } + let(:method) { 'Test' } + let(:data) do + { + 'result' => '1', + 'orderid' => '1187741486' + } + end + let(:payload) do + { + 'version' => '1.1', + 'result' => { + 'signature' => signature, + 'method' => method, + 'data' => data, + 'uuid' => uuid } + } + end + let(:status) { 200 } + let(:phrase) { 'OK' } + let(:response) do + Faraday::Response.new( + status: status, + response_headers: { 'Content-Type': 'application/json' }, + response_body: payload, + method: :post, + reason_phrase: phrase + ) + end + subject { described_class.new(http_response: response) } + describe '#new' do + shared_examples_for 'parsed response' do |success| + it 'has method' do + expect(subject.method).to eq(method) + end + + it 'has signature' do + expect(subject.signature).to eq(signature) + end + + it 'has UUID' do + expect(subject.uuid).to eq(uuid) + end + + it 'has data' do + expect(subject.data).to eq(data) + end + + if success + it 'is marked as a success' do + expect(subject.success?).to be_truthy + end + it 'is not marked as an error' do + expect(subject.error?).to be_falsy + end + it 'does not have an error code' do + expect(subject.error_code).to be_nil + end + it 'has an error message' do + expect(subject.error_message).to be_nil + end + else + it 'is marked as en error' do + expect(subject.error?).to be_truthy + end + it 'is not marked as a success' do + expect(subject.success?).to be_falsy + end + it 'has an error code' do + expect(subject.error_code).to eq(error_code) + end + it 'has an error message' do + expect(subject.error_message).to eq(error_message) + end + end end - let(:status) { 200 } - let(:phrase) { 'OK' } - let(:response) do - Faraday::Response.new( - status: status, - response_headers: { 'Content-Type': 'application/json' }, - response_body: payload, - method: :post, - reason_phrase: phrase - ) + + context 'with an invalid response payload' do + before do + payload.delete('result') + payload['data'] = {} + end + + it 'fails' do + expect { subject }.to raise_error( + Trustly::Exception::DataError, + "No result or error in response #{payload}" + ) + end end context 'with an invalid API version' do before do @@ -42,5 +107,51 @@ ) end end + context 'with a valid response' do + include_examples 'parsed response', true + end + context 'with an error response' do + let(:error_code) { 616 } + let(:error_message) { 'ERROR_INVALID_CREDENTIALS' } + let(:data) do + { + 'code' => error_code, + 'message' => error_message + } + end + let(:payload) do + { + 'version' => '1.1', + 'error' => { + 'name' => 'JSONRPCError', + 'code' => error_code, + 'message' => error_message, + 'error' => { + 'signature' => signature, + 'uuid' => uuid, + 'method' => method, + 'data' => data + } + } + } + end + include_examples 'parsed response', false + end + end + describe '#data_at' do + context 'with response data' do + it 'returns data for a specific key' do + expect(subject.data_at('orderid')).to eq('1187741486') + end + end + context 'without data' do + before do + payload['result'].delete('data') + end + + it 'returns nil' do + expect(subject.data_at('orderid')).to be_nil + end + end end end diff --git a/spec/jsonrpcnotification_request_spec.rb b/spec/jsonrpcnotification_request_spec.rb new file mode 100644 index 0000000..0cb09d2 --- /dev/null +++ b/spec/jsonrpcnotification_request_spec.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Trustly::Data::JSONRPCNotificationRequest do + let(:method) { 'Test' } + let(:uuid) { '8bedfbd4-8181-38e1-f0be-f360171aefc6' } + let(:request_payload) do + { + 'method' => method, + 'params' => { + 'signature' => 'signature', + 'uuid' => uuid, + 'data' => { + 'notificationid' => '35673567', + 'messageid' => '453455465', + 'orderid' => '3473567567', + 'accountid' => '1234567890', + 'verified' => '1', + 'attributes' => + { + 'clearinghouse' => 'SWEDEN', + 'bank' => 'SEB', + 'descriptor' => '**** *084057', + 'lastdigits' => '084057' + } + } + }, + 'version' => '1.1' + } + end + subject { described_class.new(notification_body: request_payload) } + + describe '#new' do + context 'with an invalid version' do + before do + request_payload['version'] = '1.0' + end + + it 'fails with a version error' do + expect { subject }.to raise_error( + Trustly::Exception::JSONRPCVersionError, + 'JSON RPC Version 1.0 is not supported' + ) + end + end + + shared_examples_for 'valid request' do + it 'has a uuid' do + expect(subject.uuid).to eq(uuid) + end + + it 'has a method' do + expect(subject.method).to eq(method) + end + + it 'has a signature' do + expect(subject.signature).to eq('signature') + end + + it 'has a version' do + expect(subject.version).to eq('1.1') + end + end + + context 'with a json request payload' do + let(:request_payload) do + super().to_json + end + + include_examples 'valid request' + end + + context 'with a valid request payload' do + include_examples 'valid request' + end + + context 'with an invalid request payload' do + let(:request_payload) do + 'invalid json' + end + + it 'fails with a data error' do + expect { subject }.to raise_error( + Trustly::Exception::DataError + ) + end + end + end + describe '#data_at' do + it 'fetches data field' do + expect(subject.data_at('messageid')).to eq('453455465') + end + end + describe '#attribute_at' do + it 'fetches attribute field' do + expect(subject.attribute_at('clearinghouse')).to eq('SWEDEN') + end + end +end diff --git a/spec/jsonrpcnotification_response_spec.rb b/spec/jsonrpcnotification_response_spec.rb new file mode 100644 index 0000000..42286a1 --- /dev/null +++ b/spec/jsonrpcnotification_response_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Trustly::Data::JSONRPCNotificationResponse do + let(:method) { 'Test' } + let(:uuid) { '8bedfbd4-8181-38e1-f0be-f360171aefc6' } + let(:request_payload) do + { + 'method' => method, + 'params' => { + 'signature' => 'signature', + 'uuid' => uuid, + 'data' => { + 'notificationid' => '35673567', + 'messageid' => '453455465', + 'orderid' => '3473567567', + 'accountid' => '1234567890', + 'verified' => '1', + 'attributes' => + { + 'clearinghouse' => 'SWEDEN', + 'bank' => 'SEB', + 'descriptor' => '**** *084057', + 'lastdigits' => '084057' + } + } + }, + 'version' => '1.1' + } + end + let(:request) do + Trustly::Data::JSONRPCNotificationRequest.new( + notification_body: request_payload + ) + end + let(:success) { true } + + shared_examples_for 'notification response' do |status| + it 'matches the request\'s uuid' do + expect(subject.uuid).to eq(uuid) + end + it 'matches the request\'s method' do + expect(subject.method).to eq(method) + end + it 'has a correct version' do + expect(subject.version).to eq('1.1') + end + it 'has response data' do + expect(subject.data).to eq('status' => status) + end + it 'builds a correct payload' do + expect(subject.payload).to eq( + 'version' => '1.1', + 'result' => { + 'method' => method, + 'uuid' => uuid, + 'data' => { + 'status' => status + } + } + ) + end + end + describe '#new' do + subject { described_class.new(request: request, success: success) } + context 'with a successful request' do + include_examples 'notification response', 'OK' + end + context 'with an error request' do + let(:success) { false } + include_examples 'notification response', 'FAILED' + end + end + describe '#signature=' do + subject { described_class.new(request: request, success: success) } + let(:signature) { 'new signature' } + + context 'with a signature provided' do + before do + subject.signature = signature + end + + it 'retrieves the signature with a getter' do + expect(subject.signature).to eq(signature) + end + + it 'properly sets up the signature in the payload' do + expect(subject.payload['result']['signature']).to eq(signature) + end + end + end +end diff --git a/spec/signed_api_spec.rb b/spec/signed_api_spec.rb index 6780c42..8f104bb 100644 --- a/spec/signed_api_spec.rb +++ b/spec/signed_api_spec.rb @@ -87,6 +87,19 @@ end include_examples 'incorrect configuration' end + context 'when private key is invalid' do + let(:params) { basic_params } + before do + expect(OpenSSL::PKey::RSA).to receive(:new) + .with(basic_params[:public_pem]).and_call_original + expect(OpenSSL::PKey::RSA).to receive(:new) + .with(basic_params[:private_pem]).and_raise(OpenSSL::PKey::RSAError) + end + let(:message) do + 'Merchant private key not specified' + end + include_examples 'incorrect configuration' + end context 'with private key is nil' do let(:params) do basic_params.merge(private_pem: nil) @@ -127,9 +140,40 @@ end describe '#verify_signed_response' do + subject { described_class.new(**params).verify_signed_response(response) } + let(:params) { basic_params } + let(:payload) do + path = File.expand_path('./data/account_payout.json', __dir__) + json = File.read(path) + JSON.parse(json) + end + let(:http_response) do + Faraday::Response.new( + status: 200, + response_headers: { 'Content-Type': 'application/json' }, + response_body: payload, + method: :post, + reason_phrase: 'OK' + ) + end + let(:response) do + Trustly::Data::JSONRPCResponse.new(http_response: http_response) + end + context 'with valid body and signature' do + it { is_expected.to be_truthy } + end + + context 'with the data in body not matching the signature' do + before do + payload['result']['method'] = 'AccountPayouts' + end + + it { is_expected.to be_falsy } + end end describe 'rpc calls' do + subject { described_class.new(**basic_params) } shared_examples_for 'rpc call' do |required_params| required_params.each do |param| context "with missing required param #{param}" do @@ -138,26 +182,441 @@ end it 'raises data error' do expect do - subject.public_send(method, params) + subject.public_send(rpc_call, **modified_params) end.to raise_error( Trustly::Exception::DataError, - "Required data is missing: #{params}" + "Required data is missing: #{param}" ) end end - context 'with valid data' do - context 'with a successful response' do + end + context 'with valid data' do + let(:request) do + instance_double(Trustly::Data::JSONRPCRequest) + end + let(:serial_data) do + "#{method}#{uuid}ArrayKey123KeyValue" + end + let(:signature) do + Base64.encode64('signature').chop + end + let(:connection) do + instance_double(Faraday::Connection) + end + let(:body) do + '{"Key":"Value"}' + end + let(:http_response) do + instance_double(Faraday::Response) + end + let(:response) do + instance_double(Trustly::Data::JSONRPCResponse) + end + let(:uuid) do + '8bedfbd4-8181-38e1-f0be-f360171aefc6' + end + let(:response_uuid) { uuid } + let(:response_body) { { 'key' => 'value' } } + let(:response_signature) { signature } + let(:serial_response_data) do + "#{method}#{response_uuid}keyvalue" + end + + before do + expect(Trustly::Data::JSONRPCRequest).to receive(:new) + .with(method: method, data: data, attributes: attributes) + .and_return(request) + expect(SecureRandom).to receive(:uuid).and_return(uuid) + expect(request).to receive(:uuid=).with(uuid) + expect(request).to receive(:signature=).with(signature) + expect(request).to receive(:uuid).and_return(nil) + expect(request).to receive(:uuid).at_least(:once).and_return(uuid) + expect(request).to receive(:method).at_least(:once).and_return(method) + expect(request).to receive(:data).and_return('Key' => 'Value', 'ArrayKey' => [1, 2, 3]) + expect(request).to receive(:update_data_at).with('Username', 'User') + expect(request).to receive(:update_data_at).with('Password', 'Password') + expect(subject.merchant_key).to receive(:sign).with( + instance_of(OpenSSL::Digest), serial_data + ).and_return('signature') + expect(request).to receive(:to_json).and_return(body) + expect(Faraday).to receive(:new).with('https://test.trustly.com') + .and_return(connection) + end + context 'with a successful response' do + before do + expect(connection).to receive(:post).with( + '/api/1', body, { 'Content-Type' => 'application/json' } + ).and_return(http_response) + expect(Trustly::Data::JSONRPCResponse).to receive(:new) + .with(http_response: http_response).and_return(response) + expect(response).to receive(:uuid).at_least(:once) + .and_return(response_uuid) + expect(response).to receive(:data).at_least(:once) + .and_return(response_body) + expect(response).to receive(:method).at_least(:once) + .and_return(method) + expect(response).to receive(:signature) + .and_return(response_signature) + expect(subject.trustly_key).to receive_message_chain( + :public_key, :verify + ).with( + instance_of(OpenSSL::Digest), 'signature', serial_response_data + ).and_return(true) + end + it 'makes a JSON RPC request and verifies its response' do + expect(subject.public_send(rpc_call, **params)).to eq(response) + end + end + context 'with a failed response' do + context 'with a faraday error' do + let(:error_response) { nil } + + before do + exception = error_klass.new( + StandardError.new(error_message), error_response + ) + expect(connection).to receive(:post).with( + '/api/1', body, { 'Content-Type' => 'application/json' } + ).and_raise(exception) + end + + shared_examples_for 'faraday error' do + it 'fails with an expected trustly error' do + expect { subject.public_send(rpc_call, **params) }.to raise_error( + expected_error, expected_error_message + ) + end + end + + context 'with a client error' do + let(:error_klass) { Faraday::ClientError } + let(:expected_error) { Trustly::Exception::DataError } + let(:error_response) { { status: 400, body: '{"error":"failed"}' } } + let(:error_message) { 'Bad request' } + let(:expected_error_message) do + "Bad request -> 400: {\"error\":\"failed\"} - #{method}, {\"Key\":\"Value\"}" + end + include_examples 'faraday error' + end + + context 'with a server error' do + let(:error_klass) { Faraday::ServerError } + let(:expected_error) { Trustly::Exception::ConnectionError } + let(:error_message) { 'Server error' } + let(:expected_error_message) do + 'Server error' + end + include_examples 'faraday error' + end + + context 'with a connection error' do + let(:error_klass) { Faraday::ConnectionFailed } + let(:expected_error) { Trustly::Exception::ConnectionError } + let(:error_message) { 'Connection error' } + let(:expected_error_message) do + 'Connection error' + end + include_examples 'faraday error' + end + + context 'with an ssl error' do + let(:error_klass) { Faraday::SSLError } + let(:expected_error) { Trustly::Exception::ConnectionError } + let(:error_message) { 'SSL error' } + let(:expected_error_message) do + 'SSL error' + end + include_examples 'faraday error' + end + + context 'with a parsing error' do + let(:error_klass) { Faraday::ParsingError } + let(:expected_error) { Trustly::Exception::DataError } + let(:error_message) { 'JSON error' } + let(:error_response) { { status: 200, body: 'abcd' } } + let(:expected_error_message) do + "JSON error -> 200: abcd - #{method}, {\"Key\":\"Value\"}" + end + include_examples 'faraday error' + end + end + + context 'with an invalid response payload' do before do - expect(Trustly::Data::JSONRPCRequest).to receive(:new) - .with(method: method, data: data, attributes: attriubtes) - .and_return(double) + expect(connection).to receive(:post).with( + '/api/1', body, { 'Content-Type' => 'application/json' } + ).and_return(http_response) + expect(Trustly::Data::JSONRPCResponse).to receive(:new) + .with(http_response: http_response).and_return(response) + expect(response).to receive(:uuid).at_least(:once) + .and_return(response_uuid) + end + + context 'with an incorrect uuid' do + let(:response_uuid) do + '8bedfbd4-8181-38e1-f0be-f360171aef6c' + end + + it 'fails with a uuid mismatch error' do + expect { subject.public_send(rpc_call, **params) }.to raise_error( + Trustly::Exception::DataError, + 'Incoming response is not related to the request. UUID mismatch.' + ) + end end - it 'makes a JSON RPC request and verifies its response' do + context 'with an incorrect signature' do + before do + expect(response).to receive(:data).and_return(response_body) + expect(response).to receive(:method).and_return(method) + expect(response).to receive(:signature) + .and_return(response_signature) + expect(subject.trustly_key).to receive_message_chain( + :public_key, :verify + ).with( + instance_of(OpenSSL::Digest), 'signature', serial_response_data + ).and_return(false) + end + + it 'fails with a signature error' do + expect { subject.public_send(rpc_call, **params) }.to raise_error( + Trustly::Exception::SignatureError, + 'Incoming message signature is not valid' + ) + end end end end end end + + describe '#refund' do + let(:method) { 'Refund' } + let(:rpc_call) { :refund } + let(:data) do + { + 'OrderId' => '12345', + 'Amount' => 100, + 'Currency' => 'EUR' + } + end + let(:attributes) do + { + 'ExternalReference' => { 'Value' => 'Test' } + } + end + let(:params) do + data.merge(attributes) + end + include_examples 'rpc call', %w[OrderId Amount Currency] + end + + describe '#void' do + let(:method) { 'Void' } + let(:rpc_call) { :void } + let(:data) do + { + 'OrderId' => '12345' + } + end + let(:attributes) do + nil + end + let(:params) do + data + end + include_examples 'rpc call', %w[OrderId] + end + + describe '#get_withdrawals' do + let(:method) { 'GetWithdrawals' } + let(:rpc_call) { :get_withdrawals } + let(:data) do + { + 'OrderId' => '12345' + } + end + let(:attributes) do + nil + end + let(:params) do + data + end + include_examples 'rpc call', %w[OrderId] + end + + describe '#refund' do + let(:method) { 'Refund' } + let(:rpc_call) { :refund } + let(:data) do + { + 'OrderId' => '12345', + 'Amount' => '100.00', + 'Currency' => 'EUR' + } + end + let(:attributes) do + { + 'ExternalReference' => { 'Value' => 'Test' } + } + end + let(:params) do + data.merge(attributes) + end + include_examples 'rpc call', %w[OrderId Amount Currency] + end + + describe '#deposit' do + let(:method) { 'Deposit' } + let(:rpc_call) { :deposit } + let(:data) do + { + 'NotificationURL' => 'https://example.com/notify', + 'EndUserID' => '123', + 'MessageID' => '123' + } + end + let(:attributes) do + { + 'Locale' => 'en-gb', + 'Country' => 'NL', + 'Currency' => 'EUR', + 'SuccessURL' => 'https://example.com/success', + 'FailURL' => 'https://example.com/fail', + 'Amount' => '200.00', + 'Firstname' => 'First', + 'Lastname' => 'Last', + 'ShopperStatement' => 'Trustly.com', + 'MobilePhone' => '+49 151 88888888' + } + end + let(:params) do + data.merge(attributes) + end + include_examples 'rpc call', %w[ + Locale Country Currency SuccessURL FailURL NotificationURL Amount + EndUserID MessageID Firstname Lastname ShopperStatement + ] + end + + describe '#select_account' do + let(:method) { 'SelectAccount' } + let(:rpc_call) { :select_account } + let(:data) do + { + 'NotificationURL' => 'https://example.com/notify', + 'EndUserID' => '123', + 'MessageID' => '123' + } + end + let(:extra_attributes) do + attributes.merge('WillBeRemoved' => true) + end + let(:attributes) do + { + 'Locale' => 'en-gb', + 'Country' => 'NL', + 'SuccessURL' => 'https://example.com/success', + 'FailURL' => 'https://example.com/fail', + 'Firstname' => 'First', + 'Lastname' => 'Last', + 'Email' => 'test@mail.com' + } + end + let(:params) do + data.merge(extra_attributes) + end + include_examples 'rpc call', %w[ + Locale Country SuccessURL FailURL NotificationURL EndUserID MessageID + Firstname Lastname + ] + end + + describe '#register_account' do + let(:method) { 'RegisterAccount' } + let(:rpc_call) { :register_account } + let(:data) do + { + 'EndUserID' => '123', + 'ClearingHouse' => 'SWEDEN', + 'BankNumber' => '6612', + 'AccountNumber' => '69706212', + 'Firstname' => 'First', + 'Lastname' => 'Last' + } + end + let(:attributes) do + { + 'DateOfBirth' => '15/08/1993', + 'MobilePhone' => '+49 151 88888888' + } + end + let(:params) do + data.merge(attributes) + end + include_examples 'rpc call', %w[ + EndUserID ClearingHouse BankNumber AccountNumber Firstname Lastname + ] + end + + describe '#account_payout' do + let(:method) { 'AccountPayout' } + let(:rpc_call) { :account_payout } + let(:data) do + { + 'NotificationURL' => 'https://example.com/notify', + 'EndUserID' => '123', + 'MessageID' => '123', + 'AccountID' => '123', + 'Amount' => '500.00', + 'Currency' => 'EUR' + } + end + let(:attributes) do + { + 'ShopperStatement' => 'Trustly.com' + } + end + let(:params) do + data.merge(attributes) + end + include_examples 'rpc call', %w[ + NotificationURL AccountID EndUserID MessageID Amount Currency + ShopperStatement + ] + end + end + describe '#notification_response' do + subject do + described_class.new(**basic_params) + end + context 'with a request' do + let(:success) { true } + let(:uuid) { '123' } + let(:method) { 'Test' } + let(:data) { { 'Key' => 'Value' } } + let(:serial_data) { 'Test123KeyValue' } + let(:signature) { Base64.encode64('signature').chop } + let(:request) { instance_double(Trustly::Data::JSONRPCNotificationRequest) } + let(:response) { instance_double(Trustly::Data::JSONRPCNotificationResponse) } + + before do + expect(Trustly::Data::JSONRPCNotificationResponse).to receive(:new) + .with(request: request, success: success).and_return(response) + expect(response).to receive(:uuid).and_return(uuid) + expect(response).to receive(:method).and_return(method) + expect(response).to receive(:data).and_return(data) + expect(subject.merchant_key).to receive(:sign) + .with(instance_of(OpenSSL::Digest), serial_data) + .and_return('signature') + expect(response).to receive(:signature=).with(signature) + end + + it 'returns a response' do + result = subject + .notification_response(request, success: success) + expect(result).to eq(response) + end + end end end From ee5911e22597346e496102e466218b58d3d7fa76 Mon Sep 17 00:00:00 2001 From: Artsiom Kuts Date: Mon, 15 Aug 2022 11:12:30 +0200 Subject: [PATCH 6/7] updated readme --- README.md | 135 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 70 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index a7998ca..af1d00a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Trustly-client-ruby -This is an example implementation of communication with the Trustly API using Ruby. This a ruby gem that allows you use Trustly Api calls in ruby. It's based on [trustly-client-python] (https://github.com/trustly/trustly-client-python) and [turstly-client-php] (https://github.com/trustly/trustly-client-php) +This is an example implementation of communication with the Trustly API using Ruby. This a ruby gem that allows you use Trustly Api calls in ruby. It implements the standard Payments API as well as gives stubs for executing calls against the API used by the backoffice. @@ -14,7 +14,7 @@ This code is provided as-is, use it as inspiration, reference or drop it directl Add this line to your application's Gemfile: ```ruby -gem 'trustly-client-ruby',:require=>'trustly' +gem 'trustly-client-ruby', require: 'trustly' ``` And then execute: @@ -41,72 +41,89 @@ You will need to copy test and live private certificates using this naming conve ## Usage -Currently only **Deposit** and **Refund** api calls. However, other calls can be implemented very easily. +Currently supports **Deposit**, **Refund**, **AccountPayout**, **RegisterAccount** and **SelectAccount** api calls. Other calls can be implemented very easily. ### Api In order to use Trustly Api, we'll need to create a **Trustly::Api::Signed**. Example: ```ruby -api = Trustly::Api::Signed.new({ - :username=>"yourusername", - :password=>"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -}) +api = Trustly::Api::Signed.new( + username: 'yourusername', + password: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' +) ``` -This will automatically load pem files from **certs/trustly** with default optons. If you want to specify other paths or options then you can call: +Also make sure you have ENV variables for certificates. Default variables are: MERCHANT_PRIVATE_KEY for the signing key and TRUSTLY_PUBLIC_KEY for the verifying key. ```ruby api = Trustly::Api::Signed.new({ - :username=>"yourusername", - :password=>"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - :host => 'test.trustly.com', - :port => 443, - :is_https => true, - :private_pem => "#{Rails.root}/certs/trustly/test.merchant.private.pem", - :public_pem => "#{Rails.root}/certs/trustly/test.trustly.public.pem" + username: 'yourusername', + password: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + host: 'test.trustly.com', + port: 443, + is_https: true, + private_pem: ENV.fetch('MY_PRIVATE_KEY_VAR', nil), + public_pem: ENV.fetch('TRUSTLY_PUBLIC_KEY_VAR', nil) }) ``` - +## Examples of RPC calls ### Deposit call -Deposit is straightfoward call. Only required arguments example: +Deposit is a straightfoward call. Only required arguments example: ```ruby -deposit = api.deposit({"EndUserID"=>10002,"MessageID"=>12349,"Amount"=>3}) +deposit = api.deposit( + 'EndUserID' => 10002, + 'MessageID' => 12349, + 'Amount' => '30.0', + 'ShopperStatement' => 'MyBrand.com', + 'Locale' => 'es_ES', + 'Country' => 'ES', + 'Currency' => 'EUR', + 'SuccessURL' => 'https://my-brand.com/thank_you.html', + 'FailURL' => 'https://my-brand.com/failure.html', + 'NotificationURL' => 'https://gateway.my-brand.com/notifications', + 'Firstname' => 'John', + 'Lastname' => 'Doe' +) ``` Optional arguments are: - -- Locale: default value "es_ES" -- Country: default "ES" -- Currency default "EUR" +- AccountID - SuggestedMinAmount - SuggestedMaxAmount -- Amount -- Currency -- Country - IP -- SuccessURL: default "https://www.trustly.com/success" -- FailURL : default "https://www.trustly.com/fail" - TemplateURL - URLTarget - MobilePhone -- Firstname -- Lastname +- Email - NationalIdentificationNumber -- ShopperStatement -- NotificationURL: default "https://test.trustly.com/demo/notifyd_test" +- UnchangeableNationalIdentificationNumber +- ShippingAddressCountry +- ShippingAddressPostalCode +- ShippingAddressLine1 +- ShippingAddressLine2 +- ShippingAddress +- RequestDirectDebitMandate +- ChargeAccountID +- QuickDeposit +- URLScheme +- ExternalReference +- PSPMerchant +- PSPMerchantURL +- MerchantCategoryCode +- RecipientInformation This will return a **Trustly::Data::JSONRPCResponse**: ```ruby -> deposit.get_data('url') +> deposit.data_at('url') => "https://test.trustly.com/_/orderclient.php?SessionID=755ea475-dcf1-476e-ac70-07913501b34e&OrderID=4257552724&Locale=es_ES" -> deposit.get_data() +> deposit.data => { - "orderid" => "4257552724", - "url" => "https://test.trustly.com/_/orderclient.php?SessionID=755ea475-dcf1-476e-ac70-07913501b34e&OrderID=4257552724&Locale=es_ES" + 'orderid' => '4257552724', + 'url' => 'https://test.trustly.com/_/orderclient.php?SessionID=755ea475-dcf1-476e-ac70-07913501b34e&OrderID=4257552724&Locale=es_ES' } ``` @@ -119,69 +136,57 @@ You can check if there was an error: > deposit.success? => false -> deposit.error_msg -=> "ERROR_DUPLICATE_MESSAGE_ID" +> deposit.error_message +=> 'ERROR_DUPLICATE_MESSAGE_ID' ``` -### Refund call - -Required parameters: - -- OrderID -- Amount -- Currency / default to "EUR" - -Example: - -```ruby -> api.refund({"OrderID"=>2205700591,"Amount"=>3,"Currency"=>"EUR"}) -``` - - ### Notifications After a **deposit** or **refund** call, Trustly will send a notification to **NotificationURL**. If you are using rails the execution flow will look like this: ```ruby def controller_action - api = Trustly::Api::Signed.new({..}) - notification = Trustly::JSONRPCNotificationRequest.new(params) + api = Trustly::Api::Signed.new({...}) + notification = Trustly::Data::JSONRPCNotificationRequest.new(notification_body: params) if api.verify_trustly_signed_notification(notification) - # do something with notification + # do something with the notification ... # reply to trustly - response = api.notification_response(notification,true) - render :text => response.json() + response = api.notification_response(notification, success: true) + render text: response.to_json else - render :nothing => true, :status => 200 + render nothing: true, status: 200 end end ``` -You can use **Trustly::JSONRPCNotificationRequest** object to access data provided using the following methods: +You can use **Trustly::Data::JSONRPCNotificationRequest** object to access data provided using the following methods: ```ruby - notification.get_data + notification.data => {"amount"=>"902.50", "currency"=>"EUR", "messageid"=>"98348932", "orderid"=>"87654567", "enduserid"=>"32123", "notificationid"=>"9876543456", "timestamp"=>"2010-01-20 14:42:04.675645+01", "attributes"=>{}} -> notification.get_method +> notification.method => "credit" -> notification.get_uuid +> notification.uuid => "258a2184-2842-b485-25ca-293525152425" -> notification.get_signature +> notification.signature => "R9+hjuMqbsH0Ku ... S16VbzRsw==" -> notification.get_data('amount') +> notification.data_at('amount') => "902.50" + +> notification.attribute_at('key') +=> nil ``` ## Contributing -1. Fork it ( https://github.com/jcarreti/trustly-client-ruby/fork ) +1. Fork it ( https://github.com/gapfish/trusty-client-ruby/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) From ed81c4ff9cdf00996850157e58caecd097919b86 Mon Sep 17 00:00:00 2001 From: Artsiom Kuts Date: Thu, 25 Aug 2022 16:28:03 +0200 Subject: [PATCH 7/7] review based improvements --- .rubocop.yml | 4 ++ lib/trustly.rb | 3 + lib/trustly/data/base.rb | 37 ----------- lib/trustly/data/jsonrpc_request.rb | 4 +- .../data/jsonrpcnotification_request.rb | 2 +- lib/trustly/data/request.rb | 5 +- lib/trustly/utils/data_cleaner.rb | 33 ++++++++++ lib/trustly/utils/data_transformer.rb | 20 ++++++ spec/signed_api_spec.rb | 64 +++++++++---------- spec/utils/data_cleaner_spec.rb | 23 +++++++ spec/utils/data_transformer_spec.rb | 27 ++++++++ 11 files changed, 148 insertions(+), 74 deletions(-) create mode 100644 lib/trustly/utils/data_cleaner.rb create mode 100644 lib/trustly/utils/data_transformer.rb create mode 100644 spec/utils/data_cleaner_spec.rb create mode 100644 spec/utils/data_transformer_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index 5b0e1a4..c388647 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,6 +2,10 @@ AllCops: TargetRubyVersion: 2.7 NewCops: enable SuggestExtensions: false +Layout/MultilineMethodCallIndentation: + EnforcedStyle: indented +Layout/DotPosition: + EnforcedStyle: trailing Style/Documentation: Enabled: false Metrics/BlockLength: diff --git a/lib/trustly.rb b/lib/trustly.rb index 69739e0..a5293f6 100644 --- a/lib/trustly.rb +++ b/lib/trustly.rb @@ -17,6 +17,9 @@ module Trustly require 'trustly/exception/jsonrpc_version_error' require 'trustly/exception/signature_error' +require 'trustly/utils/data_transformer' +require 'trustly/utils/data_cleaner' + require 'trustly/data/base' require 'trustly/data/request' require 'trustly/data/response' diff --git a/lib/trustly/data/base.rb b/lib/trustly/data/base.rb index 13eb081..508e4c5 100644 --- a/lib/trustly/data/base.rb +++ b/lib/trustly/data/base.rb @@ -12,43 +12,6 @@ def initialize(**_options) def to_json(*_args) payload.to_json end - - private - - # Vacuum out all keys being set to nil in the data to be communicated - def vacuum(data) - case data - when Array then vacuum_array_data(data) - when Hash then vacuum_hash_data(data) - else data - end - end - - def vacuum_array_data(data) - ret = data.each_with_object([]) do |element, acc| - processed_element = vacuum(element) unless element.nil? - next if processed_element.nil? - - acc.push(processed_element) - end - ret.length.zero? ? nil : ret - end - - def vacuum_hash_data(data) - ret = data.each_with_object({}) do |(key, element), acc| - processed_element = vacuum(element) unless element.nil? - next if processed_element.nil? - - acc[key] = processed_element - end - ret.length.zero? ? nil : ret - end - - def stringify_hash(hash) - hash.each_with_object({}) do |(k, v), acc| - acc[k.to_s] = v.is_a?(Hash) ? stringify_hash(v) : v - end - end end end end diff --git a/lib/trustly/data/jsonrpc_request.rb b/lib/trustly/data/jsonrpc_request.rb index bd3ee6e..6984644 100644 --- a/lib/trustly/data/jsonrpc_request.rb +++ b/lib/trustly/data/jsonrpc_request.rb @@ -81,14 +81,14 @@ def initialize_data(data, with_attributes) else raise TypeError, 'Data must be a Hash if attributes are provided' if !data.is_a?(Hash) && with_attributes - payload['params']['Data'] = vacuum(data) + payload['params']['Data'] = Utils::DataCleaner.vacuum(data) end end def initialize_attributes(attributes) return if attributes.nil? - payload['params']['Data']['Attributes'] ||= vacuum(attributes) + payload['params']['Data']['Attributes'] ||= Utils::DataCleaner.vacuum(attributes) end end end diff --git a/lib/trustly/data/jsonrpcnotification_request.rb b/lib/trustly/data/jsonrpcnotification_request.rb index 2a0c302..1d71a22 100644 --- a/lib/trustly/data/jsonrpcnotification_request.rb +++ b/lib/trustly/data/jsonrpcnotification_request.rb @@ -38,7 +38,7 @@ def attribute_at(key) private def notification_body(body) - return stringify_hash(body) if body.is_a?(Hash) + return Utils::DataTransformer.deep_stringify_hash(body) if body.is_a?(Hash) JSON.parse(body) rescue JSON::ParserError => e diff --git a/lib/trustly/data/request.rb b/lib/trustly/data/request.rb index cd991b3..588b848 100644 --- a/lib/trustly/data/request.rb +++ b/lib/trustly/data/request.rb @@ -8,8 +8,9 @@ class Request < Base def initialize(**options) super if (new_payload = options[:payload]) - vacuumed_payload = vacuum(new_payload) - self.payload = stringify_hash(vacuumed_payload) + vacuumed_payload = Utils::DataCleaner.vacuum(new_payload) + self.payload = Utils::DataTransformer. + deep_stringify_hash(vacuumed_payload) end self.method = options[:method] || payload['method'] end diff --git a/lib/trustly/utils/data_cleaner.rb b/lib/trustly/utils/data_cleaner.rb new file mode 100644 index 0000000..c0d99a7 --- /dev/null +++ b/lib/trustly/utils/data_cleaner.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Trustly + module Utils + class DataCleaner + class << self + def vacuum(data) + case data + when Array then vacuum_array_data(data) + when Hash then vacuum_hash_data(data) + else data + end + end + + private + + def vacuum_array_data(data) + ret = data.filter_map { |element| vacuum(element) } + ret.empty? ? nil : ret + end + + def vacuum_hash_data(data) + ret = data.each_with_object({}) do |(key, element), acc| + next if (processed_element = vacuum(element)).nil? + + acc[key] = processed_element + end + ret.empty? ? nil : ret + end + end + end + end +end diff --git a/lib/trustly/utils/data_transformer.rb b/lib/trustly/utils/data_transformer.rb new file mode 100644 index 0000000..4f74361 --- /dev/null +++ b/lib/trustly/utils/data_transformer.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Trustly + module Utils + class DataTransformer + def self.deep_stringify_hash(object) + case object + when Hash + object.each_with_object({}) do |(key, value), result| + result[key.to_s] = deep_stringify_hash(value) + end + when Array + object.map { |element| deep_stringify_hash(element) } + else + object + end + end + end + end +end diff --git a/spec/signed_api_spec.rb b/spec/signed_api_spec.rb index 8f104bb..3d85404 100644 --- a/spec/signed_api_spec.rb +++ b/spec/signed_api_spec.rb @@ -90,10 +90,10 @@ context 'when private key is invalid' do let(:params) { basic_params } before do - expect(OpenSSL::PKey::RSA).to receive(:new) - .with(basic_params[:public_pem]).and_call_original - expect(OpenSSL::PKey::RSA).to receive(:new) - .with(basic_params[:private_pem]).and_raise(OpenSSL::PKey::RSAError) + expect(OpenSSL::PKey::RSA).to receive(:new). + with(basic_params[:public_pem]).and_call_original + expect(OpenSSL::PKey::RSA).to receive(:new). + with(basic_params[:private_pem]).and_raise(OpenSSL::PKey::RSAError) end let(:message) do 'Merchant private key not specified' @@ -223,9 +223,9 @@ end before do - expect(Trustly::Data::JSONRPCRequest).to receive(:new) - .with(method: method, data: data, attributes: attributes) - .and_return(request) + expect(Trustly::Data::JSONRPCRequest).to receive(:new). + with(method: method, data: data, attributes: attributes). + and_return(request) expect(SecureRandom).to receive(:uuid).and_return(uuid) expect(request).to receive(:uuid=).with(uuid) expect(request).to receive(:signature=).with(signature) @@ -239,24 +239,24 @@ instance_of(OpenSSL::Digest), serial_data ).and_return('signature') expect(request).to receive(:to_json).and_return(body) - expect(Faraday).to receive(:new).with('https://test.trustly.com') - .and_return(connection) + expect(Faraday).to receive(:new).with('https://test.trustly.com'). + and_return(connection) end context 'with a successful response' do before do expect(connection).to receive(:post).with( '/api/1', body, { 'Content-Type' => 'application/json' } ).and_return(http_response) - expect(Trustly::Data::JSONRPCResponse).to receive(:new) - .with(http_response: http_response).and_return(response) - expect(response).to receive(:uuid).at_least(:once) - .and_return(response_uuid) - expect(response).to receive(:data).at_least(:once) - .and_return(response_body) - expect(response).to receive(:method).at_least(:once) - .and_return(method) - expect(response).to receive(:signature) - .and_return(response_signature) + expect(Trustly::Data::JSONRPCResponse).to receive(:new). + with(http_response: http_response).and_return(response) + expect(response).to receive(:uuid).at_least(:once). + and_return(response_uuid) + expect(response).to receive(:data).at_least(:once). + and_return(response_body) + expect(response).to receive(:method).at_least(:once). + and_return(method) + expect(response).to receive(:signature). + and_return(response_signature) expect(subject.trustly_key).to receive_message_chain( :public_key, :verify ).with( @@ -346,10 +346,10 @@ expect(connection).to receive(:post).with( '/api/1', body, { 'Content-Type' => 'application/json' } ).and_return(http_response) - expect(Trustly::Data::JSONRPCResponse).to receive(:new) - .with(http_response: http_response).and_return(response) - expect(response).to receive(:uuid).at_least(:once) - .and_return(response_uuid) + expect(Trustly::Data::JSONRPCResponse).to receive(:new). + with(http_response: http_response).and_return(response) + expect(response).to receive(:uuid).at_least(:once). + and_return(response_uuid) end context 'with an incorrect uuid' do @@ -369,8 +369,8 @@ before do expect(response).to receive(:data).and_return(response_body) expect(response).to receive(:method).and_return(method) - expect(response).to receive(:signature) - .and_return(response_signature) + expect(response).to receive(:signature). + and_return(response_signature) expect(subject.trustly_key).to receive_message_chain( :public_key, :verify ).with( @@ -601,20 +601,20 @@ let(:response) { instance_double(Trustly::Data::JSONRPCNotificationResponse) } before do - expect(Trustly::Data::JSONRPCNotificationResponse).to receive(:new) - .with(request: request, success: success).and_return(response) + expect(Trustly::Data::JSONRPCNotificationResponse).to receive(:new). + with(request: request, success: success).and_return(response) expect(response).to receive(:uuid).and_return(uuid) expect(response).to receive(:method).and_return(method) expect(response).to receive(:data).and_return(data) - expect(subject.merchant_key).to receive(:sign) - .with(instance_of(OpenSSL::Digest), serial_data) - .and_return('signature') + expect(subject.merchant_key).to receive(:sign). + with(instance_of(OpenSSL::Digest), serial_data). + and_return('signature') expect(response).to receive(:signature=).with(signature) end it 'returns a response' do - result = subject - .notification_response(request, success: success) + result = subject. + notification_response(request, success: success) expect(result).to eq(response) end end diff --git a/spec/utils/data_cleaner_spec.rb b/spec/utils/data_cleaner_spec.rb new file mode 100644 index 0000000..046de7a --- /dev/null +++ b/spec/utils/data_cleaner_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Trustly::Utils::DataCleaner do + describe '#vacuum' do + subject { described_class.vacuum(params) } + + context 'with a hash containing empty values and hollow arrays' do + let(:params) do + { + a: 10, + b: [], + c: { a: nil }, + e: { a: [] }, + f: { a: { a: [] }, b: 10 } + } + end + + it { is_expected.to eq(a: 10, f: { b: 10 }) } + end + end +end diff --git a/spec/utils/data_transformer_spec.rb b/spec/utils/data_transformer_spec.rb new file mode 100644 index 0000000..f4a1700 --- /dev/null +++ b/spec/utils/data_transformer_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Trustly::Utils::DataTransformer do + describe '#deep_stringify_hash' do + subject { described_class.deep_stringify_hash(params) } + + context 'with a hash containing symbolized keys' do + let(:params) do + { + a: 10, + b: [{ a: 10 }, :b, :c], + c: { a: [], b: { a: 10 } } + } + end + + it do + is_expected.to eq( + 'a' => 10, + 'b' => [{ 'a' => 10 }, :b, :c], + 'c' => { 'a' => [], 'b' => { 'a' => 10 } } + ) + end + end + end +end