node.js - 我的 Paypal 结帐流程 Angular-Node 安全吗?
问题描述
我正在使用“基本智能支付按钮集成”开发贝宝结帐,并将其与安装“结帐-服务器-sdk”的服务器节点集成。
我遵循了文档:
https://developer.paypal.com/docs/checkout/reference/server-integration/set-up-transaction/ https://github.com/paypal/Checkout-NodeJS-SDK
他们建议:
- 'createOrder' 从客户端开始并调用服务器
- 在服务器上生成一个 orderID 并将其返回给客户端
- 'onApprove' 将 orderID 发送到服务器并在服务器上批准它
- 将响应返回给客户端
我不认为这是一个很好的流程。有人可以:
- 开始付款
- 所以应用程序在服务器上创建从 db 获取购物车的订单并详细说明 100 欧元的总价格。
- 生成 orderID 并将其发送回客户端
- 而不是批准这个订单,一个“坏用户”可以以某种方式向服务器发送另一个可能对应于较低价格(2欧元)的订单ID
- 所以他可以批准支付 2 欧元
所以我不明白为什么我们需要让结帐从客户端跳到服务器更多次。或者我在结账流程上做错了什么?
不幸的是,我觉得 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);
}
节点 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
};
解决方案
您在客户端和服务器之间“跳跃”的原因是付款人的批准必须发生在客户端上。付款人无法在您的服务器上给予批准,他们不在您的服务器上。他们正在使用客户端浏览器。
关于:
“坏用户”可能会以某种方式向服务器发送另一个可能对应于较低价格(2 欧元)的 orderID
如果发生这种情况,您的服务器应该拒绝不需要的交易,而不是继续它。这就是拥有服务器的意义所在。除非您的服务器正常,否则什么都不会发生。
推荐阅读
- swift - 使用用户定义运行时属性的行间距
- javascript - 在描述块和每个测试块中定义浅层之间的区别?
- javascript - 从当前月份向后返回数组
- javascript - 如何在html中将数据从一个页面传递到另一个页面?
- php - 如何在 Laravel 中使用来自 RabbitMQ 的消息
- javascript - 如何避免在Typescript的子类中重新声明类方法参数类型
- java - 如何渲染 Angular 应用程序服务器端并嵌入另一个 Angular 应用程序?
- javascript - 单击PHP中的按钮后如何重定向到URL
- sql - 执行错误:创建数据库汽车。原因:org.h2.jdbc.JdbcSQLSyntaxErrorException:SQL 语句中的语法错误“CREATE[*] DATABASE CARS
- powershell - Azure DevOps REST API 调用仅检索前 100 条记录并且 ContinuationToken 获取为空