lib/active_merchant/billing/gateways/sage_pay.rb in activemerchant-1.133.0 vs lib/active_merchant/billing/gateways/sage_pay.rb in activemerchant-1.137.0
- old
+ new
@@ -4,12 +4,12 @@
cattr_accessor :simulate
self.simulate = false
class_attribute :simulator_url
- self.test_url = 'https://test.sagepay.com/gateway/service'
- self.live_url = 'https://live.sagepay.com/gateway/service'
+ self.test_url = 'https://sandbox.opayo.eu.elavon.com/gateway/service'
+ self.live_url = 'https://live.opayo.eu.elavon.com/gateway/service'
self.simulator_url = 'https://test.sagepay.com/Simulator'
APPROVED = 'OK'
TRANSACTIONS = {
@@ -76,18 +76,22 @@
self.homepage_url = 'http://www.sagepay.com'
self.display_name = 'SagePay'
def initialize(options = {})
requires!(options, :login)
+ @protocol_version = options.fetch(:protocol_version, '3.00')
super
end
def purchase(money, payment_method, options = {})
requires!(options, :order_id)
post = {}
+ add_override_protocol_version(options)
+ add_three_ds_data(post, options)
+ add_stored_credentials_data(post, options)
add_amount(post, money, options)
add_invoice(post, options)
add_payment_method(post, payment_method, options)
add_address(post, options)
add_customer_data(post, options)
@@ -99,10 +103,13 @@
def authorize(money, payment_method, options = {})
requires!(options, :order_id)
post = {}
+ add_three_ds_data(post, options)
+ add_stored_credentials_data(post, options)
+ add_override_protocol_version(options)
add_amount(post, money, options)
add_invoice(post, options)
add_payment_method(post, payment_method, options)
add_address(post, options)
add_customer_data(post, options)
@@ -113,19 +120,21 @@
# You can only capture a transaction once, even if you didn't capture the full amount the first time.
def capture(money, identification, options = {})
post = {}
+ add_override_protocol_version(options)
add_reference(post, identification)
add_release_amount(post, money, options)
commit(:capture, post)
end
def void(identification, options = {})
post = {}
+ add_override_protocol_version(options)
add_reference(post, identification)
action = abort_or_void_from(identification)
commit(action, post)
end
@@ -134,10 +143,11 @@
def refund(money, identification, options = {})
requires!(options, :order_id, :description)
post = {}
+ add_override_protocol_version(options)
add_related_reference(post, identification)
add_amount(post, money, options)
add_invoice(post, options)
commit(:credit, post)
@@ -148,18 +158,20 @@
refund(money, identification, options)
end
def store(credit_card, options = {})
post = {}
+ add_override_protocol_version(options)
add_credit_card(post, credit_card)
add_currency(post, 0, options)
commit(:store, post)
end
def unstore(token, options = {})
post = {}
+ add_override_protocol_version(options)
add_token(post, token)
commit(:unstore, post)
end
def verify(credit_card, options = {})
@@ -180,10 +192,62 @@
gsub(%r((&?CV2=)\d+(&?)), '\1[FILTERED]\2')
end
private
+ def add_override_protocol_version(options)
+ @protocol_version = options[:protocol_version] if options[:protocol_version]
+ end
+
+ def add_three_ds_data(post, options)
+ return unless @protocol_version == '4.00'
+ return unless three_ds_2_options = options[:three_ds_2]
+
+ add_pair(post, :ThreeDSNotificationURL, three_ds_2_options[:notification_url])
+ return unless three_ds_2_options[:browser_info]
+
+ add_browser_info(post, three_ds_2_options[:browser_info])
+ end
+
+ def add_browser_info(post, browser_info)
+ add_pair(post, :BrowserAcceptHeader, browser_info[:accept_header])
+ add_pair(post, :BrowserColorDepth, browser_info[:depth])
+ add_pair(post, :BrowserJavascriptEnabled, format_boolean(browser_info[:java]))
+ add_pair(post, :BrowserJavaEnabled, format_boolean(browser_info[:java]))
+ add_pair(post, :BrowserLanguage, browser_info[:language])
+ add_pair(post, :BrowserScreenHeight, browser_info[:height])
+ add_pair(post, :BrowserScreenWidth, browser_info[:width])
+ add_pair(post, :BrowserTZ, browser_info[:timezone])
+ add_pair(post, :BrowserUserAgent, browser_info[:user_agent])
+ add_pair(post, :ChallengeWindowSize, browser_info[:browser_size])
+ end
+
+ def add_stored_credentials_data(post, options)
+ return unless @protocol_version == '4.00'
+ return unless stored_credential = options[:stored_credential]
+
+ initiator = stored_credential[:initiator] == 'cardholder' ? 'CIT' : 'MIT'
+ cof_usage = if stored_credential[:initial_transaction] && initiator == 'CIT'
+ 'FIRST'
+ elsif !stored_credential[:initial_transaction] && initiator == 'MIT'
+ 'SUBSEQUENT'
+ end
+
+ add_pair(post, :COFUsage, cof_usage) if cof_usage
+ add_pair(post, :InitiatedTYPE, initiator)
+ add_pair(post, :SchemeTraceID, stored_credential[:network_transaction_id]) if stored_credential[:network_transaction_id]
+
+ reasoning = stored_credential[:reason_type] == 'installment' ? 'instalment' : stored_credential[:reason_type]
+ add_pair(post, :MITType, reasoning.upcase)
+
+ if %w(instalment recurring).any?(reasoning)
+ add_pair(post, :RecurringExpiry, options[:recurring_expiry])
+ add_pair(post, :RecurringFrequency, options[:recurring_frequency])
+ add_pair(post, :PurchaseInstalData, options[:installment_data])
+ end
+ end
+
def truncate(value, max_size)
return nil unless value
return value.to_s if CGI.escape(value.to_s).length <= max_size
if value.size > max_size
@@ -344,18 +408,22 @@
end
def commit(action, parameters)
response = parse(ssl_post(url_for(action), post_data(action, parameters)))
- Response.new(response['Status'] == APPROVED, message_from(response), response,
+ Response.new(
+ response['Status'] == APPROVED,
+ message_from(response),
+ response,
test: test?,
authorization: authorization_from(response, parameters, action),
avs_result: {
street_match: AVS_CODE[response['AddressResult']],
postal_match: AVS_CODE[response['PostCodeResult']]
},
- cvv_result: CVV_CODE[response['CV2Result']])
+ cvv_result: CVV_CODE[response['CV2Result']]
+ )
end
def authorization_from(response, params, action)
case action
when :store
@@ -399,15 +467,21 @@
def post_data(action, parameters = {})
parameters.update(
Vendor: @options[:login],
TxType: TRANSACTIONS[action],
- VPSProtocol: @options.fetch(:protocol_version, '3.00')
+ VPSProtocol: @protocol_version
)
parameters.update(ReferrerID: application_id) if application_id && (application_id != Gateway.application_id)
parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
+ end
+
+ def format_boolean(value)
+ return if value.nil?
+
+ value ? '1' : '0'
end
# SagePay returns data in the following format
# Key1=value1
# Key2=value2