test payments working (sort of)
This commit is contained in:
@@ -18,6 +18,10 @@ from django.forms import formset_factory
|
|||||||
from utils import landingpages
|
from utils import landingpages
|
||||||
from metrics import axes
|
from metrics import axes
|
||||||
|
|
||||||
|
# Braintree form
|
||||||
|
class BrainTreeForm(forms.Form):
|
||||||
|
amount = forms.FloatField(required=True)
|
||||||
|
payment_method_nonce = forms.CharField(max_length=255)
|
||||||
|
|
||||||
|
|
||||||
# login form
|
# login form
|
||||||
|
|||||||
295
rowers/templates/payments.html
Normal file
295
rowers/templates/payments.html
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
|
||||||
|
{% extends "newbase.html" %}
|
||||||
|
{% block title %}Rowsandall Pro Membership{% endblock title %}
|
||||||
|
{% block main %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
|
||||||
|
<h1>Payments</h1>
|
||||||
|
|
||||||
|
<ul class="main-content">
|
||||||
|
<li class="grid_2">
|
||||||
|
<p>Donations are welcome to keep this web site going. To help cover the hosting
|
||||||
|
costs, I have created several paid plans offering advanced functionality.
|
||||||
|
Once I process your
|
||||||
|
donation, I will give you access to some <q>special</q> features on this
|
||||||
|
website. </p>
|
||||||
|
|
||||||
|
<p>The following table gives an overview of the different plans. As we are
|
||||||
|
constantly developing new functionality, the table might be slightly outdated. Don't
|
||||||
|
hesitate to contact us. </p>
|
||||||
|
|
||||||
|
<p>The Pro membership is open for a free 14 day trial</p>
|
||||||
|
<p>
|
||||||
|
<table class="listtable paddedtable" width="80%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th> </th>
|
||||||
|
<th>BASIC</th>
|
||||||
|
<th>PRO</th>
|
||||||
|
<th>SELF-COACH</th>
|
||||||
|
<th>COACH</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Basic rowing metrics (spm, time, distance, heart rate, power)</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Manual Import, Export, Synchronization and download of all your data</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Automatic Synchronization with other fitness sites</td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Heart rate and power zones</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Ranking Pieces, Stroke Analysis</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Advanced Analysis (Critical Power, Stats, Box Chart, Trend Flex)</td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Compare Workouts</td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Empower Stroke Profile</td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Sensor Fusion, Split Workout, In-stroke metrics</td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Create Training plans, tests and challenges for yourself. Track your performance
|
||||||
|
against plan.</td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Create Training plans, tests and challenges for your athletes. Track their performance
|
||||||
|
against plan. </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Create and manage teams.</td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Manage your athlete's workouts</td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
<h2>Coach and Self-Coach Membership</h2>
|
||||||
|
|
||||||
|
<p>The Coach plan functionality listed is available to the coach only. Individual athletes
|
||||||
|
can purchase upgrades to "Pro" and "Self-Coach" plans.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Rowsandall.com's Training Planning functionality
|
||||||
|
is part of the paid "Self-Coach" and "Coach" plans.</p>
|
||||||
|
|
||||||
|
<p>On the "Self-Coach" plan, you can plan your own sessions.</p>
|
||||||
|
|
||||||
|
<p>On the "Coach" plan, you can establish teams, see workouts done by
|
||||||
|
athletes on your team, and plan individual and group sessions for your
|
||||||
|
athletes.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>If you would like to find a coach who helps you plan your training
|
||||||
|
through rowsandall.com, contact me throught the contact form.</p>
|
||||||
|
|
||||||
|
{% if user.rower.rowerplan == 'basic' and user.rower.protrialexpires|date_dif == 1 %}
|
||||||
|
<h2>Free Trial</h2>
|
||||||
|
<p>
|
||||||
|
You qualify for a 14 day free trial. No credit card needed.
|
||||||
|
Try out Pro or Self-Coach membership for two weeks. Click the button below to
|
||||||
|
sign up for the trial. After your trial period expires, you will be
|
||||||
|
automatically reset to the Basic plan, unless you upgrade to Pro.
|
||||||
|
</p>
|
||||||
|
<p><a class="button green small" href="/rowers/starttrial">Yes, I want to try Pro membership for 14 days for free. No strings attached.</a></p>
|
||||||
|
<p><a class="button green small" href="/rowers/startplantrial">Yes, I want to try Self-Coach membership for 14 days for free. No strings attached.</a></p>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
<li class="grid_2">
|
||||||
|
<p>Click on the PayPal button to pay for your Pro membership. Before you pay, please <a href="/rowers/register">register</a> for the free Basic membership and add your user name to the form.
|
||||||
|
Your payment will be valid for one year.
|
||||||
|
You will be taken to the secure PayPal payment site.
|
||||||
|
</p>
|
||||||
|
<h2>Recurring Payment</h2>
|
||||||
|
<p>You need a Paypal account for this. This is plan will automatically renew each year.</p>
|
||||||
|
<p>
|
||||||
|
<form class="paypal" action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||||
|
<input type="hidden" name="cmd" value="_s-xclick">
|
||||||
|
<input type="hidden" name="hosted_button_id" value="964GLEXX3THAW">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="hidden" name="on0" value="Plans">Plans
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<select name="os0">
|
||||||
|
<option value="Pro Membership">Pro Membership : €15.00 EUR - yearly</option>
|
||||||
|
<option value="Self-Coach Membership">Self-Coach Membership : €65.00 EUR - yearly</option>
|
||||||
|
<option value="Coach 4 athletes or less">Coach 4 athletes or less : €90.00 EUR - yearly</option>
|
||||||
|
<option value="Coach 4-10 athletes">Coach 4-10 athletes : €200.00 EUR - yearly</option>
|
||||||
|
<option value="Coach more than 10 athletes">Coach more than 10 athletes : €450.00 EUR - yearly</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="hidden" name="on1" value="Your User Name">Your User Name
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="os1" maxlength="200">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<input type="hidden" name="currency_code" value="EUR">
|
||||||
|
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_subscribeCC_LG_global.gif" border="0" name="submit" alt="PayPal – The safer, easier way to pay online!">
|
||||||
|
<img class="paypalpix" alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
||||||
|
</form>
|
||||||
|
</p>
|
||||||
|
<h2>One Year Subscription</h2>
|
||||||
|
<p>Only a credit card needed. Will not automatically renew</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<form class="paypal" action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top"
|
||||||
|
>
|
||||||
|
<input type="hidden" name="cmd" value="_s-xclick">
|
||||||
|
<input type="hidden" name="hosted_button_id" value="2YB32HQTF96QW">
|
||||||
|
<table>
|
||||||
|
<tr><td><input type="hidden" name="on0" value="Plans">Plans</td></tr><tr><td><select name="os0">
|
||||||
|
<option value="Pro Membership">Pro Membership €20.00 EUR</option>
|
||||||
|
<option value="Self-Coach Membership">Self-Coach Membership €75.00 EUR</option>
|
||||||
|
<option value="Coach - 4 athletes or less">Coach - 4 athletes or less €120.00 EUR</option>
|
||||||
|
<option value="Coach - 4-10 athletes">Coach - 4-10 athletes €250.00 EUR</option>
|
||||||
|
<option value="Coach - more than 10 athletes">Coach - more than 10 athletes €500.00 EUR</option>
|
||||||
|
</select> </td></tr>
|
||||||
|
<tr><td><input type="hidden" name="on1" value="Your User Name">Your User Name</td></tr><tr><td><input type="text" name="os1" maxlength="200"></td></tr>
|
||||||
|
</table>
|
||||||
|
<input type="hidden" name="currency_code" value="EUR">
|
||||||
|
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_buynowCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||||
|
<img class="paypalpix" alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
||||||
|
</form>
|
||||||
|
</p>
|
||||||
|
<h2>Payment Processing</h2>
|
||||||
|
<p>After you do the payment, we will manually change your membership to
|
||||||
|
"Pro". Depending on our availability, this may take some time
|
||||||
|
(typically one working day). Don't hesitate to contact us
|
||||||
|
if you have any questions at this stage.</p>
|
||||||
|
|
||||||
|
<p>If, for any reason, you are not happy with your Pro membership, please let me know through the contact form. I will contact you as soon as possible to discuss how we can make things better.</p>
|
||||||
|
|
||||||
|
<h2>BrainTree Experimental Corner</h2>
|
||||||
|
|
||||||
|
<form id="payment-form" method="post" action="/rowers/checkouts">
|
||||||
|
<section>
|
||||||
|
<label for="amount">
|
||||||
|
<span class="input-label">Amount</span>
|
||||||
|
<div class="input-wrapper amount-wrapper">
|
||||||
|
<input id="amount" name="amount" type="tel" min="1" placeholder="Amount" value="10">
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="bt-drop-in-wrapper">
|
||||||
|
<div id="bt-dropin"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<input type="hidden" id="nonce" name="payment_method_nonce" />
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit" id="submit-button"><span>Test Transaction</span></button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script src="https://js.braintreegateway.com/web/dropin/1.14.1/js/dropin.min.js"></script>
|
||||||
|
<script>
|
||||||
|
var form = document.querySelector('#payment-form');
|
||||||
|
var client_token = '{{ client_token }}';
|
||||||
|
braintree.dropin.create({
|
||||||
|
authorization: client_token,
|
||||||
|
container: '#bt-dropin',
|
||||||
|
paypal: {
|
||||||
|
flow: 'checkout'
|
||||||
|
}
|
||||||
|
}, function (createErr, instance) {
|
||||||
|
form.addEventListener('submit', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
instance.requestPaymentMethod(function (err, payload) {
|
||||||
|
if (err) {
|
||||||
|
console.log('Error', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Add the nonce to the form and submit
|
||||||
|
document.querySelector('#nonce').value = payload.nonce;
|
||||||
|
form.submit();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block sidebar %}
|
||||||
|
{% include 'menu_help.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
@@ -233,6 +233,7 @@
|
|||||||
|
|
||||||
<p>If, for any reason, you are not happy with your Pro membership, please let me know through the contact form. I will contact you as soon as possible to discuss how we can make things better.</p>
|
<p>If, for any reason, you are not happy with your Pro membership, please let me know through the contact form. I will contact you as soon as possible to discuss how we can make things better.</p>
|
||||||
|
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|||||||
@@ -437,6 +437,8 @@ urlpatterns = [
|
|||||||
url(r'^analysis/$', views.analysis_view,name='analysis'),
|
url(r'^analysis/$', views.analysis_view,name='analysis'),
|
||||||
url(r'^laboratory/$', views.laboratory_view,name='laboratory'),
|
url(r'^laboratory/$', views.laboratory_view,name='laboratory'),
|
||||||
url(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'),
|
url(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'),
|
||||||
|
url(r'^checkouts',views.checkouts_view,name='checkouts'),
|
||||||
|
url(r'^payments',views.payments_view,name='payments'),
|
||||||
url(r'^planrequired',views.planrequired_view),
|
url(r'^planrequired',views.planrequired_view),
|
||||||
url(r'^starttrial$',views.start_trial_view),
|
url(r'^starttrial$',views.start_trial_view),
|
||||||
url(r'^startplantrial$',views.start_plantrial_view),
|
url(r'^startplantrial$',views.start_plantrial_view),
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import isodate
|
|||||||
import re
|
import re
|
||||||
import cgi
|
import cgi
|
||||||
from icalendar import Calendar, Event
|
from icalendar import Calendar, Event
|
||||||
|
import braintree
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
@@ -50,7 +51,7 @@ from rowers.forms import (
|
|||||||
RaceResultFilterForm,PowerIntervalUpdateForm,FlexAxesForm,
|
RaceResultFilterForm,PowerIntervalUpdateForm,FlexAxesForm,
|
||||||
FlexOptionsForm,DataFrameColumnsForm,OteWorkoutTypeForm,
|
FlexOptionsForm,DataFrameColumnsForm,OteWorkoutTypeForm,
|
||||||
MetricsForm,DisqualificationForm,disqualificationreasons,
|
MetricsForm,DisqualificationForm,disqualificationreasons,
|
||||||
disqualifiers,SearchForm,
|
disqualifiers,SearchForm,BrainTreeForm
|
||||||
)
|
)
|
||||||
from django.core.urlresolvers import reverse, reverse_lazy
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
|
|
||||||
@@ -139,6 +140,7 @@ from rowsandall_app.settings import (
|
|||||||
UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY,
|
UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY,
|
||||||
RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET,
|
RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET,
|
||||||
TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET,
|
TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET,
|
||||||
|
BRAINTREE_MERCHANT_ID,BRAINTREE_PUBLIC_KEY,BRAINTREE_PRIVATE_KEY
|
||||||
)
|
)
|
||||||
|
|
||||||
from rowers.tasks_standalone import addcomment2
|
from rowers.tasks_standalone import addcomment2
|
||||||
@@ -1027,6 +1029,87 @@ def add_defaultfavorites(r):
|
|||||||
f.save()
|
f.save()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# Experimental - Payments
|
||||||
|
@login_required()
|
||||||
|
def payments_view(request):
|
||||||
|
|
||||||
|
r = getrequestrower(request)
|
||||||
|
|
||||||
|
gateway = braintree.BraintreeGateway(
|
||||||
|
braintree.Configuration(
|
||||||
|
braintree.Environment.Sandbox,
|
||||||
|
merchant_id=BRAINTREE_MERCHANT_ID,
|
||||||
|
public_key=BRAINTREE_PUBLIC_KEY,
|
||||||
|
private_key=BRAINTREE_PRIVATE_KEY,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# add code to store customer_id
|
||||||
|
|
||||||
|
client_token = gateway.client_token.generate({
|
||||||
|
"customer_id": r.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
return render(request,
|
||||||
|
"payments.html",
|
||||||
|
{
|
||||||
|
'client_token':client_token,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def checkouts_view(request):
|
||||||
|
|
||||||
|
r = getrequestrower(request)
|
||||||
|
|
||||||
|
if request.method != 'POST':
|
||||||
|
url = reverse(payments_view)
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
# we're still here
|
||||||
|
gateway = braintree.BraintreeGateway(
|
||||||
|
braintree.Configuration(
|
||||||
|
braintree.Environment.Sandbox,
|
||||||
|
merchant_id=BRAINTREE_MERCHANT_ID,
|
||||||
|
public_key=BRAINTREE_PUBLIC_KEY,
|
||||||
|
private_key=BRAINTREE_PRIVATE_KEY,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
form = BrainTreeForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
nonce_from_the_client = form.cleaned_data['payment_method_nonce']
|
||||||
|
amount = form.cleaned_data['amount']
|
||||||
|
amount = str(amount)
|
||||||
|
|
||||||
|
result = gateway.transaction.sale({
|
||||||
|
"amount": amount,
|
||||||
|
"payment_method_nonce": nonce_from_the_client,
|
||||||
|
"options": {
|
||||||
|
"submit_for_settlement": True
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if result.is_success:
|
||||||
|
transaction = result.transaction
|
||||||
|
amount = transaction.amount
|
||||||
|
messages.info(request,
|
||||||
|
"We have successfully received your payment of {amount} Euro".format(
|
||||||
|
amount=amount
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
messages.error(request,"We are sorry but there was an error with the payment")
|
||||||
|
url = reverse(payments_view)
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
messages.error(request,"There was an error in the payment form")
|
||||||
|
url = reverse(payments_view)
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
url = reverse(payments_view)
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
|
||||||
# User registration
|
# User registration
|
||||||
def rower_register_view(request):
|
def rower_register_view(request):
|
||||||
|
|||||||
@@ -421,3 +421,20 @@ try:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
workoutemailbox = 'workouts@rowsandall.com'
|
workoutemailbox = 'workouts@rowsandall.com'
|
||||||
|
|
||||||
|
|
||||||
|
# payments
|
||||||
|
|
||||||
|
try:
|
||||||
|
BRAINTREE_MERCHANT_ID = CFG['braintree_merchant_id']
|
||||||
|
except KeyError:
|
||||||
|
BRAINTREE_MERCHANT_ID = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
BRAINTREE_PUBLIC_KEY = CFG['braintree_public_key']
|
||||||
|
except KeyError:
|
||||||
|
BRAINTREE_PUBLIC_KEY = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
BRAINTREE_PRIVATE_KEY = CFG['braintree_private_key']
|
||||||
|
except KeyError:
|
||||||
|
BRAINTREE_PRIVATE_KEY = ''
|
||||||
|
|||||||
Reference in New Issue
Block a user