bs2_api 1.2.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36180931b2458b20647c4502b35a3cc31bc6ab1b683e1162119bec53fb5e5010
4
- data.tar.gz: 5d6d7af417105158da1ce1f1d90fb0980c32974ca9d1057b77323d570bf0e09c
3
+ metadata.gz: 7ba55ce23aae59313a798b0b5d983e29b7a5ef279810d795b513b99888196811
4
+ data.tar.gz: c61832cf0382e1d9b1be22615963303d6f3c4e8bfdf2fe217cd2292e4082a5a3
5
5
  SHA512:
6
- metadata.gz: 73090ceb182ed5569c12937540174a3bcd41e480bc92ffe4e88d0cc00f85d2796d76857d27b5099c3e5e54f7029a11d06433f6c57492a2fd042bc5eec118b439
7
- data.tar.gz: d8996fb89daba65b337dfca59cf783c043db7b1c79891f88e9442f262fc9fcefd2eafd5ec5924bbfdb87b52740b8f33af74d45b8432033528d1f21a94644b0c3
6
+ metadata.gz: 9b5a00b54f7c859977f81041da155713e625988f5432c76e6f3d8d6c5f1448594685e6c7933690cabae03c5cec08a35c4f22a07fec4e7908ab5d0226fca9e746
7
+ data.tar.gz: 9c74747a9be8e1f0b064e46de4a8a8345c1f162417e7c155fabeab98f3c00999f0e712bb942c2f4599d0e584c33c3d92e675df0beb71a015d37f6a71b270681e
data/.gitignore CHANGED
@@ -6,10 +6,17 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /vendor/
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
12
13
 
13
14
  *.gem
14
15
 
15
- .env.test
16
+ .env.test
17
+ .env.*.local
18
+
19
+ /.idea/
20
+ *.swp
21
+ *.swo
22
+ .byebug_history
data/Gemfile CHANGED
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source "https://rubygems.org"
3
+ source 'https://rubygems.org'
4
4
 
5
- gemspec
5
+ group :development, :test do
6
+ gem 'factory_bot', '~> 6.1'
7
+ gem 'pry-byebug', '~> 3.7'
8
+ end
9
+
10
+ gemspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bs2_api (1.2.0)
4
+ bs2_api (1.3.2)
5
5
  activesupport
6
6
  builder
7
7
  bundler
@@ -11,7 +11,7 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- activesupport (7.0.0)
14
+ activesupport (7.0.3)
15
15
  concurrent-ruby (~> 1.0, >= 1.0.2)
16
16
  i18n (>= 1.6, < 2)
17
17
  minitest (>= 5.1)
@@ -21,16 +21,18 @@ GEM
21
21
  builder (3.2.4)
22
22
  byebug (11.1.3)
23
23
  coderay (1.1.3)
24
- concurrent-ruby (1.1.9)
24
+ concurrent-ruby (1.1.10)
25
25
  crack (0.4.5)
26
26
  rexml
27
27
  diff-lcs (1.4.4)
28
28
  dotenv (2.7.6)
29
+ factory_bot (6.2.1)
30
+ activesupport (>= 5.0.0)
29
31
  hashdiff (1.0.1)
30
32
  httparty (0.18.1)
31
33
  mime-types (~> 3.0)
32
34
  multi_xml (>= 0.5.2)
33
- i18n (1.8.11)
35
+ i18n (1.10.0)
34
36
  concurrent-ruby (~> 1.0)
35
37
  macaddr (1.7.2)
36
38
  systemu (~> 2.6.5)
@@ -80,7 +82,8 @@ PLATFORMS
80
82
  DEPENDENCIES
81
83
  bs2_api!
82
84
  dotenv (~> 2.7, >= 2.7.6)
83
- pry-byebug (~> 3.9)
85
+ factory_bot (~> 6.1)
86
+ pry-byebug (~> 3.7)
84
87
  rspec (~> 3.10)
85
88
  uuid (~> 2.3, >= 2.3.9)
86
89
  vcr (~> 6.0)
data/README.md CHANGED
@@ -147,6 +147,46 @@ payment.class
147
147
  => Bs2Api::Entities::Payment
148
148
  ```
149
149
 
150
+
151
+ ### Async API
152
+ Add initial asyc BS2 API implementation.
153
+
154
+ The API allows to pass multiple request all at once. In order to do so you must:
155
+ 1. Create `Bs2Api::Payment::Async`
156
+ 2. Create one or more PIX keys
157
+ 3. For each PIX key create `Bs2Api::Entities::AsyncRequest` passing in the PIX key, internal identifier and the value of the transaction
158
+ 4. Add each async request to the async payment via `Bs2Api::Payment::Async#add_request`
159
+ 5. When all requests are added call `Bs2Api::Payment::Async#call`
160
+ 6. In the response you will get list of payments of type `Bs2Api::Entities::AsyncResponse` whose confirmation will be sent via webhook
161
+ 6.1 If even one of the requests has invalid data, the response will be 400 and we won't get anything via webhook
162
+ 7. If the response from 6 was 202 but we don't get a webhook notification we should start polling the response manually. This is done by calling `Bs2Api::Payment::Async::check_payment_status`. It has one parameter the `Bs2Api::Entities::AsyncResponse#request_id`.The result from this will be `Bs2Api::Entities::AsyncStatus`, using it you can check if the payment was rejected or confirmed.
163
+
164
+ ```ruby
165
+ pix_key = Bs2Api::Entities::PixKey.new(
166
+ key: 'joao@gmail.com',
167
+ type: 'EMAIL'
168
+ )
169
+
170
+ async_request = Bs2Api::Entities::AsyncRequest.new(
171
+ pix_key: pix_key,
172
+ value: 10.0,
173
+ identificator: 'payment1'
174
+ )
175
+
176
+ async_payment = Bs2Api::Payment::Async.new
177
+ async_payment.add_request(async_request)
178
+
179
+ response_list = async_payment.call
180
+
181
+ # Wait for webhook if notification does not arrive (for example for the first item)
182
+ response_status = Bs2Api::Payment::Async.check_payment_status(response_list[0].request_id)
183
+
184
+ # Check the status
185
+ if response_status.rejected?
186
+ puts response_status.rejection_description
187
+ end
188
+ ```
189
+
150
190
  ### Classes de erros:
151
191
  ```ruby
152
192
  # Todos erros herdam de:
@@ -166,4 +206,4 @@ Bs2Api::Errors::Unauthorized
166
206
 
167
207
  ### Observações
168
208
  - Método `call` retorna o próprio objeto
169
- - Em caso de retorno diferente de sucesso na comunicação com a API do Bs2, um erro sempre será lançado.
209
+ - Em caso de retorno diferente de sucesso na comunicação com a API do Bs2, um erro sempre será lançado.
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Entities
5
+ # Used by Bs2Api::Payment::Async. This class stores
6
+ # PIX key, user defined identificador for the key and
7
+ # the value to be transfered. Bs2Api::Payment::Async
8
+ # sends a bucket of AsyncRequest and in the response
9
+ # we get a list of passed payments. The identificador
10
+ # can be used to keep track of the transactino in the
11
+ # response.
12
+ class AsyncRequest
13
+ attr_accessor :identificator, :pix_key, :value
14
+
15
+ ALLOWED_PIX_KEY_TYPES = %w[
16
+ CPF
17
+ CNPJ
18
+ EMAIL
19
+ EVP
20
+ ].freeze
21
+
22
+ def initialize(args = {})
23
+ @pix_key = args[:pix_key]
24
+ raise Errors::InvalidPixKey unless ALLOWED_PIX_KEY_TYPES.include? @pix_key.type
25
+
26
+ @identificator = args.fetch(:identificator)
27
+ @value = args[:value]
28
+ end
29
+
30
+ def to_hash
31
+ ActiveSupport::HashWithIndifferentAccess.new(
32
+ {
33
+ identificador: @identificator,
34
+ chave: @pix_key.to_hash,
35
+ valor: @value
36
+ }
37
+ )
38
+ end
39
+
40
+ def to_json(*)
41
+ to_hash.to_json
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Entities
5
+ # After we send async payment request via calling Bs2Api::Payment::Async#call
6
+ # the API is expected to return array of JSON objects describing each payment
7
+ # this class is used to wrap those JSON objects
8
+ # identificator is optional and is for internal purposes it won't be sent via
9
+ # the webhook when the payment confirmation is done.
10
+ # request_id is the id of the payment which the bank will send via webhook
11
+ class AsyncResponse
12
+ attr_accessor :identificator, :pix_key, :value, :error, :request_id
13
+
14
+ class << self
15
+ def from_response(input_hash)
16
+ pix_key = PixKey.new(
17
+ key: input_hash['chave']['valor'],
18
+ type: input_hash['chave']['tipo']
19
+ )
20
+ AsyncResponse.new(
21
+ identificator: input_hash['identificador'],
22
+ pix_key: pix_key,
23
+ value: input_hash['valor'],
24
+ error: input_hash['erros'],
25
+ request_id: input_hash['solicitacaoId']
26
+ )
27
+ end
28
+ end
29
+
30
+ def initialize(args = {})
31
+ @identificator = args.fetch(:identificator, '')
32
+ @pix_key = args[:pix_key]
33
+ @value = args[:value]
34
+ @error = args[:error]
35
+ @request_id = args[:request_id]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Entities
5
+ # Used with Bs2Api::Payment::Async. When we call
6
+ # Bs2Api::Payment::Async#call we expect to get data for each
7
+ # item in the bucket via webhook. If we don't get webhooks in
8
+ # a few seconds (roughly about 10s) we should start polling the
9
+ # status of the payment by calling # Bs2Api::Payment::Async::check_payment_status.
10
+ # The following # class is used to wrap the result of
11
+ # Bs2Api::Payment::Async::check_payment_status
12
+ class AsyncStatus
13
+ attr_accessor :request_id, :payment_id, :end_to_end_id, :status,
14
+ :agency, :number, :pix_key, :value, :free_field,
15
+ :rejection_description, :error_description
16
+
17
+ class << self
18
+ # Parses the has from the response body of
19
+ # Bs2Api::Payment::Async::check_payment_status
20
+ # @param[Hash] The response body as a hash with string keys
21
+ def from_response(hash)
22
+ new(
23
+ request_id: hash['solicitacaoId'],
24
+ payment_id: hash['pagamentoId'],
25
+ end_to_end_id: hash['endToEndId'],
26
+ status: STATUS[hash['status']],
27
+ agency: hash['agencia'],
28
+ number: hash['numero'],
29
+ pix_key: Bs2Api::Entities::PixKey.from_response(hash['chave']),
30
+ value: hash['valor'],
31
+ free_field: hash['campoLivre'],
32
+ rejection_description: hash['rejeitadoDescricao'],
33
+ error_description: hash['erroDescricao']
34
+ )
35
+ end
36
+ end
37
+
38
+ STATUS = {
39
+ 'Realizado' => :awaiting_validation,
40
+ 'Iniciado' => :in_process,
41
+ 'Confirmado' => :confirmed,
42
+ 'Rejeitado' => :rejected,
43
+ 'Erro' => :error
44
+ }.freeze
45
+
46
+ def initialize(args = {})
47
+ @request_id = args[:request_id]
48
+ @payment_id = args[:payment_id]
49
+ @end_to_end_id = args[:end_to_end_id]
50
+ @status = args[:status]
51
+ @agency = args[:agency]
52
+ @number = args[:number]
53
+ @pix_key = args[:pix_key]
54
+ @value = args[:value]
55
+ @free_field = args[:free_field]
56
+ @rejection_description = args[:rejection_description]
57
+ @error_description = args[:error_description]
58
+ end
59
+
60
+ def rejected?
61
+ @status == :rejected
62
+ end
63
+
64
+ def confirmed?
65
+ @status == :confirmed
66
+ end
67
+
68
+ def error?
69
+ @status == :error
70
+ end
71
+ end
72
+ end
73
+ end
@@ -3,19 +3,21 @@
3
3
  module Bs2Api
4
4
  module Entities
5
5
  class Payment
6
- attr_accessor :payment_id, :end_to_end_id, :receiver, :payer
6
+ attr_accessor :payment_id, :end_to_end_id, :receiver, :payer, :status
7
7
 
8
8
  def initialize(args = {})
9
9
  @payment_id = args.fetch(:payment_id, nil)
10
10
  @end_to_end_id = args.fetch(:end_to_end_id, nil)
11
11
  @receiver = args.fetch(:receiver, nil)
12
12
  @payer = args.fetch(:payer, nil)
13
+ @status = args.fetch(:status, nil)
13
14
  end
14
15
 
15
16
  def to_hash
16
17
  hash_data = {
17
18
  "pagamentoId": @payment_id,
18
- "endToEndId": @end_to_end_id
19
+ "endToEndId": @end_to_end_id,
20
+ "status": @status
19
21
  }
20
22
 
21
23
  hash_data.merge!({ "recebedor": @receiver.to_hash } ) if @receiver.present?
@@ -27,10 +29,11 @@ module Bs2Api
27
29
  hash = ActiveSupport::HashWithIndifferentAccess.new(hash_payload)
28
30
 
29
31
  Bs2Api::Entities::Payment.new(
30
- payment_id: hash["cobranca"]["id"] || hash["pagamentoId"],
32
+ payment_id: hash["pagamentoId"] || hash["cobranca"]["id"],
31
33
  end_to_end_id: hash["endToEndId"],
32
34
  receiver: Bs2Api::Entities::Bank.from_response(hash["recebedor"]),
33
- payer: Bs2Api::Entities::Bank.from_response(hash["pagador"])
35
+ payer: Bs2Api::Entities::Bank.from_response(hash["pagador"]),
36
+ status: hash["status"]
34
37
  )
35
38
  end
36
39
  end
@@ -11,7 +11,7 @@ module Bs2Api
11
11
  phone: 'PHONE',
12
12
  email: 'EMAIL',
13
13
  random: 'EVP'
14
- }
14
+ }.freeze
15
15
 
16
16
  def initialize(args = {})
17
17
  @key = args.fetch(:key, nil)
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Payment
5
+ # Class used to store a bucket of requests which will be sent
6
+ # altogether.
7
+ class Async < Base
8
+ attr_reader :requests
9
+
10
+ class << self
11
+ # Check the status of a request manually. Usually we will be notified
12
+ # via webhook, but in case we do not get notification for a specific
13
+ # payment we need to start to poll the BS2 API manually.
14
+ #
15
+ # @param[String] request_id The id (SolicitacaoId) for the payment
16
+ # returned by Bs2Api::Payment::Async#call
17
+ # @param[String] client_id ID of the client issued by BS2 used for
18
+ # authorizatino. Optional, if not passed the default will be used
19
+ # @param[String] client_secret The password for the account with
20
+ # id client_id. Optional, if not passed the default will be used.
21
+ def check_payment_status(
22
+ request_id,
23
+ client_id: Bs2Api.configuration.client_id,
24
+ client_secret: Bs2Api.configuration.client_secret
25
+ )
26
+ url = request_status_url(request_id)
27
+ bearer_token = Bs2Api::Request::Auth.token(
28
+ client_id: client_id,
29
+ client_secret: client_secret
30
+ )
31
+ headers = { 'Authorization': "Bearer #{bearer_token}" }
32
+ response = HTTParty.get(url, headers: headers)
33
+ Bs2Api::Entities::AsyncStatus.from_response response.parsed_response
34
+ end
35
+
36
+ private
37
+
38
+ # The url where one can manually check the status of an async request
39
+ #
40
+ # @param[String] request_id The id (SolicitacaoId) for the payment
41
+ # returned by Bs2Api::Payment::Async#call
42
+ def request_status_url(request_id)
43
+ "#{Bs2Api.endpoint}/pix/direto/forintegration/v1/pagamentos/chave/solicitacoes/#{request_id}"
44
+ end
45
+ end
46
+
47
+ def initialize(
48
+ client_id: Bs2Api.configuration.client_id,
49
+ client_secret: Bs2Api.configuration.client_secret
50
+ )
51
+ @client_id = client_id
52
+ @client_secret = client_secret
53
+ @requests = []
54
+ end
55
+
56
+ # Push request into the request bucker. This will not send the request.
57
+ # When Bs2Api::Payment::Async#call is called all requeststs from the bucket
58
+ # will be sent simultaneously
59
+ #
60
+ # @param[Bs2Api::Entities::AsyncRequest] The async request which is going
61
+ # to be placed in the bucket
62
+ def add_request(request)
63
+ @requests << request
64
+ end
65
+
66
+ # Get the size of the bucket list
67
+ def requests_count
68
+ @requests.length
69
+ end
70
+
71
+ # The response from call differs from Payment::Base#call because
72
+ # for the async method we get 202 and a list of payment transactions IDs
73
+ # Later on we will notified via webhook for all payments transactions
74
+ # which were successful.
75
+ #
76
+ # @important The API call will fail if even one of the bucket items has
77
+ # data (PIX key, value, etc).
78
+ #
79
+ # @return[Hash] List of payments which we should expect to be confirmed via
80
+ # webhook. The response will contain field "SolicitacaoId" genreated by BS2
81
+ # this ID is used to match the payment in the webhook
82
+ def call
83
+ response = post_request
84
+ raise Bs2Api::Errors::BadRequest, ::Util::Response.parse_error(response) unless response.code == 202
85
+
86
+ response.parsed_response.map do |async_response|
87
+ Bs2Api::Entities::AsyncResponse.from_response(async_response)
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ # The url which will be called in order to send the bucket request for processing
94
+ def url
95
+ "#{Bs2Api.endpoint}/pix/direto/forintegration/v1/pagamentos/chave/solicitacoes"
96
+ end
97
+
98
+ def payload
99
+ {
100
+ "solicitacoes": @requests
101
+ }
102
+ end
103
+ end
104
+ end
105
+ end
@@ -16,29 +16,33 @@ module Bs2Api
16
16
  end
17
17
 
18
18
  private
19
- def post_request
20
- HTTParty.post(url, headers: headers, body: payload.to_json)
21
- end
22
-
23
- def headers
24
- {
25
- "Content-Type": "application/json",
26
- "Accept": "application/json",
27
- "Authorization": "Bearer #{bearer_token}"
28
- }
29
- end
30
-
31
- def bearer_token
32
- Bs2Api::Request::Auth.token
33
- end
34
-
35
- def payload
36
- raise NoMethodError, "Missing #{__method__} to #{self.class}"
37
- end
38
-
39
- def url
40
- raise NoMethodError, "Missing #{__method__} to #{self.class}"
41
- end
19
+
20
+ def post_request
21
+ HTTParty.post(url, headers: headers, body: payload.to_json)
22
+ end
23
+
24
+ def headers
25
+ {
26
+ 'Content-Type': 'application/json',
27
+ 'Accept': 'application/json',
28
+ 'Authorization': "Bearer #{bearer_token}"
29
+ }
30
+ end
31
+
32
+ def bearer_token
33
+ Bs2Api::Request::Auth.token(
34
+ client_id: @client_id || Bs2Api.configuration.client_id,
35
+ client_secret: @client_secret || Bs2Api.configuration.client_secret
36
+ )
37
+ end
38
+
39
+ def payload
40
+ raise NoMethodError, "Missing #{__method__} to #{self.class}"
41
+ end
42
+
43
+ def url
44
+ raise NoMethodError, "Missing #{__method__} to #{self.class}"
45
+ end
42
46
  end
43
47
  end
44
48
  end
@@ -3,7 +3,13 @@ module Bs2Api
3
3
  class Detail < Base
4
4
  attr_reader :success
5
5
 
6
- def initialize payment_id
6
+ def initialize(
7
+ payment_id,
8
+ client_id: Bs2Api.configuration.client_id,
9
+ client_secret: Bs2Api.configuration.client_secret
10
+ )
11
+ @client_id = client_id
12
+ @client_secret = client_secret
7
13
  @payment_id = payment_id
8
14
  end
9
15
 
@@ -15,13 +21,21 @@ module Bs2Api
15
21
  end
16
22
 
17
23
  private
18
- def url
19
- "#{Bs2Api.endpoint}/pix/direto/forintegration/v1/pagamentos/#{@payment_id}"
20
- end
21
24
 
22
- def detail_request
23
- HTTParty.get(url, headers: headers)
24
- end
25
+ def url
26
+ "#{Bs2Api.endpoint}/pix/direto/forintegration/v1/pagamentos/#{@payment_id}"
27
+ end
28
+
29
+ def detail_request
30
+ HTTParty.get(url, headers: headers)
31
+ end
32
+
33
+ def bearer_token
34
+ Bs2Api::Request::Auth.token(
35
+ client_id: @client_id,
36
+ client_secret: @client_secret
37
+ )
38
+ end
25
39
  end
26
40
  end
27
41
  end
@@ -0,0 +1,52 @@
1
+ module Bs2Api
2
+ module Pix
3
+ class Detail
4
+ def initialize(
5
+ client_id:,
6
+ client_secret:,
7
+ end_to_end_id:,
8
+ time_range:,
9
+ transaction_id: nil,
10
+ proxy: nil
11
+ )
12
+ @client_id = client_id
13
+ @client_secret = client_secret
14
+ @end_to_end_id = end_to_end_id
15
+ @time_range = time_range
16
+ @transaction_id = transaction_id
17
+ @proxy = proxy
18
+ end
19
+
20
+ def call
21
+ url = "#{Bs2Api.endpoint}/pix/direto/forintegration/v1/recebimentos/#{@end_to_end_id}/recebimento"
22
+
23
+ access_token = Bs2Api::Request::Auth.token(
24
+ client_id: @client_id,
25
+ client_secret: @client_secret
26
+ )
27
+
28
+ response = HTTParty.get(
29
+ url,
30
+ http_proxyaddr: @proxy&.host,
31
+ http_proxyport: @proxy&.port,
32
+ http_proxyuser: @proxy&.user,
33
+ http_proxypass: @proxy&.password,
34
+ headers: {
35
+ 'Content-Type' => 'application/json',
36
+ 'Accept' => 'application/json',
37
+ 'Authorization' => "Bearer #{access_token}",
38
+ },
39
+ query: {
40
+ Inicio: @time_range.begin.iso8601,
41
+ Fim: @time_range.end.iso8601,
42
+ TxId: @transaction_id,
43
+ }
44
+ )
45
+
46
+ raise response.body unless response.success?
47
+
48
+ response.body
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Refund
5
+ module Pix
6
+ class Create
7
+ def initialize(
8
+ client_id:,
9
+ client_secret:,
10
+ end_to_end_id:,
11
+ transaction_id:,
12
+ value:,
13
+ proxy: nil
14
+ )
15
+ @client_id = client_id
16
+ @client_secret = client_secret
17
+ @end_to_end_id = end_to_end_id
18
+ @transaction_id = transaction_id
19
+ @value = value
20
+ @proxy = proxy
21
+ end
22
+
23
+ # https://devs.bs2.com/manual/pix-clientes/#tag/Devolucoes-Cliente/paths/~1pix~1direto~1forintegration~1v1~1pix~1{e2eid}~1devolucao~1{idExterno}/put
24
+ def call
25
+ url = "#{Bs2Api.endpoint}/pix/direto/forintegration/v1/pix/#{@end_to_end_id}/devolucao/#{@transaction_id}"
26
+
27
+ access_token = Bs2Api::Request::Auth.token(
28
+ client_id: @client_id,
29
+ client_secret: @client_secret
30
+ )
31
+
32
+ # TODO: is PUT the right method?
33
+ response = HTTParty.put(
34
+ url,
35
+ http_proxyaddr: @proxy&.host,
36
+ http_proxyport: @proxy&.port,
37
+ http_proxyuser: @proxy&.user,
38
+ http_proxypass: @proxy&.password,
39
+ headers: {
40
+ 'Content-Type' => 'application/json',
41
+ 'Accept' => 'application/json',
42
+ 'Authorization' => "Bearer #{access_token}",
43
+ },
44
+ body: {
45
+ valor: value
46
+ }.to_json
47
+ )
48
+
49
+ raise response.body unless response.success?
50
+
51
+ response.body
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,58 @@
1
+ module Bs2Api
2
+ module Refund
3
+ module Pix
4
+ class Detail
5
+ STATUSES = {
6
+ 'EM_PROCESSAMENTO' => :processing,
7
+ 'DEVOLVIDO' => :refunded,
8
+ 'NAO_REALIZADO' => :not_achieved,
9
+ }.transform_keys(&:downcase).freeze
10
+
11
+ def initialize(
12
+ client_id:,
13
+ client_secret:,
14
+ end_to_end_id:,
15
+ transaction_id:,
16
+ proxy: nil
17
+ )
18
+ @client_id = client_id
19
+ @client_secret = client_secret
20
+ @end_to_end_id = end_to_end_id
21
+ @transaction_id = transaction_id
22
+ @proxy = proxy
23
+ end
24
+
25
+ # https://devs.bs2.com/manual/pix-clientes/#tag/Devolucoes-Cliente/paths/~1pix~1direto~1forintegration~1v1~1pix~1{e2eid}~1devolucao~1{idExterno}/get
26
+ def call
27
+ url = "#{Bs2Api.endpoint}/pix/direto/forintegration/v1/pix/#{@end_to_end_id}/devolucao/#{@transaction_id}"
28
+
29
+ access_token = Bs2Api::Request::Auth.token(
30
+ client_id: @client_id,
31
+ client_secret: @client_secret
32
+ )
33
+
34
+ response = HTTParty.get(
35
+ url,
36
+ http_proxyaddr: @proxy&.host,
37
+ http_proxyport: @proxy&.port,
38
+ http_proxyuser: @proxy&.user,
39
+ http_proxypass: @proxy&.password,
40
+ headers: {
41
+ 'Content-Type' => 'application/json',
42
+ 'Accept' => 'application/json',
43
+ 'Authorization' => "Bearer #{access_token}",
44
+ }
45
+ )
46
+
47
+ raise response.body unless response.success?
48
+
49
+ response.body
50
+ end
51
+
52
+ def status
53
+ @status ||= STATUSES.fetch(call.fetch('status'))
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -3,28 +3,55 @@
3
3
  module Bs2Api
4
4
  module Request
5
5
  class Auth
6
+ # TODO: See which scopes are required.
7
+ SCOPES = [
8
+ 'saldo',
9
+ 'extrato',
10
+ 'pagamento',
11
+ 'transferencia',
12
+ 'cobv.write',
13
+ 'cobv.read',
14
+ 'comprovante',
15
+ 'webhook-mov-conta',
16
+ 'aprovacoes',
17
+ 'pagamento-tributo',
18
+ 'webhook-conclusao-transf',
19
+ 'webhook-conclusao-pag',
20
+ 'cob.write',
21
+ 'cob.read',
22
+ 'pix.write',
23
+ 'pix.read',
24
+ 'dict.write',
25
+ 'dict.read',
26
+ 'webhook.read',
27
+ 'webhook.write',
28
+ ].freeze
29
+
6
30
  class << self
7
- def token
31
+ def token(
32
+ client_id: Bs2Api.configuration.client_id,
33
+ client_secret: Bs2Api.configuration.client_secret
34
+ )
8
35
  Bs2Api.configuration.valid?
9
36
 
10
- response = create_session
37
+ response = create_session(client_id, client_secret)
11
38
 
12
- raise Bs2Api::Errors::Unauthorized, response["error_description"] if response.unauthorized?
13
- raise Bs2Api::Errors::BadRequest, response["error_description"] if response.bad_request?
14
- raise Bs2Api::Errors::ServerError, response.body if !response.success?
39
+ raise Bs2Api::Errors::Unauthorized, response['error_description'] if response.unauthorized?
40
+ raise Bs2Api::Errors::BadRequest, response['error_description'] if response.bad_request?
41
+ raise Bs2Api::Errors::ServerError, response.body unless response.success?
15
42
 
16
- response["access_token"]
43
+ response['access_token']
17
44
  end
18
45
 
19
46
  private
20
- def create_session
47
+ def create_session(client_id, client_secret)
21
48
  HTTParty.post(
22
49
  auth_url,
23
50
  headers: headers,
24
51
  body: body,
25
52
  basic_auth: {
26
- username: Bs2Api.configuration.client_id,
27
- password: Bs2Api.configuration.client_secret
53
+ username: client_id,
54
+ password: client_secret
28
55
  }
29
56
  )
30
57
  end
@@ -39,14 +66,14 @@ module Bs2Api
39
66
  def body
40
67
  {
41
68
  grant_type: "client_credentials",
42
- scope: "pix.write pix.read"
69
+ scope: SCOPES.join("\s")
43
70
  }.to_query
44
71
  end
45
72
 
46
73
  def auth_url
47
74
  "#{Bs2Api.endpoint}/auth/oauth/v2/token"
48
75
  end
49
- end
76
+ end
50
77
  end
51
78
  end
52
79
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bs2Api
4
- VERSION = "1.2.0"
4
+ VERSION = '1.4.0'
5
5
  end
data/lib/bs2_api.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
- require "httparty"
3
- require "active_support/core_ext/hash/indifferent_access"
4
- require "active_support/core_ext/hash/except"
5
- require "active_support/core_ext/object/to_query"
6
- require "active_support/core_ext/object/blank"
2
+
3
+ require 'httparty'
4
+ require 'active_support/core_ext/hash/indifferent_access'
5
+ require 'active_support/core_ext/hash/except'
6
+ require 'active_support/core_ext/object/to_query'
7
+ require 'active_support/core_ext/object/blank'
7
8
 
8
9
  require 'bs2_api/version'
9
10
  require 'bs2_api/configuration'
@@ -26,20 +27,28 @@ require 'bs2_api/entities/bank'
26
27
  require 'bs2_api/entities/customer'
27
28
  require 'bs2_api/entities/payment'
28
29
  require 'bs2_api/entities/pix_key'
30
+ require 'bs2_api/entities/async_request'
31
+ require 'bs2_api/entities/async_response'
32
+ require 'bs2_api/entities/async_status'
29
33
 
30
34
  require 'bs2_api/payment/base'
31
35
  require 'bs2_api/payment/key'
32
36
  require 'bs2_api/payment/manual'
33
37
  require 'bs2_api/payment/confirmation'
34
38
  require 'bs2_api/payment/detail'
39
+ require 'bs2_api/payment/async'
35
40
 
36
41
  require 'bs2_api/request/auth'
37
42
 
43
+
44
+ require 'bs2_api/refund/pix/create'
45
+ require 'bs2_api/refund/pix/detail'
46
+
38
47
  module Bs2Api
39
48
  ENDPOINT = {
40
49
  production: 'https://api.bs2.com',
41
50
  sandbox: 'https://apihmz.bancobonsucesso.com.br'
42
- }
51
+ }.freeze
43
52
 
44
53
  class << self
45
54
  attr_writer :configuration
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bs2_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kim Pastro
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-18 00:00:00.000000000 Z
11
+ date: 2022-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: builder
@@ -201,6 +201,9 @@ files:
201
201
  - lib/bs2_api.rb
202
202
  - lib/bs2_api/configuration.rb
203
203
  - lib/bs2_api/entities/account.rb
204
+ - lib/bs2_api/entities/async_request.rb
205
+ - lib/bs2_api/entities/async_response.rb
206
+ - lib/bs2_api/entities/async_status.rb
204
207
  - lib/bs2_api/entities/bank.rb
205
208
  - lib/bs2_api/entities/customer.rb
206
209
  - lib/bs2_api/entities/payment.rb
@@ -215,11 +218,15 @@ files:
215
218
  - lib/bs2_api/errors/missing_configuration.rb
216
219
  - lib/bs2_api/errors/server_error.rb
217
220
  - lib/bs2_api/errors/unauthorized.rb
221
+ - lib/bs2_api/payment/async.rb
218
222
  - lib/bs2_api/payment/base.rb
219
223
  - lib/bs2_api/payment/confirmation.rb
220
224
  - lib/bs2_api/payment/detail.rb
221
225
  - lib/bs2_api/payment/key.rb
222
226
  - lib/bs2_api/payment/manual.rb
227
+ - lib/bs2_api/pix/detail.rb
228
+ - lib/bs2_api/refund/pix/create.rb
229
+ - lib/bs2_api/refund/pix/detail.rb
223
230
  - lib/bs2_api/request/auth.rb
224
231
  - lib/bs2_api/util/response.rb
225
232
  - lib/bs2_api/version.rb
@@ -230,7 +237,7 @@ metadata:
230
237
  homepage_uri: https://github.com/latamgateway/bs2_api
231
238
  source_code_uri: https://github.com/latamgateway/bs2_api
232
239
  changelog_uri: https://github.com/latamgateway/bs2_api/blob/main/CHANGELOG.md
233
- post_install_message:
240
+ post_install_message:
234
241
  rdoc_options: []
235
242
  require_paths:
236
243
  - lib
@@ -249,7 +256,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
249
256
  version: '0'
250
257
  requirements: []
251
258
  rubygems_version: 3.1.4
252
- signing_key:
259
+ signing_key:
253
260
  specification_version: 4
254
261
  summary: Integração com a API do BS2
255
262
  test_files: []