Merge branch 'development' of ssh://git.eigen.co.id:2222/eigen/pos-be into feat/otp-cancel
commit
67129a8c69
|
@ -4,6 +4,7 @@ import { VerificationModel } from '../models/verification.model';
|
||||||
import { BookingVerification } from '../../domain/entities/booking-verification.entity';
|
import { BookingVerification } from '../../domain/entities/booking-verification.entity';
|
||||||
import { UnprocessableEntityException } from '@nestjs/common';
|
import { UnprocessableEntityException } from '@nestjs/common';
|
||||||
import { JwtService } from '@nestjs/jwt';
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
import { WhatsappService } from 'src/services/whatsapp/whatsapp.service';
|
||||||
export class VerificationService {
|
export class VerificationService {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(VerificationModel)
|
@InjectRepository(VerificationModel)
|
||||||
|
@ -25,21 +26,25 @@ export class VerificationService {
|
||||||
|
|
||||||
async register(data: BookingVerification) {
|
async register(data: BookingVerification) {
|
||||||
const currentTime = Math.floor(Date.now()); // current time in seconds
|
const currentTime = Math.floor(Date.now()); // current time in seconds
|
||||||
if (
|
|
||||||
data.created_at &&
|
|
||||||
currentTime - data.created_at > this.expiredTimeRegister
|
|
||||||
) {
|
|
||||||
throw new UnprocessableEntityException('Please try again in 1 minute');
|
|
||||||
}
|
|
||||||
// Generate a 4 digit OTP code
|
// Generate a 4 digit OTP code
|
||||||
data.code = Math.floor(1000 + Math.random() * 9000).toString();
|
const otpCode = Math.floor(1000 + Math.random() * 9000).toString();
|
||||||
data.tried = 0;
|
|
||||||
data.updated_at = currentTime;
|
|
||||||
|
|
||||||
let verification = await this.verificationRepository.findOne({
|
let verification = await this.verificationRepository.findOne({
|
||||||
where: { phone_number: data.phone_number },
|
where: { phone_number: data.phone_number },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
verification.updated_at &&
|
||||||
|
currentTime - verification.updated_at < this.expiredTimeRegister
|
||||||
|
) {
|
||||||
|
throw new UnprocessableEntityException('Please try again in 1 minute');
|
||||||
|
}
|
||||||
|
|
||||||
|
data.code = otpCode;
|
||||||
|
data.tried = 0;
|
||||||
|
data.updated_at = currentTime;
|
||||||
|
|
||||||
if (verification) {
|
if (verification) {
|
||||||
// Update existing record
|
// Update existing record
|
||||||
verification = this.verificationRepository.merge(verification, data);
|
verification = this.verificationRepository.merge(verification, data);
|
||||||
|
@ -47,7 +52,15 @@ export class VerificationService {
|
||||||
// Create new record
|
// Create new record
|
||||||
verification = this.verificationRepository.create(data);
|
verification = this.verificationRepository.create(data);
|
||||||
}
|
}
|
||||||
return this.verificationRepository.save(verification);
|
const payload = await this.verificationRepository.save(verification);
|
||||||
|
|
||||||
|
const notificationService = new WhatsappService();
|
||||||
|
notificationService.sendOtpNotification({
|
||||||
|
phone: data.phone_number,
|
||||||
|
code: otpCode,
|
||||||
|
});
|
||||||
|
|
||||||
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByPhoneNumber(phoneNumber: string) {
|
async findByPhoneNumber(phoneNumber: string) {
|
||||||
|
|
|
@ -50,6 +50,7 @@ export class BookingOrderController {
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
async get(@Param('id') transactionId: string) {
|
async get(@Param('id') transactionId: string) {
|
||||||
const data = await this.serviceData.getOneByOptions({
|
const data = await this.serviceData.getOneByOptions({
|
||||||
|
relations: ['items'],
|
||||||
where: { id: transactionId },
|
where: { id: transactionId },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,15 +61,49 @@ export class BookingOrderController {
|
||||||
invoice_code,
|
invoice_code,
|
||||||
status,
|
status,
|
||||||
id,
|
id,
|
||||||
|
items,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
|
const usageItems = items.map((item) => {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
item_id,
|
||||||
|
item_name,
|
||||||
|
item_price,
|
||||||
|
item_category_name,
|
||||||
|
total_price,
|
||||||
|
total_net_price,
|
||||||
|
qty,
|
||||||
|
qty_remaining,
|
||||||
|
} = item;
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
item_id,
|
||||||
|
item_name,
|
||||||
|
item_price,
|
||||||
|
item_category_name,
|
||||||
|
total_price,
|
||||||
|
total_net_price,
|
||||||
|
qty,
|
||||||
|
qty_remaining,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mask customer_phone with * and keep last 4 numbers
|
||||||
|
let maskedCustomerPhone = customer_phone;
|
||||||
|
if (typeof customer_phone === 'string' && customer_phone.length > 4) {
|
||||||
|
const last4 = customer_phone.slice(-4);
|
||||||
|
maskedCustomerPhone = '*'.repeat(customer_phone.length - 4) + last4;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
customer_name,
|
customer_name,
|
||||||
customer_phone,
|
customer_phone: maskedCustomerPhone,
|
||||||
booking_date,
|
booking_date,
|
||||||
invoice_code,
|
invoice_code,
|
||||||
status,
|
status,
|
||||||
id,
|
id,
|
||||||
|
items: usageItems,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ export const WHATSAPP_BUSINESS_API_URL =
|
||||||
process.env.WHATSAPP_BUSINESS_API_URL ?? 'https://graph.facebook.com/';
|
process.env.WHATSAPP_BUSINESS_API_URL ?? 'https://graph.facebook.com/';
|
||||||
|
|
||||||
export const WHATSAPP_BUSINESS_VERSION =
|
export const WHATSAPP_BUSINESS_VERSION =
|
||||||
process.env.WHATSAPP_BUSINESS_VERSION ?? 'v21.0';
|
process.env.WHATSAPP_BUSINESS_VERSION ?? 'v22.0';
|
||||||
|
|
||||||
export const WHATSAPP_BUSINESS_QUEUE_URL =
|
export const WHATSAPP_BUSINESS_QUEUE_URL =
|
||||||
process.env.WHATSAPP_BUSINESS_QUEUE_URL ?? 'auth/login';
|
process.env.WHATSAPP_BUSINESS_QUEUE_URL ?? 'auth/login';
|
||||||
|
|
|
@ -30,6 +30,19 @@ export class WhatsappService {
|
||||||
const response = await axios(config);
|
const response = await axios(config);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
if (error.response) {
|
||||||
|
console.error('Axios error response:', {
|
||||||
|
status: error.response.status,
|
||||||
|
data: error.response.data,
|
||||||
|
headers: error.response.headers,
|
||||||
|
});
|
||||||
|
} else if (error.request) {
|
||||||
|
console.error('Axios error request:', error.request);
|
||||||
|
} else {
|
||||||
|
console.error('Axios error message:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
Logger.error(error);
|
Logger.error(error);
|
||||||
apm?.captureError(error);
|
apm?.captureError(error);
|
||||||
return null;
|
return null;
|
||||||
|
@ -105,6 +118,50 @@ export class WhatsappService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendOtpNotification(data: { phone: string; code: string }) {
|
||||||
|
// Compose the WhatsApp message payload for OTP using Facebook WhatsApp API
|
||||||
|
const payload = {
|
||||||
|
messaging_product: 'whatsapp',
|
||||||
|
to: data.phone, // recipient's phone number in international format
|
||||||
|
type: 'template',
|
||||||
|
template: {
|
||||||
|
name: 'booking_otp', // Make sure this template is approved in WhatsApp Business Manager
|
||||||
|
language: {
|
||||||
|
code: 'id', // or 'en' if you want English
|
||||||
|
},
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 'body',
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: parseInt(data.code), // OTP code
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'button',
|
||||||
|
sub_type: 'url',
|
||||||
|
index: '0',
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: `${data.code}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await this.sendMessage(payload);
|
||||||
|
if (response) {
|
||||||
|
Logger.log(
|
||||||
|
`OTP notification for code ${data.code} sent to ${data.phone}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async queueProcess(data: WhatsappQueue) {
|
async queueProcess(data: WhatsappQueue) {
|
||||||
const queueUrl = `${WHATSAPP_BUSINESS_QUEUE_URL}?id=${data.id}`;
|
const queueUrl = `${WHATSAPP_BUSINESS_QUEUE_URL}?id=${data.id}`;
|
||||||
const payload = {
|
const payload = {
|
||||||
|
|
Loading…
Reference in New Issue