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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +12 -0
  5. data/Gemfile.lock +35 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +100 -0
  8. data/Rakefile +1 -0
  9. data/lib/ps.rb +32 -0
  10. data/lib/ps/api.rb +80 -0
  11. data/lib/ps/api/json.rb +78 -0
  12. data/lib/ps/base.rb +22 -0
  13. data/lib/ps/enumerations.rb +191 -0
  14. data/lib/ps/exceptions.rb +5 -0
  15. data/lib/ps/object.rb +48 -0
  16. data/lib/ps/objects/ach_account.rb +34 -0
  17. data/lib/ps/objects/credit_card_account.rb +38 -0
  18. data/lib/ps/objects/customer.rb +79 -0
  19. data/lib/ps/objects/customer_account.rb +25 -0
  20. data/lib/ps/objects/payment.rb +73 -0
  21. data/lib/ps/objects/payment_status_filter.rb +6 -0
  22. data/lib/ps/objects/recurring_payment.rb +60 -0
  23. data/lib/ps/objects/recurring_payment_filter.rb +26 -0
  24. data/lib/ps/objects/user.rb +10 -0
  25. data/lib/ps/response.rb +83 -0
  26. data/lib/ps/util.rb +28 -0
  27. data/lib/ps/util/hash.rb +17 -0
  28. data/lib/ps/util/state.rb +15 -0
  29. data/lib/ps/util/states.yml +263 -0
  30. data/lib/ps/util/string.rb +15 -0
  31. data/paysimple.gemspec +19 -0
  32. data/spec/config.yml.example +13 -0
  33. data/spec/factories/ach_account.rb +11 -0
  34. data/spec/factories/credit_card_accounts.rb +11 -0
  35. data/spec/factories/customer_accounts.rb +7 -0
  36. data/spec/factories/customers.rb +18 -0
  37. data/spec/factories/payment.rb +12 -0
  38. data/spec/factories/recurring_payment.rb +25 -0
  39. data/spec/ps/api/json_spec.rb +12 -0
  40. data/spec/ps/api_spec.rb +53 -0
  41. data/spec/ps/base_spec.rb +40 -0
  42. data/spec/ps/format_spec.rb +16 -0
  43. data/spec/ps/object_spec.rb +35 -0
  44. data/spec/ps/objects/ach_account_spec.rb +57 -0
  45. data/spec/ps/objects/credit_card_account_spec.rb +69 -0
  46. data/spec/ps/objects/customer_account_spec.rb +43 -0
  47. data/spec/ps/objects/customer_spec.rb +153 -0
  48. data/spec/ps/objects/payment_spec.rb +98 -0
  49. data/spec/ps/objects/recurring_payment_spec.rb +145 -0
  50. data/spec/ps/objects/user_spec.rb +14 -0
  51. data/spec/ps/response_spec.rb +39 -0
  52. data/spec/ps/util/hash_spec.rb +33 -0
  53. data/spec/ps/util/states_spec.rb +25 -0
  54. data/spec/ps/util/string_spec.rb +21 -0
  55. data/spec/ps/util_spec.rb +30 -0
  56. data/spec/spec_functions.rb +155 -0
  57. data/spec/spec_helper.rb +22 -0
  58. metadata +156 -0
@@ -0,0 +1,5 @@
1
+ module PS
2
+ class RequestError < Exception; end
3
+ class ConnectionError < Exception; end
4
+ class CreditCardError < Exception; end
5
+ end
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
@@ -0,0 +1,10 @@
1
+ module PS
2
+ class User < Object
3
+ attr_accessor :ps_reference_id, :first_name, :last_name, :company, :email, :phone
4
+
5
+ #NOTE: This doesn't work in paysimple...
6
+ def self.get
7
+ request("getuserinfo", &instantiate_object)
8
+ end
9
+ end
10
+ end
@@ -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