From cc200e010bc8358747b86b2f62c7a14f6407a32f Mon Sep 17 00:00:00 2001 From: Nhon Dang Date: Thu, 22 Aug 2024 14:43:50 -0700 Subject: [PATCH] Mercado Pago: add idempotency key field --- .../billing/gateways/mercado_pago.rb | 15 ++++++++++++++- .../gateways/remote_mercado_pago_test.rb | 18 ++++++++++++++++++ test/unit/gateways/mercado_pago_test.rb | 17 +++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/lib/active_merchant/billing/gateways/mercado_pago.rb b/lib/active_merchant/billing/gateways/mercado_pago.rb index c4e1e0c5271..f2f30944f1b 100644 --- a/lib/active_merchant/billing/gateways/mercado_pago.rb +++ b/lib/active_merchant/billing/gateways/mercado_pago.rb @@ -43,6 +43,7 @@ def refund(money, authorization, options = {}) post = {} authorization, original_amount = authorization.split('|') post[:amount] = amount(money).to_f if original_amount && original_amount.to_f > amount(money).to_f + add_idempotency_key(post, options) commit('refund', "payments/#{authorization}/refunds", post) end @@ -53,8 +54,10 @@ def void(authorization, options = {}) end def verify(credit_card, options = {}) + post = {} verify_amount = 100 verify_amount = options[:amount].to_i if options[:amount] + add_idempotency_key(post, options) MultiResponse.run(:use_first_response) do |r| r.process { authorize(verify_amount, credit_card, options) } r.process(:ignore_result) { void(r.authorization, options) } @@ -105,6 +108,7 @@ def purchase_request(money, payment, options = {}) add_net_amount(post, options) add_taxes(post, options) add_notification_url(post, options) + add_idempotency_key(post, options) add_3ds(post, options) post[:binary_mode] = options.fetch(:binary_mode, true) unless options[:execute_threed] post @@ -212,6 +216,10 @@ def add_net_amount(post, options) post[:net_amount] = Float(options[:net_amount]) if options[:net_amount] end + def add_idempotency_key(post, options) + post[:idempotency_key] = options[:idempotency_key] if options[:idempotency_key] + end + def add_notification_url(post, options) post[:notification_url] = options[:notification_url] if options[:notification_url] end @@ -301,7 +309,11 @@ def authorization_from(response, params) end def post_data(parameters = {}) - parameters.clone.tap { |p| p.delete(:device_id) }.to_json + params = parameters.clone.tap do |p| + p.delete(:device_id) + p.delete(:idempotency_key) + end + params.to_json end def inquire_path(authorization, options) @@ -340,6 +352,7 @@ def headers(options = {}) 'Content-Type' => 'application/json' } headers['X-meli-session-id'] = options[:device_id] if options[:device_id] + headers['X-Idempotency-Key'] = options[:idempotency_key] if options[:idempotency_key] headers end diff --git a/test/remote/gateways/remote_mercado_pago_test.rb b/test/remote/gateways/remote_mercado_pago_test.rb index da2ce430abd..6e943dd800e 100644 --- a/test/remote/gateways/remote_mercado_pago_test.rb +++ b/test/remote/gateways/remote_mercado_pago_test.rb @@ -125,6 +125,12 @@ def test_successful_purchase_with_notification_url assert_equal 'https://www.spreedly.com/', response.params['notification_url'] end + def test_successful_purchase_with_idempotency_key + response = @gateway.purchase(@amount, @credit_card, @options.merge(idempotency_key: '0d5020ed-1af6-469c-ae06-c3bec19954bb')) + assert_success response + assert_equal 'accredited', response.message + end + def test_successful_purchase_with_payer response = @gateway.purchase(@amount, @credit_card, @options.merge({ payer: @payer })) assert_success response @@ -157,6 +163,12 @@ def test_successful_authorize_and_capture assert_equal 'accredited', capture.message end + def test_successful_authorize_with_idempotency_key + response = @gateway.authorize(@amount, @credit_card, @options.merge(idempotency_key: '0d5020ed-1af6-469c-ae06-c3bec19954bb')) + assert_success response + assert_equal 'accredited', response.message + end + def test_successful_authorize_and_capture_with_elo auth = @gateway.authorize(@amount, @elo_credit_card, @options) assert_success auth @@ -312,6 +324,12 @@ def test_successful_verify assert_match %r{pending_capture}, response.message end + def test_successful_verify_with_idempotency_key + response = @gateway.verify(@credit_card, @options.merge(idempotency_key: '0d5020ed-1af6-469c-ae06-c3bec19954bb')) + assert_success response + assert_match %r{accredited}, response.message + end + def test_successful_verify_with_amount @options[:amount] = 200 response = @gateway.verify(@credit_card, @options) diff --git a/test/unit/gateways/mercado_pago_test.rb b/test/unit/gateways/mercado_pago_test.rb index dfee8726d19..71c1dc9e2ba 100644 --- a/test/unit/gateways/mercado_pago_test.rb +++ b/test/unit/gateways/mercado_pago_test.rb @@ -366,6 +366,23 @@ def test_includes_deviceid_header assert_success response end + def test_includes_idempotency_key_header + @options[:idempotency_key] = '12345' + @gateway.expects(:ssl_post).with(anything, anything, { 'Content-Type' => 'application/json' }).returns(successful_purchase_response) + @gateway.expects(:ssl_post).with(anything, anything, { 'Content-Type' => 'application/json', 'X-Idempotency-Key' => '12345' }).returns(successful_purchase_response) + + response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + end + + def test_includes_idempotency_key_header_for_refund + @options[:idempotency_key] = '12345' + @gateway.expects(:ssl_post).with(anything, anything, { 'Content-Type' => 'application/json', 'X-Idempotency-Key' => '12345' }).returns(successful_refund_response) + + response = @gateway.refund(@amount, 'authorization|1.0', @options) + assert_success response + end + def test_includes_additional_data @options[:additional_info] = { 'foo' => 'bar', 'baz' => 'quux' } response = stub_comms do