pay_simple 0.0.1

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