sicoob_api 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4d201618226be8e7c5495ff1dba58de27b32c04a1cb32f23fb0f2aa8fc152f53
4
+ data.tar.gz: e6e2c854ff1ec1f2b9cf3a80a0324c71279638dfd251fd4f21742ea3c0320c68
5
+ SHA512:
6
+ metadata.gz: 5ef3778e556f6cd251d7d0dc30a2a4e5f9475b707f264de5d79cd7ac95803fef44bcb72b8d1205d6a630ca8073a5a63a37e2fc33d0a0139cdc8acf15c88cdc9d
7
+ data.tar.gz: ef8e4e299689e1e30d156b5d87d9cd1bb8440fe90cb0124fa9c8762b9ca33f9c2fee3ef2f8087808717d7c4eb39e7dc632e7a18274a029cd4e3fdec3d1afab39
data/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # Sicoob Api
2
+ <!-- Explain what your extension does. -->
3
+
4
+ ## Installation
5
+
6
+ Add sicoob_api to your Gemfile:
7
+
8
+ ```ruby
9
+ gem 'sicoob_api'
10
+ ```
@@ -0,0 +1,106 @@
1
+ module SicoobApi
2
+ class Client < Ac::Base
3
+ BASE_URL = "https://api.sicoob.com.br/pix/api/v2"
4
+ # SAVE_RESPONSES = true
5
+ MAX_RETIES = 2
6
+
7
+ attr_accessor :client_id, :chave_pix, :crt, :key, :token, :token_expires_at, :scopes
8
+
9
+ def initialize(client_id:, chave_pix:, crt:, key:, token: nil, token_expires_at: nil, scopes: "cob.write cob.read cobv.write cobv.read lotecobv.write lotecobv.read pix.write pix.read webhook.read webhook.write payloadlocation.write payloadlocation.read")
10
+ @client_id = client_id
11
+ @chave_pix = chave_pix
12
+ @crt = crt
13
+ @key = key
14
+ @token = token
15
+ @token_expires_at = token_expires_at
16
+ @scopes = scopes
17
+ access_token
18
+ end
19
+
20
+ def access_token
21
+ if token.nil? || token_expires_at.nil? || token_expires_at < Time.now
22
+ response = Typhoeus.post("https://auth.sicoob.com.br/auth/realms/cooperado/protocol/openid-connect/token",
23
+ body: {
24
+ client_id: client_id,
25
+ scope: scopes,
26
+ grant_type: "client_credentials"
27
+ },
28
+ sslcert: crt,
29
+ sslkey: key) { |response| validate_response(response, "access_token") }
30
+ raise "Erro ao obter token: #{response.body}" unless response.success?
31
+ @token = response.json["access_token"]
32
+ @token_expires_at = Time.now + response.json["expires_in"]
33
+ end
34
+ token
35
+ end
36
+
37
+ def create_payment amount:, payer_tax_id: nil, payer_name: nil, expiration: 3600, solicitacao_pagador: nil, info_adicionais: []
38
+ body = {
39
+ calendario: {
40
+ expiracao: expiration
41
+ },
42
+ valor: {
43
+ original: format("%.2f", amount),
44
+ modalidadeAlteracao: 0
45
+ },
46
+ chave: chave_pix
47
+ }
48
+ body[:devedor] = devedor(payer_tax_id, payer_name) if payer_tax_id && payer_name
49
+ body[:solicitacaoPagador] = solicitacao_pagador if solicitacao_pagador
50
+ body[:infoAdicionais] = info_adicionais if info_adicionais.any?
51
+ response = post("cob", sslcert: crt, sslkey: key, body: JSON.dump(body), headers: {"content-type": "application/json"}) { |response| validate_response(response, "txid") }
52
+ SicoobApi::Payment.new(response.json, self)
53
+ end
54
+
55
+ def get_payment txid
56
+ response = get("cob/#{txid}", sslcert: crt, sslkey: key) { |response| validate_response(response, "txid") }
57
+ SicoobApi::Payment.new(response.json, self)
58
+ end
59
+
60
+ def update_payment(payment_id, body)
61
+ response = patch("cob/#{payment_id}", headers: {"content-type": "application/json"}, sslcert: @crt, sslkey: @key, body: JSON.dump(body)) { |response| validate_response(response, "txid") }
62
+ SicoobApi::Payment.new(response.json, self)
63
+ end
64
+
65
+ def refund_payment(end_to_end_id, amount, refund_nature, description)
66
+ id = SecureRandom.hex(10)
67
+ body = {
68
+ valor: format("%.2f", amount),
69
+ natureza: refund_nature,
70
+ descricao: description
71
+ }
72
+ response = put("pix/#{end_to_end_id}/devolucao/#{id}", headers: {"content-type": "application/json"}, sslcert: @crt, sslkey: @key, body: JSON.dump(body)) { |response| validate_response(response, "id") }
73
+ response.json
74
+ end
75
+
76
+ def get_refund(end_to_end_id, id)
77
+ response = get("pix/#{end_to_end_id}/devolucao/#{id}", headers: {"content-type": "application/json"}, sslcert: @crt, sslkey: @key) { |response| validate_response(response, "id") }
78
+ response.json
79
+ end
80
+
81
+ def update_webhook url
82
+ put("webhook/#{chave_pix}", sslcert: crt, sslkey: key, body: JSON.dump({webhookUrl: url})) { |response| validate_response(response, "") }
83
+ end
84
+
85
+ def get_webhook
86
+ get("webhook/#{chave_pix}", sslcert: crt, sslkey: key) { |response| validate_response(response, "webhookUrl") }
87
+ end
88
+
89
+ private
90
+
91
+ def devedor(payer_tax_id, payer_name)
92
+ return {} if payer_tax_id.nil? || payer_name.nil?
93
+ formatted_tax_id = payer_tax_id.gsub(/\D/, "")
94
+ document_type = (formatted_tax_id.length == 11) ? "cpf" : "cnpj"
95
+ {
96
+ document_type => formatted_tax_id,
97
+ "nome" => payer_name
98
+ }
99
+ end
100
+
101
+ def validate_response(response, required_response_key)
102
+ puts response.json unless response.success?
103
+ ![500, 429].include?(response.code) || response.json.dig(*required_response_key)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,87 @@
1
+ module SicoobApi
2
+ class Payment
3
+ attr_reader :response, :txid, :end_to_end_id, :devolucoes, :copia_e_cola, :status, :solicitacao, :info_adicionais, :valor_original, :valor_pago, :criacao, :expiracao, :pago_em, :devedor_nome, :devedor_tax_id, :internal_error
4
+ def initialize(response, client)
5
+ @client = client
6
+ @response = response
7
+ @txid = response.dig("txid")
8
+ @end_to_end_id = response.dig("pix", 0, "endToEndId")
9
+ @devolucoes = response.dig("pix", 0, "devolucoes")
10
+ @copia_e_cola = response.dig("brcode") || response.dig("qrCode")
11
+ @status = response.dig("status")
12
+ @solicitacao = response.dig("solicitacaoPagador")
13
+ @info_adicionais = response.dig("infoAdicionais")
14
+ @valor_original = response.dig("valor", "original")
15
+ @valor_pago = response.dig("pix", 0, "valor")
16
+ @criacao = creation_date
17
+ @expiracao = expiration_date
18
+ @pago_em = paid_date
19
+ @devedor_nome = response.dig("devedor", "nome")
20
+ @devedor_tax_id = response.dig("devedor", "cpf") || response.dig("devedor", "cnpj")
21
+ @internal_error = PaymentError.new(response).internal_error
22
+ end
23
+
24
+ def paid? external_value = nil
25
+ external_value = @valor_original if external_value.nil?
26
+ @status == "CONCLUIDA" && @valor_original.to_d == @valor_pago.to_d && external_value.to_d == @valor_pago.to_d
27
+ end
28
+
29
+ def valid?
30
+ @status == "ATIVA" && @expiracao > Time.now
31
+ end
32
+
33
+ def retrieve
34
+ @client.get_payment(@txid)
35
+ end
36
+
37
+ def reload!
38
+ new_payment = retrieve
39
+ instance_variables.each do |variable|
40
+ instance_variable_set(variable, new_payment.instance_variable_get(variable))
41
+ end
42
+ self
43
+ end
44
+
45
+ def update(body)
46
+ @client.update_payment(@txid, body)
47
+ end
48
+
49
+ def invalidate!
50
+ update({status: "REMOVIDA_PELO_USUARIO_RECEBEDOR"})
51
+ reload!
52
+ end
53
+
54
+ def refund(amount: nil, refund_nature: nil, description: nil)
55
+ amount ||= @valor_pago
56
+ refund_nature ||= "ORIGINAL"
57
+ @client.refund_payment(@end_to_end_id, amount, refund_nature, description)
58
+ end
59
+
60
+ def qr_code(module_size: 4)
61
+ return false unless @copia_e_cola
62
+ qr = RQRCode::QRCode.new(@copia_e_cola, size: 10, level: :l)
63
+ qr.as_svg(module_size: module_size)
64
+ end
65
+
66
+ def puts_qrcode
67
+ puts RQRCode::QRCode.new(@copia_e_cola).to_s(dark: "██", light: " ")
68
+ end
69
+
70
+ private
71
+
72
+ def creation_date
73
+ return nil unless @response.dig("calendario", "criacao")
74
+ Time.new(@response.dig("calendario", "criacao"))
75
+ end
76
+
77
+ def expiration_date
78
+ return nil unless @response.dig("calendario", "criacao")
79
+ Time.new(@response.dig("calendario", "criacao")) + @response.dig("calendario", "expiracao")
80
+ end
81
+
82
+ def paid_date
83
+ return nil unless @response.dig("pix", 0, "horario")
84
+ Time.new(@response.dig("pix", 0, "horario"))
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,25 @@
1
+ module SicoobApi
2
+ class PaymentError
3
+ STATUS_ERROR = [400, 403, 404, 503]
4
+ attr_accessor :json_response
5
+ def initialize json_response
6
+ @json_response = json_response
7
+ end
8
+
9
+ def internal_error
10
+ return nil unless json_response["violacoes"] || STATUS_ERROR.include?(json_response["status"])
11
+ failed_message_error || rejected_message_error || "Pagamento recusado."
12
+ end
13
+
14
+ private
15
+
16
+ def rejected_message_error
17
+ "#{json_response["title"]} => #{json_response["detail"]}"
18
+ end
19
+
20
+ def failed_message_error
21
+ return false unless json_response["violacoes"]
22
+ "#{json_response["violacoes"][0]["razao"]} => #{json_response["violacoes"][0]["propriedade"]}"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SicoobApi
4
+ VERSION = "0.0.1"
5
+ end
data/lib/sicoob_api.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ac"
4
+ require "bigdecimal/util"
5
+ require "securerandom"
6
+ require "rqrcode"
7
+
8
+ require_relative "sicoob_api/version"
9
+ require_relative "sicoob_api/client"
10
+ require_relative "sicoob_api/payment"
11
+ require_relative "sicoob_api/payment_error"
12
+
13
+ module SicoobApi
14
+ Typhoeus::Config.timeout = 10
15
+
16
+ class Error < StandardError; end
17
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sicoob_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - hamilton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-01-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ac
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rqrcode
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bigdecimal
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: securerandom
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: ''
70
+ email: hamilton@todasessascoisas.com.br
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - README.md
76
+ - lib/sicoob_api.rb
77
+ - lib/sicoob_api/client.rb
78
+ - lib/sicoob_api/payment.rb
79
+ - lib/sicoob_api/payment_error.rb
80
+ - lib/sicoob_api/version.rb
81
+ homepage: https://github.com/todasessascoisas/sicoob_api
82
+ licenses: []
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '2.5'
93
+ - - "<"
94
+ - !ruby/object:Gem::Version
95
+ version: '4'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubygems_version: 3.5.10
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: ''
106
+ test_files: []