subscription_fu 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/app/models/subscription_fu/subscription.rb +9 -3
- data/app/models/subscription_fu/transaction.rb +21 -7
- data/lib/subscription_fu/config.rb +6 -5
- data/lib/subscription_fu/paypal.rb +13 -95
- data/lib/subscription_fu/version.rb +1 -1
- data/spec/support/paypal_test_helper.rb +1 -1
- data/subscription_fu.gemspec +1 -1
- metadata +6 -6
data/Gemfile
CHANGED
@@ -55,7 +55,7 @@ class SubscriptionFu::Subscription < ActiveRecord::Base
|
|
55
55
|
# billing time data about the subscription
|
56
56
|
|
57
57
|
def next_billing_date
|
58
|
-
paypal_recurring_details[:next_billing_date]
|
58
|
+
paypal_recurring_details[:next_billing_date] if paypal?
|
59
59
|
end
|
60
60
|
|
61
61
|
def estimated_next_billing_date
|
@@ -64,7 +64,7 @@ class SubscriptionFu::Subscription < ActiveRecord::Base
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def last_billing_date
|
67
|
-
paypal_recurring_details[:last_payment_date]
|
67
|
+
paypal_recurring_details[:last_payment_date] if paypal?
|
68
68
|
end
|
69
69
|
|
70
70
|
def successor_start_date(new_plan_name)
|
@@ -103,8 +103,14 @@ class SubscriptionFu::Subscription < ActiveRecord::Base
|
|
103
103
|
|
104
104
|
private
|
105
105
|
|
106
|
+
def paypal?
|
107
|
+
! paypal_profile_id.blank?
|
108
|
+
end
|
109
|
+
|
106
110
|
def paypal_recurring_details
|
107
|
-
@paypal_recurring_details ||=
|
111
|
+
@paypal_recurring_details ||= begin
|
112
|
+
SubscriptionFu::Paypal.recurring_details(paypal_profile_id)
|
113
|
+
end
|
108
114
|
end
|
109
115
|
|
110
116
|
def convert_paypal_status(paypal_status)
|
@@ -77,9 +77,15 @@ class SubscriptionFu::Transaction < ActiveRecord::Base
|
|
77
77
|
private
|
78
78
|
|
79
79
|
def start_checkout_paypal(return_url, cancel_url)
|
80
|
-
|
81
|
-
|
82
|
-
|
80
|
+
# TODO: set initiator_email
|
81
|
+
pay_req = Paypal::Payment::Request.new(
|
82
|
+
:currency_code => sub_plan.currency,
|
83
|
+
:billing_type => :RecurringPayments,
|
84
|
+
:billing_agreement_description => sub_human_description)
|
85
|
+
|
86
|
+
response = SubscriptionFu::Paypal.express_request.setup(pay_req, return_url, cancel_url, :no_shipping => true)
|
87
|
+
update_attributes!(:identifier => response.token)
|
88
|
+
response.redirect_uri
|
83
89
|
end
|
84
90
|
|
85
91
|
def start_checkout_nogw(return_url, cancel_url)
|
@@ -91,9 +97,17 @@ class SubscriptionFu::Transaction < ActiveRecord::Base
|
|
91
97
|
raise "did you call start_checkout first?" if identifier.blank?
|
92
98
|
raise "already activated" if sub_activated?
|
93
99
|
|
94
|
-
|
95
|
-
|
96
|
-
|
100
|
+
profile = Paypal::Payment::Recurring.new(
|
101
|
+
:start_date => sub_billing_starts_at,
|
102
|
+
:description => sub_human_description,
|
103
|
+
:billing => {
|
104
|
+
:period => :Month,
|
105
|
+
:frequency => 1,
|
106
|
+
:amount => sub_plan.price,
|
107
|
+
:tax => sub_plan.price_tax,
|
108
|
+
:currency_code => sub_plan.currency } )
|
109
|
+
response = SubscriptionFu::Paypal.express_request.subscribe!(identifier, profile)
|
110
|
+
subscription.update_attributes!(:paypal_profile_id => response.recurring.identifier, :activated_at => Time.now)
|
97
111
|
complete_activation
|
98
112
|
end
|
99
113
|
|
@@ -114,7 +128,7 @@ class SubscriptionFu::Transaction < ActiveRecord::Base
|
|
114
128
|
# update the record beforehand, because paypal raises an error if
|
115
129
|
# the profile is already cancelled
|
116
130
|
complete_cancellation(opts)
|
117
|
-
SubscriptionFu::Paypal.
|
131
|
+
SubscriptionFu::Paypal.express_request.renew!(sub_paypal_profile_id, :Cancel, :note => sub_cancel_reason)
|
118
132
|
end
|
119
133
|
|
120
134
|
def complete_cancellation_nogw(opts)
|
@@ -1,22 +1,23 @@
|
|
1
|
+
require "paypal"
|
2
|
+
|
1
3
|
module SubscriptionFu
|
2
4
|
class Config
|
3
|
-
attr_accessor :plan_class_name, :
|
5
|
+
attr_accessor :plan_class_name, :paypal_api_user_id, :paypal_api_pwd, :paypal_api_sig
|
4
6
|
attr_reader :available_plans
|
5
7
|
|
6
8
|
def initialize
|
7
9
|
@available_plans = {}
|
8
10
|
@plan_class_name = "SubscriptionFu::Plan"
|
9
11
|
paypal_use_production!
|
12
|
+
::Paypal.logger = Rails.logger
|
10
13
|
end
|
11
14
|
|
12
15
|
def paypal_use_sandbox!
|
13
|
-
|
14
|
-
self.paypal_landing_url = "https://www.sandbox.paypal.com/cgi-bin/webscr"
|
16
|
+
::Paypal.sandbox = true
|
15
17
|
end
|
16
18
|
|
17
19
|
def paypal_use_production!
|
18
|
-
|
19
|
-
self.paypal_landing_url = "https://www.paypal.com/cgi-bin/webscr"
|
20
|
+
::Paypal.sandbox = false
|
20
21
|
end
|
21
22
|
|
22
23
|
def add_plan(key, price, data = {})
|
@@ -1,101 +1,19 @@
|
|
1
|
-
require "
|
1
|
+
require "paypal"
|
2
2
|
|
3
|
-
|
3
|
+
module SubscriptionFu::Paypal
|
4
|
+
UTC_TZ = ActiveSupport::TimeZone.new("UTC")
|
4
5
|
|
5
|
-
def self.
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
def self.express_request
|
7
|
+
config = SubscriptionFu.config
|
8
|
+
::Paypal::Express::Request.new(
|
9
|
+
:username => config.paypal_api_user_id,
|
10
|
+
:password => config.paypal_api_pwd,
|
11
|
+
:signature => config.paypal_api_sig)
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
@request_opts = request_opts
|
18
|
-
@response = response
|
19
|
-
super "PayPal did not return success: #{@response.inspect}"
|
20
|
-
end
|
21
|
-
alias :data :request_opts
|
22
|
-
end
|
23
|
-
|
24
|
-
def initialize(user, pwd, sig, api_url, logger)
|
25
|
-
@user = user
|
26
|
-
@pwd = pwd
|
27
|
-
@sig = sig
|
28
|
-
@api_url = api_url
|
29
|
-
@logger = logger
|
30
|
-
end
|
31
|
-
|
32
|
-
def start_checkout(return_url, cancel_url, email, maxamt, currency_code, desc)
|
33
|
-
res = call_paypal('SetExpressCheckout',
|
34
|
-
'RETURNURL' => return_url,
|
35
|
-
'CANCELURL' => cancel_url,
|
36
|
-
'EMAIL' => email,
|
37
|
-
'NOSHIPPING' => 1,
|
38
|
-
'MAXAMT' => maxamt,
|
39
|
-
'PAYMENTREQUEST_0_AMT' => 0,
|
40
|
-
'PAYMENTREQUEST_0_CURRENCYCODE' => currency_code,
|
41
|
-
'L_BILLINGTYPE0' => 'RecurringPayments',
|
42
|
-
'L_BILLINGAGREEMENTDESCRIPTION0' => desc)
|
43
|
-
res['TOKEN'].first
|
44
|
-
end
|
45
|
-
|
46
|
-
def create_recurring(token, start_date, amt, taxamt, currency_code, desc)
|
47
|
-
res = call_paypal('CreateRecurringPaymentsProfile',
|
48
|
-
'PROFILESTARTDATE' => start_date.utc.iso8601,
|
49
|
-
'BILLINGPERIOD' => 'Month',
|
50
|
-
'BILLINGFREQUENCY' => 1,
|
51
|
-
'MAXFAILEDPAYMENTS' => 0,
|
52
|
-
'AUTOBILLOUTAMT' => 'AddToNextBilling',
|
53
|
-
'AMT' => amt,
|
54
|
-
'TAXAMT' => taxamt,
|
55
|
-
'CURRENCYCODE' => currency_code,
|
56
|
-
'DESC' => desc,
|
57
|
-
'TOKEN' => token)
|
58
|
-
[res['PROFILEID'].first, res['PROFILESTATUS'].first || res['STATUS'].first]
|
59
|
-
end
|
60
|
-
|
61
|
-
def recurring_details(profile_id)
|
62
|
-
utc = ActiveSupport::TimeZone.new("UTC")
|
63
|
-
res = call_paypal('GetRecurringPaymentsProfileDetails', 'PROFILEID' => profile_id)
|
64
|
-
{ :id => res['PROFILEID'].first,
|
65
|
-
:status => res['PROFILESTATUS'].first || res['STATUS'].first,
|
66
|
-
:next_billing_date => utc.parse(res['NEXTBILLINGDATE'].first.to_s),
|
67
|
-
:start_date => utc.parse(res['PROFILESTARTDATE'].first.to_s),
|
68
|
-
:cycles_completed => res['NUMCYCLESCOMPLETED'].first.to_i,
|
69
|
-
:outstanding_balance => res['OUTSTANDINGBALANCE'].first.to_f,
|
70
|
-
:last_payment_date => utc.parse(res['LASTPAYMENTDATE'].first.to_s) }
|
14
|
+
def self.recurring_details(profile_id)
|
15
|
+
res = SubscriptionFu::Paypal.express_request.subscription(profile_id)
|
16
|
+
{ :next_billing_date => UTC_TZ.parse(res.recurring.summary.next_billing_date.to_s),
|
17
|
+
:last_payment_date => UTC_TZ.parse(res.recurring.summary.last_payment_date.to_s), }
|
71
18
|
end
|
72
|
-
|
73
|
-
def cancel_recurring(profile_id, reason)
|
74
|
-
call_paypal('ManageRecurringPaymentsProfileStatus',
|
75
|
-
'PROFILEID' => profile_id,
|
76
|
-
'ACTION' => 'Cancel',
|
77
|
-
'NOTE' => I18n.t(reason, :scope => [:subscription_fu, :subscription, :cancel_notes]))
|
78
|
-
end
|
79
|
-
|
80
|
-
private
|
81
|
-
|
82
|
-
def call_paypal(method, opts)
|
83
|
-
full_opts = paypal_api_opts(method, opts)
|
84
|
-
CGI.parse(RestClient.post(@api_url, full_opts)).tap do |result|
|
85
|
-
if result['ACK'].first != 'Success'
|
86
|
-
@logger.warn("PayPal did not return success for call to #{method}")
|
87
|
-
@logger.info(" called with: #{full_opts.inspect}")
|
88
|
-
@logger.info(" result: #{result.inspect}")
|
89
|
-
raise Failure.new(full_opts, result)
|
90
|
-
else
|
91
|
-
@logger.info("PayPal returned success for call to #{method}")
|
92
|
-
@logger.info(" result: #{result.inspect}")
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def paypal_api_opts(method, opts)
|
98
|
-
opts.merge('USER' => @user, 'PWD' => @pwd, 'SIGNATURE' => @sig, 'VERSION' => '65.0', 'METHOD' => method)
|
99
|
-
end
|
100
|
-
|
101
19
|
end
|
data/subscription_fu.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.rubyforge_project = "subscriptionfu"
|
17
17
|
|
18
18
|
s.add_dependency 'rails', '>= 3.0.3'
|
19
|
-
s.add_dependency '
|
19
|
+
s.add_dependency 'paypal-express', '~> 0.3.0'
|
20
20
|
|
21
21
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
22
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: subscription_fu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.2.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Paul McMahon
|
@@ -11,7 +11,7 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date: 2011-05-
|
14
|
+
date: 2011-05-29 00:00:00 +09:00
|
15
15
|
default_executable:
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
@@ -26,14 +26,14 @@ dependencies:
|
|
26
26
|
type: :runtime
|
27
27
|
version_requirements: *id001
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
|
-
name:
|
29
|
+
name: paypal-express
|
30
30
|
prerelease: false
|
31
31
|
requirement: &id002 !ruby/object:Gem::Requirement
|
32
32
|
none: false
|
33
33
|
requirements:
|
34
|
-
- -
|
34
|
+
- - ~>
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version:
|
36
|
+
version: 0.3.0
|
37
37
|
type: :runtime
|
38
38
|
version_requirements: *id002
|
39
39
|
description: SubscriptionFu helps with building services which have paid subscriptions. It includes the models to store subscription status, and provides integration with PayPal for paid subscriptions.
|
@@ -126,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
126
|
requirements: []
|
127
127
|
|
128
128
|
rubyforge_project: subscriptionfu
|
129
|
-
rubygems_version: 1.
|
129
|
+
rubygems_version: 1.5.2
|
130
130
|
signing_key:
|
131
131
|
specification_version: 3
|
132
132
|
summary: Rails support for handling free/paid subscriptions
|