From 8498d23264ab5f77dcfa8befe234a272b2249bc0 Mon Sep 17 00:00:00 2001 From: Luis Date: Fri, 10 Feb 2023 15:27:32 -0500 Subject: [PATCH] CybersourceREST - Refund | Credit Description ------------------------- This integration support the following payment operations: - Refund - Credit Unit test ------------------------- Finished in 40.91494 seconds. 5454 tests, 77134 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed Remote test ------------------------- 133.30 tests/s, 1885.23 assertions/s Rubocop ------------------------- 760 files inspected, no offenses detected GWI-471 --- .../billing/gateways/cyber_source_rest.rb | 44 ++++++++++-- .../gateways/remote_cyber_source_rest_test.rb | 71 +++++++++++++++++++ test/unit/gateways/cyber_source_rest_test.rb | 2 +- 3 files changed, 112 insertions(+), 5 deletions(-) diff --git a/lib/active_merchant/billing/gateways/cyber_source_rest.rb b/lib/active_merchant/billing/gateways/cyber_source_rest.rb index a4f3b62747d..bace0b3f1ae 100644 --- a/lib/active_merchant/billing/gateways/cyber_source_rest.rb +++ b/lib/active_merchant/billing/gateways/cyber_source_rest.rb @@ -44,7 +44,17 @@ def authorize(money, payment, options = {}, capture = false) post = build_auth_request(money, payment, options) post[:processingInformation] = { capture: true } if capture - commit('/pts/v2/payments/', post) + commit('payments', post) + end + + def refund(money, authorization, options = {}) + post = build_refund_request(money, options) + commit("payments/#{authorization}/refunds", post) + end + + def credit(money, payment, options = {}) + post = build_credit_request(money, payment, options) + commit('credits', post) end def supports_scrubbing? @@ -73,6 +83,23 @@ def build_auth_request(amount, payment, options) end.compact end + def build_refund_request(amount, options) + { clientReferenceInformation: {}, orderInformation: {} }.tap do |post| + add_code(post, options) + add_amount(post, amount) + end.compact + end + + def build_credit_request(amount, payment, options) + { clientReferenceInformation: {}, paymentInformation: {}, orderInformation: {} }.tap do |post| + add_code(post, options) + add_credit_card(post, payment) + add_amount(post, amount) + add_address(post, payment, options[:billing_address], options, :billTo) + add_merchant_description(post, options) + end.compact + end + def add_code(post, options) return unless options[:order_id].present? @@ -129,8 +156,17 @@ def add_address(post, payment_method, address, options, address_type) }.compact end + def add_merchant_description(post, options) + return unless options[:merchant_descriptor_name] || options[:merchant_descriptor_address1] || options[:merchant_descriptor_locality] + + merchant = post[:merchantInformation][:merchantDescriptor] = {} + merchant[:name] = options[:merchant_descriptor_name] if options[:merchant_descriptor_name] + merchant[:address1] = options[:merchant_descriptor_address1] if options[:merchant_descriptor_address1] + merchant[:locality] = options[:merchant_descriptor_locality] if options[:merchant_descriptor_locality] + end + def url(action) - "#{(test? ? test_url : live_url)}#{action}" + "#{(test? ? test_url : live_url)}/pts/v2/#{action}" end def host @@ -160,7 +196,7 @@ def commit(action, post) end def success_from(response) - response['status'] == 'AUTHORIZED' + %w(AUTHORIZED PENDING).include?(response['status']) end def message_from(response) @@ -183,7 +219,7 @@ def get_http_signature(resource, digest, http_method = 'post', gmtdatetime = Tim string_to_sign = { host: host, date: gmtdatetime, - "(request-target)": "#{http_method} #{resource}", + "(request-target)": "#{http_method} /pts/v2/#{resource}", digest: digest, "v-c-merchant-id": @options[:merchant_id] }.map { |k, v| "#{k}: #{v}" }.join("\n").force_encoding(Encoding::UTF_8) diff --git a/test/remote/gateways/remote_cyber_source_rest_test.rb b/test/remote/gateways/remote_cyber_source_rest_test.rb index 06b688a3a2e..a692461d5fc 100644 --- a/test/remote/gateways/remote_cyber_source_rest_test.rb +++ b/test/remote/gateways/remote_cyber_source_rest_test.rb @@ -70,6 +70,77 @@ def test_successful_purchase assert_nil response.params['_links']['capture'] end + def test_successful_refund + purchase = @gateway.purchase(@amount, @visa_card, @options) + response = @gateway.refund(@amount, purchase.authorization, @options) + + assert_success response + assert response.test? + assert_equal 'PENDING', response.message + assert response.params['id'].present? + assert response.params['_links']['void'].present? + end + + def test_failure_refund + purchase = @gateway.purchase(@amount, @card_without_funds, @options) + response = @gateway.refund(@amount, purchase.authorization, @options) + + assert_failure response + assert response.test? + assert_match %r{Declined - One or more fields in the request contains invalid data}, response.params['message'] + assert_equal 'INVALID_DATA', response.params['reason'] + end + + def test_successful_partial_refund + purchase = @gateway.purchase(@amount, @visa_card, @options) + response = @gateway.refund(@amount / 2, purchase.authorization, @options) + + assert_success response + assert response.test? + assert_equal 'PENDING', response.message + assert response.params['id'].present? + assert response.params['_links']['void'].present? + end + + def test_successful_repeat_refund_transaction + purchase = @gateway.purchase(@amount, @visa_card, @options) + response1 = @gateway.refund(@amount, purchase.authorization, @options) + + assert_success response1 + assert response1.test? + assert_equal 'PENDING', response1.message + assert response1.params['id'].present? + assert response1.params['_links']['void'] + + response2 = @gateway.refund(@amount, purchase.authorization, @options) + assert_success response2 + assert response2.test? + assert_equal 'PENDING', response2.message + assert response2.params['id'].present? + assert response2.params['_links']['void'] + + assert_not_equal response1.params['_links']['void'], response2.params['_links']['void'] + end + + def test_successful_credit + response = @gateway.credit(@amount, @visa_card, @options) + + assert_success response + assert response.test? + assert_equal 'PENDING', response.message + assert response.params['id'].present? + assert_nil response.params['_links']['capture'] + end + + def test_failure_credit + response = @gateway.credit(@amount, @card_without_funds, @options) + + assert_failure response + assert response.test? + assert_match %r{Decline - Invalid account number}, response.message + assert_equal 'INVALID_ACCOUNT', response.error_code + end + def test_transcript_scrubbing transcript = capture_transcript(@gateway) do @gateway.authorize(@amount, @visa_card, @options) diff --git a/test/unit/gateways/cyber_source_rest_test.rb b/test/unit/gateways/cyber_source_rest_test.rb index 70258beb9d2..326f820883c 100644 --- a/test/unit/gateways/cyber_source_rest_test.rb +++ b/test/unit/gateways/cyber_source_rest_test.rb @@ -149,7 +149,7 @@ def test_add_shipping_address end def test_url_building - assert_equal "#{@gateway.class.test_url}/action", @gateway.send(:url, '/action') + assert_equal "#{@gateway.class.test_url}/pts/v2/action", @gateway.send(:url, 'action') end private