我的 Paypal 结帐流程 Angular-Node 安全吗?

Posted

技术标签:

【中文标题】我的 Paypal 结帐流程 Angular-Node 安全吗?【英文标题】:Is my Paypal checkout flow Angular-Node secure? 【发布时间】:2021-03-09 15:00:21 【问题描述】:

我正在使用“基本智能支付按钮集成”开发贝宝结账,并将其与安装“checkout-server-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 欧元)的 orderID,而不是批准此订单 这样他就可以批准 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
;

【问题讨论】:

改写你的问题,它们没有任何意义 @Preston PHX i 用一个例子重新表述问题。我希望他们更清楚。 【参考方案1】:

您在客户端和服务器之间“跳跃”的原因是付款人的批准必须发生在客户端上。付款人无法在您的服务器上给予批准,他们不在您的服务器上。他们正在使用客户端浏览器。


关于:

“不良用户”可能会以某种方式向服务器发送另一个可能对应于较低价格(2 欧元)的 orderID

如果发生这种情况,您的服务器应该拒绝不需要的事务,而不是继续它。这就是拥有服务器的意义所在。除非您的服务器正常,否则什么都不会发生。

【讨论】:

以上是关于我的 Paypal 结帐流程 Angular-Node 安全吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用 activemerchant 和 ipn 的 Paypal 快速结帐

Paypal 在结帐时无法检测到移动设备

如何进一步自定义我的 Paypal 结帐页面?

Paypal 快速结帐。在 onAuthorize 函数中将数据存储在 DB 中是不是安全?

使用 AngularJS 的 Paypal Express 结帐

Woocommerce - Paypal 结帐显示我的电子邮件,而不是业务