lib/active_merchant/billing/gateways/worldpay_online_payments.rb
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class WorldpayOnlinePaymentsGateway < Gateway
self.live_url = 'https://api.worldpay.com/v1/'
self.default_currency = 'GBP'
self.money_format = :cents
self.supported_countries = %w(HK US GB BE CH CZ DE DK ES FI FR GR HU IE IT LU MT NL NO PL PT SE SG TR)
self.supported_cardtypes = %i[visa master american_express discover jcb maestro]
self.homepage_url = 'http://online.worldpay.com'
self.display_name = 'Worldpay Online Payments'
def initialize(options = {})
requires!(options, :client_key, :service_key)
@client_key = options[:client_key]
@service_key = options[:service_key]
super
end
def authorize(money, credit_card, options = {})
response = create_token(true, credit_card.first_name + ' ' + credit_card.last_name, credit_card.month, credit_card.year, credit_card.number, credit_card.verification_value)
if response.success?
options[:authorizeOnly] = true
post = create_post_for_auth_or_purchase(response.authorization, money, options)
response = commit(:post, 'orders', post, {}, 'authorize')
end
response
end
def capture(money, authorization, options = {})
if authorization
commit(:post, "orders/#{CGI.escape(authorization)}/capture", { 'captureAmount' => money }, options, 'capture')
else
Response.new(
false,
'FAILED',
'FAILED',
test: test?,
authorization: false,
avs_result: {},
cvv_result: {},
error_code: false
)
end
end
def purchase(money, credit_card, options = {})
response = create_token(true, credit_card.first_name + ' ' + credit_card.last_name, credit_card.month, credit_card.year, credit_card.number, credit_card.verification_value)
if response.success?
post = create_post_for_auth_or_purchase(response.authorization, money, options)
response = commit(:post, 'orders', post, options, 'purchase')
end
response
end
def refund(money, orderCode, options = {})
obj = money ? { 'refundAmount' => money } : {}
commit(:post, "orders/#{CGI.escape(orderCode)}/refund", obj, options, 'refund')
end
def void(orderCode, options = {})
response = commit(:delete, "orders/#{CGI.escape(orderCode)}", nil, options, 'void')
response = refund(nil, orderCode) if !response.success? && (response.params && response.params['customCode'] != 'ORDER_NOT_FOUND')
response
end
def verify(credit_card, options = {})
authorize(0, credit_card, options)
end
private
def create_token(reusable, name, exp_month, exp_year, number, cvc)
obj = {
'reusable' => reusable,
'paymentMethod' => {
'type' => 'Card',
'name' => name,
'expiryMonth' => exp_month,
'expiryYear' => exp_year,
'cardNumber' => number,
'cvc' => cvc
},
'clientKey' => @client_key
}
commit(:post, 'tokens', obj, { 'Authorization' => @service_key }, 'token')
end
def create_post_for_auth_or_purchase(token, money, options)
{
'token' => token,
'orderDescription' => options[:description] || 'Worldpay Order',
'amount' => money,
'currencyCode' => options[:currency] || default_currency,
'name' => options[:billing_address] && options[:billing_address][:name] ? options[:billing_address][:name] : '',
'billingAddress' => {
'address1' => options[:billing_address] && options[:billing_address][:address1] ? options[:billing_address][:address1] : '',
'address2' => options[:billing_address] && options[:billing_address][:address2] ? options[:billing_address][:address2] : '',
'address3' => '',
'postalCode' => options[:billing_address] && options[:billing_address][:zip] ? options[:billing_address][:zip] : '',
'city' => options[:billing_address] && options[:billing_address][:city] ? options[:billing_address][:city] : '',
'state' => options[:billing_address] && options[:billing_address][:state] ? options[:billing_address][:state] : '',
'countryCode' => options[:billing_address] && options[:billing_address][:country] ? options[:billing_address][:country] : ''
},
'customerOrderCode' => options[:order_id],
'orderType' => 'ECOM',
'authorizeOnly' => options[:authorizeOnly] ? true : false
}
end
def parse(body)
body ? JSON.parse(body) : {}
end
def headers(options = {})
headers = {
'Authorization' => @service_key,
'Content-Type' => 'application/json',
'User-Agent' => "Worldpay/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
'X-Worldpay-Client-User-Agent' => user_agent,
'X-Worldpay-Client-User-Metadata' => { ip: options[:ip] }.to_json
}
headers['Authorization'] = options['Authorization'] if options['Authorization']
headers
end
def commit(method, url, parameters = nil, options = {}, type = false)
raw_response = response = nil
success = false
begin
json = parameters ? parameters.to_json : nil
raw_response = ssl_request(method, self.live_url + url, json, headers(options))
if raw_response == ''
success = true
response = {}
else
response = parse(raw_response)
if type == 'token'
success = response.key?('token')
else
if response.key?('httpStatusCode')
success = false
else
if type == 'authorize' && response['paymentStatus'] == 'AUTHORIZED'
success = true
elsif type == 'purchase' && response['paymentStatus'] == 'SUCCESS'
success = true
elsif type == 'capture' || type == 'refund' || type == 'void'
success = true
end
end
end
end
rescue ResponseError => e
raw_response = e.response.body
response = response_error(raw_response)
rescue JSON::ParserError
response = json_error(raw_response)
end
if response['orderCode']
authorization = response['orderCode']
elsif response['token']
authorization = response['token']
else
authorization = response['message']
end
Response.new(
success,
success ? 'SUCCESS' : response['message'],
response,
test: test?,
authorization: authorization,
avs_result: {},
cvv_result: {},
error_code: success ? nil : response['customCode']
)
end
def test?
@service_key[0] == 'T'
end
def response_error(raw_response)
parse(raw_response)
rescue JSON::ParserError
json_error(raw_response)
end
def json_error(raw_response)
msg = 'Invalid response received from the Worldpay Online Payments API. Please contact techsupport.online@worldpay.com if you continue to receive this message.'
msg += " (The raw response returned by the API was #{raw_response.inspect})"
{
'error' => {
'message' => msg
}
}
end
def handle_response(response)
response.body
end
end
end
end