subscription_fu 0.1.0 → 0.2.0
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.
- 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
|