Support for yearly subscriptions.
This commit is contained in:
parent
a4eecfaa65
commit
269c28983d
3
NEWS
3
NEWS
|
@ -1,3 +1,6 @@
|
||||||
|
1.3.19: May 8, 2008
|
||||||
|
* Support for yearly subscriptions.
|
||||||
|
|
||||||
1.3.18: May 7, 2008
|
1.3.18: May 7, 2008
|
||||||
* No longer showing "settings" link unless you're viewing your wiki.
|
* No longer showing "settings" link unless you're viewing your wiki.
|
||||||
* In account settings note, now showing link to upgrade/downgrade/cancel.
|
* In account settings note, now showing link to upgrade/downgrade/cancel.
|
||||||
|
|
|
@ -30,24 +30,33 @@ settings = {
|
||||||
"storage_quota_bytes": 30 * MEGABYTE,
|
"storage_quota_bytes": 30 * MEGABYTE,
|
||||||
"notebook_collaboration": False,
|
"notebook_collaboration": False,
|
||||||
"fee": None,
|
"fee": None,
|
||||||
|
"yearly_fee": None,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "basic",
|
"name": "basic",
|
||||||
"storage_quota_bytes": 250 * MEGABYTE,
|
"storage_quota_bytes": 250 * MEGABYTE,
|
||||||
"notebook_collaboration": True,
|
"notebook_collaboration": True,
|
||||||
"fee": 5,
|
"fee": 5,
|
||||||
|
"yearly_fee": 50,
|
||||||
"button":
|
"button":
|
||||||
"""
|
"""
|
||||||
""",
|
""",
|
||||||
|
"yearly_button":
|
||||||
|
"""
|
||||||
|
""",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "standard",
|
"name": "standard",
|
||||||
"storage_quota_bytes": 500 * MEGABYTE,
|
"storage_quota_bytes": 500 * MEGABYTE,
|
||||||
"notebook_collaboration": True,
|
"notebook_collaboration": True,
|
||||||
"fee": 9,
|
"fee": 9,
|
||||||
|
"yearly_fee": 90,
|
||||||
"button":
|
"button":
|
||||||
"""
|
"""
|
||||||
""",
|
""",
|
||||||
|
"yearly_button":
|
||||||
|
"""
|
||||||
|
""",
|
||||||
},
|
},
|
||||||
# {
|
# {
|
||||||
# "name": "premium",
|
# "name": "premium",
|
||||||
|
|
|
@ -1082,6 +1082,9 @@ class Users( object ):
|
||||||
|
|
||||||
# verify item_number
|
# verify item_number
|
||||||
plan_index = params.get( u"item_number" )
|
plan_index = params.get( u"item_number" )
|
||||||
|
if plan_index == None:
|
||||||
|
return dict() # ignore this transaction if there's no item number
|
||||||
|
|
||||||
try:
|
try:
|
||||||
plan_index = int( plan_index )
|
plan_index = int( plan_index )
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -1092,13 +1095,14 @@ class Users( object ):
|
||||||
# verify mc_gross
|
# verify mc_gross
|
||||||
rate_plan = self.__rate_plans[ plan_index ]
|
rate_plan = self.__rate_plans[ plan_index ]
|
||||||
fee = u"%0.2f" % rate_plan[ u"fee" ]
|
fee = u"%0.2f" % rate_plan[ u"fee" ]
|
||||||
|
yearly_fee = u"%0.2f" % rate_plan[ u"yearly_fee" ]
|
||||||
mc_gross = params.get( u"mc_gross" )
|
mc_gross = params.get( u"mc_gross" )
|
||||||
if mc_gross and mc_gross != fee:
|
if mc_gross and mc_gross not in ( fee, yearly_fee ):
|
||||||
raise Payment_error( u"invalid mc_gross", params )
|
raise Payment_error( u"invalid mc_gross", params )
|
||||||
|
|
||||||
# verify mc_amount3
|
# verify mc_amount3
|
||||||
mc_amount3 = params.get( u"mc_amount3" )
|
mc_amount3 = params.get( u"mc_amount3" )
|
||||||
if mc_amount3 and mc_amount3 != fee:
|
if mc_amount3 and mc_amount3 not in ( fee, yearly_fee ):
|
||||||
raise Payment_error( u"invalid mc_amount3", params )
|
raise Payment_error( u"invalid mc_amount3", params )
|
||||||
|
|
||||||
# verify item_name
|
# verify item_name
|
||||||
|
@ -1112,8 +1116,12 @@ class Users( object ):
|
||||||
|
|
||||||
# verify period3
|
# verify period3
|
||||||
period3 = params.get( u"period3" )
|
period3 = params.get( u"period3" )
|
||||||
if period3 and period3 != u"1 M": # one-month subscription
|
if mc_amount3 == yearly_fee:
|
||||||
raise Payment_error( u"invalid period3", params )
|
if period3 and period3 != u"12 M": # one-year subscription
|
||||||
|
raise Payment_error( u"invalid period3", params )
|
||||||
|
else:
|
||||||
|
if period3 and period3 != u"1 M": # one-month subscription
|
||||||
|
raise Payment_error( u"invalid period3", params )
|
||||||
|
|
||||||
params[ u"cmd" ] = u"_notify-validate"
|
params[ u"cmd" ] = u"_notify-validate"
|
||||||
encoded_params = urllib.urlencode( params )
|
encoded_params = urllib.urlencode( params )
|
||||||
|
|
|
@ -405,14 +405,18 @@ class Test_controller( object ):
|
||||||
u"storage_quota_bytes": 1337 * 10,
|
u"storage_quota_bytes": 1337 * 10,
|
||||||
u"notebook_collaboration": True,
|
u"notebook_collaboration": True,
|
||||||
u"fee": 1.99,
|
u"fee": 1.99,
|
||||||
|
u"yearly_fee": 19.90,
|
||||||
u"button": u"[subscribe here user %s!] button",
|
u"button": u"[subscribe here user %s!] button",
|
||||||
|
u"yearly_button": u"[yearly subscribe here user %s!] button",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
u"name": "extra super",
|
u"name": "extra super",
|
||||||
u"storage_quota_bytes": 31337 * 1000,
|
u"storage_quota_bytes": 31337 * 1000,
|
||||||
u"notebook_collaboration": True,
|
u"notebook_collaboration": True,
|
||||||
u"fee": 9.00,
|
u"fee": 9.00,
|
||||||
|
u"yearly_fee": 90.00,
|
||||||
u"button": u"[or here user %s!] button",
|
u"button": u"[or here user %s!] button",
|
||||||
|
u"yearly_button": u"[yearly or here user %s!] button",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -2501,6 +2501,25 @@ class Test_users( Test_controller ):
|
||||||
user = self.database.load( User, self.user.object_id )
|
user = self.database.load( User, self.user.object_id )
|
||||||
assert user.rate_plan == 0
|
assert user.rate_plan == 0
|
||||||
|
|
||||||
|
def test_paypal_notify_payment_yearly( self ):
|
||||||
|
data = dict( self.PAYMENT_DATA )
|
||||||
|
data[ u"custom" ] = self.user.object_id
|
||||||
|
data[ u"payment_gross" ] = u"90.00"
|
||||||
|
data[ u"mc_gross" ] = u"90.00"
|
||||||
|
result = self.http_post( "/users/paypal_notify", data );
|
||||||
|
|
||||||
|
assert len( result ) == 1
|
||||||
|
assert result.get( u"session_id" )
|
||||||
|
assert Stub_urllib2.result == u"VERIFIED"
|
||||||
|
assert Stub_urllib2.headers.get( u"Content-type" ) == u"application/x-www-form-urlencoded"
|
||||||
|
assert Stub_urllib2.url.startswith( "https://" )
|
||||||
|
assert u"paypal.com" in Stub_urllib2.url
|
||||||
|
assert Stub_urllib2.encoded_params
|
||||||
|
|
||||||
|
# being notified of a mere payment should not change the user's rate plan
|
||||||
|
user = self.database.load( User, self.user.object_id )
|
||||||
|
assert user.rate_plan == 0
|
||||||
|
|
||||||
def test_paypal_notify_payment_invalid( self ):
|
def test_paypal_notify_payment_invalid( self ):
|
||||||
data = dict( self.PAYMENT_DATA )
|
data = dict( self.PAYMENT_DATA )
|
||||||
data[ u"custom" ] = self.user.object_id
|
data[ u"custom" ] = self.user.object_id
|
||||||
|
@ -2544,6 +2563,14 @@ class Test_users( Test_controller ):
|
||||||
|
|
||||||
assert result.get( u"error" )
|
assert result.get( u"error" )
|
||||||
|
|
||||||
|
def test_paypal_notify_payment_missing_item_number( self ):
|
||||||
|
data = dict( self.PAYMENT_DATA )
|
||||||
|
data[ u"custom" ] = self.user.object_id
|
||||||
|
result = self.http_post( "/users/paypal_notify", data );
|
||||||
|
|
||||||
|
assert len( result ) == 1
|
||||||
|
assert result.get( u"session_id" )
|
||||||
|
|
||||||
def test_paypal_notify_payment_incorrect_gross( self ):
|
def test_paypal_notify_payment_incorrect_gross( self ):
|
||||||
data = dict( self.PAYMENT_DATA )
|
data = dict( self.PAYMENT_DATA )
|
||||||
data[ u"custom" ] = self.user.object_id
|
data[ u"custom" ] = self.user.object_id
|
||||||
|
@ -2785,6 +2812,25 @@ class Test_users( Test_controller ):
|
||||||
user = self.database.load( User, self.user.object_id )
|
user = self.database.load( User, self.user.object_id )
|
||||||
assert user.rate_plan == 1
|
assert user.rate_plan == 1
|
||||||
|
|
||||||
|
def test_paypal_notify_signup_yearly( self ):
|
||||||
|
data = dict( self.SUBSCRIPTION_DATA )
|
||||||
|
data[ u"custom" ] = self.user.object_id
|
||||||
|
data[ u"amount3" ] = u"90.00"
|
||||||
|
data[ u"mc_amount3" ] = u"90.00"
|
||||||
|
data[ u"period3" ] = u"12 M"
|
||||||
|
result = self.http_post( "/users/paypal_notify", data );
|
||||||
|
|
||||||
|
assert len( result ) == 1
|
||||||
|
assert result.get( u"session_id" )
|
||||||
|
assert Stub_urllib2.result == u"VERIFIED"
|
||||||
|
assert Stub_urllib2.headers.get( u"Content-type" ) == u"application/x-www-form-urlencoded"
|
||||||
|
assert Stub_urllib2.url.startswith( "https://" )
|
||||||
|
assert u"paypal.com" in Stub_urllib2.url
|
||||||
|
assert Stub_urllib2.encoded_params
|
||||||
|
|
||||||
|
user = self.database.load( User, self.user.object_id )
|
||||||
|
assert user.rate_plan == 1
|
||||||
|
|
||||||
def test_paypal_notify_signup_invalid( self ):
|
def test_paypal_notify_signup_invalid( self ):
|
||||||
data = dict( self.SUBSCRIPTION_DATA )
|
data = dict( self.SUBSCRIPTION_DATA )
|
||||||
data[ u"custom" ] = self.user.object_id
|
data[ u"custom" ] = self.user.object_id
|
||||||
|
@ -2888,6 +2934,26 @@ class Test_users( Test_controller ):
|
||||||
user = self.database.load( User, self.user.object_id )
|
user = self.database.load( User, self.user.object_id )
|
||||||
assert user.rate_plan == 0
|
assert user.rate_plan == 0
|
||||||
|
|
||||||
|
def test_paypal_notify_signup_yearly_period3_with_monthly_amount( self ):
|
||||||
|
data = dict( self.SUBSCRIPTION_DATA )
|
||||||
|
data[ u"custom" ] = self.user.object_id
|
||||||
|
data[ u"period3" ] = u"12 M"
|
||||||
|
result = self.http_post( "/users/paypal_notify", data );
|
||||||
|
|
||||||
|
assert result.get( u"error" )
|
||||||
|
user = self.database.load( User, self.user.object_id )
|
||||||
|
assert user.rate_plan == 0
|
||||||
|
|
||||||
|
def test_paypal_notify_signup_yearly_amount_with_monthly_period3( self ):
|
||||||
|
data = dict( self.SUBSCRIPTION_DATA )
|
||||||
|
data[ u"custom" ] = self.user.object_id
|
||||||
|
data[ u"mc_amount3" ] = u"19.90"
|
||||||
|
result = self.http_post( "/users/paypal_notify", data );
|
||||||
|
|
||||||
|
assert result.get( u"error" )
|
||||||
|
user = self.database.load( User, self.user.object_id )
|
||||||
|
assert user.rate_plan == 0
|
||||||
|
|
||||||
def test_paypal_notify_signup_missing_custom( self ):
|
def test_paypal_notify_signup_missing_custom( self ):
|
||||||
data = dict( self.SUBSCRIPTION_DATA )
|
data = dict( self.SUBSCRIPTION_DATA )
|
||||||
result = self.http_post( "/users/paypal_notify", data );
|
result = self.http_post( "/users/paypal_notify", data );
|
||||||
|
@ -2952,6 +3018,29 @@ class Test_users( Test_controller ):
|
||||||
user = self.database.load( User, self.user.object_id )
|
user = self.database.load( User, self.user.object_id )
|
||||||
assert user.rate_plan == 1
|
assert user.rate_plan == 1
|
||||||
|
|
||||||
|
def test_paypal_notify_modify_yearly( self ):
|
||||||
|
self.user.rate_plan = 2
|
||||||
|
user = self.database.save( self.user )
|
||||||
|
|
||||||
|
data = dict( self.SUBSCRIPTION_DATA )
|
||||||
|
data[ u"txn_type" ] = u"subscr_modify"
|
||||||
|
data[ u"custom" ] = self.user.object_id
|
||||||
|
data[ u"amount3" ] = u"90.00"
|
||||||
|
data[ u"mc_amount3" ] = u"90.00"
|
||||||
|
data[ u"period3" ] = u"12 M"
|
||||||
|
result = self.http_post( "/users/paypal_notify", data );
|
||||||
|
|
||||||
|
assert len( result ) == 1
|
||||||
|
assert result.get( u"session_id" )
|
||||||
|
assert Stub_urllib2.result == u"VERIFIED"
|
||||||
|
assert Stub_urllib2.headers.get( u"Content-type" ) == u"application/x-www-form-urlencoded"
|
||||||
|
assert Stub_urllib2.url.startswith( "https://" )
|
||||||
|
assert u"paypal.com" in Stub_urllib2.url
|
||||||
|
assert Stub_urllib2.encoded_params
|
||||||
|
|
||||||
|
user = self.database.load( User, self.user.object_id )
|
||||||
|
assert user.rate_plan == 1
|
||||||
|
|
||||||
def test_paypal_notify_modify_invalid( self ):
|
def test_paypal_notify_modify_invalid( self ):
|
||||||
self.user.rate_plan = 2
|
self.user.rate_plan = 2
|
||||||
user = self.database.save( self.user )
|
user = self.database.save( self.user )
|
||||||
|
@ -3179,6 +3268,29 @@ class Test_users( Test_controller ):
|
||||||
user = self.database.load( User, self.user.object_id )
|
user = self.database.load( User, self.user.object_id )
|
||||||
assert user.rate_plan == 0
|
assert user.rate_plan == 0
|
||||||
|
|
||||||
|
def test_paypal_notify_cancel_yearly( self ):
|
||||||
|
self.user.rate_plan = 1
|
||||||
|
user = self.database.save( self.user )
|
||||||
|
|
||||||
|
data = dict( self.SUBSCRIPTION_DATA )
|
||||||
|
data[ u"txn_type" ] = u"subscr_cancel"
|
||||||
|
data[ u"custom" ] = self.user.object_id
|
||||||
|
data[ u"amount3" ] = u"90.00"
|
||||||
|
data[ u"mc_amount3" ] = u"90.00"
|
||||||
|
data[ u"period3" ] = u"12 M"
|
||||||
|
result = self.http_post( "/users/paypal_notify", data );
|
||||||
|
|
||||||
|
assert len( result ) == 1
|
||||||
|
assert result.get( u"session_id" )
|
||||||
|
assert Stub_urllib2.result == u"VERIFIED"
|
||||||
|
assert Stub_urllib2.headers.get( u"Content-type" ) == u"application/x-www-form-urlencoded"
|
||||||
|
assert Stub_urllib2.url.startswith( "https://" )
|
||||||
|
assert u"paypal.com" in Stub_urllib2.url
|
||||||
|
assert Stub_urllib2.encoded_params
|
||||||
|
|
||||||
|
user = self.database.load( User, self.user.object_id )
|
||||||
|
assert user.rate_plan == 0
|
||||||
|
|
||||||
def test_paypal_notify_cancel_invalid( self ):
|
def test_paypal_notify_cancel_invalid( self ):
|
||||||
self.user.rate_plan = 1
|
self.user.rate_plan = 1
|
||||||
user = self.database.save( self.user )
|
user = self.database.save( self.user )
|
||||||
|
|
|
@ -137,7 +137,12 @@ class Upgrade_page( Product_page ):
|
||||||
|
|
||||||
P(
|
P(
|
||||||
Table(
|
Table(
|
||||||
self.fee_row( rate_plans, user, include_blank = False ),
|
Tr( Td(
|
||||||
|
u"Get two months free with a yearly subscription!",
|
||||||
|
class_ = u"upgrade_subtitle",
|
||||||
|
colspan = u"3",
|
||||||
|
), colspan = u"3" ),
|
||||||
|
self.fee_row( rate_plans, user, include_blank = False, yearly = True ),
|
||||||
Tr(
|
Tr(
|
||||||
[ Td(
|
[ Td(
|
||||||
plan[ u"storage_quota_bytes" ] // MEGABYTE, " MB",
|
plan[ u"storage_quota_bytes" ] // MEGABYTE, " MB",
|
||||||
|
@ -164,20 +169,26 @@ class Upgrade_page( Product_page ):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def fee_row( self, rate_plans, user, include_blank = True ):
|
def fee_row( self, rate_plans, user, include_blank = True, yearly = False ):
|
||||||
return Tr(
|
return Tr(
|
||||||
include_blank and Th( u" " ) or None,
|
include_blank and Th( u" " ) or None,
|
||||||
[ Th(
|
[ Th(
|
||||||
plan[ u"name" ].capitalize(),
|
plan[ u"name" ].capitalize(),
|
||||||
plan[ u"fee" ] and Div(
|
plan[ u"fee" ] and Div(
|
||||||
Span(
|
yearly and Span(
|
||||||
|
u"$%s" % plan[ u"yearly_fee" ],
|
||||||
|
Span( u"/year", class_ = u"month_text" ),
|
||||||
|
class_ = u"price_text",
|
||||||
|
separator = u"",
|
||||||
|
) or Span(
|
||||||
u"$%s" % plan[ u"fee" ],
|
u"$%s" % plan[ u"fee" ],
|
||||||
Span( u"/month", class_ = u"month_text" ),
|
Span( u"/month", class_ = u"month_text" ),
|
||||||
class_ = u"price_text",
|
class_ = u"price_text",
|
||||||
separator = u"",
|
separator = u"",
|
||||||
),
|
),
|
||||||
user and user.username not in ( u"anonymous", None ) and user.rate_plan != index \
|
user and user.username not in ( u"anonymous", None ) and user.rate_plan != index \
|
||||||
and plan.get( u"button" ).strip() and plan.get( u"button" ) % user.object_id or None,
|
and yearly and ( plan.get( u"yearly_button" ).strip() and plan.get( u"yearly_button" ) % user.object_id or None ) or \
|
||||||
|
( plan.get( u"button" ).strip() and plan.get( u"button" ) % user.object_id or None ),
|
||||||
) or None,
|
) or None,
|
||||||
( not user or user.username in ( u"anonymous", None ) ) and Div(
|
( not user or user.username in ( u"anonymous", None ) ) and Div(
|
||||||
A(
|
A(
|
||||||
|
|
Reference in New Issue