pay_simple 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +35 -0
- data/LICENSE.txt +22 -0
- data/README.md +100 -0
- data/Rakefile +1 -0
- data/lib/ps.rb +32 -0
- data/lib/ps/api.rb +80 -0
- data/lib/ps/api/json.rb +78 -0
- data/lib/ps/base.rb +22 -0
- data/lib/ps/enumerations.rb +191 -0
- data/lib/ps/exceptions.rb +5 -0
- data/lib/ps/object.rb +48 -0
- data/lib/ps/objects/ach_account.rb +34 -0
- data/lib/ps/objects/credit_card_account.rb +38 -0
- data/lib/ps/objects/customer.rb +79 -0
- data/lib/ps/objects/customer_account.rb +25 -0
- data/lib/ps/objects/payment.rb +73 -0
- data/lib/ps/objects/payment_status_filter.rb +6 -0
- data/lib/ps/objects/recurring_payment.rb +60 -0
- data/lib/ps/objects/recurring_payment_filter.rb +26 -0
- data/lib/ps/objects/user.rb +10 -0
- data/lib/ps/response.rb +83 -0
- data/lib/ps/util.rb +28 -0
- data/lib/ps/util/hash.rb +17 -0
- data/lib/ps/util/state.rb +15 -0
- data/lib/ps/util/states.yml +263 -0
- data/lib/ps/util/string.rb +15 -0
- data/paysimple.gemspec +19 -0
- data/spec/config.yml.example +13 -0
- data/spec/factories/ach_account.rb +11 -0
- data/spec/factories/credit_card_accounts.rb +11 -0
- data/spec/factories/customer_accounts.rb +7 -0
- data/spec/factories/customers.rb +18 -0
- data/spec/factories/payment.rb +12 -0
- data/spec/factories/recurring_payment.rb +25 -0
- data/spec/ps/api/json_spec.rb +12 -0
- data/spec/ps/api_spec.rb +53 -0
- data/spec/ps/base_spec.rb +40 -0
- data/spec/ps/format_spec.rb +16 -0
- data/spec/ps/object_spec.rb +35 -0
- data/spec/ps/objects/ach_account_spec.rb +57 -0
- data/spec/ps/objects/credit_card_account_spec.rb +69 -0
- data/spec/ps/objects/customer_account_spec.rb +43 -0
- data/spec/ps/objects/customer_spec.rb +153 -0
- data/spec/ps/objects/payment_spec.rb +98 -0
- data/spec/ps/objects/recurring_payment_spec.rb +145 -0
- data/spec/ps/objects/user_spec.rb +14 -0
- data/spec/ps/response_spec.rb +39 -0
- data/spec/ps/util/hash_spec.rb +33 -0
- data/spec/ps/util/states_spec.rb +25 -0
- data/spec/ps/util/string_spec.rb +21 -0
- data/spec/ps/util_spec.rb +30 -0
- data/spec/spec_functions.rb +155 -0
- data/spec/spec_helper.rb +22 -0
- metadata +156 -0
data/lib/ps/object.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module PS
|
2
|
+
class Object < Base
|
3
|
+
attr_accessor :ps_reference_id
|
4
|
+
|
5
|
+
def initialize(params = {})
|
6
|
+
set_attributes(params)
|
7
|
+
end
|
8
|
+
|
9
|
+
def attributes
|
10
|
+
attributes_hash = {}
|
11
|
+
self.instance_variables.each do |v|
|
12
|
+
attributes_hash[v[1..-1].to_sym] = self.send(v[1..-1])
|
13
|
+
end
|
14
|
+
attributes_hash
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_attributes(params={})
|
18
|
+
params.each do |k, v|
|
19
|
+
next unless self.class.method_defined?(k)
|
20
|
+
instance_variable_set("@#{k}", v)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
attrs = self.attributes.to_a.map { |k_v|
|
26
|
+
case k_v[1]
|
27
|
+
when Fixnum
|
28
|
+
"#{k_v[0]}: #{k_v[1]}"
|
29
|
+
when String || Time
|
30
|
+
"#{k_v[0]}: '#{k_v[1]}'" unless k_v[1].empty?
|
31
|
+
end
|
32
|
+
|
33
|
+
}.delete_if(&:nil?).join(", ")
|
34
|
+
"#<#{self.class} #{attrs}>"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
#this block will fail if more than one PS::Object is returned.
|
39
|
+
def update_self
|
40
|
+
Proc.new { |response| set_attributes(response.ps_object) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.instantiate_object
|
44
|
+
Proc.new { |response| PS::Util.instantiate_ps_objects(response.ps_object) }
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module PS
|
2
|
+
class AchAccount < CustomerAccount
|
3
|
+
attr_accessor :is_checking_account, :routing_number, :account_number, :bank_name
|
4
|
+
|
5
|
+
def self.create(params={})
|
6
|
+
aa = self.new(params)
|
7
|
+
aa.save
|
8
|
+
return aa
|
9
|
+
end
|
10
|
+
|
11
|
+
def save
|
12
|
+
begin
|
13
|
+
save!()
|
14
|
+
true
|
15
|
+
rescue Exception
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def save!
|
21
|
+
request("addcustomerachaccount", { :customerAccount => attributes }, &update_self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def update
|
25
|
+
request("updatecustomerachaccount", { :customerAccount => attributes })
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete
|
30
|
+
request("deletecustomerachaccount", { :accountId => self.ps_reference_id })
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module PS
|
2
|
+
class CreditCardAccount < CustomerAccount
|
3
|
+
attr_accessor :c_c_expiry, :c_c_type, :account_number
|
4
|
+
|
5
|
+
def save
|
6
|
+
begin
|
7
|
+
save!()
|
8
|
+
true
|
9
|
+
rescue Exception
|
10
|
+
false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def save!
|
15
|
+
request("addcustomercreditcardaccount", { :customerAccount => attributes }, &update_self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def update
|
19
|
+
request("updatecustomercreditcardaccount", { :customerAccount => attributes() })
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete
|
24
|
+
request("deletecustomercreditcardaccount", { :accountId => self.ps_reference_id })
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.default_for_customer_id(customer_id)
|
29
|
+
request("getdefaultcreditcardaccount", { :customerId => customer_id }, &instantiate_object)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.create(params={})
|
33
|
+
cc = self.new(params)
|
34
|
+
cc.save()
|
35
|
+
return cc
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module PS
|
2
|
+
class Customer < Object
|
3
|
+
attr_accessor :first_name,:middle_name,:last_name,:email,:alt_email,:phone,:alt_phone,:fax,:web_site,:billing_address1,:billing_address2,:billing_city,:billing_state,:billing_postal_code,:billing_country_code,:shipping_same_as_billing,:shipping_address1,:shipping_address2,:shipping_city,:shipping_state,:shipping_postal_code,:shipping_country_code,:company_name,:notes,:last_modified,:created_on
|
4
|
+
|
5
|
+
def save
|
6
|
+
begin
|
7
|
+
save!()
|
8
|
+
true
|
9
|
+
rescue Exception
|
10
|
+
false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def save!
|
15
|
+
request("addcustomer", { :customer => attributes }, &update_self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def destroy
|
19
|
+
if self.ps_reference_id then
|
20
|
+
request("deletecustomer", { :id => self.ps_reference_id } )
|
21
|
+
true
|
22
|
+
else
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def payments
|
28
|
+
Payment.find(self.ps_reference_id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_default_customer_account(account_id)
|
32
|
+
CustomerAccount.find(account_id).make_default
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_customer_account
|
36
|
+
CustomerAccount.default(self.ps_reference_id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def default_credit_card_account
|
40
|
+
CreditCardAccount.default_for_customer_id(self.ps_reference_id)
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
#returns [ PS::Customer, PS::CustomerAccount, PS::Payment ]
|
45
|
+
def create_and_make_cc_payment(customer={}, account={}, amount=0.0, cid="")
|
46
|
+
request("addcustomerandmakeccpayment", {
|
47
|
+
:customer => customer,
|
48
|
+
#account must have a customer_id of 0
|
49
|
+
:customerAccount => account,
|
50
|
+
:amount => amount,
|
51
|
+
:cid => cid
|
52
|
+
}, &instantiate_object)
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_and_make_ach_payment(customer={}, account={}, amount=0.0, cid="")
|
56
|
+
request("addcustomerandmakeachpayment", {
|
57
|
+
:customer => customer,
|
58
|
+
:customerAccount => account,
|
59
|
+
:amount => amount,
|
60
|
+
:cid => cid
|
61
|
+
}, &instantiate_object)
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_customer_and_default_accounts(customer_id)
|
65
|
+
request("GetCustomerAndDefaultAccounts", { :customerId => customer_id }, &instantiate_object)
|
66
|
+
end
|
67
|
+
|
68
|
+
def find(id)
|
69
|
+
request("getcustomer", { :id => id }, &instantiate_object)
|
70
|
+
end
|
71
|
+
|
72
|
+
def create(options={})
|
73
|
+
customer = self.new(options)
|
74
|
+
customer.save()
|
75
|
+
return customer
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PS
|
2
|
+
class CustomerAccount < Object
|
3
|
+
attr_accessor :customer_id
|
4
|
+
|
5
|
+
def make_default
|
6
|
+
request("SetDefaultCustomerAccount",
|
7
|
+
{
|
8
|
+
:customerId => self.customer_id,
|
9
|
+
:customerAccountId => self.ps_reference_id
|
10
|
+
}
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def find(account_id, customer_id)
|
16
|
+
#The name of this method in Paysimple is sooo misleading...
|
17
|
+
request("GetCustomerAccountByAccountId", { :accountId => account_id, :customerId => customer_id }, &instantiate_object)
|
18
|
+
end
|
19
|
+
|
20
|
+
def default(customer_id)
|
21
|
+
request("GetDefaultCustomerAccount", { :customerId => customer_id }, &instantiate_object)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module PS
|
2
|
+
class Payment < Object
|
3
|
+
attr_accessor :ref_payment_id,:customer_id,:customer_account_id,:amount,:status,:payment_date,:invoice_id,:recurring_schedule_id,:is_debit,:payment_type,:payment_sub_type,:provider_auth_code,:trace_number,:estimate_settled_date,:actual_settled_date,:can_void_until,:invoice_number,:purchase_order_number,:order_id,:description
|
4
|
+
|
5
|
+
def cancel
|
6
|
+
request("cancelpayment", { :paymentId => self.ps_reference_id }, &update_by_find)
|
7
|
+
end
|
8
|
+
|
9
|
+
def reverse
|
10
|
+
request("reversepayment", { :paymentId => self.ps_reference_id }, &update_by_find)
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def list(customer_id, criteria=nil)
|
15
|
+
request("listpayments", { :customerId => customer_id, :criteria => criteria }, &instantiate_object)
|
16
|
+
end
|
17
|
+
|
18
|
+
def reverse_by_id(payment_id)
|
19
|
+
request("reversepayment", { :paymentId => payment_id })
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def cancel_by_id(payment_id)
|
24
|
+
request("cancelpayment", { :paymentId => payment_id })
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def make(customer_id, amount, account_id=nil, cid="", order_details=nil)
|
29
|
+
request("makepayment",
|
30
|
+
{
|
31
|
+
:customerId => customer_id,
|
32
|
+
:customerAccountId => account_id,
|
33
|
+
:amount => amount,
|
34
|
+
:cid => cid,
|
35
|
+
:detail => order_details
|
36
|
+
}, &instantiate_object)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def update_by_find
|
42
|
+
Proc.new do
|
43
|
+
i = 0
|
44
|
+
loop do
|
45
|
+
payments = self.class
|
46
|
+
.list(self.customer_id, { :page => i, :items_per_page => 200 })
|
47
|
+
case payments
|
48
|
+
when []
|
49
|
+
break
|
50
|
+
when Array
|
51
|
+
updated = false
|
52
|
+
payments.each do |payment|
|
53
|
+
if payment.ps_reference_id == self.ps_reference_id then
|
54
|
+
set_attributes(payment.attributes)
|
55
|
+
updated = true
|
56
|
+
break
|
57
|
+
end
|
58
|
+
end
|
59
|
+
break if updated
|
60
|
+
when PS::Payment
|
61
|
+
if payments.ps_reference_id == self.ps_reference_id then
|
62
|
+
set_attributes(payments.attributes)
|
63
|
+
end
|
64
|
+
# break here because if only one payment is returned, then it's
|
65
|
+
# only one that exists or it is the last one...
|
66
|
+
break
|
67
|
+
end
|
68
|
+
i += 1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
module PS
|
2
|
+
class PaymentStatusFilter < Object
|
3
|
+
attr_accessor :all, :pending, :posted, :settled, :failed, :resubmitted, :voided, :reversed, :saved, :scheduled, :reverse_posted, :charge_back, :close_charge_back, :authorized, :returned, :reverse_charge_back, :reverse_NSF, :reverse_return, :refund_settled
|
4
|
+
|
5
|
+
end
|
6
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module PS
|
2
|
+
class RecurringPayment < Object
|
3
|
+
attr_accessor :customer_id, :customer_account_id, :schedule_type, :start_date, :has_end_date, :end_date, :billing_frequency_type, :billing_frequency_param, :payment_amount, :first_payment_done, :first_payment_amount, :first_payment_date, :total_due_amount, :total_number_of_payments, :balance_remaining, :number_of_payments_remaining, :invoice_no, :order_id, :description, :schedule_status, :number_of_payment_made, :total_amount_paid, :date_of_last_payment_made, :pause_until_date
|
4
|
+
|
5
|
+
def save
|
6
|
+
begin
|
7
|
+
save!()
|
8
|
+
true
|
9
|
+
rescue Exception
|
10
|
+
false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def save!
|
15
|
+
request("addrecurringpayment", { :recurringPayment => attributes }, &update_self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def update
|
19
|
+
request("modifyrecurringpaymentschedule", { :paymentSchedule => attributes })
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def suspend
|
24
|
+
request("suspendrecurringpaymentschedule", { :scheduleId => self.ps_reference_id }, &update_self)
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def resume
|
29
|
+
request("resumerecurringpaymentschedule", { :scheduleId => self.ps_reference_id }, &update_self)
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def destroy
|
34
|
+
request("deleterecurringschedule", { :scheduleId => self.ps_reference_id })
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.create(params={})
|
39
|
+
recurring_payment = new(params)
|
40
|
+
recurring_payment.save()
|
41
|
+
return recurring_payment
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.list(start_date, end_date, customer_id, criteria=nil, filter={})
|
45
|
+
criteria ||= { :Page => 1, :items_per_page => 12 }
|
46
|
+
request("listrecurringpayments",
|
47
|
+
{
|
48
|
+
:startDate => start_date,
|
49
|
+
:endDate => end_date,
|
50
|
+
:customerId => customer_id,
|
51
|
+
:filter => filter,
|
52
|
+
:criteria => criteria
|
53
|
+
}, &instantiate_object)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.find(schedule_id)
|
57
|
+
request("GetRecurringPaymentSchedule", { :scheduleId => schedule_id }, &instantiate_object)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module PS
|
2
|
+
class RecurringPaymentFilter < PsObject
|
3
|
+
attr_accessor :active, :disabled, :paused, :expired, :suspended
|
4
|
+
|
5
|
+
def active?
|
6
|
+
self.active
|
7
|
+
end
|
8
|
+
|
9
|
+
def disabled?
|
10
|
+
self.disabled
|
11
|
+
end
|
12
|
+
|
13
|
+
def paused?
|
14
|
+
self.paused
|
15
|
+
end
|
16
|
+
|
17
|
+
def expired?
|
18
|
+
self.expired
|
19
|
+
end
|
20
|
+
|
21
|
+
def suspended?
|
22
|
+
self.suspended
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/ps/response.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
module PS
|
2
|
+
class Response < Base
|
3
|
+
attr_accessor :is_success,:error_message,:sub_type,:ps_object,:total_items,:items_per_page,:current_page,:error_type,:exception_detail
|
4
|
+
|
5
|
+
#### Some Basic fields returned by Paysimple
|
6
|
+
# {
|
7
|
+
## '__type' => String,
|
8
|
+
## 'CurrentPage => Int,
|
9
|
+
## 'ErrorMessage => String
|
10
|
+
## 'ErrorType' => Int,
|
11
|
+
## 'IsSuccess' => boolean,
|
12
|
+
## 'itemsPerPage' => Int,
|
13
|
+
## 'PsObject' => [
|
14
|
+
### { ... },
|
15
|
+
### ...
|
16
|
+
## ],
|
17
|
+
## 'SubType' => String,
|
18
|
+
## 'TotalItems' => Int
|
19
|
+
# }
|
20
|
+
|
21
|
+
## God this is terribly worded...
|
22
|
+
# After the format classes; JSON, XML, SOAP, etc; get a response from
|
23
|
+
# Paysimple, the response should be parsed from the respective format into
|
24
|
+
# a ruby hash. That hash is then passed here, and the response is setup.
|
25
|
+
# example:
|
26
|
+
# post_response = HTTParty.post("https://api.paysimple.com/3.00/paysimpleapi", options_hash(params))
|
27
|
+
# parsed_response = post_response.parsed_response['d'] #the 'd' key is specific to the json format
|
28
|
+
# Response.new(parsed_response)
|
29
|
+
def initialize(params={})
|
30
|
+
params.each { |k,v| instance_variable_set("@#{k.to_snake_case}", v) }
|
31
|
+
successful_request?()
|
32
|
+
@ps_object ||= []
|
33
|
+
parse_ps_object()
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
##
|
39
|
+
# Checks for errors in the PsResponse.
|
40
|
+
def successful_request?
|
41
|
+
raise RequestError, @exception_detail["InnerException"]["Message"] if @exception_detail
|
42
|
+
raise RequestError, @error_message.join("; ") unless @is_success == true
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_ps_object()
|
46
|
+
snake_case_ps_object_keys()
|
47
|
+
parse_object_dates()
|
48
|
+
if @ps_object.length == 1 then
|
49
|
+
@ps_object = @ps_object.first
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Paysimple returns the attribute names in CamelCase, but the attributes use
|
55
|
+
# snake_case within the code base. The method bellow converts the attribute
|
56
|
+
# names into snake_case so that they can be more easily dynamically assigned
|
57
|
+
# to the appropriate class.
|
58
|
+
def snake_case_ps_object_keys
|
59
|
+
# this line might seem a bit odd, but it is necessary because there
|
60
|
+
# are two paysimple methods that, depending on the context, will
|
61
|
+
# return @ps_object where one of the elements is an instance of
|
62
|
+
# NilClass.
|
63
|
+
@ps_object.delete_if(&:nil?)
|
64
|
+
@ps_object.map!(&:snake_case_keys)
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# parses date fields into a rubyesque format.
|
69
|
+
def parse_object_dates
|
70
|
+
@ps_object.each_with_index do |object, i|
|
71
|
+
object.each do |key, value|
|
72
|
+
# here we are asking the format class if the value is a date in its
|
73
|
+
# format. For Example, the json format class returns dates in the
|
74
|
+
# following format: "/Date(1248908403000-0600)/"
|
75
|
+
if date?(value) then
|
76
|
+
# if so, then we now ask the format class how to parse that date.
|
77
|
+
@ps_object[i][key] = parse_date(value)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|