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 +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/.github/workflows/release.yml +1 -1
- data/.pre-commit-config.yaml +2 -2
- data/CHANGELOG.md +28 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +26 -18
- data/examples/order/create_checkout_pro.rb +120 -0
- data/lib/mercadopago/config/config.rb +1 -1
- data/lib/mercadopago/config/request_options.rb +11 -3
- data/lib/mercadopago/resources/order.rb +34 -0
- data/lib/mercadopago/webhook/validator.rb +1 -1
- data/mercadopago.gemspec +1 -1
- data/tests/test_order.rb +209 -0
- data/tests/test_webhook_signature_validator.rb +4 -3
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d3eb3c15c909d8872d5810ac30966353a794f2c70bfcd983824898a93e2adf96
|
|
4
|
+
data.tar.gz: cd2bbac8379dcedaae481d4f4715130e21f88f819dc34a73779c238575e98f3b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ec0c57a6703933698e7fb99643274658a871ac3bb3738bcefa756ffde307ac9037d54ddd36f585c5a8b60f90f9bb7c433b7b97047a0f4f5ded2bf3aafbf9a94
|
|
7
|
+
data.tar.gz: 3379ec1633e23f25eae001deea156c96010e345298b0f8c58113d30916d644d0a263078261ad6bc9d9aff3d65ba2a488bc723d906db7a8196d0aca514d6f0d02
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -13,13 +13,13 @@ jobs:
|
|
|
13
13
|
|
|
14
14
|
steps:
|
|
15
15
|
- name: Checkout code
|
|
16
|
-
uses: actions/checkout@
|
|
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@
|
|
22
|
+
uses: actions/cache@v6
|
|
23
23
|
with:
|
|
24
24
|
path: vendor/bundle
|
|
25
25
|
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
data/.pre-commit-config.yaml
CHANGED
|
@@ -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
data/Gemfile.lock
CHANGED
|
@@ -1,51 +1,59 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
mercadopago-sdk (3.
|
|
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.
|
|
11
|
+
ast (2.4.3)
|
|
12
12
|
coderay (1.1.3)
|
|
13
|
-
faraday (2.14.
|
|
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.
|
|
17
|
+
faraday-net_http (3.4.4)
|
|
18
18
|
net-http (~> 0.5)
|
|
19
|
-
|
|
20
|
-
|
|
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.
|
|
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.
|
|
27
|
-
parser (3.3.
|
|
28
|
+
parallel (2.1.0)
|
|
29
|
+
parser (3.3.11.1)
|
|
28
30
|
ast (~> 2.4.1)
|
|
29
31
|
racc
|
|
30
|
-
|
|
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.
|
|
37
|
-
|
|
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 (
|
|
40
|
-
|
|
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.
|
|
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.
|
|
48
|
-
parser (>= 3.3.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
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
|
|
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
|
|
44
|
-
|
|
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.
|
|
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
|