payload-api 0.0.0 → 0.1.0
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/README.md +77 -5
- data/lib/payload.rb +11 -1
- data/lib/payload/arm/object.rb +85 -0
- data/lib/payload/arm/request.rb +145 -0
- data/lib/payload/exceptions.rb +48 -0
- data/lib/payload/objects.rb +79 -0
- data/lib/payload/utils.rb +38 -0
- data/lib/payload/version.rb +1 -1
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0711e29f8e4a39b865cda04ddc414b3b34c38d8b9f9314f785410fb4f1346c7e
|
4
|
+
data.tar.gz: 04cfdc8927d2c47e16db1ceaf8aff90c54b8e87bb1cb57b6e4edc907755f44b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2f51ded017699686030ca7c8c4ab38ebcdbf3efd11f416a467c2aac5435b68b7935b9802429f2665338e2bc5e886aa0b3399d06a881ac3d89a382200e0a2ce4
|
7
|
+
data.tar.gz: 82c40295cc2a47068ad93cf7dcce41c1bdaef381fde723aefb07da4826d6d52a0284b45f343b9eb6cf07d1762481e0b8bfea380d760eec33bf3a2002a27019cc
|
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# Payload RubyGem
|
2
2
|
|
3
|
-
A RubyGem for
|
3
|
+
A RubyGem for integrating [Payload](https://payload.co).
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
7
|
To install using [Bundler](https://bundler.io):
|
8
8
|
|
9
9
|
```ruby
|
10
|
-
gem '
|
10
|
+
gem 'payload-api', '~> 0.1.0'
|
11
11
|
```
|
12
12
|
|
13
13
|
To install using gem:
|
@@ -16,9 +16,81 @@ To install using gem:
|
|
16
16
|
gem install payload
|
17
17
|
```
|
18
18
|
|
19
|
+
## Get Started
|
19
20
|
|
20
|
-
|
21
|
-
|
21
|
+
Once you've installed the Payload Python library to your environment,
|
22
|
+
import the `payload` module to get started. **Note:** We recommend
|
23
|
+
using the shorthand name of `pl` when importing.
|
24
|
+
|
25
|
+
```python
|
26
|
+
import payload as pl
|
27
|
+
```
|
28
|
+
|
29
|
+
### API Authentication
|
30
|
+
|
31
|
+
To authenticate with the Payload API, you'll need a live or test API key. API
|
32
|
+
keys are accessible from within the Payload dashboard.
|
33
|
+
|
34
|
+
```python
|
35
|
+
import payload as pl
|
36
|
+
pl.api_key = 'secret_key_3bW9JMZtPVDOfFNzwRdfE'
|
37
|
+
```
|
38
|
+
|
39
|
+
### Creating an Object
|
40
|
+
|
41
|
+
Interfacing with the Payload API is done primarily through Payload Objects. Below is an example of
|
42
|
+
creating a customer using the `Payload::Customer` object.
|
43
|
+
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
# Create a Customer
|
47
|
+
customer = Payload::Customer.create(
|
48
|
+
email: 'matt.perez@example.com',
|
49
|
+
full_name: 'Matt Perez'
|
50
|
+
)
|
51
|
+
```
|
52
|
+
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
# Create a Payment
|
56
|
+
payment = Payload::Payment.create(
|
57
|
+
amount: 100.0,
|
58
|
+
payment_method: Payload::Card(
|
59
|
+
card_number: '4242 4242 4242 4242'
|
60
|
+
)
|
61
|
+
)
|
62
|
+
```
|
63
|
+
|
64
|
+
### Accessing Object Attributes
|
65
|
+
|
66
|
+
Object attributes are accessible through both dot and bracket notation.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
customer.name
|
70
|
+
customer['name']
|
71
|
+
```
|
72
|
+
|
73
|
+
### Updating an Object
|
74
|
+
|
75
|
+
Updating an object is a simple call to the `update` object method.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
# Updating a customer's email
|
79
|
+
customer.update( email: 'matt.perez@newwork.com' )
|
80
|
+
```
|
81
|
+
|
82
|
+
### Selecting Objects
|
83
|
+
|
84
|
+
Objects can be selected using any of their attributes.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
# Select a customer by email
|
88
|
+
customers = Payload::Customer.filter_by(
|
89
|
+
email: 'matt.perez@example.com'
|
90
|
+
)
|
91
|
+
```
|
22
92
|
|
23
93
|
## Documentation
|
24
|
-
|
94
|
+
|
95
|
+
To get further information on Payload's RubyGem and API capabilities,
|
96
|
+
visit the unabridged [Payload Documentation](https://docs.payload.co/?ruby).
|
data/lib/payload.rb
CHANGED
@@ -1,6 +1,16 @@
|
|
1
|
-
require "
|
1
|
+
require "payload/version"
|
2
|
+
require "payload/objects"
|
2
3
|
|
3
4
|
module Payload
|
5
|
+
@URL = "https://api.payload.co"
|
6
|
+
@api_url = @URL
|
7
|
+
@api_key = nil
|
8
|
+
|
4
9
|
class << self
|
10
|
+
attr_accessor :api_key, :api_url
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.create(objects)
|
14
|
+
return Payload::ARMRequest.new().create(objects)
|
5
15
|
end
|
6
16
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require "payload/arm/request"
|
2
|
+
|
3
|
+
module Payload
|
4
|
+
class ARMObject
|
5
|
+
@poly = nil
|
6
|
+
@data = nil
|
7
|
+
@@cache = {}
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_reader :spec, :poly, :data
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(data)
|
14
|
+
self.set_data(data)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.new(data)
|
18
|
+
if data.key?('id') and @@cache.key?(data['id'])
|
19
|
+
@@cache[data['id']].set_data(data)
|
20
|
+
return @@cache[data['id']]
|
21
|
+
else
|
22
|
+
inst = super
|
23
|
+
if data.key?('id') and not data['id'].nil? and not data['id'].empty?
|
24
|
+
@@cache[data['id']] = inst
|
25
|
+
end
|
26
|
+
|
27
|
+
return inst
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def data
|
32
|
+
@data
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_data(data)
|
36
|
+
@data = data
|
37
|
+
end
|
38
|
+
|
39
|
+
def method_missing(name, *args)
|
40
|
+
attr = name.to_s
|
41
|
+
if @data.key?(attr)
|
42
|
+
return @data[attr]
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def [](key)
|
49
|
+
return @data[key]
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.select(*args, **data)
|
53
|
+
return Payload::ARMRequest.new(self).select(*args, **data)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.filter_by(*args, **data)
|
57
|
+
return Payload::ARMRequest.new(self).filter_by(*args, **data)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.create(*args, **data)
|
61
|
+
if args.length != 0
|
62
|
+
return Payload::ARMRequest.new(self).create(args[0])
|
63
|
+
else
|
64
|
+
return Payload::ARMRequest.new(self).create(data)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.get(id)
|
69
|
+
return Payload::ARMRequest.new(self).get(id)
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.delete(objects)
|
73
|
+
return Payload::ARMRequest.new(self).delete(objects)
|
74
|
+
end
|
75
|
+
|
76
|
+
def update(**update)
|
77
|
+
return Payload::ARMRequest.new(self.class)._request('Put', id: self.id, json: update)
|
78
|
+
end
|
79
|
+
|
80
|
+
def delete()
|
81
|
+
return Payload::ARMRequest.new(self.class)._request('Delete', id: self.id)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require "payload/exceptions"
|
2
|
+
require "payload/utils"
|
3
|
+
require "net/http"
|
4
|
+
require "uri"
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
module Payload
|
8
|
+
class ARMRequest
|
9
|
+
@cls = nil
|
10
|
+
|
11
|
+
def initialize(cls=nil)
|
12
|
+
@cls = cls
|
13
|
+
@filters = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def select(*args, **data)
|
17
|
+
if @cls.poly
|
18
|
+
data = data.merge(@cls.poly)
|
19
|
+
end
|
20
|
+
|
21
|
+
return self
|
22
|
+
end
|
23
|
+
|
24
|
+
def filter_by(*args, **data)
|
25
|
+
if @cls.poly
|
26
|
+
data = data.merge(@cls.poly)
|
27
|
+
end
|
28
|
+
|
29
|
+
@filters = @filters.merge(data)
|
30
|
+
|
31
|
+
return self
|
32
|
+
end
|
33
|
+
|
34
|
+
def all()
|
35
|
+
return self._request('Get')
|
36
|
+
end
|
37
|
+
|
38
|
+
def get(id)
|
39
|
+
if id.nil? || id.empty?
|
40
|
+
throw 'id cannot be empty'
|
41
|
+
end
|
42
|
+
|
43
|
+
return self._request('Get', id: id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def update(**updates)
|
47
|
+
return self.filter_by(mode: 'query')
|
48
|
+
._request('Put', json: updates)
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete(objects)
|
52
|
+
deletes = objects.map {|o| o.id }.join('|')
|
53
|
+
return self.filter_by(mode: 'query', id: deletes)
|
54
|
+
._request('Delete')
|
55
|
+
end
|
56
|
+
|
57
|
+
def create(data)
|
58
|
+
if data.is_a? Array
|
59
|
+
data = data.map do |obj|
|
60
|
+
if obj.kind_of?(ARMObject)
|
61
|
+
if @cls and not obj.instance_of?(@cls)
|
62
|
+
throw "All objects must be of the same type"
|
63
|
+
end
|
64
|
+
|
65
|
+
@cls = obj.class
|
66
|
+
obj = obj.data
|
67
|
+
end
|
68
|
+
|
69
|
+
if @cls.poly
|
70
|
+
obj = obj.merge(@cls.poly)
|
71
|
+
end
|
72
|
+
|
73
|
+
obj
|
74
|
+
end
|
75
|
+
|
76
|
+
data = { object: 'list', values: data }
|
77
|
+
else
|
78
|
+
if @cls.poly
|
79
|
+
data = data.merge(@cls.poly)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
return self._request('Post', json: data)
|
84
|
+
end
|
85
|
+
|
86
|
+
def _request(method, id: nil, json: nil)
|
87
|
+
if @cls.spec.key?("endpoint")
|
88
|
+
endpoint = @cls.spec["endpoint"]
|
89
|
+
else
|
90
|
+
endpoint = "/"+@cls.spec["object"]+"s"
|
91
|
+
end
|
92
|
+
|
93
|
+
if id
|
94
|
+
endpoint = File.join(endpoint, id)
|
95
|
+
end
|
96
|
+
|
97
|
+
url = URI.join(Payload::api_url, endpoint)
|
98
|
+
url.query = URI.encode_www_form(@filters)
|
99
|
+
|
100
|
+
http = Net::HTTP.new(url.host, url.port)
|
101
|
+
|
102
|
+
if url.port == 443
|
103
|
+
http.use_ssl = true
|
104
|
+
end
|
105
|
+
|
106
|
+
request = Net::HTTP.const_get(method).new(url.request_uri)
|
107
|
+
request.basic_auth(Payload::api_key, '')
|
108
|
+
|
109
|
+
if json
|
110
|
+
request.body = json.to_json
|
111
|
+
request.add_field('Content-Type', 'application/json')
|
112
|
+
end
|
113
|
+
|
114
|
+
response = http.request(request)
|
115
|
+
|
116
|
+
begin
|
117
|
+
data = JSON.parse(response.body)
|
118
|
+
rescue JSON::ParserError
|
119
|
+
if response.code == '500'
|
120
|
+
raise Payload::InternalError.new
|
121
|
+
else
|
122
|
+
raise Place::UnknownResponse.new
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
if response.code == '200'
|
127
|
+
if data['object'] == 'list'
|
128
|
+
return data['values'].map {|obj| Payload::get_cls(obj).new(obj) }
|
129
|
+
else
|
130
|
+
return Payload::get_cls(data).new(data)
|
131
|
+
end
|
132
|
+
else
|
133
|
+
for error in Payload::subclasses(Payload::PayloadError)
|
134
|
+
if error.code != response.code or error.name.split('::')[-1] != data['error_type']
|
135
|
+
next
|
136
|
+
end
|
137
|
+
|
138
|
+
raise error.new(data['description'], data)
|
139
|
+
end
|
140
|
+
|
141
|
+
raise Payload::BadRequest.new(data['description'], data)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Payload
|
2
|
+
class PayloadError < StandardError
|
3
|
+
@code = nil
|
4
|
+
class << self
|
5
|
+
attr_reader :code
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(msg, details = nil)
|
9
|
+
super(msg)
|
10
|
+
@details = details
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class UnknownResponse < PayloadError
|
15
|
+
end
|
16
|
+
|
17
|
+
class BadRequest < PayloadError
|
18
|
+
@code='400'
|
19
|
+
end
|
20
|
+
|
21
|
+
class InvalidAttributes < PayloadError
|
22
|
+
@code='400'
|
23
|
+
end
|
24
|
+
|
25
|
+
class Unauthorized < PayloadError
|
26
|
+
@code='401'
|
27
|
+
end
|
28
|
+
|
29
|
+
class Forbidden < PayloadError
|
30
|
+
@code='403'
|
31
|
+
end
|
32
|
+
|
33
|
+
class NotFound < PayloadError
|
34
|
+
@code='404'
|
35
|
+
end
|
36
|
+
|
37
|
+
class TooManyRequests < PayloadError
|
38
|
+
@code='429'
|
39
|
+
end
|
40
|
+
|
41
|
+
class InternalServerError < PayloadError
|
42
|
+
@code='500'
|
43
|
+
end
|
44
|
+
|
45
|
+
class ServiceUnavailable < PayloadError
|
46
|
+
@code='503'
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "payload/arm/object"
|
2
|
+
|
3
|
+
module Payload
|
4
|
+
class Account < ARMObject
|
5
|
+
@spec = { 'object' => 'account' }
|
6
|
+
end
|
7
|
+
|
8
|
+
class Customer < ARMObject
|
9
|
+
@spec = { 'object' => 'account' }
|
10
|
+
@poly = { 'type' => 'customer' }
|
11
|
+
end
|
12
|
+
|
13
|
+
class ProcessingAccount < ARMObject
|
14
|
+
@spec = { 'object' => 'account' }
|
15
|
+
@poly = { 'type' => 'processing' }
|
16
|
+
end
|
17
|
+
|
18
|
+
class Org < ARMObject
|
19
|
+
@spec = { 'object' => 'org', 'endoint' => '/account/orgs' }
|
20
|
+
end
|
21
|
+
|
22
|
+
class Transaction < ARMObject
|
23
|
+
@spec = { 'object' => 'transaction' }
|
24
|
+
end
|
25
|
+
|
26
|
+
class Payment < ARMObject
|
27
|
+
@spec = { 'object' => 'transaction' }
|
28
|
+
@poly = { 'type' => 'payment' }
|
29
|
+
end
|
30
|
+
|
31
|
+
class Refund < ARMObject
|
32
|
+
@spec = { 'object' => 'transaction' }
|
33
|
+
@poly = { 'type' => 'refund' }
|
34
|
+
end
|
35
|
+
|
36
|
+
class Ledger < ARMObject
|
37
|
+
@spec = { 'object' => 'transaction_ledger' }
|
38
|
+
end
|
39
|
+
|
40
|
+
class PaymentMethod < ARMObject
|
41
|
+
@spec = { 'object' => 'payment_method' }
|
42
|
+
end
|
43
|
+
|
44
|
+
class Card < ARMObject
|
45
|
+
@spec = { 'object' => 'payment_method' }
|
46
|
+
@poly = { 'type' => 'card' }
|
47
|
+
end
|
48
|
+
|
49
|
+
class BankAccount < ARMObject
|
50
|
+
@spec = { 'object' => 'payment_method' }
|
51
|
+
@poly = { 'type' => 'bank_account' }
|
52
|
+
end
|
53
|
+
|
54
|
+
class BillingSchedule < ARMObject
|
55
|
+
@spec = { 'object' => 'billing_schedule' }
|
56
|
+
end
|
57
|
+
|
58
|
+
class BillingCharge < ARMObject
|
59
|
+
@spec = { 'object' => 'billing_charge' }
|
60
|
+
end
|
61
|
+
|
62
|
+
class Invoice < ARMObject
|
63
|
+
@spec = { 'object' => 'invoice' }
|
64
|
+
end
|
65
|
+
|
66
|
+
class LineItem < ARMObject
|
67
|
+
@spec = { 'object' => 'line_item' }
|
68
|
+
end
|
69
|
+
|
70
|
+
class ChargeItem < ARMObject
|
71
|
+
@spec = { 'object' => 'line_item' }
|
72
|
+
@poly = { 'type' => 'charge' }
|
73
|
+
end
|
74
|
+
|
75
|
+
class PaymentItem < ARMObject
|
76
|
+
@spec = { 'object' => 'line_item' }
|
77
|
+
@poly = { 'type' => 'payment' }
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Payload
|
2
|
+
|
3
|
+
def self.subclasses(super_cls)
|
4
|
+
ObjectSpace.each_object(Class).select { |cls| cls < super_cls }
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.get_cls(data)
|
8
|
+
match = nil
|
9
|
+
for cls in subclasses(Payload::ARMObject)
|
10
|
+
if cls.spec['object'] != data['object']
|
11
|
+
next
|
12
|
+
end
|
13
|
+
|
14
|
+
if not cls.poly and not match
|
15
|
+
match = cls
|
16
|
+
|
17
|
+
elsif cls.poly
|
18
|
+
|
19
|
+
invalid = false
|
20
|
+
cls.poly.each do |key, value|
|
21
|
+
if data[key] != value
|
22
|
+
invalid = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if invalid
|
27
|
+
next
|
28
|
+
end
|
29
|
+
|
30
|
+
match = cls
|
31
|
+
break
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
match
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/payload/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: payload-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Payload
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A simple library to interface with the Payload API. See https://docs.payload.co
|
14
14
|
for details.
|
@@ -20,6 +20,11 @@ files:
|
|
20
20
|
- LICENSE
|
21
21
|
- README.md
|
22
22
|
- lib/payload.rb
|
23
|
+
- lib/payload/arm/object.rb
|
24
|
+
- lib/payload/arm/request.rb
|
25
|
+
- lib/payload/exceptions.rb
|
26
|
+
- lib/payload/objects.rb
|
27
|
+
- lib/payload/utils.rb
|
23
28
|
- lib/payload/version.rb
|
24
29
|
- payload.gemspec
|
25
30
|
homepage: https://docs.payload.co
|
@@ -41,8 +46,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
41
46
|
- !ruby/object:Gem::Version
|
42
47
|
version: '0'
|
43
48
|
requirements: []
|
44
|
-
|
45
|
-
rubygems_version: 2.7.7
|
49
|
+
rubygems_version: 3.0.1
|
46
50
|
signing_key:
|
47
51
|
specification_version: 4
|
48
52
|
summary: Payload ruby library
|