Как правильно сериализовать и отправить идентификатор транзакции PayPal в бэкенд Django при стандартной интеграции на стороне клиента
Я пытаюсь получить ID транзакции PayPal после того, как платеж был одобрен на стороне клиента. Я делаю интеграцию PayPal и Django на стороне клиента. Я могу полностью получить ID платежа, ID заказа и так далее, но они будут отброшены PayPal после одобрения платежа. PayPal записывает только Transaction ID, который может быть использован для отслеживания платежа в PayPal. Когда я пытаюсь сериализовать действия возврата, которые фиксируют Transaction ID - почему-то я получаю код состояния 500 - Internal Server Error. Забавно то, что я могу полностью выполнить console.log(transaction.id) и получить ID транзакции в консоли. В любом случае, мой код, вызывающий ошибку, выглядит следующим образом:
В файле payment.html я получил огромную порцию html материала, но я не публикую его здесь. Я публикую только то, где начинается JavaScript:
В представлении моего заказа я получил следующее:
def payment(request):
body = json.loads(request.body)
order = Order.objects.get(user=request.user, is_ordered=False, order_number=body['orderID'])
# Store transaction details inside Payment model
processed_payment = Payment(
user=request.user,
payment_id=body['transID'],
payment_method=body['payment_method'],
amount_paid=order.order_total,
status=body['status'],
)
processed_payment.save()
order.payment = processed_payment
order.is_ordered = True
order.save()
# Move the cart items to Ordered Product table
cart_items = CartItem.objects.filter(user=request.user)
for item in cart_items:
ordered_product = OrderProduct()
ordered_product.order_id = order.id
ordered_product.payment = processed_payment
ordered_product.user_id = request.user.id
ordered_product.product_id = item.product_id
ordered_product.quantity = item.quantity
ordered_product.product_price = item.product.price
ordered_product.ordered = True
ordered_product.save()
cart_item = CartItem.objects.get(id=item.id)
product_variation = cart_item.variations.all()
ordered_product = OrderProduct.objects.get(id=ordered_product.id)
ordered_product.variation.set(product_variation)
ordered_product.save()
# Reduce the quantity of the sold products
product = Product.objects.get(id=item.product_id)
product.stock -= item.quantity
product.save()
# Clear the cart of cart items
CartItem.objects.filter(user=request.user).delete()
# Send order received email to customer
mail_subject = 'Thank you for your order!'
message = render_to_string('order_received_email.html', {
'user': request.user,
'order': order,
})
to_email = order.email
send_email = EmailMessage(mail_subject, message, to=[to_email])
send_email.send()
# Send order number and transaction id back to sendData method via JsonResponse
data = {
'order_number': order.order_number,
'transID': processed_payment.payment_id,
}
return JsonResponse(data)
Если я уберу это в файле payment.html:
return actions.order.capture().then(function(orderData) {
// Successful capture! For dev/demo purposes:
const transaction = orderData.purchase_units[0].payments.captures[0];
sendTransactionID();
function sendTransactionID() {
fetch(url, {
method: "POST",
headers: {
"Content-type": "application/json",
"X-CSRFToken": csrftoken,
},
body: JSON.stringify({
actualTransID: transaction.id,
}),
})
}
});
Который мне останется:
Это бы полностью сработало - и в моей модели Payment я могу записывать только ID платежа, ID заказа и так далее - но они бесполезны после того, как платеж прошел через PayPal - потому что PayPal сохраняет только ID транзакции - и я не могу получить ID транзакции для отправки в бэкэнд - но я могу только печатать в консоль с помощью console.log - и это расстраивает.
Если я могу получить ID транзакции для отправки на бэкэнд с помощью fetch - тогда я могу сделать что-то вроде этого:
completed_payment = Payment(
paypal_transaction_id=body['actualTransID']
)
completed_payment.save()
Но можно ли это сделать, даже если первое перенаправление уже произошло с помощью этого кода:
.then((data) => {
window.location.href = redirect_url + '?order_number=' + data.order_number + '&payment_id=' + data.transID;
Значит, мне нужно получить redirect_url (например, в представлении payment_complete), а не предыдущий url (например, в представлении payment)? В общем, материал JavaScript действительно запутал меня. Что-то не так с моим кодом? Есть ли какая-нибудь помощь? Спасибо...
Не перехватывайте информацию на стороне клиента, а затем отправляйте ее на сервер уже после факта. Это принципиально плохой дизайн для платежей в электронной коммерции.
Вместо этого реализуйте вызовы API на стороне сервера для создания и захвата заказа PayPal. Вы можете использовать для этого Checkout-Python-SDK или прямые вызовы HTTPS API после предварительного получения access_token с вашим client_id и secret. Вам понадобятся два маршрута django, которые выводят только JSON данные, один для API операции создания заказа и один для захвата, который принимает id в качестве входных данных (параметр пути, строка запроса, или w/e) и захватывает его. Маршрут захвата может затем записать ID успешной операции в вашу базу данных, а также запустить любую другую логику, которая вам нужна (например, отправку письма с подтверждением или резервирование товара) - непосредственно перед передачей результата в формате JSON вызывающей стороне (независимо от успеха или неудачи, всегда передавайте результат в формате, который распознает фронтенд, чтобы обработать любые соответствующие ошибки на стороне клиента)
Получив два маршрута, используйте это для внешнего потока утверждения: https://developer.paypal.com/demo/checkout/#/pattern/server
Один мой инструктор на Udemy решил эту проблему. Ответ заключается в том, чтобы сделать это в функции onApprove, используя код ниже:
transaction_id = details['purchase_units'][0]['payments']['captures'][0].id
// console.log(transaction_id)
Вот готовый рабочий код для интеграции PayPal на стороне клиента с возможностью записи PayPal Transaction ID в базу данных.
В представлении платежей вы всегда можете сделать что-то вроде этого:
def payment(request):
body = json.loads(request.body)
order = Order.objects.get(user=request.user, is_ordered=False, order_number=body['orderID'])
# Store transaction details inside Payment model
processed_payment = Payment(
user=request.user,
payment_id=body['transID'],
paypal_transaction_id=body['paypal_transaction_id'],
payment_method=body['payment_method'],
amount_paid=order.order_total,
status=body['status'],
)
processed_payment.save()
order.payment = processed_payment
order.is_ordered = True
order.save()
# Send order number and transaction id back to sendData method via
# JsonResponse
data = {
'order_number': order.order_number,
'transID': processed_payment.payment_id,
}
return JsonResponse(data)