Skip to content

Commit

Permalink
Remove reliance on fixed Price IDs.
Browse files Browse the repository at this point in the history
Switch to using metadata on the price to get the number of calls for a
price, rather than assuming it can be worked out from the price (plan)
ID.
  • Loading branch information
dracos committed Nov 20, 2024
1 parent 5040065 commit e12aa05
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 65 deletions.
6 changes: 6 additions & 0 deletions conf/general.yml-example
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ STRIPE_TAX_RATE: ''

BULK_LOOKUP_AMOUNT: 50
BULK_LOOKUP_PRICE_ID: 'price_123'
PRICING_TIER_1_ID: 'price_123'
PRICING_TIER_2_ID: 'price_456'
PRICING_TIER_3_ID: 'price_789'
PRICING_TIER_1_AMOUNT: '20'
PRICING_TIER_2_AMOUNT: '100'
PRICING_TIER_3_AMOUNT: '300'

# Mapped to Django's DEBUG and TEMPLATE_DEBUG settings. Optional, defaults to True.
DEBUG: True
Expand Down
13 changes: 10 additions & 3 deletions mapit_mysociety_org/management/commands/add_mapit_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class Command(BaseCommand):
help = "Create a new user with associated Stripe subscription"

def add_arguments(self, parser):
prices = stripe.Price.list(limit=100)
price_ids = [price.id for price in prices.data if price.id.startswith('mapit')]
products = stripe.Product.list(limit=100)
price_ids = [p.name for p in products if p.name.startswith('MapIt')]
coupons = stripe.Coupon.list()
self.coupon_ids = [coupon['id'] for coupon in coupons if coupon['id'].startswith('charitable')]
parser.add_argument('--email', required=True)
Expand All @@ -31,6 +31,9 @@ def handle(self, *args, **options):
coupon = options['coupon']
price = options['price']

products = stripe.Product.list(limit=100, expand=['data.default_price'])
price = [p for p in products if p.name == price][0].default_price

if coupon not in self.coupon_ids:
# coupon ID of the form charitableN(-Nmonths)
m = re.match(r'charitable(\d+)(?:-(\d+)month)?', coupon)
Expand All @@ -53,7 +56,11 @@ def handle(self, *args, **options):

customer = stripe.Customer.create(email=email).id
stripe_sub = stripe.Subscription.create(
customer=customer, items=[{"price": price}], coupon=coupon, trial_period_days=options['trial']).id
default_tax_rates=[settings.STRIPE_TAX_RATE],
customer=customer,
items=[{"price": price.id}],
coupon=coupon,
trial_period_days=options['trial']).id

sub = Subscription.objects.create(user=user, stripe_id=stripe_sub)
sub.redis_update_max(price)
Expand Down
17 changes: 12 additions & 5 deletions mapit_mysociety_org/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,15 @@ def allow_migrate(self, db, app_label, model_name=None, **hints):
BULK_LOOKUP_PRICE_ID = config.get('BULK_LOOKUP_PRICE_ID')

# API subscriptions
PRICING = [
{'id': 'mapit-10k-v', 'price': 20, 'calls': '10,000'},
{'id': 'mapit-100k-v', 'price': 100, 'calls': '100,000'},
{'id': 'mapit-0k-v', 'price': 300, 'calls': '0'},
]
if 'test' in sys.argv:
PRICING = [
{'id': 'price_123', 'price': 20, 'calls': '10,000'},
{'id': 'price_456', 'price': 100, 'calls': '100,000'},
{'id': 'price_789', 'price': 300, 'calls': '0'},
]
else:
PRICING = [
{'id': config.get('PRICING_TIER_1_ID'), 'price': config.get('PRICING_TIER_1_AMOUNT'), 'calls': '10,000'},
{'id': config.get('PRICING_TIER_2_ID'), 'price': config.get('PRICING_TIER_2_AMOUNT'), 'calls': '100,000'},
{'id': config.get('PRICING_TIER_3_ID'), 'price': config.get('PRICING_TIER_3_AMOUNT'), 'calls': '0'},
]
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@
{% include 'account/_form_field.html' with field=form.password_confirm %}
{% endif %}

{% include 'account/_form_field.html' with field=form.plan %}
<span id="js-price-information"
data-{{ PRICING.0.id }}="{{ PRICING.0.price }}"
data-{{ PRICING.1.id }}="{{ PRICING.1.price }}"
data-{{ PRICING.2.id }}="{{ PRICING.2.price }}"></span>

{% include 'account/_form_field.html' with field=form.price %}
{% include 'account/_form_field_checkbox.html' with field=form.charitable_tick %}
<div id="charitable-qns"{% if not form.charitable_tick.value %} style="display:none"{% endif %}>
{% include 'account/_form_field.html' with field=form.charitable %}
Expand Down
29 changes: 14 additions & 15 deletions mapit_mysociety_org/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import re
from io import StringIO
from mock import patch
from mock import patch, ANY

from django.contrib.auth.models import User
from django.contrib.sites.models import Site
Expand All @@ -28,7 +28,7 @@ def test_signup(self, socket):
'password': 'password',
'password_confirm': 'password',
'tandcs_tick': 1,
'price': 'mapit-10k-v',
'price': 'price_123',
'charitable_tick': 1,
'charitable': 'c',
'charity_number': '123',
Expand Down Expand Up @@ -59,7 +59,7 @@ def test_signup_card_error(self):
'password': 'password',
'password_confirm': 'password',
'tandcs_tick': 1,
'price': 'mapit-10k-v',
'price': 'price_123',
'charitable_tick': 1,
'charitable': 'c',
'charity_number': '123',
Expand All @@ -71,25 +71,24 @@ def test_signup_card_error(self):
class ManagementTest(PatchedStripeMixin, PatchedRedisTestCase):
def test_add_mapit_user(self):
with patch('mapit_mysociety_org.management.commands.add_mapit_user.stripe', self.MockStripe):
self.MockStripe.Price.list.return_value = convert_to_stripe_object({
'data': [
{'id': 'mapit-0k-v',
'product': {'id': 'prod_GHI', 'name': 'MapIt, unlimited calls'}},
{'id': 'mapit-10k-v',
'product': {'id': 'prod_ABC', 'name': 'MapIt, 10,000 calls'}},
{'id': 'mapit-100k-v',
'product': {'id': 'prod_DEF', 'name': 'MapIt, 100,000 calls'}},
]
}, None, None)
self.MockStripe.Product.list.return_value = convert_to_stripe_object([
{'id': 'prod_GHI', 'name': 'MapIt, unlimited calls',
'default_price': {'id': 'price_789', 'metadata': {'calls': '0'}}},
{'id': 'prod_ABC', 'name': 'MapIt, 10,000 calls',
'default_price': {'id': 'price_123', 'metadata': {'calls': '10000'}}},
{'id': 'prod_DEF', 'name': 'MapIt, 100,000 calls',
'default_price': {'id': 'price_456', 'metadata': {'calls': '100000'}}},
], None, None)
call_command(
'add_mapit_user', '--email', '[email protected]', '--price', "mapit-100k-v",
'add_mapit_user', '--email', '[email protected]', '--price', "MapIt, 100,000 calls",
coupon='charitable25-6months', trial='10',
stdout=StringIO(), stderr=StringIO())

self.MockStripe.Coupon.create.assert_called_once_with(
id='charitable25-6months', duration='repeating', duration_in_months='6', percent_off='25')
self.MockStripe.Subscription.create.assert_called_once_with(
customer='CUSTOMER-ID', items=[{"price": 'mapit-100k-v'}],
customer='CUSTOMER-ID', items=[{"price": 'price_456'}],
default_tax_rates=[ANY],
coupon='charitable25-6months', trial_period_days='10')

user = User.objects.get(email='[email protected]')
Expand Down
8 changes: 2 additions & 6 deletions static/js/payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@

function price_cost() {
var price = document.querySelector('input[name=price]:checked'),
pricing = document.getElementById('js-price-information'),
ctick = document.getElementById('id_charitable_tick'),
c = document.querySelector('input[name=charitable]:checked');
price = price ? price.value : '';
ctick = ctick ? ctick.checked : '';
c = c ? c.value : '';
var num = 20;
if (plan === 'mapit-100k-v') {
num = 100;
} else if (plan === 'mapit-0k-v') {
num = 300;
}
var num = pricing.dataset[price] || 20;
if (ctick) {
if (c === 'c' || c === 'i') {
if (num === 20) {
Expand Down
5 changes: 1 addition & 4 deletions subscriptions/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import re

from django.conf import settings
from django.db import models
from django.dispatch import receiver
Expand Down Expand Up @@ -42,8 +40,7 @@ def redis_key_history(self):
return "{0}:history".format(self.redis_key)

def redis_update_max(self, price):
m = re.match(r'mapit-(\d+)k', price)
max = int(m.group(1)) * 1000
max = int(price.metadata['calls'])
r = redis_connection()
r.set(self.redis_key_max, max)
r.delete(self.redis_key_blocked)
Expand Down
Loading

0 comments on commit e12aa05

Please sign in to comment.