Yaltopia-Ticket-Email/docs/INTEGRATION_GUIDE.md

448 lines
12 KiB
Markdown

# Integration Guide for Yaltopia Backend
This email service is designed to be called by your Yaltopia backend with the data you already have. No complex API integrations needed!
## 🚀 Quick Integration
### 1. Deploy the Email Service
```bash
# Using Docker (recommended)
docker-compose up -d
# Or Node.js
npm run build && npm start
```
### 2. Configure Environment
```env
RESEND_API_KEY=re_your_resend_api_key
FROM_DOMAIN=yaltopia.com
FROM_EMAIL=noreply@yaltopia.com
```
### 3. Call from Your Backend
## 📧 Available Endpoints
### Send Event Invitation
**Endpoint**: `POST /api/emails/invitation`
**When to use**: When user registers for an event or you want to send event invitations
```javascript
// Example from your Yaltopia backend
async function sendEventInvitation(user, event) {
const emailData = {
to: user.email,
eventName: event.name,
dateTime: formatEventDateTime(event.startDate),
location: event.location,
ctaUrl: `https://yaltopia.com/events/${event.id}/rsvp`,
ctaLabel: "RSVP Now",
company: {
name: "Yaltopia Ticket",
logoUrl: "https://yaltopia.com/logo.png",
primaryColor: "#f97316"
}
}
const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/invitation`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(emailData)
})
return response.json()
}
```
### Send Team Member Invitation
**Endpoint**: `POST /api/emails/team-invitation`
**When to use**: When inviting users to join a team
```javascript
async function sendTeamInvitation(inviter, recipient, team, invitationToken) {
const emailData = {
to: recipient.email,
recipientName: recipient.name,
inviterName: inviter.name,
teamName: team.name,
invitationLink: `https://yaltopia.com/teams/join?token=${invitationToken}`,
customMessage: "Join our team and let's build something amazing together!",
company: {
name: "Yaltopia Ticket",
logoUrl: "https://yaltopia.com/logo.png",
primaryColor: "#10b981"
}
}
const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/team-invitation`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(emailData)
})
return response.json()
}
```
### Send Invoice Share
**Endpoint**: `POST /api/emails/invoice-share`
**When to use**: When sharing invoices with clients or stakeholders
```javascript
async function sendInvoiceShare(sender, recipient, invoice, shareToken) {
const emailData = {
to: recipient.email,
recipientName: recipient.name,
senderName: sender.name,
invoiceNumber: invoice.number,
customerName: invoice.customer.name,
amount: invoice.totalAmount,
currency: invoice.currency,
status: invoice.status, // 'DRAFT', 'SENT', 'PAID', 'OVERDUE', 'CANCELLED'
shareLink: `https://yaltopia.com/invoices/shared/${shareToken}`,
expirationDate: "March 31, 2026",
accessLimit: 5,
customMessage: "Please review this invoice and let me know if you have any questions.",
company: {
name: "Yaltopia Ticket",
logoUrl: "https://yaltopia.com/logo.png"
}
}
const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/invoice-share`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(emailData)
})
return response.json()
}
```
### Send Enhanced Payment Request
**Endpoint**: `POST /api/emails/enhanced-payment-request`
**When to use**: When requesting payment with detailed line items and automatic status updates
```javascript
async function sendEnhancedPaymentRequest(customer, paymentRequest) {
const emailData = {
to: customer.email,
recipientName: customer.name,
paymentRequestNumber: paymentRequest.number,
amount: paymentRequest.totalAmount,
currency: paymentRequest.currency,
description: paymentRequest.description,
dueDate: formatDate(paymentRequest.dueDate),
lineItems: paymentRequest.items.map(item => ({
description: item.description,
quantity: item.quantity,
unitPrice: item.unitPrice,
total: item.quantity * item.unitPrice
})),
notes: "Please ensure payment is made by the due date to avoid late fees.",
company: {
name: "Yaltopia Ticket",
bankDetails: {
bankName: "Your Bank",
accountName: "Yaltopia Ticket Ltd",
accountNumber: "123456789",
routingNumber: "021000021",
referenceNote: `Payment for ${paymentRequest.number}`
}
}
}
const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/enhanced-payment-request`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(emailData)
})
// Automatically update payment request status to "SENT"
if (response.ok) {
await updatePaymentRequestStatus(paymentRequest.id, 'SENT')
}
return response.json()
}
```
### Send Payment Request
**Endpoint**: `POST /api/emails/payment-request`
**When to use**: When payment is due for tickets or services
```javascript
async function sendPaymentRequest(customer, ticket, event) {
const emailData = {
to: customer.email,
amount: ticket.totalPrice,
currency: ticket.currency || "USD",
description: `Ticket for ${event.name}`,
dueDate: formatDate(ticket.paymentDueDate),
company: {
name: "Yaltopia Ticket",
paymentLink: `https://yaltopia.com/pay/${ticket.id}`,
bankDetails: {
bankName: "Your Bank",
accountName: "Yaltopia Ticket Ltd",
accountNumber: "123456789"
}
}
}
const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/payment-request`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(emailData)
})
return response.json()
}
```
### Send Password Reset
**Endpoint**: `POST /api/emails/password-reset`
**When to use**: When user requests password reset
```javascript
async function sendPasswordReset(user, resetToken) {
const emailData = {
to: user.email,
resetLink: `https://yaltopia.com/reset-password?token=${resetToken}`,
recipientName: user.firstName || user.name,
company: {
name: "Yaltopia Ticket",
logoUrl: "https://yaltopia.com/logo.png"
}
}
const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/password-reset`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(emailData)
})
return response.json()
}
```
## 🔧 Integration Patterns
### 1. Event-Driven Integration
```javascript
// In your event handlers
eventEmitter.on('user.registered', async (user, event) => {
await sendEventInvitation(user, event)
})
eventEmitter.on('payment.due', async (customer, ticket, event) => {
await sendPaymentRequest(customer, ticket, event)
})
eventEmitter.on('password.reset.requested', async (user, resetToken) => {
await sendPasswordReset(user, resetToken)
})
```
### 2. Direct Integration
```javascript
// In your API endpoints
app.post('/api/events/:eventId/register', async (req, res) => {
// Your registration logic
const registration = await registerUserForEvent(userId, eventId)
// Send invitation email
await sendEventInvitation(user, event)
res.json({ success: true, registration })
})
```
### 3. Background Job Integration
```javascript
// Using a job queue (Bull, Agenda, etc.)
queue.add('send-invitation-email', {
userId: user.id,
eventId: event.id
})
queue.process('send-invitation-email', async (job) => {
const { userId, eventId } = job.data
const user = await getUserById(userId)
const event = await getEventById(eventId)
await sendEventInvitation(user, event)
})
```
## 📊 Response Format
All endpoints return consistent responses:
**Success Response**:
```json
{
"success": true,
"messageId": "abc123-def456",
"duration": "1.2s"
}
```
**Error Response**:
```json
{
"success": false,
"error": "Validation error: Invalid email address",
"code": "VALIDATION_ERROR"
}
```
## 🛡️ Error Handling
```javascript
async function sendEmailSafely(emailData, endpoint) {
try {
const response = await fetch(`${EMAIL_SERVICE_URL}${endpoint}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(emailData)
})
const result = await response.json()
if (!result.success) {
console.error('Email sending failed:', result.error)
// Handle error (retry, log, notify admin, etc.)
return false
}
console.log('Email sent successfully:', result.messageId)
return true
} catch (error) {
console.error('Email service unreachable:', error)
// Handle network errors
return false
}
}
```
## 🚀 Deployment Options
### Option 1: Same Server
Deploy the email service on the same server as your Yaltopia backend:
```javascript
const EMAIL_SERVICE_URL = 'http://localhost:3001'
```
### Option 2: Separate Container
Deploy as a separate Docker container:
```javascript
const EMAIL_SERVICE_URL = 'http://email-service:3001'
```
### Option 3: Cloud Service
Deploy to a cloud platform:
```javascript
const EMAIL_SERVICE_URL = 'https://your-email-service.herokuapp.com'
```
## 🔍 Health Check
Monitor the email service health:
```javascript
async function checkEmailServiceHealth() {
try {
const response = await fetch(`${EMAIL_SERVICE_URL}/health`)
const health = await response.json()
return health.status === 'healthy'
} catch (error) {
return false
}
}
```
## 📝 Best Practices
1. **Use environment variables** for the email service URL
2. **Implement retry logic** for failed email sends
3. **Log email sending attempts** for debugging
4. **Handle errors gracefully** - don't let email failures break your main flow
5. **Monitor email delivery** through Resend dashboard
6. **Test with real email addresses** in development
## 🎯 Example Integration
Here's a complete example of integrating with your user registration flow:
```javascript
// In your Yaltopia backend
class EventService {
constructor() {
this.emailServiceUrl = process.env.EMAIL_SERVICE_URL || 'http://localhost:3001'
}
async registerUserForEvent(userId, eventId) {
// Your existing registration logic
const user = await User.findById(userId)
const event = await Event.findById(eventId)
const registration = await Registration.create({ userId, eventId })
// Send invitation email
try {
await this.sendInvitationEmail(user, event)
} catch (error) {
console.error('Failed to send invitation email:', error)
// Don't fail the registration if email fails
}
return registration
}
async sendInvitationEmail(user, event) {
const emailData = {
to: user.email,
eventName: event.name,
dateTime: this.formatEventDateTime(event.startDate),
location: event.location,
ctaUrl: `https://yaltopia.com/events/${event.id}/rsvp`,
company: {
name: "Yaltopia Ticket",
logoUrl: "https://yaltopia.com/logo.png"
}
}
const response = await fetch(`${this.emailServiceUrl}/api/emails/invitation`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(emailData)
})
if (!response.ok) {
throw new Error(`Email service responded with ${response.status}`)
}
return response.json()
}
formatEventDateTime(startDate) {
return new Date(startDate).toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}
}
```
This approach gives you beautiful, professional emails with minimal integration effort!