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.
- data/lib/simplify.rb +41 -0
- data/lib/simplify/apiexception.rb +160 -0
- data/lib/simplify/cardtoken.rb +102 -0
- data/lib/simplify/chargeback.rb +95 -0
- data/lib/simplify/constants.rb +56 -0
- data/lib/simplify/coupon.rb +143 -0
- data/lib/simplify/customer.rb +169 -0
- data/lib/simplify/deposit.rb +95 -0
- data/lib/simplify/event.rb +65 -0
- data/lib/simplify/invoice.rb +105 -0
- data/lib/simplify/invoiceitem.rb +140 -0
- data/lib/simplify/payment.rb +133 -0
- data/lib/simplify/paymentsapi.rb +411 -0
- data/lib/simplify/plan.rb +138 -0
- data/lib/simplify/refund.rb +120 -0
- data/lib/simplify/subscription.rb +149 -0
- data/lib/simplify/webhook.rb +135 -0
- metadata +113 -0
@@ -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
|