你好几天来我一直在为这个问题而苦苦挣扎,我已经在 React 中构建了一个应用程序,后端使用的是 django 和 DRF,我正在使用 braintree 作为支付平台,我正在尝试从 React 中发布一个表单到 django 但在反应中我得到错误说获取失败因为它被 CORS 阻止,在 django 中我得到一个错误 400 说错误的请求,并且请求类型是 OPTIONS,我做了一些故障排除并证明表单是发送为空(我正在阅读有关 OPTIONS 的内容,似乎如果服务器不允许该请求,那么它就会变成一个空数组)。我已将 django-cors-headers 添加到已安装的应用程序、中间件、允许的主机等。无论如何,这是我的代码。
借记卡.js
import { useState, useEffect } from 'react';
import axios from 'axios';
import * as braintree from 'braintree-web'
function DebitCard() {
const storedData = localStorage.getItem('donationData');
const formData = storedData ? JSON.parse(storedData) : null;
const [clientToken, setClientToken] = useState(null);
const [client, setClient] = useState(null);
const [hostedFieldsInstance, setHostedFieldsInstance] = useState(null)
useEffect(() => {
async function fetchClientToken() {
try {
const response = await fetch('http://127.0.0.1:3001/client-token/');
const jsonResponse = await response.json();
const clientToken = jsonResponse.client_token;
setClientToken(clientToken);
} catch (err) {
console.error('Failed to fetch client token:', err);
}
}
fetchClientToken();
}, []);
useEffect(() => {
if (clientToken) {
braintree.client
.create({ authorization:clientToken })
.then((clientInstance) => {
setClient(clientInstance);
})
.catch((err) => {
console.log('Failed to create client:', err)
});
}
},[clientToken]);
useEffect(()=> {
if (client) {
braintree.hostedFields
.create({
client: client,
styles:{
},
fields:{
number:{
selector: '#card-number',
},
cvv:{
selector: '#card-cvv',
},
expirationDate: {
selector: '#card-expiry',
},
postalCode: {
selector: '#zipcode',
}
}
})
.then((hostedFieldsInstance) => {
setHostedFieldsInstance(hostedFieldsInstance)
})
.catch((err) => {
console.log('Failed to created hosted fields', err)
})
}
}, [client]);
const handleSubmit = async (event) => {
event.preventDefault();
const transactionData = {
first_name: formData.firstName,
last_name: formData.lastName,
email: formData.email,
phone: formData.phone,
amount: formData.donationAmount,
program: formData.donationType,
billing_address: '560 Lambert Rd',
billing_city_town: 'Brea',
billing_state: 'California',
};
if (hostedFieldsInstance) {
try {
const { nonce } = await hostedFieldsInstance.tokenize();
transactionData['payment_method_nonce'] = nonce;
const response = await axios.post('http://127.0.0.1:3001/mobile-booth-donation/', transactionData, {
headers: {
'Content-Type': 'application/json'
}
});
console.log(response);
if (!response.data.success) {
throw new Error('Failed to submit transaction data');
}
console.log('Transaction data submitted successfully');
} catch (err) {
console.error('Failed to tokenize hosted fields', err);
}
}
};
return (
<div className='debit-card-payment'>
<form onSubmit={handleSubmit}>
<div className='input-field'>
<label htmlFor="card-number-label">Card Number:</label>
<div id="card-number" placeholder="4444 4444 4444 4444" ></div>
</div>
<div className='exp-cvc-wrapper'>
<div className='input-field'>
<label htmlFor="card-expiry-label">Exp Date:</label>
<div id="card-expiry" placeholder="12/24" ></div>
</div>
<div className='input-field'>
<label htmlFor="card-cvv-label">CVC:</label>
<div id="card-cvv" placeholder="123" ></div>
</div>
<div className='input-field'>
<label htmlFor="zipcode-label">Zipcode:</label>
<div id="zipcode" placeholder="123" ></div>
</div>
</div>
<button type="submit">Pay with Debit Card</button>
</form>
</div>
);
}
export default DebitCard;
Django API
gateway = braintree.BraintreeGateway(
braintree.Configuration(
braintree.Environment.Production,
merchant_id=settings.BRAINTREE_MERCHANT_ID_PRODUCTION,
public_key=settings.BRAINTREE_PUBLIC_KEY_PRODUCTION,
private_key=settings.BRAINTREE_PRIVATE_KEY_PRODUCTION
@csrf_exempt
def braintree_token(request):
gateway = braintree_gateway(request)
client_token = gateway.client_token.generate()
print("Generated client token:", client_token)
response = JsonResponse({"client_token": client_token})
response["Access-Control-Allow-Origin"] = "http://localhost:3000"
return response
@method_decorator(csrf_exempt, name='dispatch')
class CheckoutFormView(FormView):
print('executing')
template_name = 'checkout.html'
success_url = '/checkout/thank-you/'
http_method_names = ['post', 'get','options']
def options(self, request, *args, **kwargs):
print(request.body)
if request.body:
try:
print(request.body)
print(json.loads(request.body))
except json.JSONDecodeError as e:
print(f"JSONDecodeError: {e}")
return JsonResponse({"error": "Invalid JSON"}, status=400)
else:
print("Empty request body")
return JsonResponse({"error": "Empty request body"}, status=400)
response = HttpResponse()
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
response['Access-Control-Allow-Headers'] = 'Content-Type'
return response
def get_form_class(self):
print('checkout type')
if self.request.session.get('checkout_type') == 'purchase':
self.form_class = OrderForm
elif self.request.session.get('checkout_type') == 'subscribe':
self.form_class = SubscribeForm
elif self.request.session.get('checkout_type') == 'registration':
self.form_class = RegistrationForm
else:
self.form_class = DonateForm
return self.form_class
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
try:
program = self.request.session['program']
program = Program.objects.get(slug=program.slug)
except:
program = Program.objects.get(slug='where-its-needed-most')
context['product'] = self.request.session.get('product', '')
context['event'] = self.request.session.get('event', '')
context['schedule'] = self.request.session.get('schedule', '')
context['checkout_type'] = self.request.session.get('checkout_type', 'donation')
context['occurrence'] = self.request.session.get('occurrence', 'onetime')
context['quantity'] = self.request.session.get('quantity', 1)
context['notes'] = self.request.session.get('notes', '')
context['special_instructions'] = self.request.session.get('special_instructions', '')
context['program'] = program
context['amount_1'] = self.request.session.get('amount', '50')
context['amount_2'] = 150
context['amount_3'] = 600
context['amount_4'] = 1000
context['programs'] = Program.objects.all()
return context
def get_initial(self):
initial = super().get_initial()
product = self.request.session.get('product', '')
program = self.request.session.get('program', 'where-its-needed-most')
schedule = self.request.session.get('schedule', '')
shirt_size = self.request.session.get('shirt_size', '')
if self.request.session.get('checkout_type') == 'purchase':
initial["product"] = product
initial["program"] = program
initial['occurrence'] = 'onetime'
elif self.request.session.get('checkout_type') == 'registration':
initial["schedule"] = schedule
initial["program"] = program
initial["shirt_size"] = shirt_size
initial['occurrence'] = 'onetime'
elif 'rapid-response' in self.request.META.get('HTTP_REFERER', ''):
initial['program'] = Program.objects.get(name="College Support")
elif 'brandsource-scholars' in self.request.META.get('HTTP_REFERER', ''):
initial['program'] = Program.objects.get(name="Scholarships")
elif 'american-industries-scholarship' in self.request.META.get('HTTP_REFERER', ''):
initial['program'] = Program.objects.get(name="Scholarships")
else:
initial['program'] = program
initial['first_name'] = self.request.session.get('first_name', '')
initial['last_name'] = self.request.session.get('last_name', '')
initial['email'] = self.request.session.get('email', '')
initial['phone'] = self.request.session.get('phone', '')
initial['billing_address'] = self.request.session.get('billing_address', '')
initial['billing_address_ext'] = self.request.session.get('billing_address_ext', '')
initial['billing_city_town'] = self.request.session.get('billing_city_town', '')
initial['billing_state'] = self.request.session.get('billing_state', '')
initial['billing_zipcode'] = self.request.session.get('billing_zipcode', '')
initial['checkout_type'] = self.request.session.get('checkout_type', 'donation')
initial['occurrence'] = self.request.session.get('occurrence', 'onetime')
initial['amount'] = self.request.session.get('amount', "50")
initial['quantity'] = self.request.session.get('quantity', "1")
initial['company_matching'] = "no"
initial['notes'] = self.request.session.get('notes', '')
initial['utm_campaign'] = self.request.COOKIES.get('utm_campaign', '')
initial['utm_content'] = self.request.COOKIES.get('utm_content', '')
initial['utm_source'] = self.request.COOKIES.get('utm_source', '')
initial['utm_medium'] = self.request.COOKIES.get('utm_medium', '')
initial['utm_date'] = self.request.COOKIES.get('utm_date', '')
initial['special_instructions'] = self.request.session.get('special_instructions', '')
initial['conversion_url'] = self.request.META.get("HTTP_REFERER", '').split("?")[0]
return initial
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
print('form valid')
return self.form_valid(form)
else:
print('form invalid mate')
print(form.errors)
messages.error(request, "An unexpected error has occurred.")
return self.form_invalid(form)
def form_valid(self, form):
gateway = braintree_gateway(self.request)
customer = find_or_create_customer(gateway, form)
if form.cleaned_data['occurrence'] == 'monthly' or form.cleaned_data['occurrence'] == 'annually' or form.cleaned_data['occurrence'] == 'biannually':
result = braintree_process_recurring_payment(self, form, gateway, customer)
print(result)
else:
result = braintree_process_onetime_payment(self, form, gateway, customer)
print(result)
if result.is_success:
return super().form_valid(form)
else:
html_content_internal = render_to_string('emails/donation_error.html', {
'message': result.message,
'params': result.params,
}
)
subject, from_email, to = 'Donation Failure', 'Foster Love <[email protected]>', "[email protected]"
text_content = 'Payment Received'
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content_internal, "text/html")
msg.send()
messages.error(self.request, result.message)
print(result)
return super().form_invalid(form)
def find_or_create_customer(gateway, form):
first_name = form.cleaned_data["first_name"]
last_name = form.cleaned_data["last_name"]
email = form.cleaned_data["email"]
phone = form.cleaned_data["phone"]
group_type = form.cleaned_data["group_type"]
billing_address = form.cleaned_data["billing_address"]
billing_address_ext = form.cleaned_data["billing_address_ext"]
billing_city_town = form.cleaned_data["billing_city_town"]
billing_state = form.cleaned_data["billing_state"]
billing_zipcode = form.cleaned_data["billing_zipcode"]
try:
customer = Customer.objects.get(email=email)
customer = gateway.customer.find(customer.braintree_id)
customer_id = customer.id
except:
try:
customer = gateway.customer.find(customer.braintree_id)
customer_id = customer.id
except:
result = gateway.customer.create({
"first_name": first_name,
"last_name": last_name,
"phone": re.sub('[^0-9]', '', phone),
"email": email,
})
if result.is_success:
customer_id = result.customer.id
else:
customer_id = uuid.uuid4()
values_to_update = {
'first_name': first_name,
'last_name': last_name,
'phone': re.sub('[^0-9]', '', phone),
'group_type': group_type,
'billing_address': billing_address,
'billing_address_ext': billing_address_ext,
'billing_city_town': billing_city_town,
'billing_state': billing_state,
'billing_zipcode': billing_zipcode,
'braintree_id': customer_id,
'zoho_contact_id': create_or_update_zoho_contact(first_name, last_name, email, phone, group_type, billing_address, billing_address_ext, billing_city_town, billing_state, billing_zipcode)
}
customer, created = Customer.objects.update_or_create(
email = email,
defaults=values_to_update
)
customer.save()
return customer