simplify 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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