首页 > 解决方案 > 为什么我得到 fetch is not defined 错误?

问题描述

按照本教程,我尝试使用 commercejs webhook 使用 SendGrid 创建自定义电子邮件收据模板。我已将此 github 存储库上传到此 测试 Netlify 站点。主要代码是我很确定我的变量是正确的,仍然没有收到并可能发送收据电子邮件。检查 Netlify 功能电子邮件日志时说:/functions/email.js.env

5:55:03 PM: f7c003e5 ERROR  Invoke Error    {"errorType":"ReferenceError","errorMessage":"fetch is not defined","stack":["ReferenceError: fetch is not defined","    at Runtime.exports.handler (/var/task/email.js:30:22)","    at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"]}
5:55:03 PM: f7c003e5 Duration: 2.24 ms  Memory Usage: 52 MB

然而我的package.json dependencies样子是这样的:

  "dependencies": {
    "@chec/commerce.js": "2.2.0",
    "@hookform/error-message": "0.0.5",
    "@sendgrid/mail": "^7.4.7",
    "@stripe/react-stripe-js": "1.1.2",
    "@stripe/stripe-js": "1.11.0",
    "autoprefixer": "10.0.4",
    "classcat": "4.1.0",
    "framer-motion": "2.9.4",
    "next": "10.0.2",
    "next-google-fonts": "1.2.1",
    "node-fetch": "^3.0.0",
    "pdf-lib": "^1.16.0",
    "postcss": "8.1.14",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "react-hook-form": "6.11.5",
    "react-toastify": "6.1.0",
    "use-debounce": "^7.0.0"
  },

因此,为什么我得到fetch is not defined 错误令人困惑。此外,我也对如何正确实现标头感到困惑,因为教程没有指定如何正确实现。所以我只是添加了这样的标题,而不知道这是否是这样做的方法:

let headers = {
    "Accept": "application/json",
    "Content-Type": "application/json",
};

let sgheaders = {
    Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
    "Content-Type": "application/json",
}; 

在当前上传到我的 netlify 站点的代码中,我不得不更改export default async function handler(req, res) {

exports.handler = async function(req, res) {根据 Netlify 函数文档 Netlify 函数文档。(因为"errorMessage": "SyntaxError: Unexpected token 'export'"

// Create the API endpoint function with a req and res parameter
exports.handler = async function(req, res) {

    const checSecretAPIKey = process.env.CHEC_SECRET_KEY;


    let headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
    };


//export default async function handler(req, res) {
    if (!req.body || req.httpMethod !== 'POST') {
        return {
            status: 405,
            headers,
            body: JSON.stringify({
                status: 'Invalid HTTP method',
            }),
        }
    }

    const { data } = JSON.parse(req.body);

    // Request for your merchant information so that you can use your email
    // to include as the 'from' property to send to the SendGrid API
    const merchant = fetch(`${process.env.CHEC_API_URL}/v1/merchants`, {
        headers: {
            'X-Authoriza†ion': process.env.CHEC_SECRET_KEY,
        },
    }).then((response) => response.json);

    // Extract the signature from the registered `orders.create` webhook
    const { signature } = data;

    delete data.signature;

    // Your Chec webhook signing key, from the Chec Dashboard webhooks view
    const webHookSigningKey = 'KEJlxz6cIlrWIpsX5jypcMeGl2uh7jJg';


    // Verify the signature
    const expectedSignature = crypto.createHmac('sha256', webHookSigningKey)
        .update(JSON.stringify(data))
        .digest('hex');
    if (expectedSignature !== signature) {
        console.error('Signature mismatched, skipping.')
    }

    // Verify the age of the request to make sure it isn't more than 5 minutes old.
    if (new Date(data.created * 1000) < new Date() - 5 * 60 * 1000) {
        console.error('Webhook was sent too long ago, could potentially be fake, ignoring');
    }

    // Because you will need to list out the order line items, map through the returned line items
    // and structure out the data you need to display in the email receipt for your customer
    // Note that we are keeping the data structure minimal here
    const orderLineItems = data.payload.order.line_items.map((lineItem) => ({
        text: lineItem.product_name,
        price: lineItem.line_total.formatted_with_symbol,
    }));

    // Signature is verified, continue to send data to SendGrid
    // Create the email object payload to fire off to SendGrid
    const emailPayload = {
        to: data.payload.customer.email,
        from: merchant.support_email,
        subject: `Thank you for your order ${data.payload.customer.firstname}`,
        text: `Your order number is ${data.payload.customer_reference}`,
        // SendGrid expects a JSON blog containing the dynamic order data your template will use
        // More information below in 'What's next?' on how to configure your dynamic template in SendGrid
        // The property key names will depend on what dynamic template you create in SendGrid
        dynamic_template_data: {
            total: data.payload.order.subtotal.formatted_with_symbol,
            items: orderLineItems,
            receipt: true,
            name: data.payload.shipping.name,
            address01: data.payload.shipping.street,
            city: data.payload.shipping.town_city,
            state: data.payload.shipping.county_state,
            zip : data.payload.shipping.postal_zip_code,
            orderId : data.payload.id,
        },
        // In addition to specifying the dynamic template data, you need to specify the template ID. This comes from your SendGrid dashboard when you create you dynamic template
    // https://mc.sendgrid.com/dynamic-templates
        template_id: 'd-xxx'
    }


    let sgheaders = {
        Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
        "Content-Type": "application/json",
    };

    let response = {};
    try {
        // Call the SendGrid send mail endpoint
        response = await sgMailClient.send(emailPayload);
        return {
            statusCode: 200,
            headers: sgheaders,
            body: 'Email sent!'
        }
    } catch (err) {
        console.error('Error', err)
    }
    // Return the response from the request
    return res.status(200).json(response);
}

需要一些关于如何让此代码实际工作的建议,该教程似乎未完成,或者我可能误解了一些缩减细节。

更新(下面的工作代码)必须在 netlify 上使用 axios 而不是 node.fetch(感谢@hotcakedev)wehn deplodey。还对代码进行了其他更改,以使其与 commerce.js 一起使用(请参阅工作代码以获取详细信息)

const axios = require('axios');
const sgMailClient = require("@sendgrid/mail");
sgMailClient.setApiKey(process.env.SENDGRID_API_KEY);
// Includes crypto module
const crypto = require('crypto');


// Create the API endpoint function with a req and res parameter
exports.handler = async function(req, res) {

//export default async function handler(req, res) {
    if (!req.body || req.httpMethod !== 'POST') {
        return {
            status: 405,
            headers: {},
            body: JSON.stringify({
                status: 'Invalid HTTP method',
            }),
        }
    }

    const data = JSON.parse(req.body);

    // Request for your merchant information so that you can use your email
    // to include as the 'from' property to send to the SendGrid API
    const merchant = axios.get(`${process.env.CHEC_API_URL}/v1/merchants`, {
        headers: {
            'X-Authorization': process.env.CHEC_SECRET_KEY,
            "Accept": "application/json",
            "Content-Type": "application/json",
        },
    }).then((response) => response.json);

    //console.log(data);

    // Extract the signature from the registered `orders.create` webhook
    const { signature } = data;

    delete data.signature;

    // Your Chec webhook signing key, from the Chec Dashboard webhooks view
    const webHookSigningKey = 'KEJlxz6cIlrWIpsX5jypcMeGl2uh7jJg';


    // Verify the signature
    const expectedSignature = crypto.createHmac('sha256', webHookSigningKey)
        .update(JSON.stringify(data))
        .digest('hex');
    if (expectedSignature !== signature) {
        console.error('Signature mismatched, skipping.')
    }

    // Verify the age of the request to make sure it isn't more than 5 minutes old.
    if (new Date(data.created * 1000) < new Date() - 5 * 60 * 1000) {
        console.error('Webhook was sent too long ago, could potentially be fake, ignoring');
    }

    // Because you will need to list out the order line items, map through the returned line items
    // and structure out the data you need to display in the email receipt for your customer
    // Note that we are keeping the data structure minimal here
    const orderLineItems = data.payload.order.line_items.map((lineItem) => ({
        text: lineItem.product_name,
        price: lineItem.line_total.formatted_with_symbol,
    }));





    // Signature is verified, continue to send data to SendGrid
    // Create the email object payload to fire off to SendGrid
    const emailPayload = {
        to: data.payload.customer.email,
        from: data.payload.merchant.support_email,
        subject: `Thank you for your order ${data.payload.customer.firstname}`,
        text: `Your order number is ${data.payload.customer_reference}`,

        // SendGrid expects a JSON blog containing the dynamic order data your template will use
        // More information below in 'What's next?' on how to configure your dynamic template in SendGrid
        // The property key names will depend on what dynamic template you create in SendGrid
        dynamic_template_data: {
            total: data.payload.order.subtotal.formatted_with_symbol,
            items: orderLineItems,
            receipt: true,
            name: data.payload.billing.name,
            address01: data.payload.billing.street,
            city: data.payload.billing.town_city,
            state: data.payload.billing.county_state,
            zip : data.payload.billing.postal_zip_code,
            orderId : data.payload.id,
        },
        // In addition to specifying the dynamic template data, you need to specify the template ID. This comes from your SendGrid dashboard when you create you dynamic template
    // https://mc.sendgrid.com/dynamic-templates
        template_id: 'd-xxx'
    };

    /*let sgheaders = {
        Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
        "Content-Type": "application/json",
    };*/

    let response = {};
    try {
        // Call the SendGrid send mail endpoint
        response = await sgMailClient.send(emailPayload);
        return {
            statusCode: 200,
            headers: {},
            body: JSON.stringify({
                status: 'Email sent!',
            }),
        }
    } catch (err) {
        console.error('Error from function: ', err)
        console.error(err.response.body);
        console.log("Payload content: ", emailPayload );
    }
    // Return the response from the request
    //return res.status(200).json(response);
}

标签: reactjsnext.jssendgrid

解决方案


如果你想使用 Rest API 集成 sendgrid,我建议你使用axios

所以在你的情况下,

    import axios from 'axios';
    ...
    const merchant = axios.get(`${process.env.CHEC_API_URL}/v1/merchants`, {
        headers: {
            'X-Authoriza†ion': process.env.CHEC_SECRET_KEY,
        },
    }).then((response) => response.json);
    ...

推荐阅读