mercadopago-sdk 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3f9c30f87f2a16606d96b4b955e9c84b2297608fa13ae849c7c1fa0c5e81e8e
4
- data.tar.gz: a85e57aab4d0cb2a0916e272b3cba305d0c43bc8b71ac395c5f20436e19b5ba0
3
+ metadata.gz: d3eb3c15c909d8872d5810ac30966353a794f2c70bfcd983824898a93e2adf96
4
+ data.tar.gz: cd2bbac8379dcedaae481d4f4715130e21f88f819dc34a73779c238575e98f3b
5
5
  SHA512:
6
- metadata.gz: eb1e14c6daa97a705470095aad0577f4b16691b62e88afde38337a65cb459adc3875611c9c245bab111ed67dece2c52552bf28bddb2f3d190974b562e0197100
7
- data.tar.gz: 63222ba960570cec0eeeae9a267d4d549ef5862bd9da27918c62acee2aa6689d93e21fb5f91c5ffe0be374d28fcad6bb429bafb34011376cfb18d85c53234731
6
+ metadata.gz: 0ec0c57a6703933698e7fb99643274658a871ac3bb3738bcefa756ffde307ac9037d54ddd36f585c5a8b60f90f9bb7c433b7b97047a0f4f5ded2bf3aafbf9a94
7
+ data.tar.gz: 3379ec1633e23f25eae001deea156c96010e345298b0f8c58113d30916d644d0a263078261ad6bc9d9aff3d65ba2a488bc723d906db7a8196d0aca514d6f0d02
@@ -13,13 +13,13 @@ jobs:
13
13
 
14
14
  steps:
15
15
  - name: Checkout code
16
- uses: actions/checkout@v6
16
+ uses: actions/checkout@v7
17
17
  with:
18
18
  ref: ${{ github.event.pull_request.head.ref }}
19
19
  repository: ${{ github.event.pull_request.head.repo.full_name }}
20
20
 
21
21
  - name: Cache gems
22
- uses: actions/cache@v4
22
+ uses: actions/cache@v6
23
23
  with:
24
24
  path: vendor/bundle
25
25
  key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
@@ -14,7 +14,7 @@ jobs:
14
14
 
15
15
  steps:
16
16
  - name: Checkout code
17
- uses: actions/checkout@v6
17
+ uses: actions/checkout@v7
18
18
 
19
19
  - name: Install dependencies
20
20
  run: bundle install
@@ -4,7 +4,7 @@ repos:
4
4
  rev: v2.0.0
5
5
  hooks:
6
6
  - id: pre_commit_hook
7
- stages: [commit]
7
+ stages: [pre-commit]
8
8
  - id: post_commit_hook
9
9
  stages: [post-commit]
10
10
 
@@ -13,7 +13,7 @@ repos:
13
13
  rev: 1.2.2
14
14
  hooks:
15
15
  - id: pre_commit_hook
16
- stages: [commit]
16
+ stages: [pre-commit]
17
17
  verbose: true
18
18
  - id: post_commit_hook
19
19
  stages: [post-commit]
data/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
5
5
  This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6
6
  and [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.2.0] - 2026-06-30
9
+
10
+ ### Added
11
+
12
+ - **Orders**: helper for creating Checkout Pro orders
13
+ (`sdk.order.create_checkout_pro`). The helper defaults the required
14
+ Checkout Pro fields when omitted, validates conflicting values, and
15
+ returns the created order response with the `checkout_url`.
16
+
17
+ ### Fixed
18
+
19
+ - **Request options**: custom headers now replace generated headers
20
+ case-insensitively, avoiding duplicate idempotency headers.
21
+
22
+ ## [3.2.0-beta.1] - 2026-06-24
23
+
24
+ ### Added
25
+
26
+ - **Orders**: helper for creating Checkout Pro orders
27
+ (`sdk.order.create_checkout_pro`). The helper defaults the required
28
+ Checkout Pro fields when omitted, validates conflicting values, and
29
+ returns the created order response with the `checkout_url`.
30
+
31
+ ### Fixed
32
+
33
+ - **Request options**: custom headers now replace generated headers
34
+ case-insensitively, avoiding duplicate idempotency headers.
35
+
8
36
  ## [3.1.0] - 2026-05-27
9
37
 
10
38
  ### Added
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ source 'https://rubygems.org'
5
5
  group :development, :test do
6
6
  gem 'rake'
7
7
  gem 'minitest', '~> 5.27'
8
- gem 'rubocop', '~> 1.70', require: false
8
+ gem 'rubocop', '~> 1.88', require: false
9
9
  end
10
10
 
11
11
  gemspec
data/Gemfile.lock CHANGED
@@ -1,51 +1,59 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mercadopago-sdk (3.1.0)
4
+ mercadopago-sdk (3.2.0)
5
5
  faraday (~> 2.0)
6
6
  json (~> 2.5)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- ast (2.4.2)
11
+ ast (2.4.3)
12
12
  coderay (1.1.3)
13
- faraday (2.14.2)
13
+ faraday (2.14.3)
14
14
  faraday-net_http (>= 2.0, < 3.5)
15
15
  json
16
16
  logger
17
- faraday-net_http (3.4.3)
17
+ faraday-net_http (3.4.4)
18
18
  net-http (~> 0.5)
19
- json (2.19.5)
20
- language_server-protocol (3.17.0.3)
19
+ io-console (0.8.2)
20
+ json (2.20.0)
21
+ language_server-protocol (3.17.0.5)
22
+ lint_roller (1.1.0)
21
23
  logger (1.7.0)
22
- method_source (1.0.0)
24
+ method_source (1.1.0)
23
25
  minitest (5.27.0)
24
26
  net-http (0.9.1)
25
27
  uri (>= 0.11.1)
26
- parallel (1.26.3)
27
- parser (3.3.6.0)
28
+ parallel (2.1.0)
29
+ parser (3.3.11.1)
28
30
  ast (~> 2.4.1)
29
31
  racc
30
- pry (0.14.1)
32
+ prism (1.9.0)
33
+ pry (0.16.0)
31
34
  coderay (~> 1.1)
32
35
  method_source (~> 1.0)
36
+ reline (>= 0.6.0)
33
37
  racc (1.8.1)
34
38
  rainbow (3.1.1)
35
39
  rake (13.4.2)
36
- regexp_parser (2.10.0)
37
- rubocop (1.70.0)
40
+ regexp_parser (2.12.0)
41
+ reline (0.6.3)
42
+ io-console (~> 0.5)
43
+ rubocop (1.88.0)
38
44
  json (~> 2.3)
39
- language_server-protocol (>= 3.17.0)
40
- parallel (~> 1.10)
45
+ language_server-protocol (~> 3.17.0.2)
46
+ lint_roller (~> 1.1.0)
47
+ parallel (>= 1.10)
41
48
  parser (>= 3.3.0.2)
42
49
  rainbow (>= 2.2.2, < 4.0)
43
50
  regexp_parser (>= 2.9.3, < 3.0)
44
- rubocop-ast (>= 1.36.2, < 2.0)
51
+ rubocop-ast (>= 1.49.0, < 2.0)
45
52
  ruby-progressbar (~> 1.7)
46
53
  unicode-display_width (>= 2.4.0, < 4.0)
47
- rubocop-ast (1.37.0)
48
- parser (>= 3.3.1.0)
54
+ rubocop-ast (1.49.1)
55
+ parser (>= 3.3.7.2)
56
+ prism (~> 1.7)
49
57
  ruby-progressbar (1.13.0)
50
58
  unicode-display_width (3.2.0)
51
59
  unicode-emoji (~> 4.1)
@@ -60,7 +68,7 @@ DEPENDENCIES
60
68
  minitest (~> 5.27)
61
69
  pry (~> 0.14)
62
70
  rake
63
- rubocop (~> 1.70)
71
+ rubocop (~> 1.88)
64
72
 
65
73
  BUNDLED WITH
66
74
  2.5.23
@@ -0,0 +1,120 @@
1
+ require_relative '../../lib/mercadopago'
2
+
3
+
4
+ sdk = Mercadopago::SDK.new('<ACCESS_TOKEN>')
5
+
6
+ def create_checkout_pro_order(sdk)
7
+ request = {
8
+ total_amount: '500.00',
9
+ external_reference: 'ext_ref_checkout_pro',
10
+ capture_mode: 'automatic',
11
+ marketplace_fee: '5.00',
12
+ description: 'Travel package SAO-RIO with insurance',
13
+ expiration_time: 'P1D',
14
+ payer: {
15
+ email: '<PAYER_EMAIL>',
16
+ first_name: 'John',
17
+ last_name: 'Smith',
18
+ phone: {
19
+ area_code: '11',
20
+ number: '999998888'
21
+ },
22
+ identification: {
23
+ type: 'CPF',
24
+ number: '12345678909'
25
+ }
26
+ },
27
+ shipment: {
28
+ mode: 'custom',
29
+ local_pickup: false,
30
+ cost: '15.00',
31
+ free_shipping: false,
32
+ address: {
33
+ zip_code: '01310-100',
34
+ street_name: 'Av. Paulista',
35
+ street_number: '1000',
36
+ neighborhood: 'Bela Vista',
37
+ city: 'Sao Paulo'
38
+ }
39
+ },
40
+ config: {
41
+ statement_descriptor: 'MYSTORE',
42
+ default_payment_due_date: 'P1D',
43
+ online: {
44
+ available_from: '2026-01-01T00:00:00Z',
45
+ allowed_user_type: 'account_only',
46
+ success_url: 'https://example.com/success',
47
+ failure_url: 'https://example.com/failure',
48
+ pending_url: 'https://example.com/pending',
49
+ auto_return: 'approved',
50
+ tracks: [
51
+ {
52
+ type: 'google_ad',
53
+ values: {
54
+ conversion_id: '21312312312123',
55
+ conversion_label: 'TEST'
56
+ }
57
+ },
58
+ {
59
+ type: 'facebook_ad',
60
+ values: {
61
+ pixel_id: '21312312312123'
62
+ }
63
+ }
64
+ ]
65
+ },
66
+ payment_method: {
67
+ max_installments: 12,
68
+ not_allowed_ids: ['amex'],
69
+ not_allowed_types: ['ticket'],
70
+ installments: {
71
+ interest_free: {
72
+ type: 'range',
73
+ values: [2, 6]
74
+ }
75
+ }
76
+ }
77
+ },
78
+ items: [
79
+ {
80
+ external_code: 'ITEM-001',
81
+ title: 'Flight SAO-RIO',
82
+ description: 'Round trip, economy class',
83
+ category_id: 'travels',
84
+ picture_url: 'https://example.com/img.jpg',
85
+ quantity: 1,
86
+ unit_price: '450.00',
87
+ type: 'travel',
88
+ event_date: '2027-01-15T00:00:00.000-03:00'
89
+ },
90
+ {
91
+ external_code: 'ITEM-002',
92
+ title: 'Travel insurance',
93
+ description: 'Basic coverage during trip',
94
+ category_id: 'travels',
95
+ picture_url: 'https://example.com/insurance.jpg',
96
+ quantity: 1,
97
+ unit_price: '50.00',
98
+ type: 'travel',
99
+ event_date: '2027-01-15T00:00:00.000-03:00'
100
+ }
101
+ ]
102
+ }
103
+
104
+ custom_headers = {
105
+ 'X-Idempotency-Key': '<SOME_UNIQUE_VALUE>'
106
+ }
107
+ custom_request_options = Mercadopago::RequestOptions.new(custom_headers: custom_headers)
108
+
109
+ result = sdk.order.create_checkout_pro(request, request_options: custom_request_options)
110
+ order = result[:response]
111
+
112
+ puts "Order id: #{order['id']}"
113
+ puts "Status: #{order['status']}"
114
+ puts "Redirect buyer to Checkout Pro: #{order['checkout_url']}"
115
+
116
+ rescue StandardError => e
117
+ puts e.message
118
+ end
119
+
120
+ create_checkout_pro_order(sdk)
@@ -9,7 +9,7 @@ module Mercadopago
9
9
  # there is no need to instantiate this class directly.
10
10
  class Config
11
11
  # Current SDK version following SemVer.
12
- @@VERSION = '3.1.0'
12
+ @@VERSION = '3.2.0'
13
13
 
14
14
  # User-Agent string sent with every HTTP request for server-side tracking.
15
15
  @@USER_AGENT = "MercadoPago Ruby SDK v#{@@VERSION}"
@@ -77,9 +77,7 @@ module Mercadopago
77
77
  headers['x-integrator-id'] = @integrator_id unless @integrator_id.nil?
78
78
  headers['x-platform-id'] = @platform_id unless @platform_id.nil?
79
79
 
80
- headers.merge!(@custom_headers) unless @custom_headers.nil?
81
-
82
- headers
80
+ merge_custom_headers(headers)
83
81
  end
84
82
 
85
83
  # @param value [String, nil] OAuth access token
@@ -137,5 +135,15 @@ module Mercadopago
137
135
 
138
136
  @max_retries = value
139
137
  end
138
+
139
+ private
140
+
141
+ def merge_custom_headers(headers)
142
+ return headers if @custom_headers.nil?
143
+
144
+ custom_header_names = @custom_headers.keys.map { |key| key.to_s.downcase }
145
+ headers.reject! { |key, _value| custom_header_names.include?(key.to_s.downcase) }
146
+ headers.merge!(@custom_headers)
147
+ end
140
148
  end
141
149
  end
@@ -69,6 +69,30 @@ module Mercadopago
69
69
  _post(uri: '/v1/orders', data: order_data, request_options: request_options)
70
70
  end
71
71
 
72
+ # Creates a Checkout Pro order.
73
+ #
74
+ # This is a convenience wrapper over +create+ that applies the Checkout Pro
75
+ # Orders API defaults: +type+ +"online"+ and +processing_mode+ +"manual"+
76
+ # when omitted. If those fields are provided, they must already match the
77
+ # Checkout Pro flow.
78
+ #
79
+ # @param order_data [Hash] Checkout Pro order attributes
80
+ # @param request_options [RequestOptions, nil] per-call configuration override
81
+ # @return [Hash{Symbol => Object}] +:status+ and +:response+ with the created order
82
+ # @raise [TypeError] if +order_data+ is not a Hash
83
+ # @raise [ArgumentError] if +type+ or +processing_mode+ are incompatible
84
+ def create_checkout_pro(order_data, request_options: nil)
85
+ raise TypeError, 'Param order_data must be a Hash' unless order_data.is_a?(Hash)
86
+
87
+ validate_checkout_pro_field(order_data, :type, 'online')
88
+ validate_checkout_pro_field(order_data, :processing_mode, 'manual')
89
+ checkout_pro_data = order_data.dup
90
+ checkout_pro_data[:type] = 'online' unless checkout_pro_data.key?(:type) || checkout_pro_data.key?('type')
91
+ checkout_pro_data[:processing_mode] = 'manual' unless checkout_pro_data.key?(:processing_mode) || checkout_pro_data.key?('processing_mode')
92
+
93
+ _post(uri: '/v1/orders', data: checkout_pro_data, request_options: request_options)
94
+ end
95
+
72
96
  # Retrieves a single order by ID.
73
97
  #
74
98
  # @param order_id [String] order ID
@@ -129,5 +153,15 @@ module Mercadopago
129
153
  def search(filters: nil, request_options: nil)
130
154
  _get(uri: '/v1/orders', params: filters, request_options: request_options)
131
155
  end
156
+
157
+ private
158
+
159
+ def validate_checkout_pro_field(order_data, field, expected_value)
160
+ value = order_data[field]
161
+ value = order_data[field.to_s] if value.nil? && order_data.key?(field.to_s)
162
+ return if value.nil? || value == expected_value
163
+
164
+ raise ArgumentError, "Param #{field} must be #{expected_value}"
165
+ end
132
166
  end
133
167
  end
@@ -157,7 +157,7 @@ module Mercadopago
157
157
  # Builds the HMAC manifest, omitting empty pairs per the documented rule.
158
158
  def self.build_manifest(data_id, request_id, timestamp)
159
159
  parts = []
160
- parts << "id:#{data_id.downcase}" unless data_id.nil?
160
+ parts << "id:#{data_id}" unless data_id.nil?
161
161
  parts << "request-id:#{request_id}" unless request_id.nil?
162
162
  parts << "ts:#{timestamp}"
163
163
  "#{parts.join(';')};"
data/mercadopago.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = 'mercadopago-sdk'
5
- gem.version = '3.1.0'
5
+ gem.version = '3.2.0'
6
6
  gem.required_ruby_version = '>= 3.3.0'
7
7
  gem.author = 'Mercado Pago'
8
8
  gem.description = 'Mercado Pago Ruby SDK'
data/tests/test_order.rb CHANGED
@@ -6,6 +6,131 @@ require_relative '../lib/mercadopago'
6
6
  require 'minitest/autorun'
7
7
 
8
8
  class TestOrder < Minitest::Test
9
+ class CaptureHttpClient < Mercadopago::HttpClient
10
+ attr_reader :last_get, :last_post
11
+
12
+ def post(url:, data:, headers:, timeout: nil)
13
+ @last_post = {
14
+ url: url,
15
+ data: data,
16
+ headers: headers,
17
+ timeout: timeout
18
+ }
19
+
20
+ {
21
+ status: 201,
22
+ response: {
23
+ 'id' => 'ORD123',
24
+ 'type' => 'online',
25
+ 'processing_mode' => 'manual',
26
+ 'checkout_url' => 'https://www.mercadopago.com/checkout/v1/redirect?order_id=ORD123'
27
+ }
28
+ }
29
+ end
30
+
31
+ def get(url:, headers:, params: nil, timeout: nil, maxretries: nil)
32
+ @last_get = {
33
+ url: url,
34
+ headers: headers,
35
+ params: params,
36
+ timeout: timeout,
37
+ maxretries: maxretries
38
+ }
39
+
40
+ {
41
+ status: 200,
42
+ response: {
43
+ 'id' => 'ORD123',
44
+ 'checkout_url' => 'https://www.mercadopago.com/checkout/v1/redirect?order_id=ORD123',
45
+ 'client_token' => 'CLIENT_TOKEN'
46
+ }
47
+ }
48
+ end
49
+ end
50
+
51
+ def test_create_checkout_pro_order
52
+ http_client = CaptureHttpClient.new
53
+ sdk = Mercadopago::SDK.new('ACCESS_TOKEN', http_client: http_client)
54
+ request_options = Mercadopago::RequestOptions.new(
55
+ custom_headers: { 'X-Idempotency-Key': 'checkout-pro-key' }
56
+ )
57
+ order_request = create_checkout_pro_order_request
58
+
59
+ result = sdk.order.create_checkout_pro(order_request, request_options: request_options)
60
+ payload = JSON.parse(http_client.last_post[:data])
61
+
62
+ assert_equal 201, result[:status]
63
+ assert_equal 'https://api.mercadopago.com/v1/orders', http_client.last_post[:url]
64
+ assert_equal 'online', payload['type']
65
+ assert_equal 'manual', payload['processing_mode']
66
+ assert_equal '500.00', payload['total_amount']
67
+ assert_equal 'https://example.com/success', payload['config']['online']['success_url']
68
+ assert_equal ['ticket'], payload['config']['payment_method']['not_allowed_types']
69
+ assert_equal 'checkout-pro-key', http_client.last_post[:headers]['X-Idempotency-Key']
70
+ refute http_client.last_post[:headers].key?('x-idempotency-key')
71
+ assert_equal 'https://www.mercadopago.com/checkout/v1/redirect?order_id=ORD123',
72
+ result[:response]['checkout_url']
73
+ end
74
+
75
+ def test_create_checkout_pro_order_rejects_incompatible_fields
76
+ sdk = Mercadopago::SDK.new(ENV['ACCESS_TOKEN'] || 'ACCESS_TOKEN')
77
+
78
+ assert_raises(ArgumentError) do
79
+ sdk.order.create_checkout_pro(create_checkout_pro_order_request.merge(type: 'wrong'))
80
+ end
81
+
82
+ assert_raises(ArgumentError) do
83
+ sdk.order.create_checkout_pro(create_checkout_pro_order_request.merge(processing_mode: 'automatic'))
84
+ end
85
+ end
86
+
87
+ def test_create_checkout_pro_order_requires_hash
88
+ sdk = Mercadopago::SDK.new(ENV['ACCESS_TOKEN'] || 'ACCESS_TOKEN')
89
+
90
+ assert_raises(TypeError) do
91
+ sdk.order.create_checkout_pro('invalid')
92
+ end
93
+ end
94
+
95
+ def test_get_order_preserves_redirect_fields
96
+ http_client = CaptureHttpClient.new
97
+ sdk = Mercadopago::SDK.new('ACCESS_TOKEN', http_client: http_client)
98
+
99
+ result = sdk.order.get('ORD123')
100
+
101
+ assert_equal 200, result[:status]
102
+ assert_equal 'https://api.mercadopago.com/v1/orders/ORD123', http_client.last_get[:url]
103
+ assert_equal 'https://www.mercadopago.com/checkout/v1/redirect?order_id=ORD123',
104
+ result[:response]['checkout_url']
105
+ assert_equal 'CLIENT_TOKEN', result[:response]['client_token']
106
+ end
107
+
108
+ def test_cancel_order_accepts_request_options
109
+ http_client = CaptureHttpClient.new
110
+ sdk = Mercadopago::SDK.new('ACCESS_TOKEN', http_client: http_client)
111
+ request_options = Mercadopago::RequestOptions.new(custom_headers: { 'X-Idempotency-Key': 'cancel-key' })
112
+
113
+ result = sdk.order.cancel('ORD123', request_options: request_options)
114
+
115
+ assert_equal 201, result[:status]
116
+ assert_equal 'https://api.mercadopago.com/v1/orders/ORD123/cancel', http_client.last_post[:url]
117
+ assert_equal 'cancel-key', http_client.last_post[:headers]['X-Idempotency-Key']
118
+ refute http_client.last_post[:headers].key?('x-idempotency-key')
119
+ end
120
+
121
+ def test_refund_order_accepts_request_options
122
+ http_client = CaptureHttpClient.new
123
+ sdk = Mercadopago::SDK.new('ACCESS_TOKEN', http_client: http_client)
124
+ request_options = Mercadopago::RequestOptions.new(custom_headers: { 'X-Idempotency-Key': 'refund-key' })
125
+
126
+ result = sdk.order.refund('ORD123', refund_data: { amount: '25.00' }, request_options: request_options)
127
+
128
+ assert_equal 201, result[:status]
129
+ assert_equal 'https://api.mercadopago.com/v1/orders/ORD123/refund', http_client.last_post[:url]
130
+ assert_equal({ 'amount' => '25.00' }, JSON.parse(http_client.last_post[:data]))
131
+ assert_equal 'refund-key', http_client.last_post[:headers]['X-Idempotency-Key']
132
+ refute http_client.last_post[:headers].key?('x-idempotency-key')
133
+ end
9
134
 
10
135
  def test_create_order
11
136
  sdk = Mercadopago::SDK.new(ENV['ACCESS_TOKEN'])
@@ -251,6 +376,90 @@ class TestOrder < Minitest::Test
251
376
  }
252
377
  end
253
378
 
379
+ def create_checkout_pro_order_request
380
+ {
381
+ total_amount: '500.00',
382
+ external_reference: 'ext_ref_checkout_pro',
383
+ capture_mode: 'automatic',
384
+ marketplace_fee: '5.00',
385
+ description: 'Travel package SAO-RIO with insurance',
386
+ expiration_time: 'P1D',
387
+ payer: {
388
+ email: 'buyer@mercadopago.com',
389
+ first_name: 'John',
390
+ last_name: 'Smith',
391
+ phone: {
392
+ area_code: '11',
393
+ number: '999998888'
394
+ },
395
+ identification: {
396
+ type: 'CPF',
397
+ number: '12345678909'
398
+ }
399
+ },
400
+ shipment: {
401
+ mode: 'custom',
402
+ local_pickup: false,
403
+ cost: '15.00',
404
+ free_shipping: false,
405
+ address: {
406
+ zip_code: '01310-100',
407
+ street_name: 'Av. Paulista',
408
+ street_number: '1000',
409
+ neighborhood: 'Bela Vista',
410
+ city: 'Sao Paulo'
411
+ }
412
+ },
413
+ config: {
414
+ statement_descriptor: 'MYSTORE',
415
+ online: {
416
+ success_url: 'https://example.com/success',
417
+ failure_url: 'https://example.com/failure',
418
+ pending_url: 'https://example.com/pending',
419
+ auto_return: 'approved',
420
+ tracks: [
421
+ {
422
+ type: 'google_ad',
423
+ values: {
424
+ conversion_id: '21312312312123',
425
+ conversion_label: 'TEST'
426
+ }
427
+ }
428
+ ]
429
+ },
430
+ payment_method: {
431
+ max_installments: 12,
432
+ not_allowed_ids: ['amex'],
433
+ not_allowed_types: ['ticket'],
434
+ installments: {
435
+ interest_free: {
436
+ type: 'range',
437
+ values: [2, 6]
438
+ }
439
+ }
440
+ }
441
+ },
442
+ items: [
443
+ {
444
+ external_code: 'ITEM-001',
445
+ title: 'Flight SAO-RIO',
446
+ description: 'Round trip, economy class',
447
+ category_id: 'travels',
448
+ quantity: 1,
449
+ unit_price: '450.00',
450
+ type: 'travel'
451
+ },
452
+ {
453
+ external_code: 'ITEM-002',
454
+ title: 'Travel insurance',
455
+ quantity: 1,
456
+ unit_price: '50.00',
457
+ type: 'travel'
458
+ }
459
+ ]
460
+ }
461
+ end
462
+
254
463
  def create_card_token_request
255
464
  {
256
465
  card_number: '5031433215406351',
@@ -19,7 +19,7 @@ class TestWebhookSignatureValidator < Minitest::Test
19
19
 
20
20
  def compute_hash(data_id, request_id, ts, secret)
21
21
  parts = []
22
- parts << "id:#{data_id.downcase}" if data_id && !data_id.empty?
22
+ parts << "id:#{data_id}" if data_id && !data_id.empty?
23
23
  parts << "request-id:#{request_id}" if request_id && !request_id.empty?
24
24
  parts << "ts:#{ts}"
25
25
  manifest = "#{parts.join(';')};"
@@ -40,8 +40,9 @@ class TestWebhookSignatureValidator < Minitest::Test
40
40
  end
41
41
 
42
42
  # case 2
43
- def test_uppercase_dataid_is_lowercased
44
- Validator.validate(build_header(valid_hash), REQUEST_ID, DATA_ID_RAW, SECRET)
43
+ def test_uppercase_dataid_is_preserved
44
+ upper_hash = compute_hash(DATA_ID_RAW, REQUEST_ID, TS, SECRET)
45
+ Validator.validate(build_header(upper_hash), REQUEST_ID, DATA_ID_RAW, SECRET)
45
46
  end
46
47
 
47
48
  # case 3
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mercadopago-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mercado Pago
@@ -166,6 +166,7 @@ files:
166
166
  - examples/order/cancel.rb
167
167
  - examples/order/capture.rb
168
168
  - examples/order/create.rb
169
+ - examples/order/create_checkout_pro.rb
169
170
  - examples/order/create_pse.rb
170
171
  - examples/order/get.rb
171
172
  - examples/order/process.rb