Я делаю курс удеми, и в нем есть часть, где вы можете автоматически отправлять электронное письмо с подтверждением после того, как пользователь отправил свои данные в форму заказа, используя strapi / sendgrid.
Когда я нажимаю отправить, все данные будут отправлены правильно, хотя появляется сообщение об ошибке и электронное письмо не отправляется. Во всплывающем окне отображается [Объект, объект], а в консоли сообщение об ошибке выглядит так:
data: [{messages: [{id: "Auth.form.error.email.invalid"}]}]
0: {messages: [{id: "Auth.form.error.email.invalid"}]}
error: "Bad Request"
message: [{messages: [{id: "Auth.form.error.email.invalid"}]}]
0: {messages: [{id: "Auth.form.error.email.invalid"}]}
statusCode: 400
Мой компонент оформления заказа выглядит следующим образом:
import React from "react";
// prettier-ignore
import { Container, Box, Button, Heading, Text, TextField, Modal, Spinner } from "gestalt";
// prettier-ignore
import { Elements, StripeProvider, CardElement, injectStripe } from 'react-stripe-elements';
import ToastMessage from "./ToastMessage";
import { getCart, calculatePrice, clearCart, calculateAmount } from "../utils";
import { withRouter } from "react-router-dom";
import Strapi from "strapi-sdk-javascript/build/main";
const apiUrl = process.env.API_URL || "http://localhost:8082";
const strapi = new Strapi(apiUrl);
class _CheckoutForm extends React.Component {
state = {
cartItems: [],
address: "",
postalCode: "",
city: "",
confirmationEmailAddress: "",
toast: false,
toastMessage: "",
orderProcessing: false,
modal: false
};
componentDidMount() {
this.setState({ cartItems: getCart() });
}
handleChange = ({ event, value }) => {
event.persist();
this.setState({ [event.target.name]: value });
};
handleConfirmOrder = async event => {
event.preventDefault();
if (this.isFormEmpty(this.state)) {
this.showToast("Fill in all fields");
return;
}
this.setState({ modal: true });
};
handleSubmitOrder = async () => {
const {
cartItems,
city,
address,
postalCode,
confirmationEmailAddress
} = this.state;
const amount = calculateAmount(cartItems);
// Process order
this.setState({ orderProcessing: true });
let token;
try {
const response = await this.props.stripe.createToken();
token = response.token.id;
await strapi.createEntry("orders", {
amount,
brews: cartItems,
city,
postalCode,
address,
token
});
await strapi.request("POST", "/email", {
data: {
to: confirmationEmailAddress,
subject: `Order Confirmation - BrewHaha ${new Date(Date.now())}`,
text: "Your order has been processed",
html: "<bold>Expect your order to arrive in 2-3 shipping days</bold>"
}
});
this.setState({ orderProcessing: false, modal: false });
clearCart();
this.showToast("Your order has been successfully submitted!", true);
} catch (err) {
this.setState({ orderProcessing: false, modal: false });
this.showToast(err.message);
}
};
isFormEmpty = ({ address, postalCode, city, confirmationEmailAddress }) => {
return !address || !postalCode || !city || !confirmationEmailAddress;
};
showToast = (toastMessage, redirect = false) => {
this.setState({ toast: true, toastMessage });
setTimeout(
() =>
this.setState(
{ toast: false, toastMessage: "" },
// if true passed to 'redirect' argument, redirect home
() => redirect && this.props.history.push("/")
),
5000
);
};
closeModal = () => this.setState({ modal: false });
render() {
// prettier-ignore
const { toast, toastMessage, cartItems, modal, orderProcessing } = this.state;
return (
<Container>
<Box
color="darkWash"
margin={4}
padding={4}
shape="rounded"
display="flex"
justifyContent="center"
alignItems="center"
direction="column"
>
{/* Checkout Form Heading */}
<Heading color="midnight">Checkout</Heading>
{cartItems.length > 0 ? (
<React.Fragment>
{/* User Cart */}
<Box
display="flex"
justifyContent="center"
alignItems="center"
direction="column"
marginTop={2}
marginBottom={6}
>
<Text color="darkGray" italic>
{cartItems.length} Items for Checkout
</Text>
<Box padding={2}>
{cartItems.map(item => (
<Box key={item._id} padding={1}>
<Text color="midnight">
{item.name} x {item.quantity} - $
{item.quantity * item.price}
</Text>
</Box>
))}
</Box>
<Text bold>Total Amount: {calculatePrice(cartItems)}</Text>
</Box>
{/* Checkout Form */}
<form
style={{
display: "inlineBlock",
textAlign: "center",
maxWidth: 450
}}
onSubmit={this.handleConfirmOrder}
>
{/* Shipping Address Input */}
<TextField
id="address"
type="text"
name="address"
placeholder="Shipping Address"
onChange={this.handleChange}
/>
{/* Postal Code Input */}
<TextField
id="postalCode"
type="text"
name="postalCode"
placeholder="Postal Code"
onChange={this.handleChange}
/>
{/* City Input */}
<TextField
id="city"
type="text"
name="city"
placeholder="City of Residence"
onChange={this.handleChange}
/>
{/* Confirmation Email Address Input */}
<TextField
id="confirmationEmailAddress"
type="email"
name="confirmationEmailAddress"
placeholder="Confirmation Email Address"
onChange={this.handleChange}
/>
{/* Credit Card Element */}
<CardElement
id="stripe__input"
onReady={input => input.focus()}
/>
<button id="stripe__button" type="submit">
Submit
</button>
</form>
</React.Fragment>
) : (
// Default Text if No Items in Cart
<Box color="darkWash" shape="rounded" padding={4}>
<Heading align="center" color="watermelon" size="xs">
Your Cart is Empty
</Heading>
<Text align="center" italic color="green">
Add some brews!
</Text>
</Box>
)}
</Box>
{/* Confirmation Modal */}
{modal && (
<ConfirmationModal
orderProcessing={orderProcessing}
cartItems={cartItems}
closeModal={this.closeModal}
handleSubmitOrder={this.handleSubmitOrder}
/>
)}
<ToastMessage show={toast} message={toastMessage} />
</Container>
);
}
}
const ConfirmationModal = ({
orderProcessing,
cartItems,
closeModal,
handleSubmitOrder
}) => (
<Modal
accessibilityCloseLabel="close"
accessibilityModalLabel="Confirm Your Order"
heading="Confirm Your Order"
onDismiss={closeModal}
footer={
<Box
display="flex"
marginRight={-1}
marginLeft={-1}
justifyContent="center"
>
<Box padding={1}>
<Button
size="lg"
color="red"
text="Submit"
disabled={orderProcessing}
onClick={handleSubmitOrder}
/>
</Box>
<Box padding={1}>
<Button
size="lg"
text="Cancel"
disabled={orderProcessing}
onClick={closeModal}
/>
</Box>
</Box>
}
role="alertdialog"
size="sm"
>
{/* Order Summary */}
{!orderProcessing && (
<Box
display="flex"
justifyContent="center"
alignItems="center"
direction="column"
padding={2}
color="lightWash"
>
{cartItems.map(item => (
<Box key={item._id} padding={1}>
<Text size="lg" color="red">
{item.name} x {item.quantity} - ${item.quantity * item.price}
</Text>
</Box>
))}
<Box paddingY={2}>
<Text size="lg" bold>
Total: {calculatePrice(cartItems)}
</Text>
</Box>
</Box>
)}
{/* Order Processing Spinner */}
<Spinner
show={orderProcessing}
accessibilityLabel="Order Processing Spinner"
/>
{orderProcessing && (
<Text align="center" italic>
Submitting Order...
</Text>
)}
</Modal>
);
const CheckoutForm = withRouter(injectStripe(_CheckoutForm));
const Checkout = () => (
<StripeProvider apiKey="pk_test_F92R0PypgxxkAKVgMt7pH3Og00RQXrWC3T">
<Elements>
<CheckoutForm />
</Elements>
</StripeProvider>
);
export default Checkout;
Мой e-mail controller. js Файл выглядит следующим образом:
'use strict';
/**
* Email.js controller
*
* @description: A set of functions called "actions" of the `email` plugin.
*/
const _ = require('lodash');
module.exports = {
send: async ctx => {
// Retrieve provider configuration.
const config = await strapi
.store({
environment: strapi.config.environment,
type: 'plugin',
name: 'email',
})
.get({ key: 'provider' });
// Verify if the file email is enable.
if (config.enabled === false) {
strapi.log.error('Email is disabled');
return ctx.badRequest(null, [
{
messages: [
{ id: 'Email.status.disabled', message: 'Emails disabled' },
],
},
]);
}
// Something is wrong
if (ctx.status === 400) {
return;
}
let options = ctx.request.body;
// await strapi.plugins.email.services.email.send(options, config);
try {
// Send email to the user
await strapi.plugins["email"].services.email.send({
to: options.to,
from: "test@example.com",
subject: options.subject,
text: options.text,
html: options.html
});
} catch (err) {
return ctx.badRequest(null, err);
}
// Send 200 `ok`
ctx.send({});
},
getEnvironments: async ctx => {
const environments = _.map(
_.keys(strapi.config.environments),
environment => {
return {
name: environment,
active: strapi.config.environment === environment,
};
}
);
ctx.send({ environments });
},
getSettings: async ctx => {
let config = await strapi.plugins.email.services.email.getProviderConfig(
ctx.params.environment
);
ctx.send({
providers: strapi.plugins.email.config.providers,
config,
});
},
updateSettings: async ctx => {
await strapi
.store({
environment: ctx.params.environment,
type: 'plugin',
name: 'email',
})
.set({ key: 'provider', value: ctx.request.body });
ctx.send({ ok: true });
},
};
У меня также есть правильный ключ API от sendgrid в моих настройках электронной почты strapi sendgrid.
Вопросы, похоже, занимают до 6 месяцев, чтобы получить ответ на курс, поэтому мы будем благодарны за более быстрое решение.
Буду рад предоставить больше кода, если это необходимо.
Спасибо