Checkpointing stripe work.

This commit is contained in:
yackob03 2013-10-02 00:48:03 -04:00
parent 211fd6bcd7
commit 7bd18c1bab
11 changed files with 172 additions and 6 deletions

3
app.py
View file

@ -1,5 +1,6 @@
import logging
import os
import stripe
from flask import Flask
from flask.ext.principal import Principal
@ -29,3 +30,5 @@ login_manager.login_view = 'signin'
mail = Mail()
mail.init_app(app)
stripe.api_key = app.config['STRIPE_SECRET_KEY']

View file

@ -54,7 +54,18 @@ class LocalStorage(object):
LOCAL_STORAGE_DIR = '/tmp/registry'
class DebugConfig(FlaskConfig, MailConfig, LocalStorage, SQLiteDB):
class StripeTestConfig(object):
STRIPE_SECRET_KEY = 'sk_test_PEbmJCYrLXPW0VRLSnWUiZ7Y'
STRIPE_PUBLISHABLE_KEY = 'pk_test_uEDHANKm9CHCvVa2DLcipGRh'
class StripeLiveConfig(object):
STRIPE_SECRET_KEY = 'sk_live_TRuTHYwTvmrLeU3ib7Z9hpqE'
STRIPE_PUBLISHABLE_KEY = 'pk_live_P5wLU0vGdHnZGyKnXlFG4oiu'
class DebugConfig(FlaskConfig, MailConfig, LocalStorage, SQLiteDB,
StripeTestConfig):
REGISTRY_SERVER = 'localhost:5000'
LOGGING_CONFIG = {
'level': logging.DEBUG,
@ -62,7 +73,8 @@ class DebugConfig(FlaskConfig, MailConfig, LocalStorage, SQLiteDB):
}
class LocalHostedConfig(FlaskConfig, MailConfig, S3Storage, RDSMySQL):
class LocalHostedConfig(FlaskConfig, MailConfig, S3Storage, RDSMySQL,
StripeLiveConfig):
REGISTRY_SERVER = 'localhost:5000'
LOGGING_CONFIG = {
'level': logging.DEBUG,
@ -70,7 +82,8 @@ class LocalHostedConfig(FlaskConfig, MailConfig, S3Storage, RDSMySQL):
}
class ProductionConfig(FlaskConfig, MailConfig, S3Storage, RDSMySQL):
class ProductionConfig(FlaskConfig, MailConfig, S3Storage, RDSMySQL,
StripeLiveConfig):
REGISTRY_SERVER = 'quay.io'
LOGGING_CONFIG = {
'stream': sys.stderr,

View file

@ -22,6 +22,7 @@ class User(BaseModel):
password_hash = CharField()
email = CharField(unique=True, index=True)
verified = BooleanField(default=False)
stripe_id = CharField(index=True, null=True)
class Visibility(BaseModel):

View file

@ -1,4 +1,5 @@
import logging
import stripe
from flask import request, make_response, jsonify, abort
from flask.ext.login import login_required, current_user
@ -317,3 +318,53 @@ def delete_permissions(namespace, repository, username):
return make_response('Deleted', 204)
abort(403) # Permission denied
def subscription_view(stripe_subscription):
return {
'current_period_start': stripe_subscription.current_period_start,
'current_period_end': stripe_subscription.current_period_end,
'plan': stripe_subscription.plan.id,
}
@app.route('/api/user/plan', methods=['PUT'])
@api_login_required
def subscribe():
# Amount in cents
amount = 500
request_data = request.get_json()
plan = request_data['plan']
card = request_data['token']
user = current_user.db_user
if not user.stripe_id:
# Create the customer and plan simultaneously
cus = stripe.Customer.create(email=user.email, plan=plan, card=card)
user.stripe_id = cus.id
user.save()
resp = jsonify(subscription_view(cus.subscription))
resp.status_code = 201
return resp
else:
# Change the plan
cus = stripe.Customer.retrieve(user.stripe_id)
cus.plan = plan
cus.save()
return jsonify(subscription_view(cus.subscription))
@app.route('/api/user/plan', methods=['GET'])
@api_login_required
def get_subscription():
user = current_user.db_user
if user.stripe_id:
cus = stripe.Customer.retrieve(user.stripe_id)
return jsonify(subscription_view(cus.subscription))
abort(404)

View file

@ -6,4 +6,5 @@ Flask-Login
Flask-Mail
python-dateutil
boto
pymysql
pymysql
stripe

View file

@ -9,9 +9,14 @@ Werkzeug==0.9.4
argparse==1.2.1
blinker==1.3
boto==2.13.3
distribute==0.6.34
ipdb==0.8
ipython==1.1.0
itsdangerous==0.23
peewee==2.1.4
py-bcrypt==0.4
python-dateutil==2.1
requests==2.0.0
six==1.4.1
stripe==1.9.5
wsgiref==0.1.2

View file

@ -72,6 +72,7 @@ quayApp = angular.module('quay', ['restangular', 'angularMoment'], function($pro
when('/repository/:namespace/:name/tag/:tag', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl}).
when('/repository/:namespace/:name/admin', {templateUrl: '/static/partials/repo-admin.html', controller:RepoAdminCtrl}).
when('/repository/', {title: 'Repositories', templateUrl: '/static/partials/repo-list.html', controller: RepoListCtrl}).
when('/user', {title: 'User Admin', templateUrl: '/static/partials/user-admin.html', controller: UserAdminCtrl}).
when('/', {title: 'Quay', templateUrl: '/static/partials/landing.html', controller: LandingCtrl}).
otherwise({redirectTo: '/'});
}]).

View file

@ -175,7 +175,7 @@ function RepoCtrl($scope, Restangular, $routeParams, $rootScope) {
$scope.listImages = function() {
if ($scope.imageHistory) { return; }
var imageFetch = Restangular.one('repository/' + namespace + '/' + name + '/tag/' + $scope.currentTag.name + '/images');
var imageFetch = Restangular.one('repository/' + namespace + '/' + name + '/tag/' + $scope.currentTag.name + '/images');
imageFetch.get().then(function(resp) {
$scope.imageHistory = resp.images;
});
@ -365,4 +365,74 @@ function RepoAdminCtrl($scope, Restangular, $routeParams, $rootScope) {
$rootScope.title = 'Unknown Repository';
$scope.loading = false;
});
}
function UserAdminCtrl($scope, Restangular) {
$scope.plans = [
{
title: 'Micro',
price: 700,
privateRepos: 5,
stripeId: 'micro',
},
{
title: 'Small',
price: 1200,
privateRepos: 10,
stripeId: 'small',
},
{
title: 'Medium',
price: 2200,
privateRepos: 20,
stripeId: 'medium',
},
];
var planDict = {};
var i;
for(i = 0; i < $scope.plans.length; i++) {
planDict[$scope.plans[i].stripeId] = $scope.plans[i];
}
var getSubscription = Restangular.one('user/plan');
getSubscription.get().then(function(sub) {
// User has a subscription
$scope.subscription = sub;
});
$scope.subscribe = function(planId) {
var submitToken = function(token) {
$scope.$apply(function() {
var subscriptionDetails = {
token: token.id,
plan: planId,
};
console.log(subscriptionDetails);
var createSubscriptionRequest = Restangular.one('user/plan');
createSubscriptionRequest.customPUT(subscriptionDetails).then(function() {
// Success
console.log('successfully created subscription');
}, function() {
// Failure
console.log('failed to created subscription');
});
});
};
console.log('Got request for plan: ' + planId);
var planDetails = planDict[planId]
StripeCheckout.open({
key: 'pk_test_uEDHANKm9CHCvVa2DLcipGRh',
address: false, // TODO change to true
amount: planDetails.price,
currency: 'usd',
name: 'Quay ' + planDetails.title + ' Subscription',
description: 'Up to ' + planDetails.privateRepos + ' private repositories',
panelLabel: 'Subscribe',
token: submitToken
});
};
}

View file

@ -0,0 +1,21 @@
<div class="container">
<div class="row">
<div class="col-md-4" ng-repeat='plan in plans'>
<div class="panel" ng-class="{'panel-success': subscription.plan == plan.stripeId, 'panel-default': subscription.plan != plan.stripeId}">
<div class="panel-heading">
{{ plan.title }}
<span class="pull-right" ng-show="subscription.plan == plan.stripeId">
<i class="icon-ok"></i>
Subscribed
</span>
</div>
<div class="panel-body">
{{ plan.privateRepos }} Private Repositories<br>
<button class="btn btn-primary" ng-hide="subscription" ng-click="subscribe(plan.stripeId)">Subscribe</button>
<button class="btn" ng-show="subscription && (subscription.plan != plan.stripeId)" ng-click="subscribe(plan.stripeId)">Change</button>
<button class="btn btn-danger" ng-show="subscription.plan == plan.stripeId" ng-click="cancel()">Cancel</button>
</div>
</div>
</div>
</div>
</div>

View file

@ -10,9 +10,9 @@
<link rel="stylesheet" href="/static/css/quay.css">
<script src="//code.jquery.com/jquery.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="https://checkout.stripe.com/v2/checkout.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
<script src="//cdn.jsdelivr.net/underscorejs/1.5.2/underscore-min.js"></script>

BIN
test.db

Binary file not shown.