lib/active_merchant/billing/gateways/sum_up.rb
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class SumUpGateway < Gateway
self.live_url = 'https://api.sumup.com/v0.1/'
self.supported_countries = %w(AT BE BG BR CH CL CO CY CZ DE DK EE ES FI FR
GB GR HR HU IE IT LT LU LV MT NL NO PL PT RO
SE SI SK US)
self.currencies_with_three_decimal_places = %w(EUR BGN BRL CHF CZK DKK GBP
HUF NOK PLN SEK USD)
self.default_currency = 'USD'
self.homepage_url = 'https://www.sumup.com/'
self.display_name = 'SumUp'
STANDARD_ERROR_CODE_MAPPING = {
multiple_invalid_parameters: 'MULTIPLE_INVALID_PARAMETERS'
}
def initialize(options = {})
requires!(options, :access_token, :pay_to_email)
super
end
def purchase(money, payment, options = {})
MultiResponse.run do |r|
r.process { create_checkout(money, payment, options) } unless options[:checkout_id]
r.process { complete_checkout(options[:checkout_id] || r.params['id'], payment, options) }
end
end
def refund(money, authorization, options = {})
transaction_id = authorization.split('#').last
post = money ? { amount: amount(money) } : {}
add_merchant_data(post, options)
commit('me/refund/' + transaction_id, post)
end
def supports_scrubbing?
true
end
def scrub(transcript)
transcript.
gsub(%r((Authorization: Bearer )\w+), '\1[FILTERED]').
gsub(%r(("pay_to_email\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
gsub(%r(("number\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
gsub(%r(("cvv\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]')
end
private
def create_checkout(money, payment, options)
post = {}
add_merchant_data(post, options)
add_invoice(post, money, options)
add_address(post, options)
add_customer_data(post, payment, options)
add_3ds_data(post, options)
commit('checkouts', post)
end
def complete_checkout(checkout_id, payment, options = {})
post = {}
add_payment(post, payment, options)
commit('checkouts/' + checkout_id, post, :put)
end
def add_customer_data(post, payment, options)
post[:customer_id] = options[:customer_id]
post[:personal_details] = {
email: options[:email],
first_name: payment&.first_name,
last_name: payment&.last_name,
tax_id: options[:tax_id]
}
end
def add_merchant_data(post, options)
# Required field: pay_to_email
# Description: Email address of the merchant to whom the payment is made.
post[:pay_to_email] = @options[:pay_to_email]
end
def add_address(post, options)
post[:personal_details] ||= {}
if address = (options[:billing_address] || options[:shipping_address] || options[:address])
post[:personal_details][:address] = {
city: address[:city],
state: address[:state],
country: address[:country],
line_1: address[:address1],
postal_code: address[:zip]
}
end
end
def add_invoice(post, money, options)
post[:checkout_reference] = options[:order_id]
post[:amount] = amount(money)
post[:currency] = options[:currency] || currency(money)
post[:description] = options[:description]
end
def add_payment(post, payment, options)
post[:payment_type] = options[:payment_type] || 'card'
post[:card] = {
name: payment.name,
number: payment.number,
expiry_month: format(payment.month, :two_digits),
expiry_year: payment.year,
cvv: payment.verification_value
}
end
def add_3ds_data(post, options)
post[:redirect_url] = options[:redirect_url] if options[:redirect_url]
end
def commit(action, post, method = :post)
response = api_request(action, post.compact, method)
succeeded = success_from(response)
Response.new(
succeeded,
message_from(succeeded, response),
action.include?('refund') ? { response_code: response.to_s } : response,
authorization: authorization_from(response),
test: test?,
error_code: error_code_from(succeeded, response)
)
end
def api_request(action, post, method)
raw_response =
begin
ssl_request(method, live_url + action, post.to_json, auth_headers)
rescue ResponseError => e
e.response.body
end
response = parse(raw_response)
response = response.is_a?(Hash) ? response.symbolize_keys : response
return format_errors(response) if raw_response.include?('error_code') && response.is_a?(Array)
response
end
def parse(body)
JSON.parse(body)
end
def success_from(response)
(response.is_a?(Hash) && response[:next_step]) ||
response == 204 ||
%w(PENDING PAID).include?(response[:status]) ||
response[:transactions]&.all? { |transaction| transaction.symbolize_keys[:status] == 'SUCCESSFUL' }
end
def message_from(succeeded, response)
if succeeded
return 'Succeeded' if (response.is_a?(Hash) && response[:next_step]) || response == 204
return response[:status]
end
response[:message] || response[:error_message] || response[:status]
end
def authorization_from(response)
return nil if response.is_a?(Integer)
return response[:id] unless response[:transaction_id]
[response[:id], response[:transaction_id]].join('#')
end
def auth_headers
{
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{options[:access_token]}"
}
end
def error_code_from(succeeded, response)
response[:error_code] unless succeeded
end
def format_error(error, key)
{
:error_code => error['error_code'],
key => error['param']
}
end
def format_errors(errors)
return format_error(errors.first, :message) if errors.size == 1
return {
error_code: STANDARD_ERROR_CODE_MAPPING[:multiple_invalid_parameters],
message: 'Validation error',
errors: errors.map { |error| format_error(error, :param) }
}
end
def handle_response(response)
case response.code.to_i
# to get the response code (204) when the body is nil
when 200...300
response.body || response.code
else
raise ResponseError.new(response)
end
end
end
end
end