Skip to content

Commit

Permalink
Nuvei: Base Gateway Layout
Browse files Browse the repository at this point in the history
Description
-------------------------
[SER-1351](https://spreedly.atlassian.net/browse/SER-1351)

This commit add a the basic structure for the new Nuvei Gateway

Unit test
-------------------------
Finished in 0.124334 seconds.

1 tests, 2 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed

8.04 tests/s, 16.09 assertions/s

Remote test
-------------------------
Finished in 0.602895 seconds.

1 tests, 2 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed

1.66 tests/s, 3.32 assertions/s

Rubocop
-------------------------
801 files inspected, no offenses detected
  • Loading branch information
Javier Pedroza committed Jul 17, 2024
1 parent dfaccf4 commit 15f6e9a
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 0 deletions.
137 changes: 137 additions & 0 deletions lib/active_merchant/billing/gateways/nuvei.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
module ActiveMerchant
module Billing
class NuveiGateway < Gateway
self.test_url = 'https://ppp-test.nuvei.com/ppp/api/v1'
self.live_url = 'https://secure.safecharge.com/ppp/api/v1'

self.supported_countries = %w[US CA IN NZ GB AU US]
self.default_currency = 'USD'
self.money_format = :cents
self.supported_cardtypes = %i[visa master american_express discover union_pay]
self.currencies_without_fractions = %w[CLP KRW JPY ISK MMK PYG UGX VND XAF XOF]
self.homepage_url = 'https://www.nuvei.com/'
self.display_name = 'Nuvei'

ENDPOINTS_MAPPING = {
authenticate: '/getSessionToken',
purchase: '/payment', # /authorize with transactionType: "Auth"
capture: '/settleTransaction',
refund: '/refundTransaction',
void: '/voidTransaction',
general_credit: '/payout'
}

def initialize(options = {})
requires!(options, :merchant_id, :merchant_site_id, :secret_key)
super
end

def authorize(money, payment, options = {}); end

def purchase(money, payment, options = {}); end

def capture(money, authorization, options = {}); end

def refund(money, authorization, options = {}); end

def void(authorization, options = {}); end

def credit(money, payment, options = {}); end

def scrub(transcript)
transcript.
gsub(%r((Authorization: Bearer )[a-zA-Z0-9._-]+)i, '\1[FILTERED]').
gsub(%r((\"cardNumber\\\":\\\")\d+), '\1[FILTERED]').
gsub(%r((\"cardCvv\\\":\\\")\d+), '\1[FILTERED]').
gsub(%r((\"cardHolderName\\\":\\\")\w+), '\1[FILTERED]')
end

private

def add_payment(post, payment); end

def commit(action, post, authorization = nil, method = :post)
MultiResponse.run do |r|
r.process { fetch_session_token } unless session_token_valid?
r.process do
api_request(action, post, authorization, method).tap do |response|
response.params.merge!(@options.slice(:session_token, :token_expires)) if @options[:new_credentials]
end
end
end
end

def session_token_valid?
@options[:session_token] && @options[:token_expires] && Time.now <= @options[:token_expires]
end

def fetch_session_token
post = {
'merchantId' => @options[:merchant_id],
'merchantSiteId' => @options[:merchant_site_id],
'merchantKey' => @options[:secret_key],
'clientRequestId' => SecureRandom.uuid,
'timeStamp' => Time.now.utc.strftime('%Y%m%d%H%M%S')
}

post.tap { |p| p['checksum'] = calculate_checksum(p) }

response = JSON.parse(ssl_post(url(:authenticate), post.to_json, headers)).with_indifferent_access
current_time = Time.now
expiration_time = current_time + (15 * 60) # 15 minutes in seconds

@options[:session_token] = response.dig('sessionToken')
@options[:token_expires] = expiration_time

Response.new(
response[:sessionToken].present?,
message_from(response),
response,
test: test?,
error_code: response[:errCode]
)
rescue ResponseError => e
raise OAuthResponseError.new(e)
end

def calculate_checksum(post)
Digest::SHA256.hexdigest("#{post['merchantId']}#{post['merchantSiteId']}#{post['clientRequestId']}#{post['timeStamp']}#{@options[:secret_key]}")
end

def url(action, id = nil)
"#{test? ? test_url : live_url}#{ENDPOINTS_MAPPING[action] % id}"
end

def api_request(action, post, authorization, method = :post)
response = parse ssl_request(method, url(action, authorization), post.to_json, headers)

Response.new(
success_from(response),
message_from(response),
response,
authorization: authorization_from(action, response, post),
test: test?,
error_code: error_code_from(action, response)
)
end

def headers
{ 'Content-Type' => 'application/json' }.tap do |headers|
headers['Authorization'] = "Bearer #{@options[:access_token]}" if @options[:access_token]
end
end

def success_from(response)
response[:status] == 'SUCCESS' && response[:transactionStatus] == 'APPROVED'
end

def authorization_from(action, response, post)
response.dig(:orderId)
end

def message_from(response)
response[:status]
end
end
end
end
5 changes: 5 additions & 0 deletions test/fixtures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,11 @@ nmi:
nmi_secure:
security_key: '6457Thfj624V5r7WUwc5v6a68Zsd6YEm'

nuvei:
merchant_id: 'merchantId'
merchant_site_id: 'siteId'
secret_key: 'secretKey'

ogone:
login: LOGIN
user: USER
Expand Down
21 changes: 21 additions & 0 deletions test/remote/gateways/remote_nuvei_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'test_helper'

class RemoteNuveiTest < Test::Unit::TestCase
def setup
@gateway = NuveiGateway.new(fixtures(:nuvei))

@amount = 100
@credit_card_cit = credit_card('4111111111111111', verification_value: '999', first_name: 'Cure', last_name: 'Tester')
@credit_card_mit = credit_card('4000002760003184')
@declined_card = credit_card('4000300011112220')

@options = {
}
end

def test_successful_session_token_generation
response = @gateway.send(:fetch_session_token)
assert_success response
assert_not_nil response.params[:sessionToken]
end
end
28 changes: 28 additions & 0 deletions test/unit/gateways/nuvei_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require 'test_helper'

class NuveiTest < Test::Unit::TestCase
include CommStub

def setup
@gateway = NuveiGateway.new(
merchant_id: 'SOMECREDENTIAL',
merchant_site_id: 'SOMECREDENTIAL',
secret_key: 'SOMECREDENTIAL'
)
@credit_card = credit_card
@amount = 100
end

def supported_card_types
assert_equal %i(visa master american_express discover union_pay), NuveiGateway.supported_cardtypes
end

def test_supported_countries
assert_equal %w(US CA IN NZ GB AU US), NuveiGateway.supported_countries
end

def build_request_authenticate_url
action = :authenticate
assert_equal @gateway.send(:url, action), "#{@gateway.test_url}/getSessionToken"
end
end

0 comments on commit 15f6e9a

Please sign in to comment.