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 +7 -0
- data/README.md +51 -0
- data/lib/perata/client.rb +91 -0
- data/lib/perata/error.rb +43 -0
- data/lib/perata/model.rb +45 -0
- data/lib/perata/models.rb +527 -0
- data/lib/perata/page.rb +34 -0
- data/lib/perata/params.rb +200 -0
- data/lib/perata/services.rb +295 -0
- data/lib/perata/version.rb +15 -0
- data/lib/perata/webhooks.rb +87 -0
- data/lib/perata.rb +37 -0
- metadata +52 -0
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
|
data/lib/perata/error.rb
ADDED
|
@@ -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
|
data/lib/perata/model.rb
ADDED
|
@@ -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
|