#node.js #paypal #checkout #paypal-rest-sdk
#node.js #paypal #Оформить покупку #paypal-rest-sdk
Вопрос:
Я разрабатываю paypal checkout, используя «базовую интеграцию кнопок смарт-платежей» и интегрирую ее с серверным узлом, устанавливающим «checkout-server-sdk».
Я следил за документацией:
https://developer.paypal.com/docs/checkout/reference/server-integration/set-up-transaction/
https://github.com/paypal/Checkout-NodeJS-SDK
где они предлагают:
- «CreateOrder» запускается с клиента и вызывает сервер
- генерируем на сервере идентификатор заказа и возвращаем его клиенту
- ‘onApprove’ отправляет на сервер идентификатор заказа и утверждает его на сервере
- верните клиенту ответ
Я не думаю, что это хороший поток. Кто-то мог:
- начните платеж
- таким образом, приложение создает заказ на сервере, извлекая корзину покупок из базы данных, и определяет общую цену в 100 евро.
- создайте идентификатор заказа и отправьте его обратно клиенту
- вместо того, чтобы одобрить этот заказ, «плохой пользователь» может каким-то образом отправить на сервер другой идентификатор заказа, который может соответствовать более низкой цене (2 евро)
- чтобы он мог одобрить платеж в размере 2 евро
Поэтому я не понимаю, почему нам нужно, чтобы проверка переходила больше раз от клиента к серверу. Или, может быть, я делаю что-то не так в моем checkoutflow?
к сожалению, я чувствую, что документация Paypal настолько неясна.
checkout.component.html
<!-- * here there is a form where i get shipment info, invoice info and so on ->
<!-- * PAYPAL SMART BUTTONS -->
<div>
<div #paypal></div>
</div>
checkout.component.ts
onFormSubmit() {
this.isFormSubmitted = true;
// set paypal settings and show the paypal buttons
this.paypalSetting(this.shippmentInfo, this.invoiceRequired, this.invoice, this.addressInvoice);
}
async paypalSetting(shipment, invoiceRequired, invoice, addressInvoice) {
await paypal
.Buttons({
style: {
size: 'responsive',
label: 'pay',
},
experience: {
input_fields: {
no_shipping: 1,
},
},
createOrder: async (data, actions) => {
console.log('CREATE ORDER -->');
var paypalOrderId;
//generate new order
await this.apiService.newOrder().toPromise().then(
(res) => {
console.log('ON CREATE: SUCCESSFULLY CREATED')
paypalOrderId = res.order.paypalOrderId;
// ????? someone here could change 'paypalOrderId' with another value !!!!
//I also would like to return the 'paypalOrderId' only here !!
},
(err) => {
console.log('ON CREATE: ERROR: ' err);
// how should i manage this error ? i should skip the flow to onError but HOW ?
}
);
return paypalOrderId;
},
onApprove: async (data, actions) => {
console.log('APPROVE ORDER -->');
var paypalOrderId = data.orderID;
console.log('ON APPROVE: save the order on server/DB')
await this.apiService.saveOrder(shipment, invoiceRequired, invoice, addressInvoice, paypalOrderId).toPromise().then(
(res) => {
console.log('ON APPROVE: ORDER APPROVED')
this.isPaid = true;
//if isPaid i can show a 'success page'
},
(err) => {
console.log('ON APPROVE: ERROR: ' err);
this.isPaid = false;
}
);
},
onError: (err) => {
console.log('ON ERROR: ' err);
},
})
.render(this.paypalElement.nativeElement);
}
Node api.js
//* paypal
const paypal = require('@paypal/checkout-server-sdk');
const payPalClient = require('../paypalManager');
router.post('/newOrder', tokenManager.verifyAccessToken, async function (req, res, next) {
const idUser = req.userId;
// I get the shoppingcart of the user 'idUser'
// i calculate the total price
var totalPrice;
//* Call PayPal to set up a transaction
let order;
const request = new paypal.orders.OrdersCreateRequest();
request.prefer("return=representation");
request.requestBody({
intent: 'CAPTURE',
purchase_units: [{
description: 'payment ecc..', /
amount: {
currency_code: 'EUR',
value: totalPrice
}
}],
application_context: {
brand_name: "brand",
shipping_preference: 'NO_SHIPPING',
},
});
let response = await payPalClient.client().execute(request);
order = response;
const paypalOrderId = order.result.id;
// return a successful response to the client with the order ID
return res.json({
status: 200,
order: {
paypalOrderId: paypalOrderId,
},
message: "Paypal order sucessfully created",
});
});
router.post('/saveOrder', tokenManager.verifyAccessToken, async function (req, res, next) {
const idUser = req.userId;
var paypalOrderId = req.body.paypalOrderId;
try {
connection.beginTransaction(async () => {
try {
// here i insert all the checkout infos in DB
// confirm the queries executions
connection.commit(async function (err) {
if (err) {
//return connection.rollback(function () {
connection.rollback(function () {
return next(createError.Unauthorized("Sql query error: " err)); //! or error.message
});
}
//* here i send the Emails to confirm the checkout
//* capture/approve the order
console.log('CAPTURING THE ORDER')
var request = new paypal.orders.OrdersCaptureRequest(paypalOrderId);
request.requestBody({});
// Call API with your client and get a response for your call
let response = await payPalClient.client().execute(request);
//*response
return res.json({
status: 200,
message: "Paypal sucessfully approved",
});
});// end commit
} catch (error) {
connection.rollback(function () {
return next(createError.Unauthorized("Sql query error " error)); //! or error.message
});
}
});// end transaction
} catch (error) {
return next(error);
}
});
Узел paypalManager.js
'use strict';
/**
* PayPal Node JS SDK dependency
*/
const checkoutNodeJssdk = require('@paypal/checkout-server-sdk');
/**
* Returns PayPal HTTP client instance with environment that has access
* credentials context. Use this instance to invoke PayPal APIs, provided the
* credentials have access.
*/
function client() {
return new checkoutNodeJssdk.core.PayPalHttpClient(environment());
}
/**
* Set up and return PayPal JavaScript SDK environment with PayPal access credentials.
* This sample uses SandboxEnvironment. In production, use LiveEnvironment.
*/
function environment() {
let clientId = process.env.PAYPAL_CLIENT_ID;
let clientSecret = process.env.PAYPAL_CLIENT_SECRET;
return new checkoutNodeJssdk.core.SandboxEnvironment(
clientId, clientSecret
);
}
module.exports = {
client: client,
prettyPrint: prettyPrint
};
Комментарии:
1. Перефразируйте свои вопросы, они не имеют никакого смысла
2. @Preston PHX я перефразирую вопросы на примере. Я надеюсь, что они более понятны.
Ответ №1:
Причина, по которой вы «прыгаете» между клиентом и сервером, заключается в том, что одобрение плательщиком должно произойти на клиенте. Плательщик не может дать свое одобрение на вашем сервере, они не находятся на вашем сервере. Они используют клиентский браузер.
Что касается:
«плохой пользователь» может каким-то образом отправить на сервер другой идентификатор заказа, который может соответствовать более низкой цене (2 евро)
Если это произойдет, ваш сервер должен отклонить нежелательную транзакцию и не выполнять ее. В этом смысл наличия сервера. Ничего не произойдет, если ваш сервер не одобрит это.