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.
- checksums.yaml +4 -4
- data/.env.example +3 -0
- data/.gitignore +4 -0
- data/CHANGELOG.md +12 -1
- data/Gemfile +1 -4
- data/Gemfile.lock +57 -23
- data/Makefile +2 -0
- data/README.md +122 -15
- data/bin/setup +1 -3
- data/bs2_api.gemspec +18 -8
- data/lib/bs2_api.rb +66 -2
- data/lib/bs2_api/configuration.rb +15 -0
- data/lib/bs2_api/entities/account.rb +64 -0
- data/lib/bs2_api/entities/bank.rb +40 -0
- data/lib/bs2_api/entities/customer.rb +51 -0
- data/lib/bs2_api/entities/payment.rb +38 -0
- data/lib/bs2_api/entities/pix_key.rb +40 -0
- data/lib/bs2_api/errors/bad_request.rb +7 -0
- data/lib/bs2_api/errors/base.rb +7 -0
- data/lib/bs2_api/errors/confirmation_error.rb +7 -0
- data/lib/bs2_api/errors/invalid_bank.rb +7 -0
- data/lib/bs2_api/errors/invalid_customer.rb +7 -0
- data/lib/bs2_api/errors/invalid_pix_key.rb +7 -0
- data/lib/bs2_api/errors/missing_bank.rb +7 -0
- data/lib/bs2_api/errors/missing_configuration.rb +7 -0
- data/lib/bs2_api/errors/server_error.rb +7 -0
- data/lib/bs2_api/errors/unauthorized.rb +7 -0
- data/lib/bs2_api/initializers/hash.rb +9 -0
- data/lib/bs2_api/initializers/object.rb +11 -0
- data/lib/bs2_api/payment/base.rb +59 -0
- data/lib/bs2_api/payment/confirmation.rb +38 -0
- data/lib/bs2_api/payment/key.rb +22 -0
- data/lib/bs2_api/payment/manual.rb +23 -0
- data/lib/bs2_api/request/auth.rb +52 -0
- data/lib/bs2_api/util/bank_service.rb +22 -0
- data/lib/bs2_api/util/banks.yml +393 -0
- data/lib/bs2_api/version.rb +1 -1
- metadata +181 -13
@@ -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,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
|