bs2_api 0.1.0 → 0.3.2

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.
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Configuration
4
+ attr_accessor :client_id, :client_secret, :pix_key, :env
5
+
6
+ def initialize
7
+ @env = ENV.fetch('BS2_ENVIRONMENT', 'sandbox')
8
+ @client_id = ENV.fetch('BS2_CLIENT_ID', nil)
9
+ @client_secret = ENV.fetch('BS2_CLIENT_SECRET', nil)
10
+ end
11
+
12
+ def valid?
13
+ raise Bs2Api::Errors::MissingConfiguration, 'Missing configuration credentials' if @client_id.blank? || @client_secret.blank?
14
+ end
15
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Entities
5
+ class Account
6
+ attr_accessor :bank_code, :bank_name, :agency, :number, :type
7
+
8
+ TYPES = {
9
+ checking: 'ContaCorrente',
10
+ salary: 'ContaSalario',
11
+ saving: 'Poupanca'
12
+ }
13
+
14
+ def initialize(args = {})
15
+ @bank_code = args.fetch(:bank_code, nil)
16
+ @agency = args.fetch(:agency, nil)
17
+ @number = args.fetch(:number, nil)
18
+ @type = args.fetch(:type, nil)
19
+ @bank_name = get_bank_name
20
+ end
21
+
22
+ def to_hash
23
+ ActiveSupport::HashWithIndifferentAccess.new(
24
+ {
25
+ "banco": @bank_code,
26
+ "bancoNome": get_bank_name,
27
+ "agencia": @agency,
28
+ "numero": @number,
29
+ "tipo": @type
30
+ }
31
+ )
32
+ end
33
+
34
+ def self.from_response(hash_payload)
35
+ hash = ActiveSupport::HashWithIndifferentAccess.new(hash_payload)
36
+
37
+ Bs2Api::Entities::Account.new(
38
+ bank_code: hash["banco"],
39
+ bank_name: hash["bancoNome"],
40
+ agency: hash["agencia"],
41
+ number: hash["numero"],
42
+ type: hash["tipo"]
43
+ )
44
+ end
45
+
46
+ def checking?
47
+ @type == TYPES[:checking]
48
+ end
49
+
50
+ def salary?
51
+ @type == TYPES[:salary]
52
+ end
53
+
54
+ def saving?
55
+ @type == TYPES[:saving]
56
+ end
57
+
58
+ private
59
+ def get_bank_name
60
+ Bs2Api::Util::BankService.find_by_code(@bank_code)["name"]
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Entities
5
+ class Bank
6
+ attr_accessor :ispb, :account, :customer
7
+
8
+ def initialize(args = {})
9
+ @ispb = args.fetch(:ispb, nil)
10
+ @account = args.fetch(:account, nil)
11
+ @customer = args.fetch(:customer, nil)
12
+ end
13
+
14
+ def to_hash
15
+ ActiveSupport::HashWithIndifferentAccess.new(
16
+ {
17
+ "ispb": get_ispb,
18
+ "conta": @account.to_hash,
19
+ "pessoa": @customer.to_hash
20
+ }
21
+ )
22
+ end
23
+
24
+ def self.from_response(hash_payload)
25
+ hash = ActiveSupport::HashWithIndifferentAccess.new(hash_payload)
26
+
27
+ Bs2Api::Entities::Bank.new(
28
+ ispb: hash["ispb"],
29
+ account: Bs2Api::Entities::Account.from_response(hash["conta"]),
30
+ customer: Bs2Api::Entities::Customer.from_response(hash["pessoa"])
31
+ )
32
+ end
33
+
34
+ private
35
+ def get_ispb
36
+ Bs2Api::Util::BankService.find_by_code(@account.bank_code)["ispb"]
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Entities
5
+ class Customer
6
+ attr_accessor :document, :type, :name, :business_name
7
+
8
+ TYPES = {
9
+ personal: 'CPF',
10
+ business: 'CNPJ'
11
+ }
12
+
13
+ def initialize(args = {})
14
+ @document = args.fetch(:document, nil)
15
+ @type = args.fetch(:type, 'CPF')
16
+ @name = args.fetch(:name, nil)
17
+ @business_name = args.fetch(:business_name, nil)
18
+ end
19
+
20
+ def to_hash
21
+ ActiveSupport::HashWithIndifferentAccess.new(
22
+ {
23
+ "documento": @document,
24
+ "tipoDocumento": @type,
25
+ "nome": @name,
26
+ "nomeFantasia": @business_name
27
+ }
28
+ )
29
+ end
30
+
31
+ def self.from_response(hash_payload)
32
+ hash = ActiveSupport::HashWithIndifferentAccess.new(hash_payload)
33
+
34
+ Bs2Api::Entities::Customer.new(
35
+ document: hash["documento"],
36
+ type: hash["tipoDocumento"],
37
+ name: hash["nome"],
38
+ business_name: hash["nomeFantasia"]
39
+ )
40
+ end
41
+
42
+ def personal?
43
+ @type == TYPES[:personal]
44
+ end
45
+
46
+ def business?
47
+ @type == TYPES[:business]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Entities
5
+ class Payment
6
+ attr_accessor :id, :merchant_id, :receiver, :payer
7
+
8
+ def initialize(args = {})
9
+ @id = args.fetch(:id, nil)
10
+ @merchant_id = args.fetch(:merchant_id, nil)
11
+ @receiver = args.fetch(:receiver, nil)
12
+ @payer = args.fetch(:payer, nil)
13
+ end
14
+
15
+ def to_hash
16
+ ActiveSupport::HashWithIndifferentAccess.new(
17
+ {
18
+ "pagamentoId": @id,
19
+ "endToEndId": @merchant_id,
20
+ "recebedor": @receiver.to_hash,
21
+ "pagador": @payer.to_hash
22
+ }
23
+ )
24
+ end
25
+
26
+ def self.from_response(hash_payload)
27
+ hash = ActiveSupport::HashWithIndifferentAccess.new(hash_payload)
28
+
29
+ Bs2Api::Entities::Payment.new(
30
+ id: hash["pagamentoId"],
31
+ merchant_id: hash["endToEndId"],
32
+ receiver: Bs2Api::Entities::Bank.from_response(hash["recebedor"]),
33
+ payer: Bs2Api::Entities::Bank.from_response(hash["pagador"])
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Entities
5
+ class PixKey
6
+ attr_accessor :key, :type
7
+
8
+ TYPES = {
9
+ cpf: 'CPF',
10
+ cnpj: 'CNPJ',
11
+ phone: 'PHONE',
12
+ email: 'EMAIL',
13
+ random: 'EVP'
14
+ }
15
+
16
+ def initialize(args = {})
17
+ @key = args.fetch(:key, nil)
18
+ @type = args.fetch(:type, 'CPF')
19
+ end
20
+
21
+ def to_hash
22
+ ActiveSupport::HashWithIndifferentAccess.new(
23
+ {
24
+ "valor": @key,
25
+ "tipo": @type
26
+ }
27
+ )
28
+ end
29
+
30
+ def self.from_response(hash_payload)
31
+ hash = ActiveSupport::HashWithIndifferentAccess.new(hash_payload)
32
+
33
+ Bs2Api::Entities::PixKey.new(
34
+ key: hash["valor"],
35
+ type: hash["tipo"]
36
+ )
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Errors
5
+ class BadRequest < Base; end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Errors
5
+ class Base < StandardError; end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Errors
5
+ class ConfirmationError < Base; end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Errors
5
+ class InvalidBank < Base; end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Errors
5
+ class InvalidCustomer < Base; end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Errors
5
+ class InvalidPixKey < Base; end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Errors
5
+ class MissingBank < Base; end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Errors
5
+ class MissingConfiguration < Base; end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Errors
5
+ class ServerError < Base; end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bs2Api
4
+ module Errors
5
+ class Unauthorized < Base; end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ def queryfy
5
+ keys.map do |key|
6
+ "#{key}=#{self[key]}"
7
+ end.join("&")
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Object
4
+ def blank?
5
+ respond_to?(:empty?) ? !!empty? : !self
6
+ end
7
+
8
+ def present?
9
+ !blank?
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ module Bs2Api
2
+ module Payment
3
+ class Base
4
+ attr_reader :payment
5
+
6
+ def initialize
7
+ raise NoMethodError, "Missing #{__method__} to #{self.class}"
8
+ end
9
+
10
+ def call
11
+ response = post_request
12
+ raise Bs2Api::Errors::BadRequest, parse_error(response) unless response.created?
13
+
14
+ @payment = Bs2Api::Entities::Payment.from_response(response)
15
+ self
16
+ end
17
+
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 parse_error(response)
36
+ hash = JSON.parse(response.body)
37
+ message = "#{response.code}: "
38
+
39
+ if hash.is_a?(Array)
40
+ message << hash[0]["descricao"]
41
+ elsif hash.key?("error_description")
42
+ message << hash["error_description"]
43
+ else
44
+ message << hash.to_s
45
+ end
46
+
47
+ message
48
+ end
49
+
50
+ def payload
51
+ raise NoMethodError, "Missing #{__method__} to #{self.class}"
52
+ end
53
+
54
+ def url
55
+ raise NoMethodError, "Missing #{__method__} to #{self.class}"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,38 @@
1
+ module Bs2Api
2
+ module Payment
3
+ class Confirmation < Base
4
+ attr_reader :success
5
+
6
+ def initialize payment, value: nil
7
+ raise Bs2Api::Errors::ConfirmationError, 'invalid payment' unless payment.present? && payment.is_a?(Bs2Api::Entities::Payment)
8
+ raise Bs2Api::Errors::ConfirmationError, 'invalid value' unless value.to_f.positive?
9
+ @payment = payment
10
+
11
+ @value = value.to_f
12
+ end
13
+
14
+ def call
15
+ response = post_request
16
+ raise Bs2Api::Errors::ConfirmationError, parse_error(response) unless response.accepted?
17
+
18
+ @success = true
19
+ self
20
+ end
21
+
22
+ private
23
+ def payload
24
+ @payment.to_hash
25
+ .except!("pagamentoId", "endToEndId")
26
+ .merge("valor": @value)
27
+ end
28
+
29
+ def url
30
+ "#{Bs2Api.endpoint}/pix/direto/forintegration/v1/pagamentos/#{@payment.id}/confirmacao"
31
+ end
32
+
33
+ def success?
34
+ !!@success
35
+ end
36
+ end
37
+ end
38
+ end