设置节点 js 服务器以侦听 webhook 和 post 数据库时出现问题

Problem setting up node js server to listen for webhook and post to database

大家早上好,我在设置服务器以侦听 webhook 数据并将其 post 发送到数据库时遇到了一些困难。我主要是前端,所以其中一些对我来说有点新。所以我有一个基于 snipcart 的熟食店网站。我有一台查询 api 并打印出新订单的收据打印机。所以我想要的是一个服务器来监听 webhook 并将信息存储在数据库中。我在正确侦听 webhook 的地方得到了它,但它拒绝 post 到数据库。这是 app.js 文件中的代码。

'use strict';


require('./config/db');



const express = require('express');
const bodyParser = require('body-parser');
const fetch = require('node-fetch');



const app = express();

var routes = require('./api/routes/apiRoutes');
routes(app);

let orderToken;

app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json());

app.listen(process.env.PORT || 8080);


app.post('/hook', (req, res) => {
  orderToken = req.body.content.token;
  console.log(orderToken);
  const secret = "snipcart api key";

  const apiFetch = async function(){


  };

  let buffered = new Buffer.from(secret);
  let base64data = buffered.toString('base64');

  const start = async function(){



  const request = await fetch('https://app.snipcart.com/api/orders/'+orderToken, {
      headers: {
         'Authorization': `Basic ${base64data}`,
          'Accept': 'application/json'
      }
  });

  const result = await request.json();
    console.log(result);
  };

  start();

  res.status(200).end();
});


app.get('/', (req, res) => {
  res.send('hello world')
});

这是我的 apiController.js 文件中的代码


const mongoose = require('mongoose'),
      Order = mongoose.model('apiModel');


// listAllOrders function - To list all orders
exports.listAllOrders = (req, res) => {
api.find({}, (err, api) => {
if (err) {
res.status(500).send(err);
}
res.status(200).json(api);
});
};

// createNewOrder function - To create new Order
exports.createNewOrder = (req, res) => {
let  newApi = new api (req.body);
newApi.save((err, api) => {
if (err) {
res.status(500).send(err);
}
res.status(201).json(api);
});
};

// deleteOrder function - To delete order by id
exports.deleteOrder = async ( req, res) => {
await  api.deleteOne({ _id:req.params.id }, (err) => {
if (err) {
return res.status(404).send(err);
}
res.status(200).json({ message:"Order successfully deleted"});
});
};

和我的 apiModel.js 文件


const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const ApiSchema = new Schema({
  customerName: {
             type:String,
             required:true
         },
 customerPhone: {
           type:String,
           required:true
         },
  name: {
           type:String,
           required:true
       },
  orderNumber: {
           type:String,
           required:true
       },
  price: {
          type:String,
          required:true
       },
  customFields: {
        type:Array,
        required:false
        },
});

module.exports = mongoose.model("apiModel", ApiSchema);

apiRoutes.js




module.exports = function(app){
  var orderList = require('../controllers/apiController');


  app
  .route('/orders')
  .get(orderList.listAllOrders)
  .post(orderList.createNewOrder);


  app
  .route('/order/:id')
  .delete(orderList.deleteOrder);


};

和我的db.js


const mongoose = require("mongoose");

//Assign MongoDB connection string to Uri and declare options settings
var  uri = "<mongodb atlas info> 
retryWrites=true&w=majority";

// Declare a variable named option and assign optional settings
const  options = {
useNewUrlParser:  true,
useUnifiedTopology:  true
};

// Connect MongoDB Atlas using mongoose connect method
mongoose.connect(uri, options).then(() => {
console.log("Database connection established!");
},
err  => {
{
console.log("Error connecting Database instance due to:", err);
}
});

这是我需要放入数据库的示例响应

{
    "token": "93c4604e-35ac-4db7-b3f1-2871476e9e6a",
    "creationDate": "2013-10-22T20:54:40.377Z",
    "modificationDate": "2013-10-22T20:55:45.617Z",
    "status": "Processed",
    "paymentMethod": "CreditCard",
    "invoiceNumber": "SNIP-1427",
    "email": "geeks@snipcart.com",
    "cardHolderName": "Geeks Snipcart",
    "creditCardLast4Digits": "4242",
    "billingAddressName": "Geeks Snipcart",
    "billingAddressCompanyName": "Snipcart",
    "billingAddressAddress1": "4885 1ere Avenue",
    "billingAddressAddress2": null,
    "billingAddressCity": "Quebec",
    "billingAddressCountry": "CA",
    "billingAddressProvince": "QC",
    "billingAddressPostalCode": "G1H2T5",
    "billingAddressPhone": "1-877-301-4813",
    "notes": null,
    "shippingAddressName": "Geeks Snipcart",
    "shippingAddressCompanyName": "Snipcart",
    "shippingAddressAddress1": "4885 1ere Avenue",
    "shippingAddressAddress2": null,
    "shippingAddressCity": "Quebec",
    "shippingAddressCountry": "CA",
    "shippingAddressProvince": "QC",
    "shippingAddressPostalCode": "G1H2T5",
    "shippingAddressPhone": "1-877-301-4813",
    "shippingAddressSameAsBilling": true,
    "finalGrandTotal": 287.44,
    "shippingFees": 10,
    "shippingMethod": "Shipping",
    "items": [
        {
            "uniqueId": "1aad3398-1260-419c-9af4-d18e6fe75fbf",
            "id": "1",
            "name": "Un poster",
            "price": 300,
            "quantity": 1,
            "url": "http://snipcart.com",
            "weight": 10,
            "description": "Bacon",
            "image": "",
            "customFieldsJson": "[]",
            "stackable": true,
            "maxQuantity": null,
            "totalPrice": 300,
            "totalWeight": 10
        },
        ...
    ],
    "taxes": [
        {
            "taxName": "TPS",
            "taxRate": 0.05,
            "amount": 12.5,
            "numberForInvoice": ""
        },
        {
            "taxName": "TVQ",
            "taxRate": 0.09975,
            "amount": 24.94,
            "numberForInvoice": ""
        },
        ...
    ],
    "rebateAmount": 0,
    "subtotal": 310,
    "itemsTotal": 300,
    "grandTotal": 347.44,
    "totalWeight": 10,
    "hasPromocode": true,
    "totalRebateRate": 20,
    "promocodes": [
        {
            "code": "PROMO",
            "name": "PROMO",
            "type": "Rate",
            "rate": 20,
        },
        ...
    ],
    "willBePaidLater": false,
    "customFields": [
        {
            "name":"Slug",
            "value": "An order"
        },
        ...
    ],
    "paymentTransactionId": null,
}

我不需要数据库中的所有信息,只需要几个关键项目,如客户姓名、phone 号码和订单信息。但是如果订单中有多个项目,我需要考虑到这一点并添加订单中的所有项目。这是我需要集成的打印机的文档 https://star-m.jp/products/s_print/CloudPRNTSDK/Documentation/en/index.html 如果你们能给我任何帮助,我将不胜感激。谢谢!

Snipcartwebhook 发送给您 endpoint 用于不同的 events。我建议您首先 filter event by eventName,因为您只想监听 order.completed 事件。之后,从请求消息的 body 中,您可以提取将在 req.body.content.items 中的 items。您可以从可用信息中获取所需信息,然后仅将其存储在数据库中。

试试这个:

app.post('/hook', (req, res) => {

   if (req.body.eventName === 'order.completed') {

      const customer_name = req.body.content.cardHolderName;
      const customer_phone req.body.content.billingAddressPhone;
      const order_number = req.body.content.invoiceNumber;
      
      let items = [];
      req.body.content.items.forEach((item) => {
           items.push({
               name: item.name,
               price: item.price,
               quantity: item.quantity,
               id: item.uniqueId
           });
      })


      // Now store in database

      apiFetch.create({
          customerName: customer_name,
          customerPhone: customer_phone
          name: customer_name,
          orderNumber: order_number 
          customFields: items 
      }).then(()=>{
          res.status(200).json({success:true});
      }, (error)=>{
          console.log('ERROR: ', error);
      })
    
   }

};