Strapi send email after payment compelete
I have a react frontend with a strapi backend.
I’m adding products to a cart and then submitting that cart to Stripe
This is all working.
When the payment is completed I would like to send an email.
I have a sendgrid account.
I have added sendgrid to strapi with
yarn add @strapi/provider-email-sendgrid
I have added my sendgrid app key to the .env file
In config/plugins I have
module.exports = ({ env }) => ({
email: {
config: {
provider: 'sendgrid',
providerOptions: {
apiKey: env('SENDGRID_API_KEY'),
},
settings: {
defaultFrom: '[email protected]',
defaultReplyTo: '[email protected]',
},
},
},
});
I have a orders content type
In src/api/order/controllers/order.ts I have
"use strict";
// @ts-ignore
const stripe = require("stripe")("sk_test_51H");
/**
* order controller
*/
const { createCoreController } = require("@strapi/strapi").factories;
module.exports = createCoreController("api::order.order");
module.exports = {
setUpStripe: async (ctx) => {
let total = 0
let validatedCart = []
let receiptCart = []
const { cart } = ctx.request.body
await Promise.all(cart.map(async product => {
try {
const validatedProduct = await strapi.db.query('api::product.product').findOne({
where: { id: product.id }
});
if (validatedProduct) {
validatedCart.push(validatedProduct);
receiptCart.push({
id: product.id
})
}
} catch (error) {
console.error('Error while querying the databases:', error);
}
}));
total = validatedCart.reduce((n, { price }) => n + price, 0) || 0;
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: total,
currency: 'usd',
metadata: { cart: JSON.stringify(validatedCart.toString()) },
payment_method_types: ['card']
});
// Send a response back to the client
ctx.send({
message: 'Payment intent created successfully',
paymentIntent,
});
} catch (error) {
// Handle errors and send an appropriate response
ctx.send({
error: true,
message: 'Error in processing payment',
details: error.message,
});
}
},
};
in src > api > order > content-types > order > lifecycles.js
module.exports = {
lifecycles: {
async afterCreate(event) {
const { result } = event
try {
await strapi.plugins['email'].services.email.send({
to: '[email protected]',
from: '[email protected]',
subject: 'Thank you for your order',
text: `Thank you for your order ${result.name}`
})
} catch (err) {
console.log(err)
}
}
}
}
in the strapi admin > settings > configuration
the send test email works
The email sent lifecycles.js doesn’t work.
Does anyone have any ideas why this might not work or how I can debug this
2
Answers
I’m not sure what version of strapi you are using, or if you are using TypeScript to compile to JavaScript.
You might want to try using vanilla JS until you get a better handle on things. If you want to use TS then use
import/export
syntax and notrequire/exports
.Using vanilla JS I would suggest these changes:
In
src/api/order/controllers/order.js
<— Note the change from.ts
to.js
In
src/api/order/content-types/order/lifecycles.js
:Stripe uses webhooks to tell to your backend if a payments was successful.
You can use it as a trigger to send an email to the concerned user. You can either customize the email according to the user’s order.
You first have to add a
String
field to the user to store his Stripe’s Customer ID. You can do it through the admin panel with the Content Manager.When the user create his account, You have to create his Stripe Customer profile from Backend.
I am using JS instead of TS, but you will be able to do the same thing normally
For easier development, I set my Stripe object in the Strapi object at bootstrap: (Not mandatory, but easier for me as I don’t need to re-instanciate Stripe each time I need it)
For exemple, I have a mandatory step where my users sends me all their information like their first name, last name and profile picture. I use this route to create the Stripe Customer
Each time an order will be done, you have to create a PaymentIntent for Stripe.
You have to store the PaymentIntent ID somewhere. A way I recommand you to do this is, for example if you store all your orders on the database, is to attach an order to the PaymentIntent ID with a new private String field.
You can also add a
status
enumeration to follow the progression of the order.But the way I am doing this is that whatever the order is paid or not, I create an
Order
element in my db, containing all the data needed as if it was paid and have to be processed, but the status is inwaitingPayment
state.So I can attach the PaymentIntent ID to the order, even if the order is not confirmed.
To prevent useless data storing, I have a CRON job that check every hour if their is some orders that are not confirmed (
order.status === 'waitingPayment'
). If it is the case, and the order have been created more than an hour before, I can delete it.I also added a way that when a user is trying to create a new order, I check if it has not already created one that was not finished.
This controller is used to send to the front-end all the needed information to create the Stripe Payment. But it is not the topic.
Now that we have stored the
PaymentIntent
ID, and that we have attached the order id to the Stripe’s metadata, we just have to wait the webhooks to give us a response.So, create a public API reserved for Stripe’s webhooks.
I created for example the
/api/payment
POST route.Now, in the controller, we will finally be able to send our email once we receive the good webhook call.
last thing to do, now that we can trigger when a payment succeeded, is to handle it accordingly. The previous example I wrote you use a service. We can do that so:
I am using the
email-designer
Strapi’s plugin, that is very useful to create and manage email templates, I recommand it.With that, you just have to send your email with the user email.
I give you my example with the
email-designer
plugin:See the webhook setup guide from Stripe in
Node
mode for further details:https://docs.stripe.com/webhooks/quickstart
Hope this will help you !