Skip to content

Commit

Permalink
FlexCharge: Adding authorize-capture functionality
Browse files Browse the repository at this point in the history
Summary:
------------------------------
Changes FlexCharge to add support for the authorize and
capture operations and also a fix a bug on the `address_names`
method

[SER-1337](https://spreedly.atlassian.net/browse/SER-1337)
[SER-1324](https://spreedly.atlassian.net/browse/SER-1324)

Remote Test:
------------------------------
Finished in 72.283834 seconds.
19 tests, 54 assertions, 1 failures, 0 errors, 0 pendings,
1 omissions, 0 notifications
94.4444% passed

Unit Tests:
------------------------------
Finished in 43.11362 seconds.
5944 tests, 79908 assertions, 0 failures, 0 errors, 0 pendings,
0 omissions, 0 notifications
100% passed

RuboCop:
------------------------------
798 files inspected, no offenses detected
  • Loading branch information
Heavyblade committed Jul 3, 2024
1 parent 3274d5b commit db57ae3
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 24 deletions.
67 changes: 50 additions & 17 deletions lib/active_merchant/billing/gateways/flex_charge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,61 @@ class FlexChargeGateway < Gateway
sync: 'outcome',
refund: 'orders/%s/refund',
store: 'tokenize',
inquire: 'orders/%s'
inquire: 'orders/%s',
capture: 'capture'
}

SUCCESS_MESSAGES = %w(APPROVED CHALLENGE SUBMITTED SUCCESS PROCESSING).freeze
SUCCESS_MESSAGES = %w(APPROVED CHALLENGE SUBMITTED SUCCESS PROCESSING CAPTUREREQUIRED).freeze

def initialize(options = {})
requires!(options, :app_key, :app_secret, :site_id, :mid)
super
end

def purchase(money, credit_card, options = {})
options[:transactionType] = 'Purchase'

post = {}
address = options[:billing_address] || options[:address]
add_merchant_data(post, options)
add_base_data(post, options)
add_invoice(post, money, credit_card, options)
add_mit_data(post, options)
add_payment_method(post, credit_card, address, options)
add_address(post, credit_card, address)
add_payment_method(post, credit_card, address(options), options)
add_address(post, credit_card, address(options))
add_customer_data(post, options)
add_three_ds(post, options)

commit(:purchase, post)
end

def authorize(money, credit_card, options = {})
options[:transactionType] = 'Authorization'
purchase(money, credit_card, options)
end

def capture(money, authorization, options = {})
order_id, currency = authorization.split('#')
post = {
idempotencyKey: options[:idempotency_key] || SecureRandom.uuid,
orderId: order_id,
amount: money,
currency: currency
}

commit(:capture, post, authorization)
end

def refund(money, authorization, options = {})
commit(:refund, { amountToRefund: (money.to_f / 100).round(2) }, authorization)
order_id, _currency = authorization.split('#')
commit(:refund, { amountToRefund: (money.to_f / 100).round(2) }, order_id)
end

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

def store(credit_card, options = {})
address = options[:billing_address] || options[:address] || {}
first_name, last_name = address_names(address[:name], credit_card)
first_name, last_name = names_from_address(address(options), credit_card)

post = {
payment_method: {
Expand Down Expand Up @@ -84,11 +107,16 @@ def scrub(transcript)
end

def inquire(authorization, options = {})
commit(:inquire, {}, authorization, :get)
order_id, _currency = authorization.split('#')
commit(:inquire, {}, order_id, :get)
end

private

def address(options)
options[:billing_address] || options[:address] || {}
end

def add_three_ds(post, options)
return unless three_d_secure = options[:three_d_secure]

Expand Down Expand Up @@ -129,7 +157,7 @@ def add_customer_data(post, options)
end

def add_address(post, payment, address)
first_name, last_name = address_names(address[:name], payment)
first_name, last_name = names_from_address(address, payment)

post[:billingInformation] = {
firstName: first_name,
Expand Down Expand Up @@ -157,6 +185,7 @@ def add_invoice(post, money, credit_card, options)
avsResultCode: options[:avs_result_code],
cvvResultCode: options[:cvv_result_code],
cavvResultCode: options[:cavv_result_code],
transactionType: options[:transactionType],
cardNotPresent: credit_card.is_a?(String) ? false : credit_card.verification_value.blank?
}.compact
end
Expand All @@ -182,10 +211,10 @@ def add_payment_method(post, credit_card, address, options)
post[:paymentMethod] = payment_method.compact
end

def address_names(address_name, payment_method)
split_names(address_name).tap do |names|
names[0] = payment_method&.first_name unless names[0].present?
names[1] = payment_method&.last_name unless names[1].present?
def names_from_address(address, payment_method)
split_names(address[:name]).tap do |names|
names[0] = payment_method&.first_name unless names[0].present? || payment_method.is_a?(String)
names[1] = payment_method&.last_name unless names[1].present? || payment_method.is_a?(String)
end
end

Expand Down Expand Up @@ -253,7 +282,7 @@ def api_request(action, post, authorization = nil, method = :post)
success_from(action, response),
message_from(response),
response,
authorization: authorization_from(action, response),
authorization: authorization_from(action, response, post),
test: test?,
error_code: error_code_from(action, response)
)
Expand All @@ -280,8 +309,12 @@ def message_from(response)
response[:title] || response[:responseMessage] || response[:statusName] || response[:status]
end

def authorization_from(action, response)
action == :store ? response.dig(:transaction, :payment_method, :token) : response[:orderId]
def authorization_from(action, response, options)
if action == :store
response.dig(:transaction, :payment_method, :token)
elsif success_from(action, response)
[response[:orderId], options[:currency] || default_currency].compact.join('#')
end
end

def error_code_from(action, response)
Expand Down
39 changes: 37 additions & 2 deletions test/remote/gateways/remote_flex_charge_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,26 @@ def test_successful_purchase_mit
set_credentials!
response = @gateway.purchase(@amount, @credit_card_mit, @options)
assert_success response
assert_equal 'SUBMITTED', response.message
assert_equal 'APPROVED', response.message
end

def test_successful_authorize_cit
@cit_options[:phone] = '998888'
set_credentials!
response = @gateway.authorize(@amount, @credit_card_mit, @cit_options)
assert_success response
assert_equal 'CAPTUREREQUIRED', response.message
end

def test_successful_authorize_and_capture_cit
@cit_options[:phone] = '998888'
set_credentials!
response = @gateway.authorize(@amount, @credit_card_mit, @cit_options)
assert_success response
assert_equal 'CAPTUREREQUIRED', response.message

assert capture = @gateway.capture(@amount, response.authorization)
assert_success capture
end

def test_failed_purchase
Expand Down Expand Up @@ -139,6 +158,16 @@ def test_successful_refund
assert_equal 'DECLINED', refund.message
end

def test_successful_void
@cit_options[:phone] = '998888'
set_credentials!
response = @gateway.authorize(@amount, @credit_card_mit, @cit_options)
assert_success response

assert void = @gateway.void(@amount, response.authorization)
assert_success void
end

def test_partial_refund
omit('Partial refunds requires to raise some limits on merchant account')
set_credentials!
Expand Down Expand Up @@ -174,9 +203,15 @@ def test_successful_purchase_with_token
end

def test_successful_inquire_request
@cit_options[:phone] = '998888'
set_credentials!
response = @gateway.inquire('abe573e3-7567-4cc6-a7a4-02766dbd881a', {})

response = @gateway.authorize(@amount, @credit_card_mit, @cit_options)
assert_success response

response = @gateway.inquire(response.authorization, {})
assert_success response
assert_equal 'CAPTUREREQUIRED', response.message
end

def test_unsuccessful_inquire_request
Expand Down
58 changes: 53 additions & 5 deletions test/unit/gateways/flex_charge_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,23 +113,33 @@ def test_successful_purchase
assert_equal request['transaction']['avsResultCode'], @options[:avs_result_code]
assert_equal request['transaction']['cvvResultCode'], @options[:cvv_result_code]
assert_equal request['transaction']['cavvResultCode'], @options[:cavv_result_code]
assert_equal request['transaction']['transactionType'], 'Purchase'
assert_equal request['payer']['email'], @options[:email]
assert_equal request['description'], @options[:description]
end
end.respond_with(successful_access_token_response, successful_purchase_response)

assert_success response

assert_equal 'ca7bb327-a750-412d-a9c3-050d72b3f0c5', response.authorization
assert_equal 'ca7bb327-a750-412d-a9c3-050d72b3f0c5#USD', response.authorization
assert response.test?
end

def test_successful_authorization
stub_comms(@gateway, :ssl_request) do
@gateway.purchase(@amount, @credit_card, @options)
end.check_request do |_method, endpoint, data, _headers|
request = JSON.parse(data)
assert_equal request['transaction']['transactionType'], 'Purchase' if /evaluate/.match?(endpoint)
end.respond_with(successful_access_token_response, successful_purchase_response)
end

def test_successful_purchase_three_ds_global
response = stub_comms(@gateway, :ssl_request) do
@gateway.purchase(@amount, @credit_card, @three_d_secure_options)
end.respond_with(successful_access_token_response, successful_purchase_response)
assert_success response
assert_equal 'ca7bb327-a750-412d-a9c3-050d72b3f0c5', response.authorization
assert_equal 'ca7bb327-a750-412d-a9c3-050d72b3f0c5#USD', response.authorization
assert response.test?
end

Expand Down Expand Up @@ -186,17 +196,25 @@ def test_scrub
end

def test_address_names_from_address
names = @gateway.send(:address_names, @options[:billing_address][:name], @credit_card)
names = @gateway.send(:names_from_address, @options[:billing_address], @credit_card)

assert_equal 'Cure', names.first
assert_equal 'Tester', names.last
end

def test_address_names_from_credit_card
names = @gateway.send(:address_names, 'Doe', @credit_card)
@options.delete(:billing_address)
names = @gateway.send(:names_from_address, {}, @credit_card)

assert_equal 'Longbob', names.first
assert_equal 'Doe', names.last
assert_equal 'Longsen', names.last
end

def test_address_names_when_passing_string_token
names = @gateway.send(:names_from_address, @options[:billing_address], SecureRandom.uuid)

assert_equal 'Cure', names.first
assert_equal 'Tester', names.last
end

def test_successful_store
Expand All @@ -218,6 +236,36 @@ def test_successful_inquire_request
end.respond_with(successful_access_token_response, successful_purchase_response)
end

def test_address_when_billing_address_provided
address = @gateway.send(:address, @options)
assert_equal 'CA', address[:country]
end

def test_address_when_address_is_provided_in_options
@options.delete(:billing_address)
@options[:address] = { country: 'US' }
address = @gateway.send(:address, @options)
assert_equal 'US', address[:country]
end

def test_authorization_from_on_store
response = stub_comms(@gateway, :ssl_request) do
@gateway.store(@credit_card, @options)
end.respond_with(successful_access_token_response, successful_store_response)

assert_success response
assert_equal 'd3e10716-6aac-4eb8-a74d-c1a3027f1d96', response.authorization
end

def test_authorization_from_on_purchase
response = stub_comms(@gateway, :ssl_request) do
@gateway.purchase(@amount, @credit_card, @options)
end.respond_with(successful_access_token_response, successful_purchase_response)

assert_success response
assert_equal 'ca7bb327-a750-412d-a9c3-050d72b3f0c5#USD', response.authorization
end

private

def pre_scrubbed
Expand Down

0 comments on commit db57ae3

Please sign in to comment.