bs2_api 0.1.0 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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