update marketing
All checks were successful
All checks were successful
This commit is contained in:
62
Modules/MarketingAddon/App/Emails/DynamicMail.php
Normal file
62
Modules/MarketingAddon/App/Emails/DynamicMail.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Emails;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Address;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
|
||||
class DynamicMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
private $data;
|
||||
private $template;
|
||||
|
||||
public function __construct($template, $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
from: new Address(
|
||||
Config::get('mail.from.address'),
|
||||
Config::get('mail.from.name')
|
||||
),
|
||||
subject: $this->template->subject,
|
||||
);
|
||||
}
|
||||
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
view: 'mail.dynamic-mail',
|
||||
with: [
|
||||
'content' => $this->buildHtml(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function buildHtml()
|
||||
{
|
||||
$message = $this->template->message;
|
||||
|
||||
foreach ($this->data as $key => $value) {
|
||||
$message = str_replace('[' . $key . ']', $value, $message);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
26
Modules/MarketingAddon/App/Exports/SmsGatewayExport.php
Normal file
26
Modules/MarketingAddon/App/Exports/SmsGatewayExport.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Exports;
|
||||
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Maatwebsite\Excel\Concerns\FromView;
|
||||
use Modules\MarketingAddon\App\Models\Smsgateway;
|
||||
|
||||
class SmsGatewayExport implements FromView
|
||||
{
|
||||
|
||||
public function view(): View
|
||||
{
|
||||
$gateways = Smsgateway::with('type:id,name')
|
||||
->where('business_id', auth()->user()->business_id)
|
||||
->when(request('channel'), function ($q) {
|
||||
$q->where('channel', request('channel'));
|
||||
})
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return view('marketingaddon::sms-gateways.excel-csv', [
|
||||
'gateways' => $gateways
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Modules\MarketingAddon\App\Models\EmailSetting;
|
||||
|
||||
class AcnooEmailSettingController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$email_setting = EmailSetting::where('business_id', $businessId)->first() ?? new EmailSetting();
|
||||
|
||||
return view('marketingaddon::email-settings.index', compact('email_setting'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'driver' => 'required|string|max:25|in:smtp,sendmail',
|
||||
'host' => 'nullable|string|max:255',
|
||||
'port' => 'nullable|numeric',
|
||||
'username' => 'nullable|string|max:255',
|
||||
'password' => 'nullable|string|max:255',
|
||||
'encryption' => 'nullable|string|max:25',
|
||||
'from_name' => 'nullable|string|max:255',
|
||||
'from_address' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
EmailSetting::updateOrCreate(
|
||||
['business_id' => auth()->user()->business_id],
|
||||
$request->only(['driver', 'host', 'port', 'username', 'password', 'encryption', 'from_name', 'from_address'])
|
||||
);
|
||||
|
||||
return response()->json(__('Email setting updated successfully.'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\MarketingAddon\App\Models\MessageReport;
|
||||
|
||||
class AcnooMessageLogController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$message_logs = MessageReport::with('user:id,email')
|
||||
->where('business_id', auth()->user()->business_id)
|
||||
->when($request->search, function ($q) use ($request) {
|
||||
$q->where(function ($q) use ($request) {
|
||||
$q->where('title', 'like', '%' . $request->search . '%')
|
||||
->orWhere('channel', 'like', '%' . $request->search . '%')
|
||||
->orWhere('phone', 'like', '%' . $request->search . '%')
|
||||
->orWhere('status', 'like', '%' . $request->search . '%')
|
||||
->orWhereHas('user', function ($q) use ($request) {
|
||||
$q->where('email', 'like', '%' . $request->search . '%');
|
||||
});
|
||||
});
|
||||
})
|
||||
->when($request->type, function ($q) use ($request) {
|
||||
$q->where(function ($q) use ($request) {
|
||||
$q->where('channel', $request->type);
|
||||
});
|
||||
})
|
||||
->when($request->status, function ($q) use ($request) {
|
||||
$q->where(function ($q) use ($request) {
|
||||
$q->where('status', $request->status);
|
||||
});
|
||||
})
|
||||
->latest()
|
||||
->paginate($request->per_page ?? 20)
|
||||
->appends($request->query());
|
||||
|
||||
if ($request->ajax()) {
|
||||
return response()->json([
|
||||
'data' => view('marketingaddon::message-logs.datas', compact('message_logs'))->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
return view('marketingaddon::message-logs.index', compact('message_logs'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Modules\MarketingAddon\App\Exports\SmsGatewayExport;
|
||||
use Modules\MarketingAddon\App\Models\GatewayType;
|
||||
use Modules\MarketingAddon\App\Models\Smsgateway;
|
||||
|
||||
class AcnooSmsGatewayController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$gateways = Smsgateway::with('type:id,name')
|
||||
->where('business_id', auth()->user()->business_id)
|
||||
->when($request->filled('channel'), function ($q) use ($request) {
|
||||
$q->where('channel', $request->channel);
|
||||
})
|
||||
->when($request->filled('search'), function ($q) use ($request) {
|
||||
$q->where(function ($query) use ($request) {
|
||||
$query->where('name', 'like', '%' . $request->search . '%')
|
||||
->orWhereHas('type', function ($type) use ($request) {
|
||||
$type->where('name', 'like', '%' . $request->search . '%');
|
||||
});
|
||||
});
|
||||
})
|
||||
->latest()
|
||||
->paginate($request->per_page ?? 20)
|
||||
->appends($request->query());
|
||||
|
||||
if ($request->ajax()) {
|
||||
return response()->json([
|
||||
'data' => view('marketingaddon::sms-gateways.datas', compact('gateways'))->render()
|
||||
]);
|
||||
}
|
||||
|
||||
return view('marketingaddon::sms-gateways.index', compact('gateways'));
|
||||
}
|
||||
|
||||
public function create(Request $request)
|
||||
{
|
||||
$types = GatewayType::where('channel', $request->channel)
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return view('marketingaddon::sms-gateways.create', compact('types'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'status' => 'required|integer',
|
||||
'params' => 'required|array',
|
||||
'params.*' => 'required|string',
|
||||
'name' => [
|
||||
'required',
|
||||
Rule::unique('smsgateways')->where(function ($q) {
|
||||
return $q->where('business_id', auth()->user()->business_id);
|
||||
}),
|
||||
],
|
||||
'type_id' => 'required|exists:gateway_types,id',
|
||||
]);
|
||||
|
||||
$type = GatewayType::findOrFail($request->type_id);
|
||||
|
||||
Smsgateway::create($request->all() + [
|
||||
'driver' => $type->driver,
|
||||
'namespace' => $type->namespace,
|
||||
'business_id' => auth()->user()->business_id
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __(':channel gateway created successfully.', ['channel' => ucfirst($request->channel)]),
|
||||
'redirect' => route('business.sms-gateways.index', ['channel' => $request->channel])
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(string $id)
|
||||
{
|
||||
$gateway = Smsgateway::findOrFail($id);
|
||||
$gateway->update(['status' => !$gateway->status]);
|
||||
return response()->json(['message' => 'Sms gateway']);
|
||||
}
|
||||
|
||||
public function edit(Request $request, string $id)
|
||||
{
|
||||
$types = GatewayType::where('channel', $request->channel)->latest()->get();
|
||||
$gateway = Smsgateway::where('business_id', auth()->user()->business_id)->findOrFail($id);
|
||||
|
||||
return view('marketingaddon::sms-gateways.edit', compact('gateway', 'types'));
|
||||
}
|
||||
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
$request->validate([
|
||||
'status' => 'required|integer',
|
||||
'params' => 'required|array',
|
||||
'params.*' => 'required|string',
|
||||
'type_id' => 'required|exists:gateway_types,id',
|
||||
'name' => [
|
||||
'required',
|
||||
Rule::unique('smsgateways', 'name')->ignore($id)->where(function ($q) {
|
||||
return $q->where('business_id', auth()->user()->business_id);
|
||||
}),
|
||||
],
|
||||
]);
|
||||
|
||||
$type = GatewayType::findOrFail($request->type_id);
|
||||
$gateway = Smsgateway::findOrFail($id);
|
||||
|
||||
$gateway->update($request->all() + [
|
||||
'driver' => $type->driver,
|
||||
'namespace' => $type->namespace
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __(':channel gateway updated successfully.', ['channel' => ucfirst($request->channel)]),
|
||||
'redirect' => route('business.sms-gateways.index', ['channel' => $request->channel])
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(Request $request, string $id)
|
||||
{
|
||||
Smsgateway::where('id', $id)->delete();
|
||||
|
||||
return response()->json([
|
||||
'message' => __(':channel gateway deleted successfully.', ['channel' => ucfirst($request->channel)]),
|
||||
'redirect' => route('business.sms-gateways.index', ['channel' => $request->channel])
|
||||
]);
|
||||
}
|
||||
|
||||
public function deleteAll(Request $request)
|
||||
{
|
||||
Smsgateway::whereIn('id', $request->ids)->delete();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Selected :channel gateway deleted successfully.', ['channel' => $request->channel]),
|
||||
'redirect' => route('business.sms-gateways.index', ['channel' => $request->channel])
|
||||
]);
|
||||
}
|
||||
|
||||
public function exportExcel()
|
||||
{
|
||||
return Excel::download(new SmsGatewayExport, 'sms-gateway.xlsx');
|
||||
}
|
||||
|
||||
public function exportCsv()
|
||||
{
|
||||
return Excel::download(new SmsGatewayExport, 'sms-gateway.csv');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Http\Controllers;
|
||||
|
||||
use App\Models\Option;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class AcnooSmsGatewaySettingController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$sms_gateway_setting_key = 'sms-gateway-setting-' . $businessId;
|
||||
$sms_gateway_setting = Option::where('key', $sms_gateway_setting_key)->first();
|
||||
return view('marketingaddon::sms-gateway-settings.index', compact('sms_gateway_setting'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'sms_gateway_setting' => 'required|string|max:100|in:api,android',
|
||||
]);
|
||||
|
||||
$key = 'sms-gateway-setting-' . auth()->user()->business_id;
|
||||
|
||||
Option::updateOrCreate(
|
||||
['key' => $key],
|
||||
['value' => $request->sms_gateway_setting]
|
||||
);
|
||||
|
||||
Cache::forget($key);
|
||||
|
||||
return response()->json(__('SMS Gateway setting updated successfully.'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Http\Controllers;
|
||||
|
||||
use App\Models\Option;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Modules\MarketingAddon\App\Models\Template;
|
||||
|
||||
class AcnooTemplateController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$type = $request->type;
|
||||
|
||||
$templates = Template::where('business_id', $businessId)
|
||||
->where('type', $type)
|
||||
->get()
|
||||
->keyBy('title');
|
||||
|
||||
$page_data = Option::where('key', 'notification_settings_' . $businessId)->first()?->value ?? [];
|
||||
|
||||
return view('marketingaddon::templates.index', compact('templates', 'page_data'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$type = $request->type;
|
||||
$tabs = ['new_sale', 'payment_received', 'payment_paid', 'purchase', 'payment_reminder', 'attendance'];
|
||||
|
||||
$request->validate(collect($tabs)
|
||||
->flatMap(function ($key) use ($request, $type) {
|
||||
if (!$request->has($key)) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
"{$key}.template" => 'nullable|string|max:255',
|
||||
"{$key}.subject" => $type === 'email' ? 'required|string|max:255' : 'nullable|string|max:255',
|
||||
"{$key}.message" => 'required|string|max:1000',
|
||||
];
|
||||
})->toArray()
|
||||
);
|
||||
|
||||
$optionKey = 'notification_settings_' . $businessId;
|
||||
|
||||
$option = Option::firstOrCreate(
|
||||
['key' => $optionKey],
|
||||
['value' => []]
|
||||
);
|
||||
|
||||
$optionValue = $option->value ?? [];
|
||||
|
||||
foreach ($tabs as $tab) {
|
||||
if (!$request->has($tab)) continue;
|
||||
|
||||
$tabData = $request->{$tab};
|
||||
|
||||
$optionValue[$tab][$type]['enabled'] = $tabData[$type]['enabled'] ?? 0;
|
||||
|
||||
$message = $tabData['message'] ?? null;
|
||||
$variables = $message ? extractTemplateVariables($message) : [];
|
||||
|
||||
Template::updateOrCreate(
|
||||
[
|
||||
'business_id' => $businessId,
|
||||
'title' => $tab,
|
||||
'type' => $type,
|
||||
],
|
||||
[
|
||||
'subject' => $tabData['subject'] ?? null,
|
||||
'template' => $tabData['template'] ?? null,
|
||||
'message' => $message,
|
||||
'variables' => $variables,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$option->update(['value' => $optionValue]);
|
||||
|
||||
Cache::forget($optionKey);
|
||||
|
||||
return response()->json(__(ucfirst($type) . ' template updated successfully'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Http\Controllers;
|
||||
|
||||
use App\Models\Party;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class PaymentReminderController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(string $party_id)
|
||||
{
|
||||
$party = Party::with('business')->findOrFail($party_id);
|
||||
|
||||
$this->paymentReminderMessage($party->business, $party);
|
||||
|
||||
return redirect()->back()->with('message', 'Payment reminder sent successfully.');
|
||||
}
|
||||
}
|
||||
121
Modules/MarketingAddon/App/Jobs/SendNotificationJob.php
Normal file
121
Modules/MarketingAddon/App/Jobs/SendNotificationJob.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Jobs;
|
||||
|
||||
use Throwable;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Modules\MarketingAddon\App\Emails\DynamicMail;
|
||||
use Modules\MarketingAddon\App\Models\EmailSetting;
|
||||
use Modules\MarketingAddon\App\Services\SmsService;
|
||||
use Modules\MarketingAddon\App\Models\MessageReport;
|
||||
use Modules\MarketingAddon\App\Services\WhatsAppService;
|
||||
|
||||
class SendNotificationJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct(
|
||||
protected string $channel,
|
||||
protected object $template,
|
||||
protected array $data,
|
||||
protected int $notificationId,
|
||||
protected int $businessId,
|
||||
) {}
|
||||
|
||||
public function handle(
|
||||
SmsService $sms,
|
||||
WhatsAppService $whatsapp
|
||||
): void {
|
||||
|
||||
try {
|
||||
|
||||
match ($this->channel) {
|
||||
|
||||
/** ===================== SMS ===================== */
|
||||
'sms' => $sms->send(
|
||||
phone: $this->data['phone'],
|
||||
template: $this->template,
|
||||
data: $this->data
|
||||
),
|
||||
|
||||
/** ===================== WHATSAPP ===================== */
|
||||
'whatsapp' => $whatsapp->send(
|
||||
phone: $this->data['phone'],
|
||||
template: $this->template,
|
||||
businessId: $this->businessId,
|
||||
name: $this->data['name'] ?? '',
|
||||
data: $this->data
|
||||
),
|
||||
|
||||
/** ===================== EMAIL ===================== */
|
||||
'email' => $this->sendEmail(),
|
||||
|
||||
default => throw new \Exception('Invalid notification channel'),
|
||||
};
|
||||
|
||||
MessageReport::where('id', $this->notificationId)->update([
|
||||
'status' => 'success',
|
||||
'send_at' => now(),
|
||||
]);
|
||||
|
||||
} catch (Throwable $e) {
|
||||
|
||||
MessageReport::where('id', $this->notificationId)->update([
|
||||
'status' => 'failed',
|
||||
'send_at' => now(),
|
||||
]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle email sending with dynamic SMTP
|
||||
*/
|
||||
protected function sendEmail(): void
|
||||
{
|
||||
$smtp = EmailSetting::where('business_id', $this->businessId)->first();
|
||||
|
||||
if ($smtp) {
|
||||
|
||||
Config::set('mail.mailers.smtp.transport', $smtp->driver);
|
||||
Config::set('mail.mailers.smtp.host', $smtp->host);
|
||||
Config::set('mail.mailers.smtp.port', $smtp->port);
|
||||
Config::set('mail.mailers.smtp.username', $smtp->username);
|
||||
Config::set('mail.mailers.smtp.password', $smtp->password);
|
||||
Config::set('mail.mailers.smtp.encryption', $smtp->encryption);
|
||||
|
||||
Config::set('mail.from.address', $smtp->from_address);
|
||||
Config::set('mail.from.name', $smtp->from_name);
|
||||
|
||||
// IMPORTANT: reset mailer to avoid queue worker cache issues
|
||||
Mail::forgetMailers();
|
||||
|
||||
Mail::to($this->data['email'])
|
||||
->send(new DynamicMail($this->template, $this->data));
|
||||
|
||||
} else {
|
||||
MessageReport::where('id', $this->notificationId)->update([
|
||||
'status' => 'failed',
|
||||
'send_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called automatically by Laravel after final failure
|
||||
*/
|
||||
public function failed(): void
|
||||
{
|
||||
MessageReport::where('id', $this->notificationId)->update([
|
||||
'status' => 'failed',
|
||||
'send_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
30
Modules/MarketingAddon/App/Models/EmailSetting.php
Normal file
30
Modules/MarketingAddon/App/Models/EmailSetting.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class EmailSetting extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected $fillable = [
|
||||
'business_id',
|
||||
'driver',
|
||||
'host',
|
||||
'port',
|
||||
'username',
|
||||
'password',
|
||||
'encryption',
|
||||
'from_name',
|
||||
'from_address',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'business_id' => 'integer'
|
||||
];
|
||||
}
|
||||
34
Modules/MarketingAddon/App/Models/GatewayType.php
Normal file
34
Modules/MarketingAddon/App/Models/GatewayType.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class GatewayType extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'inputs',
|
||||
'driver',
|
||||
'channel',
|
||||
'status',
|
||||
'namespace',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'inputs' => 'json',
|
||||
];
|
||||
}
|
||||
63
Modules/MarketingAddon/App/Models/MessageReport.php
Normal file
63
Modules/MarketingAddon/App/Models/MessageReport.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Models;
|
||||
|
||||
use App\Models\Business;
|
||||
use App\Models\User;
|
||||
use App\Traits\ImageUrlTrait;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class MessageReport extends Model
|
||||
{
|
||||
use HasFactory, ImageUrlTrait;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'business_id',
|
||||
'template_id',
|
||||
'smsgateway_id',
|
||||
'user_id',
|
||||
'channel',
|
||||
'phone',
|
||||
'message',
|
||||
'attachment',
|
||||
'send_at',
|
||||
'status'
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function business()
|
||||
{
|
||||
return $this->belongsTo(Business::class);
|
||||
}
|
||||
|
||||
public function smsgateway()
|
||||
{
|
||||
return $this->belongsTo(Smsgateway::class, 'smsgateway_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'user_id' => 'integer',
|
||||
'device_id' => 'integer',
|
||||
'business_id' => 'integer',
|
||||
'send_attempts' => 'integer',
|
||||
];
|
||||
|
||||
protected $imageFields = ['attachment'];
|
||||
|
||||
}
|
||||
32
Modules/MarketingAddon/App/Models/Smsgateway.php
Normal file
32
Modules/MarketingAddon/App/Models/Smsgateway.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class Smsgateway extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'type_id',
|
||||
'business_id',
|
||||
'priority',
|
||||
'status',
|
||||
'params',
|
||||
'namespace',
|
||||
'driver',
|
||||
'channel',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'params' => 'json',
|
||||
];
|
||||
|
||||
public function type()
|
||||
{
|
||||
return $this->belongsTo(GatewayType::class, 'type_id');
|
||||
}
|
||||
}
|
||||
34
Modules/MarketingAddon/App/Models/Template.php
Normal file
34
Modules/MarketingAddon/App/Models/Template.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Models;
|
||||
|
||||
use App\Traits\ImageUrlTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Template extends Model
|
||||
{
|
||||
use ImageUrlTrait;
|
||||
|
||||
protected $fillable = [
|
||||
'business_id',
|
||||
'title',
|
||||
'template',
|
||||
'type',
|
||||
'attachment',
|
||||
'subject',
|
||||
'variables',
|
||||
'message',
|
||||
'status',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'variables' => 'array',
|
||||
];
|
||||
|
||||
protected $imageFields = ['attachment'];
|
||||
}
|
||||
0
Modules/MarketingAddon/App/Providers/.gitkeep
Normal file
0
Modules/MarketingAddon/App/Providers/.gitkeep
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class MarketingAddonServiceProvider extends ServiceProvider
|
||||
{
|
||||
protected string $moduleName = 'MarketingAddon';
|
||||
|
||||
protected string $moduleNameLower = 'marketingaddon';
|
||||
|
||||
/**
|
||||
* Boot the application events.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$this->registerCommands();
|
||||
$this->registerCommandSchedules();
|
||||
$this->registerTranslations();
|
||||
$this->registerConfig();
|
||||
$this->registerViews();
|
||||
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/migrations'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->register(RouteServiceProvider::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register commands in the format of Command::class
|
||||
*/
|
||||
protected function registerCommands(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Register command Schedules.
|
||||
*/
|
||||
protected function registerCommandSchedules(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Register translations.
|
||||
*/
|
||||
public function registerTranslations(): void
|
||||
{
|
||||
$langPath = resource_path('lang/modules/'.$this->moduleNameLower);
|
||||
|
||||
if (is_dir($langPath)) {
|
||||
$this->loadTranslationsFrom($langPath, $this->moduleNameLower);
|
||||
$this->loadJsonTranslationsFrom($langPath);
|
||||
} else {
|
||||
$this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower);
|
||||
$this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register config.
|
||||
*/
|
||||
protected function registerConfig(): void
|
||||
{
|
||||
$this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower.'.php')], 'config');
|
||||
$this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register views.
|
||||
*/
|
||||
public function registerViews(): void
|
||||
{
|
||||
$viewPath = resource_path('views/modules/'.$this->moduleNameLower);
|
||||
$sourcePath = module_path($this->moduleName, 'resources/views');
|
||||
|
||||
$this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']);
|
||||
|
||||
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
|
||||
|
||||
$componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.config('modules.paths.generator.component-class.path'));
|
||||
Blade::componentNamespace($componentNamespace, $this->moduleNameLower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*/
|
||||
public function provides(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function getPublishableViewPaths(): array
|
||||
{
|
||||
$paths = [];
|
||||
foreach (config('view.paths') as $path) {
|
||||
if (is_dir($path.'/modules/'.$this->moduleNameLower)) {
|
||||
$paths[] = $path.'/modules/'.$this->moduleNameLower;
|
||||
}
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The module namespace to assume when generating URLs to actions.
|
||||
*/
|
||||
protected string $moduleNamespace = 'Modules\MarketingAddon\App\Http\Controllers';
|
||||
|
||||
/**
|
||||
* Called before routes are registered.
|
||||
*
|
||||
* Register any model bindings or pattern based filters.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
*/
|
||||
public function map(): void
|
||||
{
|
||||
$this->mapApiRoutes();
|
||||
|
||||
$this->mapWebRoutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "web" routes for the application.
|
||||
*
|
||||
* These routes all receive session state, CSRF protection, etc.
|
||||
*/
|
||||
protected function mapWebRoutes(): void
|
||||
{
|
||||
Route::middleware('web')
|
||||
->namespace($this->moduleNamespace)
|
||||
->group(module_path('MarketingAddon', '/routes/web.php'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "api" routes for the application.
|
||||
*
|
||||
* These routes are typically stateless.
|
||||
*/
|
||||
protected function mapApiRoutes(): void
|
||||
{
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->moduleNamespace)
|
||||
->group(module_path('MarketingAddon', '/routes/api.php'));
|
||||
}
|
||||
}
|
||||
64
Modules/MarketingAddon/App/Services/NotifyService.php
Normal file
64
Modules/MarketingAddon/App/Services/NotifyService.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Services;
|
||||
|
||||
use Modules\MarketingAddon\App\Models\Template;
|
||||
use Modules\MarketingAddon\App\Models\MessageReport;
|
||||
use Modules\MarketingAddon\App\Jobs\SendNotificationJob;
|
||||
|
||||
class NotifyService
|
||||
{
|
||||
public function send(string $event, array $data, string $businessId)
|
||||
{
|
||||
$settings = get_option('notification_settings_' . $businessId);
|
||||
|
||||
if (!isset($settings[$event])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$array = [];
|
||||
|
||||
foreach (['sms', 'whatsapp', 'email'] as $channel) {
|
||||
|
||||
// Check enabled in settings
|
||||
if (empty($settings[$event][$channel]['enabled'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check required data per channel
|
||||
if (in_array($channel, ['sms', 'whatsapp']) && empty($data['phone'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($channel === 'email' && empty($data['email'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$array[] = $channel;
|
||||
$template = Template::where('business_id', $businessId)->where('title', $event)->where('type', $channel)->first();
|
||||
|
||||
if (empty($template)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$notification = MessageReport::create([
|
||||
'title' => $event,
|
||||
'channel' => $channel,
|
||||
'business_id' => $businessId,
|
||||
'template_id' => $template->id,
|
||||
'message' => $template->message,
|
||||
]);
|
||||
|
||||
// Dispatch job
|
||||
SendNotificationJob::dispatch(
|
||||
data: $data,
|
||||
channel: $channel,
|
||||
template: $template,
|
||||
businessId: $businessId,
|
||||
notificationId: $notification->id,
|
||||
)->onQueue('high');
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
46
Modules/MarketingAddon/App/Services/SmsService.php
Normal file
46
Modules/MarketingAddon/App/Services/SmsService.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Services;
|
||||
|
||||
use Exception;
|
||||
use Modules\MarketingAddon\App\Models\Smsgateway;
|
||||
|
||||
class SmsService
|
||||
{
|
||||
public function send(string $phone, $template, array $data)
|
||||
{
|
||||
$gateways = Smsgateway::where('status', 1)->where('channel', 'sms')->where('business_id', $template->business_id)->get();
|
||||
|
||||
if (empty($gateways)) {
|
||||
throw new \Exception('No active SMS gateway found for this business.');
|
||||
}
|
||||
|
||||
$response = [
|
||||
'status' => false,
|
||||
'message' => 'Something went wrong, Please try again.'
|
||||
];
|
||||
|
||||
$contents = $this->buildContent($template, $data);
|
||||
|
||||
foreach ($gateways as $gateway) {
|
||||
$response = $gateway->namespace::send_message($gateway, $phone, $contents);
|
||||
|
||||
if (($response['status'] ?? false) === true) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception('All SMS gateways failed');
|
||||
}
|
||||
|
||||
protected function buildContent($template, array $data)
|
||||
{
|
||||
$message = $template->message;
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$message = str_replace('[' . $key . ']', $value, $message);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
33
Modules/MarketingAddon/App/Services/WhatsAppService.php
Normal file
33
Modules/MarketingAddon/App/Services/WhatsAppService.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\Services;
|
||||
|
||||
use Modules\MarketingAddon\App\Models\Smsgateway;
|
||||
|
||||
class WhatsAppService
|
||||
{
|
||||
/**
|
||||
* Send WhatsApp campaign via AiSensy
|
||||
*/
|
||||
public function send(
|
||||
string $phone,
|
||||
object $template,
|
||||
int $businessId,
|
||||
string $name,
|
||||
array $data = [],
|
||||
) {
|
||||
|
||||
$gateways = Smsgateway::where('channel', 'whatsapp')
|
||||
->where('business_id', $businessId)
|
||||
->where('status', 1)
|
||||
->get();
|
||||
|
||||
if (empty($gateways)) {
|
||||
throw new \Exception('No active WhatsApp gateway found for this business.');
|
||||
}
|
||||
|
||||
foreach ($gateways as $gateway) {
|
||||
$gateway->namespace::send_message($gateway, $phone, $template, $name, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Modules/MarketingAddon/App/SmsGateways/AcnooSms.php
Normal file
30
Modules/MarketingAddon/App/SmsGateways/AcnooSms.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\SmsGateways;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class AcnooSms
|
||||
{
|
||||
public static function send_message($gateway, $numbers, $contents)
|
||||
{
|
||||
$response = Http::post('https://acnoosms.com/send-message', [
|
||||
'client_id' => $gateway->params['client_id'] ?? '',
|
||||
'client_secret' => $gateway->params['client_secret'] ?? '',
|
||||
'gateway_id' => $gateway->params['gateway_id'] ?? '',
|
||||
'type' => 0,
|
||||
'content' => $contents,
|
||||
'numbers' => $numbers,
|
||||
]);
|
||||
|
||||
if ($response['status']) {
|
||||
return [
|
||||
'status' => true,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
21
Modules/MarketingAddon/App/SmsGateways/HaDavar.php
Normal file
21
Modules/MarketingAddon/App/SmsGateways/HaDavar.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\SmsGateways;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class HaDavar
|
||||
{
|
||||
public static function send_message($gateway, $numbers, $contents, $sender_name = null)
|
||||
{
|
||||
$response = Http::asForm()->{$gateway->params['api_method']}($gateway->params['api_url'], array_merge([
|
||||
'Username' => $gateway->params['username'],
|
||||
'Token' => $gateway->params['token'],
|
||||
'Sender' => $sender_name ?? $gateway->params['sender'],
|
||||
$gateway->params['number_key'] => $numbers,
|
||||
$gateway->params['message_key'] => $contents,
|
||||
]));
|
||||
|
||||
return $response->body();
|
||||
}
|
||||
}
|
||||
30
Modules/MarketingAddon/App/SmsGateways/RouteMobile.php
Normal file
30
Modules/MarketingAddon/App/SmsGateways/RouteMobile.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\SmsGateways;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class RouteMobile
|
||||
{
|
||||
public static function send_message($gateway, $numbers, $contents, $sender_name = null)
|
||||
{
|
||||
$response = Http::asForm()->{$gateway->params['api_method']}($gateway->params['api_url'], array_merge([
|
||||
'dlr' => $gateway->params['dlr'],
|
||||
'type' => $gateway->params['type'],
|
||||
'source' => $sender_name ?? $gateway->params['source'],
|
||||
$gateway->params['number_key'] => $numbers,
|
||||
'username' => $gateway->params['username'],
|
||||
'password' => $gateway->params['password'],
|
||||
$gateway->params['message_key'] => $contents,
|
||||
]));
|
||||
|
||||
$words = explode('|', $response);
|
||||
$firstFourWords = array_slice($words, 0, 4);
|
||||
|
||||
if ($firstFourWords[0] == 1701) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Modules/MarketingAddon/App/SmsGateways/TzskSms.php
Normal file
21
Modules/MarketingAddon/App/SmsGateways/TzskSms.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\SmsGateways;
|
||||
|
||||
class TzskSms
|
||||
{
|
||||
public static function send_message($gateway, $numbers, $contents)
|
||||
{
|
||||
$numbers = is_array($numbers) ? $numbers : explode(',', $numbers);
|
||||
|
||||
foreach ($gateway->params as $key => $param) {
|
||||
config(['sms.drivers.' . $gateway->driver . '.' . $key => $param]);
|
||||
}
|
||||
|
||||
sms()->via($gateway->driver)->send($contents, function ($sms) use ($numbers) {
|
||||
$sms->to($numbers);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
99
Modules/MarketingAddon/App/WhatsappGateways/AiSensy.php
Normal file
99
Modules/MarketingAddon/App/WhatsappGateways/AiSensy.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\WhatsappGateways;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class AiSensy
|
||||
{
|
||||
public static function send_message($gateway, $phone, $template, $name, $data)
|
||||
{
|
||||
$payload = [
|
||||
'apiKey' => $gateway->params['api_key'],
|
||||
'campaignName' => $template->template,
|
||||
'destination' => $phone,
|
||||
'userName' => $name,
|
||||
'templateParams' => self::buildParams($template, $data),
|
||||
'buttons' => self::aiSensyButtons($data['copy_text'] ?? ''),
|
||||
'media' => self::buildMedia($template),
|
||||
];
|
||||
|
||||
// 🔍 Log payload before sending
|
||||
\Illuminate\Support\Facades\Log::info('AiSensy Payload', $payload);
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'Content-Type' => 'application/json',
|
||||
])->post(
|
||||
'https://backend.aisensy.com/campaign/t1/api/v2',
|
||||
$payload
|
||||
);
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
throw new \Exception(
|
||||
'AiSensy Error: ' . $response->body()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert named data → positional params (WhatsApp only)
|
||||
*/
|
||||
protected static function buildParams($template, array $data): array
|
||||
{
|
||||
if (empty($template->variables) || !is_array($template->variables)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$params = [];
|
||||
|
||||
foreach ($template->variables as $variable) {
|
||||
// Skip button-related vars
|
||||
if (in_array($variable, ['copy_text', 'expire_at'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($data[$variable])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$params[] = $data[$variable];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
protected static function aiSensyButtons(? string $copyText = null)
|
||||
{
|
||||
if (!$copyText) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
[
|
||||
'type' => 'button',
|
||||
'sub_type' => 'url',
|
||||
'index' => 0,
|
||||
'parameters' => [
|
||||
[
|
||||
'type' => 'text',
|
||||
'text' => $copyText ?? ''
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
protected static function buildMedia($template): ?array
|
||||
{
|
||||
if (empty($template?->attachment)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'url' => asset($template->attachment),
|
||||
'filename' => 'sample_media',
|
||||
];
|
||||
}
|
||||
}
|
||||
161
Modules/MarketingAddon/App/WhatsappGateways/WhatsAppCloudApi.php
Normal file
161
Modules/MarketingAddon/App/WhatsappGateways/WhatsAppCloudApi.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MarketingAddon\App\WhatsappGateways;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class WhatsAppCloudApi
|
||||
{
|
||||
public static function send_message($gateway, $phone, $template, $name, $data)
|
||||
{
|
||||
$payload = [
|
||||
'messaging_product' => 'whatsapp',
|
||||
'recipient_type' => 'individual',
|
||||
'to' => $phone,
|
||||
'type' => 'template',
|
||||
'template' => [
|
||||
'name' => $template->template,
|
||||
'language' => [
|
||||
'code' => $gateway->params['language_code'] ?? 'en_US',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$components = self::buildComponents($template, $data);
|
||||
|
||||
if (!empty($components)) {
|
||||
$payload['template']['components'] = $components;
|
||||
}
|
||||
|
||||
$apiVersion = $gateway->params['api_version'] ?? 'v25.0';
|
||||
$phoneNumberId = $gateway->params['phone_number_id'];
|
||||
$url = "https://graph.facebook.com/{$apiVersion}/{$phoneNumberId}/messages";
|
||||
|
||||
$response = Http::withToken($gateway->params['access_token'])
|
||||
->withHeaders([
|
||||
'Content-Type' => 'application/json',
|
||||
])
|
||||
->post($url, $payload);
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
throw new \Exception(
|
||||
'WhatsApp Cloud API Error: ' . $response->body()
|
||||
);
|
||||
}
|
||||
|
||||
protected static function buildComponents($template, array $data): array
|
||||
{
|
||||
$components = [];
|
||||
$header = self::buildHeader($template);
|
||||
$body = self::buildBody($template, $data);
|
||||
$button = self::buildButton($data['copy_text'] ?? null);
|
||||
|
||||
if (!empty($header)) {
|
||||
$components[] = $header;
|
||||
}
|
||||
|
||||
if (!empty($body)) {
|
||||
$components[] = $body;
|
||||
}
|
||||
|
||||
if (!empty($button)) {
|
||||
$components[] = $button;
|
||||
}
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
protected static function buildBody($template, array $data): ?array
|
||||
{
|
||||
if (empty($template->variables) || !is_array($template->variables)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parameters = [];
|
||||
|
||||
foreach ($template->variables as $variable) {
|
||||
if (in_array($variable, ['copy_text', 'expire_at'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($data[$variable])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parameters[] = [
|
||||
'type' => 'text',
|
||||
'text' => (string) $data[$variable],
|
||||
];
|
||||
}
|
||||
|
||||
if (empty($parameters)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'type' => 'body',
|
||||
'parameters' => $parameters,
|
||||
];
|
||||
}
|
||||
|
||||
protected static function buildHeader($template): ?array
|
||||
{
|
||||
if (empty($template?->attachment)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$mediaType = self::mediaType($template->attachment);
|
||||
$media = ['link' => asset($template->attachment)];
|
||||
|
||||
if ($mediaType === 'document') {
|
||||
$media['filename'] = basename($template->attachment);
|
||||
}
|
||||
|
||||
return [
|
||||
'type' => 'header',
|
||||
'parameters' => [
|
||||
[
|
||||
'type' => $mediaType,
|
||||
$mediaType => $media,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected static function buildButton(?string $copyText = null): ?array
|
||||
{
|
||||
if (!$copyText) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'type' => 'button',
|
||||
'sub_type' => 'url',
|
||||
'index' => 0,
|
||||
'parameters' => [
|
||||
[
|
||||
'type' => 'text',
|
||||
'text' => $copyText,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected static function mediaType(string $attachment): string
|
||||
{
|
||||
$extension = strtolower(pathinfo($attachment, PATHINFO_EXTENSION));
|
||||
|
||||
if (in_array($extension, ['jpg', 'jpeg', 'png', 'webp'])) {
|
||||
return 'image';
|
||||
}
|
||||
|
||||
if (in_array($extension, ['mp4', '3gp'])) {
|
||||
return 'video';
|
||||
}
|
||||
|
||||
return 'document';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user