lib/active_merchant/billing/gateways/moneris_us.rb in activemerchant-1.44.1 vs lib/active_merchant/billing/gateways/moneris_us.rb in activemerchant-1.45.0

- old
+ new

@@ -16,33 +16,61 @@ self.supported_countries = ['US'] self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover] self.homepage_url = 'http://www.monerisusa.com/' self.display_name = 'Moneris (US)' - # login is your Store ID - # password is your API Token + # Initialize the Gateway + # + # The gateway requires that a valid login and password be passed + # in the +options+ hash. + # + # ==== Options + # + # * <tt>:login</tt> -- Your Store ID + # * <tt>:password</tt> -- Your API Token + # * <tt>:cvv_enabled</tt> -- Specify that you would like the CVV passed to the gateway. + # Only particular account types at Moneris will allow this. + # Defaults to false. (optional) def initialize(options = {}) requires!(options, :login, :password) + @cvv_enabled = options[:cvv_enabled] + @avs_enabled = options[:avs_enabled] options = { :crypt_type => 7 }.merge(options) super end # Referred to as "PreAuth" in the Moneris integration guide, this action # verifies and locks funds on a customer's card, which then must be # captured at a later date. # # Pass in +order_id+ and optionally a +customer+ parameter. - def authorize(money, creditcard, options = {}) - debit_commit 'us_preauth', money, creditcard, options + def authorize(money, creditcard_or_datakey, options = {}) + requires!(options, :order_id) + post = {} + add_payment_source(post, creditcard_or_datakey, options) + post[:amount] = amount(money) + post[:order_id] = options[:order_id] + post[:address] = options[:billing_address] || options[:address] + post[:crypt_type] = options[:crypt_type] || @options[:crypt_type] + action = (post[:data_key].blank?) ? 'us_preauth' : 'us_res_preauth_cc' + commit(action, post) end - # This action verifies funding on a customer's card, and readies them for + # This action verifies funding on a customer's card and readies them for # deposit in a merchant's account. # # Pass in <tt>order_id</tt> and optionally a <tt>customer</tt> parameter - def purchase(money, creditcard, options = {}) - debit_commit 'us_purchase', money, creditcard, options + def purchase(money, creditcard_or_datakey, options = {}) + requires!(options, :order_id) + post = {} + add_payment_source(post, creditcard_or_datakey, options) + post[:amount] = amount(money) + post[:order_id] = options[:order_id] + post[:address] = options[:billing_address] || options[:address] + post[:crypt_type] = options[:crypt_type] || @options[:crypt_type] + action = (post[:data_key].blank?) ? 'us_purchase' : 'us_res_purchase_cc' + commit(action, post) end # This method retrieves locked funds from a customer's account (from a # PreAuth) and prepares them for deposit in a merchant's account. # @@ -77,33 +105,51 @@ def refund(money, authorization, options = {}) commit 'us_refund', crediting_params(authorization, :amount => amount(money)) end + def store(credit_card, options = {}) + post = {} + post[:pan] = credit_card.number + post[:expdate] = expdate(credit_card) + post[:crypt_type] = options[:crypt_type] || @options[:crypt_type] + commit('us_res_add_cc', post) + end + + def unstore(data_key, options = {}) + post = {} + post[:data_key] = data_key + commit('us_res_delete', post) + end + + def update(data_key, credit_card, options = {}) + post = {} + post[:pan] = credit_card.number + post[:expdate] = expdate(credit_card) + post[:data_key] = data_key + post[:crypt_type] = options[:crypt_type] || @options[:crypt_type] + commit('us_res_update_cc', post) + end + private # :nodoc: all def expdate(creditcard) sprintf("%.4i", creditcard.year)[-2..-1] + sprintf("%.2i", creditcard.month) end - def debit_commit(commit_type, money, creditcard, options) - requires!(options, :order_id) - commit(commit_type, debit_params(money, creditcard, options)) + def add_payment_source(post, source, options) + if source.is_a?(String) + post[:data_key] = source + post[:cust_id] = options[:customer] + else + post[:pan] = source.number + post[:expdate] = expdate(source) + post[:cvd_value] = source.verification_value if source.verification_value? + post[:cust_id] = options[:customer] || source.name + end end - # Common params used amongst the +purchase+ and +authorization+ methods - def debit_params(money, creditcard, options = {}) - { - :order_id => options[:order_id], - :cust_id => options[:customer], - :amount => amount(money), - :pan => creditcard.number, - :expdate => expdate(creditcard), - :crypt_type => options[:crypt_type] || @options[:crypt_type] - } - end - # Common params used amongst the +credit+, +void+ and +capture+ methods def crediting_params(authorization, options = {}) { :txn_number => split_authorization(authorization).first, :order_id => split_authorization(authorization).last, @@ -120,14 +166,19 @@ authorization.split(';') end end def commit(action, parameters = {}) - response = parse(ssl_post(test? ? self.test_url : self.live_url, post_data(action, parameters))) + data = post_data(action, parameters) + url = test? ? self.test_url : self.live_url + raw = ssl_post(url, data) + response = parse(raw) Response.new(successful?(response), message_from(response[:message]), response, :test => test?, + :avs_result => { :code => response[:avs_result_code] }, + :cvv_result => response[:cvd_result_code] && response[:cvd_result_code][-1,1], :authorization => authorization_from(response) ) end # Generates a Moneris authorization string of the form 'trans_id;receipt_id'. @@ -161,37 +212,79 @@ def post_data(action, parameters = {}) xml = REXML::Document.new root = xml.add_element("request") root.add_element("store_id").text = options[:login] root.add_element("api_token").text = options[:password] - transaction = root.add_element(action) + root.add_element(transaction_element(action, parameters)) + xml.to_s + end + + def transaction_element(action, parameters) + transaction = REXML::Element.new(action) + # Must add the elements in the correct order actions[action].each do |key| - transaction.add_element(key.to_s).text = parameters[key] unless parameters[key].blank? + case key + when :avs_info + transaction.add_element(avs_element(parameters[:address])) if @avs_enabled && parameters[:address] + when :cvd_info + transaction.add_element(cvd_element(parameters[:cvd_value])) if @cvv_enabled + else + transaction.add_element(key.to_s).text = parameters[key] unless parameters[key].blank? + end end - xml.to_s + transaction end + def avs_element(address) + full_address = "#{address[:address1]} #{address[:address2]}" + tokens = full_address.split(/\s+/) + + element = REXML::Element.new('avs_info') + element.add_element('avs_street_number').text = tokens.select{|x| x =~ /\d/}.join(' ') + element.add_element('avs_street_name').text = tokens.reject{|x| x =~ /\d/}.join(' ') + element.add_element('avs_zipcode').text = address[:zip] + element + end + + def cvd_element(cvd_value) + element = REXML::Element.new('cvd_info') + if cvd_value + element.add_element('cvd_indicator').text = "1" + element.add_element('cvd_value').text = cvd_value + else + element.add_element('cvd_indicator').text = "0" + end + element + end + def message_from(message) return 'Unspecified error' if message.blank? message.gsub(/[^\w]/, ' ').split.join(" ").capitalize end def actions { - "us_purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type], - "us_preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type], + "us_purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type, :avs_info, :cvd_info], + "us_preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type, :avs_info, :cvd_info], + "us_command" => [:order_id], "us_refund" => [:order_id, :amount, :txn_number, :crypt_type], - "us_ind_refund" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type], + "us_indrefund" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type], "us_completion" => [:order_id, :comp_amount, :txn_number, :crypt_type], "us_purchasecorrection" => [:order_id, :txn_number, :crypt_type], - "us_cavv_purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv], - "us_cavv_preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv], - "us_batchcloseall" => [], + "us_cavvpurcha" => [:order_id, :cust_id, :amount, :pan, :expdate, :cav], + "us_cavvpreaut" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv], + "us_transact" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type], + "us_Batchcloseall" => [], "us_opentotals" => [:ecr_number], - "us_batchclose" => [:ecr_number] + "us_batchclose" => [:ecr_number], + "us_res_add_cc" => [:pan, :expdate, :crypt_type], + "us_res_delete" => [:data_key], + "us_res_update_cc" => [:data_key, :pan, :expdate, :crypt_type], + "us_res_purchase_cc" => [:data_key, :order_id, :cust_id, :amount, :crypt_type], + "us_res_preauth_cc" => [:data_key, :order_id, :cust_id, :amount, :crypt_type] } end end end end