lib/active_merchant/billing/gateways/sage_pay.rb in activemerchant-1.43.3 vs lib/active_merchant/billing/gateways/sage_pay.rb in activemerchant-1.44.0

- old
+ new

@@ -16,11 +16,13 @@ :purchase => 'PAYMENT', :credit => 'REFUND', :authorization => 'DEFERRED', :capture => 'RELEASE', :void => 'VOID', - :abort => 'ABORT' + :abort => 'ABORT', + :store => 'TOKEN', + :unstore => 'REMOVETOKEN' } CREDIT_CARDS = { :visa => "VISA", :master => "MC", @@ -53,33 +55,33 @@ def initialize(options = {}) requires!(options, :login) super end - def purchase(money, credit_card, options = {}) + def purchase(money, payment_method, options = {}) requires!(options, :order_id) post = {} add_amount(post, money, options) add_invoice(post, options) - add_credit_card(post, credit_card) + add_payment_method(post, payment_method, options) add_address(post, options) add_customer_data(post, options) add_optional_data(post, options) commit(:purchase, post) end - def authorize(money, credit_card, options = {}) + def authorize(money, payment_method, options = {}) requires!(options, :order_id) post = {} add_amount(post, money, options) add_invoice(post, options) - add_credit_card(post, credit_card) + add_payment_method(post, payment_method, options) add_address(post, options) add_customer_data(post, options) add_optional_data(post, options) commit(:authorization, post) @@ -116,14 +118,28 @@ commit(:credit, post) end def credit(money, identification, options = {}) - deprecated CREDIT_DEPRECATION_MESSAGE + ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE refund(money, identification, options) end + def store(credit_card, options = {}) + post = {} + add_credit_card(post, credit_card) + add_currency(post, 0, options) + + commit(:store, post) + end + + def unstore(token, options = {}) + post = {} + add_token(post, token) + commit(:unstore, post) + end + private def add_reference(post, identification) order_id, transaction_id, authorization, security_key = identification.split(';') add_pair(post, :VendorTxCode, order_id) @@ -145,59 +161,78 @@ currency = options[:currency] || currency(money) add_pair(post, :Amount, localized_amount(money, currency), :required => true) add_pair(post, :Currency, currency, :required => true) end + def add_currency(post, money, options) + currency = options[:currency] || currency(money) + add_pair(post, :Currency, currency, :required => true) + end + # doesn't actually use the currency -- dodgy! def add_release_amount(post, money, options) add_pair(post, :ReleaseAmount, amount(money), :required => true) end def add_customer_data(post, options) - add_pair(post, :CustomerEMail, options[:email][0,255]) unless options[:email].blank? - add_pair(post, :BillingPhone, options[:phone].gsub(/[^0-9+]/, '')[0,20]) unless options[:phone].blank? + add_pair(post, :CustomerEMail, truncate(options[:email], 255)) unless options[:email].blank? add_pair(post, :ClientIPAddress, options[:ip]) end def add_optional_data(post, options) add_pair(post, :GiftAidPayment, options[:gift_aid_payment]) unless options[:gift_aid_payment].blank? add_pair(post, :Apply3DSecure, options[:apply_3d_secure]) unless options[:apply_3d_secure].blank? + add_pair(post, :CreateToken, 1) unless options[:store].blank? + add_pair(post, :FIRecipientAcctNumber, options[:recipient_account_number]) + add_pair(post, :FIRecipientSurname, options[:recipient_surname]) + add_pair(post, :FIRecipientPostcode, options[:recipient_postcode]) + add_pair(post, :FIRecipientDoB, options[:recipient_dob]) end def add_address(post, options) if billing_address = options[:billing_address] || options[:address] first_name, last_name = parse_first_and_last_name(billing_address[:name]) add_pair(post, :BillingSurname, last_name) add_pair(post, :BillingFirstnames, first_name) - add_pair(post, :BillingAddress1, billing_address[:address1]) - add_pair(post, :BillingAddress2, billing_address[:address2]) - add_pair(post, :BillingCity, billing_address[:city]) - add_pair(post, :BillingState, billing_address[:state]) if billing_address[:country] == 'US' - add_pair(post, :BillingCountry, billing_address[:country]) - add_pair(post, :BillingPostCode, billing_address[:zip]) + add_pair(post, :BillingAddress1, truncate(billing_address[:address1], 100)) + add_pair(post, :BillingAddress2, truncate(billing_address[:address2], 100)) + add_pair(post, :BillingCity, truncate(billing_address[:city], 40)) + add_pair(post, :BillingState, truncate(billing_address[:state], 2)) if is_usa(billing_address[:country]) + add_pair(post, :BillingCountry, truncate(billing_address[:country], 2)) + add_pair(post, :BillingPhone, sanitize_phone(billing_address[:phone])) + add_pair(post, :BillingPostCode, truncate(billing_address[:zip], 10)) end if shipping_address = options[:shipping_address] || billing_address first_name, last_name = parse_first_and_last_name(shipping_address[:name]) add_pair(post, :DeliverySurname, last_name) add_pair(post, :DeliveryFirstnames, first_name) - add_pair(post, :DeliveryAddress1, shipping_address[:address1]) - add_pair(post, :DeliveryAddress2, shipping_address[:address2]) - add_pair(post, :DeliveryCity, shipping_address[:city]) - add_pair(post, :DeliveryState, shipping_address[:state]) if shipping_address[:country] == 'US' - add_pair(post, :DeliveryCountry, shipping_address[:country]) - add_pair(post, :DeliveryPostCode, shipping_address[:zip]) + add_pair(post, :DeliveryAddress1, truncate(shipping_address[:address1], 100)) + add_pair(post, :DeliveryAddress2, truncate(shipping_address[:address2], 100)) + add_pair(post, :DeliveryCity, truncate(shipping_address[:city], 40)) + add_pair(post, :DeliveryState, truncate(shipping_address[:state], 2)) if is_usa(shipping_address[:country]) + add_pair(post, :DeliveryCountry, truncate(shipping_address[:country], 2)) + add_pair(post, :DeliveryPhone, sanitize_phone(shipping_address[:phone])) + add_pair(post, :DeliveryPostCode, truncate(shipping_address[:zip], 10)) end end def add_invoice(post, options) add_pair(post, :VendorTxCode, sanitize_order_id(options[:order_id]), :required => true) - add_pair(post, :Description, truncate_description(options[:description] || options[:order_id])) + add_pair(post, :Description, truncate(options[:description] || options[:order_id], 100)) end + def add_payment_method(post, payment_method, options) + if payment_method.respond_to?(:number) + add_credit_card(post, payment_method) + else + add_token_details(post, payment_method, options) + end + end + def add_credit_card(post, credit_card) - add_pair(post, :CardHolder, credit_card.name, :required => true) + add_pair(post, :CardHolder, truncate(credit_card.name, 50), :required => true) add_pair(post, :CardNumber, credit_card.number, :required => true) add_pair(post, :ExpiryDate, format_date(credit_card.month, credit_card.year), :required => true) if requires_start_date_or_issue_number?(credit_card) @@ -207,19 +242,39 @@ add_pair(post, :CardType, map_card_type(credit_card)) add_pair(post, :CV2, credit_card.verification_value) end + def add_token_details(post, token, options) + add_token(post, token) + add_pair(post, :StoreToken, options[:customer]) + end + + def add_token(post, token) + add_pair(post, :Token, token) + end + def sanitize_order_id(order_id) - order_id.to_s.gsub(/[^-a-zA-Z0-9._]/, '') + cleansed = order_id.to_s.gsub(/[^-a-zA-Z0-9._]/, '') + truncate(cleansed, 40) end - def truncate_description(description) - return nil unless description - description[0, 100] + def sanitize_phone(phone) + return nil unless phone + cleansed = phone.to_s.gsub(/[^0-9+]/, '') + truncate(cleansed, 20) end + def truncate(value, max_size) + return nil unless value + value[0, max_size] + end + + def is_usa(country) + truncate(country, 2) == 'US' + end + def map_card_type(credit_card) raise ArgumentError, "The credit card type must be provided" if card_brand(credit_card).blank? card_type = card_brand(credit_card).to_sym @@ -254,15 +309,20 @@ :cvv_result => AVS_CVV_CODE[ response["CV2Result"] ] ) end def authorization_from(response, params, action) + case action + when :store + response['Token'] + else [ params[:VendorTxCode], response["VPSTxId"], response["TxAuthNo"], response["SecurityKey"], action ].join(";") + end end def abort_or_void_from(identification) original_transaction = identification.split(';').last original_transaction == 'authorization' ? :abort : :void @@ -271,11 +331,15 @@ def url_for(action) simulate ? build_simulator_url(action) : build_url(action) end def build_url(action) - endpoint = [ :purchase, :authorization ].include?(action) ? "vspdirect-register" : TRANSACTIONS[action].downcase + endpoint = case action + when :purchase, :authorization then "vspdirect-register" + when :store then 'directtoken' + else TRANSACTIONS[action].downcase + end "#{test? ? self.test_url : self.live_url}/#{endpoint}.vsp" end def build_simulator_url(action) endpoint = [ :purchase, :authorization ].include?(action) ? "VSPDirectGateway.asp" : "VSPServerGateway.asp?Service=Vendor#{TRANSACTIONS[action].capitalize}Tx" @@ -318,16 +382,17 @@ def parse_first_and_last_name(value) name = value.to_s.split(' ') last_name = name.pop || '' first_name = name.join(' ') - [ first_name[0,20], last_name[0,20] ] + [ truncate(first_name, 20), truncate(last_name, 20) ] end def localized_amount(money, currency) amount = amount(money) CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s) ? amount.split('.').first : amount end end + end end