perata-sdk 0.1.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: 64384c4af37e5af51b1adea727b0475da68c843b5c25aedb6392bd3b3cd4bc8a
4
+ data.tar.gz: b87c39face8ac30151097de8cc07b5be89787420501ea236afc5f4d875f77348
5
+ SHA512:
6
+ metadata.gz: 2cc402f3ac1361b6eaa815bc97615c7d0754486b677fe930623e83a5c7b73f454dd348b6b9f3a942ff235d42e84512580f6ad29fd9af31757cace6f1094fb62c
7
+ data.tar.gz: 497bec18e6a4a0b4d1f2fd1a7aeccb289877a2cd638d8a8297008d07fd86b0d87b276a3e193532a8160a4f774418dd4da51fd4ce497c58ea554673ef04af6385
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Peráta SDK para Ruby
2
+
3
+ SDK oficial da API da Peráta para Ruby 3.1+, sem dependências: só `net/http`, `json` e `openssl`. É gerado a partir do contrato da API (`openapi.json`, na raiz de [perata-sh/sdk](https://github.com/perata-sh/sdk)) por `generator/`. Não edite os arquivos desta pasta: regenere.
4
+
5
+ ```sh
6
+ gem install perata-sdk
7
+ ```
8
+
9
+ ```ruby
10
+ require "perata"
11
+
12
+ perata = Perata::Client.new(ENV.fetch("PERATA_API_KEY")) # prk_test_… ou prk_live_…
13
+
14
+ product = perata.products.create({ name: "Curso" }, idempotency_key: "produto-curso")
15
+
16
+ perata.products.list_iter.each do |product|
17
+ puts product.name
18
+ end
19
+ ```
20
+
21
+ ## Recursos
22
+
23
+ | Serviço | Métodos |
24
+ |---|---|
25
+ | `perata.checkouts` | `create` |
26
+ | `perata.customers` | `create`, `get`, `list`, `list_iter` |
27
+ | `perata.orders` | `list`, `list_iter` |
28
+ | `perata.payment_methods` | `detach`, `list`, `list_iter` |
29
+ | `perata.payments` | `get`, `list`, `list_iter` |
30
+ | `perata.prices` | `create`, `list`, `list_iter` |
31
+ | `perata.products` | `create`, `get`, `list`, `list_iter` |
32
+ | `perata.refunds` | `create` |
33
+ | `perata.subscriptions` | `cancel`, `get`, `list`, `list_iter` |
34
+
35
+ Os modelos são objetos imutáveis com leitores em snake_case (`payment.amount_cents`). O corpo de uma escrita é a classe de parâmetros do método (`Perata::ProductsCreateParams.new(name: "Curso")`) ou um `Hash` com os mesmos nomes; um campo opcional não informado fica fora do JSON. Os métodos `_iter` devolvem um `Enumerator` que percorre todas as páginas a partir de `page:` (ou recebem um bloco).
36
+
37
+ As escritas aceitam `idempotency_key:`, e todas aceitam `api_key:` para usar a chave de outra organização numa chamada. `Perata::Client.new` aceita `base_url:`, `timeout:` (30 segundos) e `transport:`.
38
+
39
+ ## Erros
40
+
41
+ Uma recusa da API levanta `Perata::Error`, com `status`, `code` (`invalid_request`, `not_found`…), `message`, `fields` (o problema de cada campo) e, num 429, `retry_after` em segundos.
42
+
43
+ ## Webhooks
44
+
45
+ ```ruby
46
+ event = Perata.verify_webhook(ENV.fetch("PERATA_WEBHOOK_SECRET"), request.headers, request.raw_post)
47
+ ```
48
+
49
+ A assinatura é a do Standard Webhooks (`webhook-id`, `webhook-timestamp`, `webhook-signature`), com tolerância de 5 minutos. Passe o corpo cru, antes de interpretar; os cabeçalhos podem vir num `Hash` ou no env do Rack. Uma assinatura que não confere levanta `Perata::WebhookVerificationError`.
50
+
51
+ Versão do SDK: 0.1.1 · contrato da API: 0.1.0
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Code generated by sdk/generator. DO NOT EDIT.
4
+
5
+ require "json"
6
+ require "net/http"
7
+ require "uri"
8
+
9
+ module Perata
10
+ # The standard library's HTTP, so the SDK depends on nothing. A transport is
11
+ # anything that answers +call(method, url, headers, body, timeout)+ with
12
+ # +[status, headers in lower case, body]+.
13
+ class NetHTTPTransport
14
+ def call(method, url, headers, body, timeout)
15
+ uri = URI(url)
16
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https",
17
+ open_timeout: timeout, read_timeout: timeout, write_timeout: timeout) do |http|
18
+ response = http.send_request(method, uri.request_uri, body, headers)
19
+ [response.code.to_i, response.each_header.to_h, response.body.to_s]
20
+ end
21
+ end
22
+ end
23
+
24
+ # Peráta's API with one organization's API key.
25
+ #
26
+ # The key says the organization and the environment: +prk_test_…+ is the
27
+ # test environment, +prk_live_…+ the live one.
28
+ #
29
+ # perata = Perata::Client.new(ENV.fetch("PERATA_API_KEY"))
30
+ # product = perata.products.create({ name: "Curso" })
31
+ # perata.products.list_iter { |product| puts product.name }
32
+ class Client
33
+ # @return [CheckoutsService]
34
+ attr_reader :checkouts
35
+ # @return [CustomersService]
36
+ attr_reader :customers
37
+ # @return [OrdersService]
38
+ attr_reader :orders
39
+ # @return [PaymentMethodsService]
40
+ attr_reader :payment_methods
41
+ # @return [PaymentsService]
42
+ attr_reader :payments
43
+ # @return [PricesService]
44
+ attr_reader :prices
45
+ # @return [ProductsService]
46
+ attr_reader :products
47
+ # @return [RefundsService]
48
+ attr_reader :refunds
49
+ # @return [SubscriptionsService]
50
+ attr_reader :subscriptions
51
+
52
+ def initialize(api_key, base_url: DEFAULT_BASE_URL, timeout: 30, transport: NetHTTPTransport.new)
53
+ @api_key = api_key
54
+ @base_url = base_url.chomp("/")
55
+ @timeout = timeout
56
+ @transport = transport
57
+ @checkouts = CheckoutsService.new(self)
58
+ @customers = CustomersService.new(self)
59
+ @orders = OrdersService.new(self)
60
+ @payment_methods = PaymentMethodsService.new(self)
61
+ @payments = PaymentsService.new(self)
62
+ @prices = PricesService.new(self)
63
+ @products = ProductsService.new(self)
64
+ @refunds = RefundsService.new(self)
65
+ @subscriptions = SubscriptionsService.new(self)
66
+ end
67
+
68
+ # Calls the API and answers what it wrapped in +data+; a refusal raises
69
+ # Perata::Error.
70
+ def request(method, path, query: nil, body: nil, idempotency_key: nil, api_key: nil)
71
+ url = @base_url + path
72
+ values = (query || {}).reject { |_, value| value.nil? || value == "" }
73
+ url += "?#{URI.encode_www_form(values)}" unless values.empty?
74
+ headers = {
75
+ "Authorization" => "Bearer #{api_key.nil? || api_key.empty? ? @api_key : api_key}",
76
+ "Accept" => "application/json",
77
+ "User-Agent" => "perata-ruby/#{VERSION}"
78
+ }
79
+ data = nil
80
+ unless body.nil?
81
+ data = JSON.generate(body)
82
+ headers["Content-Type"] = "application/json"
83
+ end
84
+ headers["Idempotency-Key"] = idempotency_key if idempotency_key && !idempotency_key.empty?
85
+ status, response_headers, raw = @transport.call(method, url, headers, data, @timeout)
86
+ raise Error.from_response(status, response_headers, raw) if status >= 300
87
+
88
+ JSON.parse(raw).fetch("data")
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Perata
6
+ # A request the API refused.
7
+ #
8
+ # +code+ is the API's: invalid_credential, forbidden, not_found,
9
+ # invalid_request, conflict, idempotency_conflict, body_too_large or
10
+ # unavailable. +message+ is the API's explanation, +fields+ says, for
11
+ # invalid_request, what is wrong with each field, and +retry_after+ is how
12
+ # many seconds to wait on a 429.
13
+ class Error < StandardError
14
+ attr_reader :status, :code, :message, :fields, :retry_after
15
+
16
+ def initialize(status, code, message = "", fields = {}, retry_after = nil)
17
+ @status = status
18
+ @code = code
19
+ @message = message
20
+ @fields = fields
21
+ @retry_after = retry_after
22
+ super(message.empty? ? "perata: #{status} #{code}" : "perata: #{status} #{code}: #{message}")
23
+ end
24
+
25
+ # What Ruby prints for an error nobody rescued: the status and code too.
26
+ def detailed_message(highlight: false, **)
27
+ highlight ? "\e[1m#{self} (#{self.class.name})\e[m" : "#{self} (#{self.class.name})"
28
+ end
29
+
30
+ # The error a response with a status of 300 or more is.
31
+ def self.from_response(status, headers, body)
32
+ data = begin
33
+ loaded = JSON.parse(body.to_s.empty? ? "{}" : body)
34
+ loaded.is_a?(Hash) ? loaded : {}
35
+ rescue JSON::ParserError
36
+ {} # A refusal without a JSON body is still a refusal.
37
+ end
38
+ retry_after = headers["retry-after"].to_s
39
+ new(status, data["error"].to_s, data["message"].to_s, data["fields"] || {},
40
+ retry_after.match?(/\A\d+\z/) ? retry_after.to_i : nil)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Perata
4
+ # What every resource and every body shares: its members, read as a hash and
5
+ # compared by value.
6
+ module Value
7
+ def to_h
8
+ self.class::MEMBERS.to_h { |name| [name, instance_variable_get(:"@#{name}")] }
9
+ end
10
+
11
+ def ==(other)
12
+ other.class == self.class && other.to_h == to_h
13
+ end
14
+ alias eql? ==
15
+
16
+ def hash
17
+ [self.class, to_h].hash
18
+ end
19
+
20
+ def inspect
21
+ "#<#{self.class.name} #{to_h.map { |name, value| "#{name}=#{value.inspect}" }.join(", ")}>"
22
+ end
23
+ alias to_s inspect
24
+ end
25
+
26
+ # A resource the API answers, built from its JSON and frozen.
27
+ class Model
28
+ include Value
29
+ end
30
+
31
+ # The body of a write: built with keyword arguments, sent with the API's
32
+ # names, an optional field left unset left out.
33
+ class Params
34
+ include Value
35
+
36
+ # The params as they are, or built from a hash of their names.
37
+ def self.coerce(params)
38
+ case params
39
+ when self then params
40
+ when Hash then new(**params.transform_keys(&:to_sym))
41
+ else raise ArgumentError, "perata: #{name} is built from a Hash, not #{params.class}"
42
+ end
43
+ end
44
+ end
45
+ end