如何正确序列化和发送PayPal交易ID到Django后端做标准客户端集成



我想在客户端批准付款后获得PayPal的交易ID。我正在做贝宝和Django的客户端集成。我完全可以获得Payment ID和order ID等信息,但是这些信息在付款被批准后会被PayPal丢弃。PayPal只记录了交易ID,可以用来跟踪PayPal的支付。当我试图序列化捕获事务ID的返回动作时-不知何故我得到了500的状态码-内部服务器错误。有趣的是,我完全可以执行console.log(transaction. ID)并在控制台中获取事务ID。总之,我的容易出错的代码如下:

在payment.html中,我得到了大量的html内容,但我没有在这里发布。我只在JavaScript开始的地方发布:

<script>
// Generating csrf_token on the fly
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}

let amount = "{{ grand_total }}"
const url = "{% url 'payment' %}"
let csrftoken = getCookie('csrftoken');
let orderID = "{{ order.order_number }}"
const payment_method = 'PayPal'
const redirect_url = "{% url 'order_complete' %}"
// Render the PayPal button into #paypal-button-container
const paypalButtonsComponent = paypal.Buttons({
// optional styling for buttons
// https://developer.paypal.com/docs/checkout/standard/customize/buttons-style-guide/
style: {
color: "gold",
shape: "pill",
layout: "vertical"
},

// set up the transaction
createOrder: (data, actions) => {
// pass in any options from the v2 orders create call:
// https://developer.paypal.com/api/orders/v2/#orders-create-request-body
const createOrderPayload = {
purchase_units: [
{
amount: {
value: amount
}
}
]
};

return actions.order.create(createOrderPayload);
},

// finalize the transaction
onApprove: (data, actions) => {
const captureOrderHandler = (details) => {
const payerName = details.payer.name.given_name;
console.log(details);
console.log('Transaction completed');
sendData();
function sendData() {
fetch(url, {
method: "POST",
headers: {
"Content-type": "application/json",
"X-CSRFToken": csrftoken,
},
body: JSON.stringify({
orderID: orderID,
transID: details.id,
payment_method: payment_method,
status: details.status,
}),
})
.then((response) => response.json())
.then((data) => {
window.location.href = redirect_url + '?order_number=' + data.order_number + '&payment_id=' + data.transID;
});
}
};

//return actions.order.capture().then(captureOrderHandler);
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,
}),
})
}

});
},

// handle unrecoverable errors
onError: (err) => {
console.error('An error prevented the buyer from checking out with PayPal');
}
});

paypalButtonsComponent
.render("#paypal-button-container")
.catch((err) => {
console.error('PayPal Buttons failed to render');
});

</script>

在我的订单视图中,我得到了这个:

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,
}),
})
}

});

我将留下:

<script>
// Generating csrf_token on the fly
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}

let amount = "{{ grand_total }}"
const url = "{% url 'payment' %}"
let csrftoken = getCookie('csrftoken');
let orderID = "{{ order.order_number }}"
const payment_method = 'PayPal'
const redirect_url = "{% url 'order_complete' %}"
// Render the PayPal button into #paypal-button-container
const paypalButtonsComponent = paypal.Buttons({
// optional styling for buttons
// https://developer.paypal.com/docs/checkout/standard/customize/buttons-style-guide/
style: {
color: "gold",
shape: "pill",
layout: "vertical"
},

// set up the transaction
createOrder: (data, actions) => {
// pass in any options from the v2 orders create call:
// https://developer.paypal.com/api/orders/v2/#orders-create-request-body
const createOrderPayload = {
purchase_units: [
{
amount: {
value: amount
}
}
]
};

return actions.order.create(createOrderPayload);
},

// finalize the transaction
onApprove: (data, actions) => {
const captureOrderHandler = (details) => {
const payerName = details.payer.name.given_name;
console.log(details);
console.log('Transaction completed');
sendData();
function sendData() {
fetch(url, {
method: "POST",
headers: {
"Content-type": "application/json",
"X-CSRFToken": csrftoken,
},
body: JSON.stringify({
orderID: orderID,
transID: details.id,
payment_method: payment_method,
status: details.status,
}),
})
.then((response) => response.json())
.then((data) => {
window.location.href = redirect_url + '?order_number=' + data.order_number + '&payment_id=' + data.transID;
});
}
};

return actions.order.capture().then(captureOrderHandler);
},

// handle unrecoverable errors
onError: (err) => {
console.error('An error prevented the buyer from checking out with PayPal');
}
});

paypalButtonsComponent
.render("#paypal-button-container")
.catch((err) => {
console.error('PayPal Buttons failed to render');
});

</script>

这将完全工作-在我的支付模式,我只能记录支付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(如支付视图)?基本上,JavaScript的东西真的把我搞糊涂了。我的代码有问题吗?任何帮助吗?谢谢…

不要在客户端捕获信息,然后在事后将信息发送到服务器。这对于电子商务支付来说是一个根本糟糕的设计。

相反,实现服务器端API调用来创建和捕获PayPal订单。您可以使用Checkout-Python-SDK来实现这一点,或者在首先获得带有client_id和secret的access_token后直接调用HTTPS API。你需要两个只输出JSON数据的django路由,一个用于创建订单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交易ID到数据库的能力。

<script>
// Generating csrf_token on the fly
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
let amount = "{{ grand_total }}"
const url = "{% url 'payment' %}"
let csrftoken = getCookie('csrftoken');
let orderID = "{{ order.order_number }}"
const payment_method = 'PayPal'
const redirect_url = "{% url 'order_complete' %}"
const order_errors_url = "{% url 'order_errors' %}"
// Render the PayPal button into #paypal-button-container
const paypalButtonsComponent = paypal.Buttons({
// optional styling for buttons
// https://developer.paypal.com/docs/checkout/standard/customize/buttons-style-guide/
style: {
color: "gold",
shape: "pill",
layout: "vertical"
},
// set up the transaction
createOrder: (data, actions) => {
// pass in any options from the v2 orders create call:
// https://developer.paypal.com/api/orders/v2/#orders-create-request-body
const createOrderPayload = {
purchase_units: [
{
amount: {
value: amount
}
}
]
};
return actions.order.create(createOrderPayload);
},
// finalize the transaction
onApprove: (data, actions) => {
const captureOrderHandler = (details) => {
const payerName = details.payer.name.given_name;
transaction_id = details['purchase_units'][0]['payments']['captures'][0].id
//console.log(transaction_id)
sendData();
function sendData() {
fetch(url, {
method: "POST",
headers: {
"Content-type": "application/json",
"X-CSRFToken": csrftoken,
},
body: JSON.stringify({
orderID: orderID,
transID: details.id,
paypal_transaction_id: transaction_id,
payment_method: payment_method,
status: details.status,
}),
})
.then((response) => response.json())
.then((data) => {
window.location.href = redirect_url + '?order_number=' + data.order_number + '&payment_id=' + data.transID;
});
}
};
return actions.order.capture().then(captureOrderHandler);
},
// handle unrecoverable errors
onError: (err) => {
// console.error('An error prevented the buyer from checking out with PayPal');
window.location.href = order_errors_url
}
});
paypalButtonsComponent
.render("#paypal-button-container")
.catch((err) => {
console.error('PayPal Buttons failed to render');
});
</script>

在支付视图中,你总是可以这样做:

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)

最新更新