Html 错误类型对象';配置';没有属性';环境';使用braintree时,Django

Html 错误类型对象';配置';没有属性';环境';使用braintree时,Django,html,python-3.x,django,braintree,braintree-sandbox,Html,Python 3.x,Django,Braintree,Braintree Sandbox,我试图通过Antiono Mele写的《Django 3 by example》一书来学习Django,我在使用braintree时遇到了沙盒环境配置的问题。他们的页面上写着: gateway = braintree.BraintreeGateway( braintree.Configuration( environment=braintree.Environment.Sandbox, merchant_id='**********', public_key='***

我试图通过Antiono Mele写的《Django 3 by example》一书来学习Django,我在使用braintree时遇到了沙盒环境配置的问题。他们的页面上写着:

gateway = braintree.BraintreeGateway(
  braintree.Configuration(
    environment=braintree.Environment.Sandbox,
    merchant_id='**********',
    public_key='**********',
    private_key='**********'
  )
)
与书中完全相同。当我尝试付款时,错误类型对象“配置”没有属性“环境”。我不知道为什么会有错误。我检查自己使用的应用程序的版本,它们与书中的版本相匹配(枕头==7.0.0.,Django==3.1.8,芹菜==4.4.2,braintree==3.59.0)。下面是我需要的代码

付款/views.py

import braintree
from django.shortcuts import render, redirect, get_object_or_404
from orders.models import Order




gateway = braintree.BraintreeGateway(
  braintree.Configuration(
    environment=braintree.Environment.Sandbox,
    merchant_id='**********',
    public_key='**********',
    private_key='**********'
  )
)
# Utworzenie egzemplarza bramki płatności Braintree.


def payment_process(request):
    order_id = request.session.get('order_id')
    order = get_object_or_404(Order, id=order_id)
    if request.method == 'POST':
        # Pobranie tokena nonce.
        nonce = request.POST.get('payment_method_nonce', None)
        # Utworzenie i przesłanie transakcji.
        result = braintree.Transaction.sale({
            'amount': '{:.2f}'.format(order.get_total_cost()),
            'payment_method_nonce': nonce,
            'options': {
            'submit_for_settlement': True
            }
        })
        if result.is_success:
            # Oznaczenie zamówienia jako opłacone.
            order.paid = True
            # Zapisanie unikatowego identyfikatora transakcji.
            order.braintree_id = result.transaction.id
            order.save()
            return redirect('payment:done')
        else:
            return redirect('payment:canceled')

    else:
        # Wygenerowanie tokena.
        client_token = braintree.ClientToken.generate()
        return render(request,
        'payment/process.html',
        {'order': order,
        'client_token': client_token})


def payment_done(request):
    return render(request, 'payment/done.html')

def payment_canceled(request):
    return render(request, 'payment/canceled.html') ```
myshop/url.py

from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('cart/', include('cart.urls', namespace='cart')),
    path('orders/', include('orders.urls', namespace='orders')),
    path('payment/', include('payment.urls', namespace='payment')),
    path('', include('shop.urls', namespace='shop')),
]
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

付款/模板/付款/流程.html

{% extends "shop/base.html" %}
{% block title %}Zapłać kartą kredytową{% endblock %}
{% block content %}
<h1>Zapłać kartą kredytową</h1>
<form action="." id="payment" method="post">
<label for="card-number">Numer karty</label>
<div id="card-number" class="field"></div>
<label for="cvv">CVV</label>
<div id="cvv" class="field"></div>
<label for="expiration-date">Data ważności</label>
<div id="expiration-date" class="field"></div>
<input type="hidden" id="nonce" name="payment_method_nonce" value="">
{% csrf_token %}
<input type="submit" value="Pay">
</form>
<!-- Załadowanie wymaganych komponentów klienta. -->
<script
src="https://js.braintreegateway.com/web/3.29.0/js/client.min.js"></script>
<!-- Załadowanie komponentu Hosted Fields. -->
<script src="https://js.braintreegateway.com/web/3.29.0/js/hosted-fields.min.js"></script>
<script>
var form = document.querySelector('#payment');
var submit = document.querySelector('input[type="submit"]');
braintree.client.create({
authorization: '{{ client_token }}'
}, function (clientErr, clientInstance) {
if (clientErr) {
console.error(clientErr);
return;
}
braintree.hostedFields.create({
    client: clientInstance,
styles: {
'input': {'font-size': '13px'},
'input.invalid': {'color': 'red'},
'input.valid': {'color': 'green'}
},
fields: {
number: {selector: '#card-number'},
cvv: {selector: '#cvv'},
expirationDate: {selector: '#expiration-date'}
}
}, function (hostedFieldsErr, hostedFieldsInstance) {
if (hostedFieldsErr) {
console.error(hostedFieldsErr);
return;
}
submit.removeAttribute('disabled');
form.addEventListener('submit', function (event) {
    event.preventDefault();
hostedFieldsInstance.tokenize(function (tokenizeErr, payload) {
if (tokenizeErr) {
console.error(tokenizeErr);
return;
}
// Ustawienie tokena nonce w celu przesłania na serwer.
document.getElementById('nonce').value = payload.nonce;
// Przesłanie formularza.
document.getElementById('payment').submit();
});
}, false);
});
});
</script>
{% endblock %}
{%extensed“shop/base.html”%}
{%block title%}Zapłaćkartąkredytowo{%endblock%}
{%block content%}
扎普·阿克·卡尔特·克雷迪托
卡蒂数
CVV
数据ważności
{%csrf_令牌%}
var form=document.querySelector(“#payment”);
var submit=document.querySelector('input[type=“submit”]”);
braintree.client.create({
授权:{{client_token}}
},函数(clienter,clientInstance){
if(clienter){
控制台错误(clienter);
返回;
}
braintree.hostedFields.create({
客户:clientInstance,
风格:{
'输入':{'font-size':'13px'},
'input.invalid':{'color':'red'},
'input.valid':{'color':'green'}
},
字段:{
编号:{选择器:'#卡号'},
cvv:{选择器:'#cvv'},
过期日期:{选择器:'#过期日期'}
}
},函数(hostedFieldsErr,hostedFieldsInstance){
如果(hostedFieldsErr){
控制台错误(hostedFieldsErr);
返回;
}
submit.removeAttribute(“已禁用”);
表单.addEventListener('submit',函数(事件){
event.preventDefault();
tokenizer(函数(tokenizeer,有效负载){
if(标记化器){
控制台错误(tokenizeErr);
返回;
}
//我们暂时不知道该怎么办。
document.getElementById('nonce')。value=payload.nonce;
//Przesłanie formularza。
document.getElementById(“付款”).submit();
});
},假);
});
});
{%endblock%}
致以最良好的祝愿