Как правильно сериализовать и отправить идентификатор транзакции 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)
Вернуться на верх