simplify 1.0.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.
@@ -0,0 +1,105 @@
1
+ #
2
+ # Copyright (c) 2013, MasterCard International Incorporated
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without modification, are
6
+ # permitted provided that the following conditions are met:
7
+ #
8
+ # Redistributions of source code must retain the above copyright notice, this list of
9
+ # conditions and the following disclaimer.
10
+ # Redistributions in binary form must reproduce the above copyright notice, this list of
11
+ # conditions and the following disclaimer in the documentation and/or other materials
12
+ # provided with the distribution.
13
+ # Neither the name of the MasterCard International Incorporated nor the names of its
14
+ # contributors may be used to endorse or promote products derived from this software
15
+ # without specific prior written permission.
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
17
+ # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
19
+ # SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22
+ # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23
+ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
+ # SUCH DAMAGE.
26
+ #
27
+
28
+ require 'simplify/paymentsapi'
29
+
30
+ module Simplify
31
+
32
+ # A Invoice object.
33
+ #
34
+ class Invoice < Hash
35
+
36
+ # Public key used to access the API.
37
+ attr_accessor :public_key
38
+
39
+ # Private key used to access the API.
40
+ attr_accessor :private_key
41
+
42
+
43
+
44
+ # Retrieve Invoice objects.
45
+ # criteria:: a hash of parameters; valid keys are:
46
+ # * <code>filter</code> Filters to apply to the list.
47
+ # * <code>max</code> Allows up to a max of 50 list items to return. <b>default:20</b>
48
+ # * <code>offset</code> Used in paging of the list. This is the start offset of the page. <b>default:0</b>
49
+ # * <code>sorting</code> Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either <code>asc</code> for ascending or <code>desc</code> for descending). Sortable properties are: <code> id</code><code> invoiceDate</code><code> customer</code><code> amount</code><code> processedDate</code>.
50
+ # public_key:: Public to use for the API call. If nil, the value of Simplify::public_key will be used.
51
+ # private_key:: Private key to use for the API call. If nil, the value of Simplify::private_key will be used.
52
+ # Returns an object where the <code>list</code> property contains the list of Invoice objects and the <code>total</code>
53
+ # property contains the total number of Invoice objects available for the given criteria.
54
+ def self.list(criteria = nil, public_key = nil, private_key = nil)
55
+
56
+ if public_key == nil then
57
+ public_key = Simplify::public_key
58
+ end
59
+ if private_key == nil then
60
+ private_key = Simplify::private_key
61
+ end
62
+
63
+ h = Simplify::PaymentsApi.execute("invoice", 'list', criteria, public_key, private_key)
64
+ obj = Invoice.new()
65
+ obj.public_key = public_key
66
+ obj.private_key = private_key
67
+ obj = obj.merge(h)
68
+ obj
69
+
70
+ end
71
+
72
+ # Retrieve a Invoice object from the API
73
+ #
74
+ # id:: ID of object to retrieve
75
+ # public_key:: Public to use for the API call. If nil, the value of Simplify::public_key will be used.
76
+ # private_key:: Private key to use for the API call. If nil, the value of Simplify::private_key will be used.
77
+ # Returns a Invoice object.
78
+ def self.find(id, public_key = nil, private_key = nil)
79
+ if public_key == nil then
80
+ public_key = Simplify::public_key
81
+ end
82
+ if private_key == nil then
83
+ private_key = Simplify::private_key
84
+ end
85
+
86
+ h = Simplify::PaymentsApi.execute("invoice", 'show', {"id" => id}, public_key, private_key)
87
+ obj = Invoice.new()
88
+ obj.public_key = public_key
89
+ obj.private_key = private_key
90
+ obj = obj.merge(h)
91
+ obj
92
+ end
93
+
94
+ # Updates this object
95
+ #
96
+ # The properties that can be updated:
97
+ # * <code>status</code> Status of the invoice. Examples: OPEN = Invoice has not been processed and can have invoice items added to it. PAID = Invoice has been paid. UNPAID = Invoice was not paid when the card was processed. System will try up to 5 times to process the card. <b>(required)</b>
98
+ def update()
99
+ h = Simplify::PaymentsApi.execute("invoice", 'update', self, self.public_key, self.private_key)
100
+ self.merge!(h)
101
+ self
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,140 @@
1
+ #
2
+ # Copyright (c) 2013, MasterCard International Incorporated
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without modification, are
6
+ # permitted provided that the following conditions are met:
7
+ #
8
+ # Redistributions of source code must retain the above copyright notice, this list of
9
+ # conditions and the following disclaimer.
10
+ # Redistributions in binary form must reproduce the above copyright notice, this list of
11
+ # conditions and the following disclaimer in the documentation and/or other materials
12
+ # provided with the distribution.
13
+ # Neither the name of the MasterCard International Incorporated nor the names of its
14
+ # contributors may be used to endorse or promote products derived from this software
15
+ # without specific prior written permission.
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
17
+ # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
19
+ # SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22
+ # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23
+ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
+ # SUCH DAMAGE.
26
+ #
27
+
28
+ require 'simplify/paymentsapi'
29
+
30
+ module Simplify
31
+
32
+ # A InvoiceItem object.
33
+ #
34
+ class InvoiceItem < Hash
35
+
36
+ # Public key used to access the API.
37
+ attr_accessor :public_key
38
+
39
+ # Private key used to access the API.
40
+ attr_accessor :private_key
41
+
42
+
43
+
44
+ # Creates an InvoiceItem object
45
+ #
46
+ # parms:: a hash of parameters; valid keys are:
47
+ # * <code>amount</code> Amount of the invoice item (minor units). Example: 1000 = 10.00 <b>required </b>
48
+ # * <code>currency</code> Currency code (ISO-4217) for the invoice item. Must match the currency associated with your account. <b>required </b><b>default:USD</b>
49
+ # * <code>description</code> Individual items of an invoice
50
+ # * <code>invoice</code> Description of the invoice item <b>required </b>
51
+ # public_key:: Public to use for the API call. If nil, the value of Simplify::public_key will be used.
52
+ # private_key:: Private key to use for the API call. If nil, the value of Simplify::private_key will be used.
53
+ # Returns a InvoiceItem object.
54
+ def self.create(parms, public_key = nil, private_key = nil)
55
+ if public_key == nil then
56
+ public_key = Simplify::public_key
57
+ end
58
+ if private_key == nil then
59
+ private_key = Simplify::private_key
60
+ end
61
+
62
+ h = Simplify::PaymentsApi.execute("invoiceItem", 'create', parms, public_key, private_key)
63
+ obj = InvoiceItem.new()
64
+ obj.public_key = public_key
65
+ obj.private_key = private_key
66
+ obj = obj.merge(h)
67
+ obj
68
+ end
69
+
70
+ # Delete this object
71
+ def delete()
72
+ h = Simplify::PaymentsApi.execute("invoiceItem", 'delete', self, self.public_key, self.private_key)
73
+ self.merge!(h)
74
+ self
75
+ end
76
+
77
+ # Retrieve InvoiceItem objects.
78
+ # criteria:: a hash of parameters; valid keys are:
79
+ # * <code>filter</code> Filters to apply to the list.
80
+ # * <code>max</code> Allows up to a max of 50 list items to return. <b>default:20</b>
81
+ # * <code>offset</code> Used in paging of the list. This is the start offset of the page. <b>default:0</b>
82
+ # * <code>sorting</code> Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either <code>asc</code> for ascending or <code>desc</code> for descending). Sortable properties are: <code> id</code><code> amount</code><code> description</code><code> invoice</code>.
83
+ # public_key:: Public to use for the API call. If nil, the value of Simplify::public_key will be used.
84
+ # private_key:: Private key to use for the API call. If nil, the value of Simplify::private_key will be used.
85
+ # Returns an object where the <code>list</code> property contains the list of InvoiceItem objects and the <code>total</code>
86
+ # property contains the total number of InvoiceItem objects available for the given criteria.
87
+ def self.list(criteria = nil, public_key = nil, private_key = nil)
88
+
89
+ if public_key == nil then
90
+ public_key = Simplify::public_key
91
+ end
92
+ if private_key == nil then
93
+ private_key = Simplify::private_key
94
+ end
95
+
96
+ h = Simplify::PaymentsApi.execute("invoiceItem", 'list', criteria, public_key, private_key)
97
+ obj = InvoiceItem.new()
98
+ obj.public_key = public_key
99
+ obj.private_key = private_key
100
+ obj = obj.merge(h)
101
+ obj
102
+
103
+ end
104
+
105
+ # Retrieve a InvoiceItem object from the API
106
+ #
107
+ # id:: ID of object to retrieve
108
+ # public_key:: Public to use for the API call. If nil, the value of Simplify::public_key will be used.
109
+ # private_key:: Private key to use for the API call. If nil, the value of Simplify::private_key will be used.
110
+ # Returns a InvoiceItem object.
111
+ def self.find(id, public_key = nil, private_key = nil)
112
+ if public_key == nil then
113
+ public_key = Simplify::public_key
114
+ end
115
+ if private_key == nil then
116
+ private_key = Simplify::private_key
117
+ end
118
+
119
+ h = Simplify::PaymentsApi.execute("invoiceItem", 'show', {"id" => id}, public_key, private_key)
120
+ obj = InvoiceItem.new()
121
+ obj.public_key = public_key
122
+ obj.private_key = private_key
123
+ obj = obj.merge(h)
124
+ obj
125
+ end
126
+
127
+ # Updates this object
128
+ #
129
+ # The properties that can be updated:
130
+ # * <code>amount</code> Amount of the invoice item (minor units). Example: 1000 = 10.00
131
+ # * <code>currency</code> Currency code (ISO-4217) for the invoice item. Must match the currency associated with your account.
132
+ # * <code>description</code> Individual items of an invoice
133
+ def update()
134
+ h = Simplify::PaymentsApi.execute("invoiceItem", 'update', self, self.public_key, self.private_key)
135
+ self.merge!(h)
136
+ self
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,133 @@
1
+ #
2
+ # Copyright (c) 2013, MasterCard International Incorporated
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without modification, are
6
+ # permitted provided that the following conditions are met:
7
+ #
8
+ # Redistributions of source code must retain the above copyright notice, this list of
9
+ # conditions and the following disclaimer.
10
+ # Redistributions in binary form must reproduce the above copyright notice, this list of
11
+ # conditions and the following disclaimer in the documentation and/or other materials
12
+ # provided with the distribution.
13
+ # Neither the name of the MasterCard International Incorporated nor the names of its
14
+ # contributors may be used to endorse or promote products derived from this software
15
+ # without specific prior written permission.
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
17
+ # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
19
+ # SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22
+ # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23
+ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
+ # SUCH DAMAGE.
26
+ #
27
+
28
+ require 'simplify/paymentsapi'
29
+
30
+ module Simplify
31
+
32
+ # A Payment object.
33
+ #
34
+ class Payment < Hash
35
+
36
+ # Public key used to access the API.
37
+ attr_accessor :public_key
38
+
39
+ # Private key used to access the API.
40
+ attr_accessor :private_key
41
+
42
+
43
+
44
+ # Creates an Payment object
45
+ #
46
+ # parms:: a hash of parameters; valid keys are:
47
+ # * <code>amount</code> Amount of the payment (minor units). Example: 1000 = 10.00 <b>required </b>
48
+ # * <code>card => addressCity</code> City of the cardholder.
49
+ # * <code>card => addressCountry</code> Country code (ISO-3166-1-alpha-2 code) of residence of the cardholder.
50
+ # * <code>card => addressLine1</code> Address of the cardholder.
51
+ # * <code>card => addressLine2</code> Address of the cardholder if needed.
52
+ # * <code>card => addressState</code> State code (USPS code) of residence of the cardholder.
53
+ # * <code>card => addressZip</code> Postal code of the cardholder.
54
+ # * <code>card => cvc</code> CVC security code of the card. This is the code on the back of the card. Example: 123
55
+ # * <code>card => expMonth</code> Expiration month of the card. Format is MM. Example: January = 01 <b>required </b>
56
+ # * <code>card => expYear</code> Expiration year of the card. Format is YY. Example: 2013 = 13 <b>required </b>
57
+ # * <code>card => name</code> Name as it appears on the card.
58
+ # * <code>card => number</code> Card number as it appears on the card. <b>required </b>
59
+ # * <code>currency</code> Currency code (ISO-4217) for the transaction. Must match the currency associated with your account. <b>required </b><b>default:USD</b>
60
+ # * <code>customer</code> ID of customer. If specified, card on file of customer will be used.
61
+ # * <code>description</code> Custom naming of payment for external systems to use.
62
+ # * <code>token</code> If specified, card associated with card token will be used.
63
+ # public_key:: Public to use for the API call. If nil, the value of Simplify::public_key will be used.
64
+ # private_key:: Private key to use for the API call. If nil, the value of Simplify::private_key will be used.
65
+ # Returns a Payment object.
66
+ def self.create(parms, public_key = nil, private_key = nil)
67
+ if public_key == nil then
68
+ public_key = Simplify::public_key
69
+ end
70
+ if private_key == nil then
71
+ private_key = Simplify::private_key
72
+ end
73
+
74
+ h = Simplify::PaymentsApi.execute("payment", 'create', parms, public_key, private_key)
75
+ obj = Payment.new()
76
+ obj.public_key = public_key
77
+ obj.private_key = private_key
78
+ obj = obj.merge(h)
79
+ obj
80
+ end
81
+
82
+ # Retrieve Payment objects.
83
+ # criteria:: a hash of parameters; valid keys are:
84
+ # * <code>filter</code> Filters to apply to the list.
85
+ # * <code>max</code> Allows up to a max of 50 list items to return. <b>default:20</b>
86
+ # * <code>offset</code> Used in paging of the list. This is the start offset of the page. <b>default:0</b>
87
+ # * <code>sorting</code> Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either <code>asc</code> for ascending or <code>desc</code> for descending). Sortable properties are: <code> dateCreated</code><code> amount</code><code> id</code><code> description</code><code> paymentDate</code>.
88
+ # public_key:: Public to use for the API call. If nil, the value of Simplify::public_key will be used.
89
+ # private_key:: Private key to use for the API call. If nil, the value of Simplify::private_key will be used.
90
+ # Returns an object where the <code>list</code> property contains the list of Payment objects and the <code>total</code>
91
+ # property contains the total number of Payment objects available for the given criteria.
92
+ def self.list(criteria = nil, public_key = nil, private_key = nil)
93
+
94
+ if public_key == nil then
95
+ public_key = Simplify::public_key
96
+ end
97
+ if private_key == nil then
98
+ private_key = Simplify::private_key
99
+ end
100
+
101
+ h = Simplify::PaymentsApi.execute("payment", 'list', criteria, public_key, private_key)
102
+ obj = Payment.new()
103
+ obj.public_key = public_key
104
+ obj.private_key = private_key
105
+ obj = obj.merge(h)
106
+ obj
107
+
108
+ end
109
+
110
+ # Retrieve a Payment object from the API
111
+ #
112
+ # id:: ID of object to retrieve
113
+ # public_key:: Public to use for the API call. If nil, the value of Simplify::public_key will be used.
114
+ # private_key:: Private key to use for the API call. If nil, the value of Simplify::private_key will be used.
115
+ # Returns a Payment object.
116
+ def self.find(id, public_key = nil, private_key = nil)
117
+ if public_key == nil then
118
+ public_key = Simplify::public_key
119
+ end
120
+ if private_key == nil then
121
+ private_key = Simplify::private_key
122
+ end
123
+
124
+ h = Simplify::PaymentsApi.execute("payment", 'show', {"id" => id}, public_key, private_key)
125
+ obj = Payment.new()
126
+ obj.public_key = public_key
127
+ obj.private_key = private_key
128
+ obj = obj.merge(h)
129
+ obj
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,411 @@
1
+ #
2
+ # Copyright (c) 2013, MasterCard International Incorporated
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without modification, are
6
+ # permitted provided that the following conditions are met:
7
+ #
8
+ # Redistributions of source code must retain the above copyright notice, this list of
9
+ # conditions and the following disclaimer.
10
+ # Redistributions in binary form must reproduce the above copyright notice, this list of
11
+ # conditions and the following disclaimer in the documentation and/or other materials
12
+ # provided with the distribution.
13
+ # Neither the name of the MasterCard International Incorporated nor the names of its
14
+ # contributors may be used to endorse or promote products derived from this software
15
+ # without specific prior written permission.
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
17
+ # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
19
+ # SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22
+ # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23
+ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
+ # SUCH DAMAGE.
26
+ #
27
+
28
+ require 'rest_client'
29
+ require 'base64'
30
+ require 'open-uri'
31
+ require 'json'
32
+ require 'hmac-sha2'
33
+ require 'securerandom'
34
+ require 'simplify/apiexception'
35
+ require 'simplify/constants'
36
+ require 'simplify/event'
37
+
38
+ module Simplify
39
+
40
+ @@public_key = nil
41
+ @@private_key = nil
42
+ @@api_base_live_url = Constants::api_base_live_url
43
+ @@api_base_sandbox_url = Constants::api_base_sandbox_url
44
+ @@user_agent = nil
45
+
46
+
47
+ # Returns the value of the public API key.
48
+ def self.public_key
49
+ @@public_key
50
+ end
51
+
52
+ # Sets the value of the public API key.
53
+ def self.public_key=(key)
54
+ @@public_key = key
55
+ end
56
+
57
+ # Returns the value of the private API key.
58
+ def self.private_key
59
+ @@private_key
60
+ end
61
+
62
+ # Sets the value of the private API key.
63
+ def self.private_key=(key)
64
+ @@private_key = key
65
+ end
66
+
67
+ # Returns the base URL for the live API.
68
+ def self.api_base_live_url
69
+ @@api_base_live_url
70
+ end
71
+
72
+ # Sets the base URL for the live API.
73
+ def self.api_base_live_url=(url)
74
+ @@api_base_live_url = url
75
+ end
76
+
77
+ # Returns the base URL for the sandbox API.
78
+ def self.api_base_sandbox_url
79
+ @@api_base_sandbox_url
80
+ end
81
+
82
+ # Sets the base URL for the sandbox API.
83
+ def self.api_base_sandbox_url=(url)
84
+ @@api_base_sandbox_url = url
85
+ end
86
+
87
+ # Returns the user agent value sent in requests to the API.
88
+ def self.user_agent
89
+ @@user_agent
90
+ end
91
+
92
+ # Sets the value of the user agent sent in requests to the API.
93
+ def self.user_agent=(ua)
94
+ @@user_agent = ua
95
+ end
96
+
97
+
98
+ class PaymentsApi
99
+
100
+ @@JWS_NUM_HEADERS = 7
101
+ @@JWS_ALGORITHM = 'HS256'
102
+ @@JWS_TYPE = 'JWS'
103
+ @@JWS_HDR_UNAME = 'uname'
104
+ @@JWS_HDR_URI = 'api.simplifycommerce.com/uri'
105
+ @@JWS_HDR_TIMESTAMP = 'api.simplifycommerce.com/timestamp'
106
+ @@JWS_HDR_NONCE = 'api.simplifycommerce.com/nonce'
107
+ @@JWS_TIMESTAMP_MAX_DIFF = 1000 * 60 * 5 # 5 minutes
108
+
109
+ @@HTTP_SUCCESS = 200
110
+ @@HTTP_REDIRECTED = 302
111
+ @@HTTP_UNAUTHORIZED = 401
112
+ @@HTTP_NOT_FOUND = 404
113
+ @@HTTP_NOT_ALLOWED = 405
114
+ @@HTTP_BAD_REQUEST = 400
115
+
116
+ def self.execute(type, action, objectMap, public_key = nil, private_key = nil)
117
+
118
+ if public_key == nil
119
+ public_key = Simplify::public_key
120
+ end
121
+
122
+ if public_key == nil
123
+ raise ArgumentError.new("Must have a valid public key to connect to the API")
124
+ end
125
+
126
+ if private_key == nil
127
+ private_key = Simplify::private_key
128
+ end
129
+
130
+ if private_key == nil
131
+ raise ArgumentError.new("Must have a valid API key to connect to the API", nil, nil)
132
+ end
133
+
134
+ content_type = 'application/json'
135
+ url = build_url(get_base_url(public_key), type, action, objectMap)
136
+
137
+ signature = jws_encode(public_key, private_key, url, objectMap, action == 'update' || action == 'create')
138
+
139
+ opts = case action
140
+ when 'show', 'projections' then
141
+ {
142
+ :method => 'GET',
143
+ :headers => { :authorization => "JWS #{signature}" }
144
+ }
145
+ when 'list' then
146
+ {
147
+ :method => 'GET',
148
+ :headers => { :authorization => "JWS #{signature}" }
149
+ }
150
+ when 'update' then
151
+ {
152
+ :method => 'PUT',
153
+ :payload => signature
154
+ }
155
+ when 'create' then
156
+ {
157
+ :method => 'POST',
158
+ :payload => signature
159
+ }
160
+ when 'delete' then
161
+ {
162
+ :method => 'DELETE',
163
+ :headers => { :authorization => "JWS #{signature}" }
164
+ }
165
+ end
166
+
167
+ user_agent = "Ruby-SDK/#{Constants::version}"
168
+ if Simplify::user_agent != nil
169
+ user_agent = "#{user_agent} #{Simplify::user_agent}"
170
+ end
171
+
172
+ opts = opts.merge({
173
+ :url => url,
174
+ :headers => {
175
+ 'Content-Type' => content_type,
176
+ 'Accept' => 'application/json',
177
+ 'User-Agent' => user_agent
178
+ }.merge(opts[:headers] || {})
179
+ })
180
+
181
+ begin
182
+ response = RestClient::Request.execute(opts)
183
+ JSON.parse(response.body)
184
+ rescue RestClient::Exception => e
185
+
186
+ begin
187
+ errorData = JSON.parse(e.response.body)
188
+ rescue JSON::ParserError => e2
189
+ raise ApiException.new("Unknown error", nil, nil)
190
+ end
191
+
192
+ if e.response.code == @@HTTP_REDIRECTED
193
+ raise BadRequestException.new("Unexpected response code returned from the API, have you got the correct URL?", responseCode, e.response.code, errorData)
194
+ elsif e.response.code == @@HTTP_BAD_REQUEST
195
+ raise BadRequestException.new("Bad request", e.response.code, errorData)
196
+ elsif e.response.code == @@HTTP_UNAUTHORIZED
197
+ raise AuthenticationException.new("You are not authorized to make this request. Are you using the correct API keys?", e.response.code, errorData)
198
+ elsif e.response.code == @@HTTP_NOT_FOUND
199
+ raise ObjectNotFoundException.new("Object not found", e.response.code, errorData)
200
+ elsif e.response.code == @@HTTP_NOT_ALLOWED
201
+ raise NotAllowedException.new("Operation not allowed", e.response.code, errorData)
202
+ elsif e.response.code < 500
203
+ raise BadRequestException.new("Bad request", e.response.code, errorData)
204
+ else
205
+ raise SystemException.new("An unexpected error has been raised. Looks like there's something wrong at our end.", e.response.code, errorData)
206
+ end
207
+ end
208
+ end
209
+
210
+ def self.build_url(base_url, type, action, objectMap)
211
+ parts = []
212
+ parts << base_url
213
+ parts << type
214
+ parts << case action
215
+ when 'show', 'update', 'delete' then
216
+ [URI::encode(objectMap["id"].to_s)]
217
+
218
+ end
219
+ url = parts.flatten().join('/')
220
+
221
+ query = Array.new
222
+
223
+ if action == "list" and objectMap != nil then
224
+
225
+ if (objectMap['max'])
226
+ query << "max=#{objectMap['max']}"
227
+ end
228
+ if (objectMap['offset'])
229
+ query << "offset=#{objectMap['offset']}"
230
+ end
231
+ if (objectMap['sorting']) then
232
+ objectMap['sorting'].each { |k, v|
233
+ query << "sorting[#{URI::encode(k.to_s)}]=#{URI::encode(v.to_s)}"
234
+ }
235
+ end
236
+ if (objectMap['filter']) then
237
+ objectMap['filter'].each { |k, v|
238
+ query << "filter[#{URI::encode(k.to_s)}]=#{URI::encode(v.to_s)}"
239
+ }
240
+ end
241
+ end
242
+
243
+ if query.size > 0 then
244
+ url = url + "?" + query.join('&')
245
+ end
246
+
247
+ return url
248
+ end
249
+
250
+ def self.get_base_url(public_key)
251
+
252
+ if live_key?(public_key)
253
+ return Simplify::api_base_live_url
254
+ end
255
+ return Simplify::api_base_sandbox_url
256
+
257
+ end
258
+
259
+ def self.jws_encode(public_key, private_key, url, objectMap, hasPayload)
260
+
261
+ jws_hdr = {'typ' => @@JWS_TYPE,
262
+ 'alg' => @@JWS_ALGORITHM,
263
+ 'kid' => public_key,
264
+ @@JWS_HDR_URI => url,
265
+ @@JWS_HDR_TIMESTAMP => Time.now.to_i * 1000,
266
+ @@JWS_HDR_NONCE => SecureRandom.hex }
267
+
268
+ hdr = urlsafe_encode64(jws_hdr.to_json)
269
+
270
+ payload = ''
271
+ if (hasPayload) then
272
+ payload = urlsafe_encode64(objectMap.to_json)
273
+ end
274
+
275
+ msg = hdr + '.' + payload
276
+ return msg + '.' + jws_sign(private_key, msg)
277
+
278
+ end
279
+
280
+ def self.jws_decode(params, public_key, private_key)
281
+
282
+ if public_key == nil
283
+ raise ArgumentError.new("Must have a valid public key to connect to the API")
284
+ end
285
+
286
+ if private_key == nil
287
+ raise ArgumentError.new("Must have a valid API key to connect to the API")
288
+ end
289
+
290
+ payload = params['payload']
291
+ if payload == nil
292
+ raise ArgumentError.new("Event data is missing payload")
293
+ end
294
+
295
+ begin
296
+
297
+ payload.strip!
298
+ data = payload.split('.')
299
+ if data.size != 3
300
+ jws_auth_error("Incorrectly formatted JWS message");
301
+ end
302
+
303
+ msg = "#{data[0]}.#{data[1]}"
304
+ header = urlsafe_decode64(data[0])
305
+ payload = urlsafe_decode64(data[1])
306
+
307
+ jws_verify_header(header, params['url'], public_key)
308
+ if !jws_verify_signature(private_key, msg, data[2])
309
+ jws_auth_error("JWS signature does not match")
310
+ end
311
+
312
+ return JSON.parse(payload)
313
+
314
+ resue Exception => e
315
+ jws_auth_error("Exception during JWS decoding: #{e}")
316
+ end
317
+
318
+ jws_auth_error("JWS decode failed")
319
+ end
320
+
321
+ def self.jws_sign(private_key, msg)
322
+ urlsafe_encode64(HMAC::SHA256.digest(Base64.decode64(private_key), msg))
323
+ end
324
+
325
+
326
+ def self.jws_verify_header(header, url, public_key)
327
+
328
+ hdr = JSON.parse(header)
329
+
330
+ if hdr.size != @@JWS_NUM_HEADERS
331
+ jws_auth_error("Incorrect number of JWS header parameters - found #{hdr.size} required #{@@JWS_NUM_HEADERS}")
332
+ end
333
+
334
+ if hdr['alg'] != @@JWS_ALGORITHM
335
+ jws_auth_error("Incorrect algorithm - found #{hdr['alg']} required #{@@JWS_ALGORITHM}")
336
+ end
337
+
338
+ if hdr['typ'] != @@JWS_TYPE
339
+ jws_auth_error("Incorrect type - found #{hdr['typ']} required #{@@JWS_TYPE}")
340
+ end
341
+
342
+ if hdr['kid'] == nil
343
+ jws_auth_error("Missing Key ID")
344
+ end
345
+
346
+ if hdr['kid'] != public_key
347
+ if live_key?(public_key)
348
+ jws_auth_error("Invalid Key ID")
349
+ end
350
+ end
351
+
352
+ if hdr[@@JWS_HDR_URI] == nil
353
+ jws_auth_error("Missing URI")
354
+ end
355
+
356
+ if url != nil && hdr[@@JWS_HDR_URI] != url
357
+ jws_auth_error("Incorrect URL - found #{hdr[@@JWS_HDR_URI]} required #{url}")
358
+ end
359
+
360
+ if hdr[@@JWS_HDR_TIMESTAMP] == nil
361
+ jws_auth_error("Missing timestamp")
362
+ end
363
+
364
+ if !jws_verify_timestamp(hdr[@@JWS_HDR_TIMESTAMP])
365
+ jws_auth_error("Invalid timestamp")
366
+ end
367
+
368
+ if hdr[@@JWS_HDR_NONCE] == nil
369
+ jws_auth_error("Missing nonce")
370
+ end
371
+
372
+ if hdr[@@JWS_HDR_UNAME] == nil
373
+ jws_auth_error("Missing username header")
374
+ end
375
+ end
376
+
377
+ def self.jws_verify_signature(private_key, msg, crypto)
378
+ return crypto == jws_sign(private_key, msg)
379
+ end
380
+
381
+
382
+ def self.jws_verify_timestamp(ts)
383
+ return (Time.now.to_i * 1000 - ts.to_i).abs < @@JWS_TIMESTAMP_MAX_DIFF
384
+ end
385
+
386
+ def self.jws_auth_error(reason)
387
+ raise AuthenticationException.new("JWS authentication failure: #{reason}", nil, nil)
388
+ end
389
+
390
+ def self.live_key?(public_key)
391
+ return public_key.start_with?("lvpb")
392
+ end
393
+
394
+ # Base64.urlsafe_encode64()/urlsafe_decode64() is not available in ruby 1.8
395
+ def self.urlsafe_encode64(s)
396
+ return Base64::encode64(s).gsub(/\n/, '').gsub('+', '-').gsub('/', '_').gsub('=', '')
397
+ end
398
+
399
+ def self.urlsafe_decode64(s)
400
+ # Put back padding
401
+ case (s.size % 4)
402
+ when 0:
403
+ when 2: s = s + "=="
404
+ when 3: s = s + "="
405
+ else raise ArgumentError.new("Webhook event data incorrectly formatted", nil, nil)
406
+ end
407
+ return Base64::decode64(s.gsub('-','+').gsub('_','/'))
408
+ end
409
+
410
+ end
411
+ end