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