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.
- 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
|